Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 2712ccc

Browse filesBrowse files
committed
improve css modules hot-reload
1 parent e6daf96 commit 2712ccc
Copy full SHA for 2712ccc

File tree

2 files changed

+59
-26
lines changed
Filter options

2 files changed

+59
-26
lines changed

‎lib/component-normalizer.js

Copy file name to clipboardExpand all lines: lib/component-normalizer.js
+2-14Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ module.exports = function normalizeComponent (
4545
}
4646
// inject component styles
4747
if (injectStyles) {
48-
injectStyles(this, context)
48+
injectStyles.call(this, context)
4949
}
5050
// register component module identifier for async chunk inferrence
5151
if (context && context._registeredComponents) {
@@ -56,19 +56,7 @@ module.exports = function normalizeComponent (
5656
// never gets called
5757
options._ssrRegister = hook
5858
} else if (injectStyles) {
59-
var injected = false // only need to inject once on the client
60-
var cssModules
61-
hook = function () {
62-
if (!injected) {
63-
injected = true
64-
cssModules = injectStyles({})
65-
}
66-
if (cssModules) {
67-
for (var key in cssModules) {
68-
this[key] = cssModules[key]
69-
}
70-
}
71-
}
59+
hook = injectStyles
7260
}
7361

7462
if (hook) {

‎lib/loader.js

Copy file name to clipboardExpand all lines: lib/loader.js
+57-12Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -278,12 +278,23 @@ module.exports = function (content) {
278278
var output = ''
279279
var parts = parse(content, fileName, this.sourceMap)
280280
var hasScoped = parts.styles.some(function (s) { return s.scoped })
281+
var needsHotReload = (
282+
!isServer &&
283+
!isProduction &&
284+
(parts.script || parts.template)
285+
)
286+
287+
if (needsHotReload) {
288+
output += 'var disposed = false\n'
289+
}
281290

282291
// add requires for styles
283292
var cssModules
284293
if (parts.styles.length) {
285-
var styleInjectionCode =
286-
'function injectStyle (cssModuleTarget, ssrContext) {\n'
294+
var styleInjectionCode = 'function injectStyle (ssrContext) {\n'
295+
if (needsHotReload) {
296+
styleInjectionCode += ` if (disposed) return\n`
297+
}
287298
parts.styles.forEach(function (style, i) {
288299
// require style
289300
var requireString = style.src
@@ -301,7 +312,12 @@ module.exports = function (content) {
301312
var moduleName = (style.module === true) ? '$style' : style.module
302313
// setCssModule
303314
if (moduleName) {
304-
cssModules = cssModules || {}
315+
if (!cssModules) {
316+
cssModules = {}
317+
if (needsHotReload) {
318+
output += `var cssModules = {}\n`
319+
}
320+
}
305321
if (moduleName in cssModules) {
306322
loaderContext.emitError('CSS module name "' + moduleName + '" is not unique!')
307323
styleInjectionCode += invokeStyle(requireString)
@@ -315,15 +331,36 @@ module.exports = function (content) {
315331
requireString += '.locals'
316332
}
317333

318-
styleInjectionCode += invokeStyle('cssModuleTarget["' + moduleName + '"] = ' + requireString)
334+
if (!needsHotReload) {
335+
styleInjectionCode += invokeStyle('this["' + moduleName + '"] = ' + requireString)
336+
} else {
337+
// handle hot reload for CSS modules.
338+
// we store the exported locals in an object and proxy to it by
339+
// defining getters inside component instances' lifecycle hook.
340+
styleInjectionCode +=
341+
invokeStyle(`cssModules["${moduleName}"] = ${requireString}`) +
342+
`Object.defineProperty(this, "${moduleName}", { get: function () { return cssModules["${moduleName}"] }})\n`
343+
344+
var requirePath = getRequireString('styles', style, i, style.scoped)
345+
output +=
346+
`module.hot && module.hot.accept([${requirePath}], function () {\n` +
347+
// 1. check if style has been injected
348+
` var oldLocals = cssModules["${moduleName}"]\n` +
349+
` if (!oldLocals) return\n` +
350+
// 2. re-import (side effect: updates the <style>)
351+
` var newLocals = ${requireString}\n` +
352+
// 3. compare new and old locals to see if selectors changed
353+
` if (JSON.stringify(newLocals) === JSON.stringify(oldLocals)) return\n` +
354+
// 4. locals changed. Update and force re-render.
355+
` cssModules["${moduleName}"] = newLocals\n` +
356+
` require("${hotReloadAPIPath}").rerender("${moduleId}")\n` +
357+
`})\n`
358+
}
319359
}
320360
} else {
321361
styleInjectionCode += invokeStyle(requireString)
322362
}
323363
})
324-
if (cssModules) {
325-
styleInjectionCode += ' return cssModuleTarget\n'
326-
}
327364
styleInjectionCode += '}\n'
328365
output += styleInjectionCode
329366
}
@@ -435,11 +472,7 @@ module.exports = function (content) {
435472

436473
if (!query.inject) {
437474
// hot reload
438-
if (
439-
!isServer &&
440-
!isProduction &&
441-
(parts.script || parts.template)
442-
) {
475+
if (needsHotReload) {
443476
output +=
444477
'\n/* hot reload */\n' +
445478
'if (module.hot) {(function () {\n' +
@@ -452,9 +485,21 @@ module.exports = function (content) {
452485
' hotAPI.createRecord("' + moduleId + '", Component.options)\n' +
453486
' } else {\n'
454487
// update
488+
if (cssModules) {
489+
output +=
490+
' if (module.hot.data.cssModules && Object.keys(module.hot.data.cssModules) !== Object.keys(cssModules)) {\n' +
491+
' delete Component.options._Ctor\n' +
492+
' }\n'
493+
}
455494
output +=
456495
' hotAPI.reload("' + moduleId + '", Component.options)\n' +
457496
' }\n'
497+
// dispose
498+
output +=
499+
' module.hot.dispose(function (data) {\n' +
500+
(cssModules ? ' data.cssModules = cssModules\n' : '') +
501+
' disposed = true\n' +
502+
' })\n'
458503
output += '})()}\n'
459504
}
460505
// final export

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.