Skip to content

Commit 58be428

Browse files
committed
hot reload: extract more into the api
1 parent eeae8d7 commit 58be428

File tree

2 files changed

+88
-39
lines changed

2 files changed

+88
-39
lines changed

lib/hot-reload-api.js

Lines changed: 82 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@
55
* - add self to current component's list
66
*/
77

8+
var Vue
9+
var map = Object.create(null)
810
var shimmed = false
9-
exports.install = function (Vue) {
11+
12+
exports.install = function (vue) {
1013
if (shimmed) return
1114
shimmed = true
12-
15+
16+
Vue = vue
1317
exports.compatible = !!Vue.internalDirectives
1418
if (!exports.compatible) {
1519
console.warn(
@@ -19,15 +23,13 @@ exports.install = function (Vue) {
1923
return
2024
}
2125

22-
// create global hot reload map
23-
var map = Vue.config._hotComponents = Object.create(null)
2426
// shim component
25-
shimComponent(Vue.internalDirectives.component, map)
27+
shimComponent(Vue.internalDirectives.component)
2628
console.log('[HMR] vue component hot reload shim applied.')
2729
// shim router-view if present
2830
var routerView = Vue.elementDirective('router-view')
2931
if (routerView) {
30-
shimComponent(routerView, map)
32+
shimComponent(routerView)
3133
console.log('[HMR] vue-router <router-view> hot reload shim applied.')
3234
}
3335
}
@@ -36,18 +38,17 @@ exports.install = function (Vue) {
3638
* Shim the component directive.
3739
*
3840
* @param {Object} dir
39-
* @param {Object} map - hot component map
4041
*/
4142

42-
function shimComponent (dir, map) {
43+
function shimComponent (dir) {
4344
shimMethod(dir, 'unbuild', function (defer) {
4445
if (!this.hotUpdating) {
4546
var prevComponent = this.childVM && this.childVM.constructor
46-
removeComponent(prevComponent, map, this)
47+
removeComponent(prevComponent, this)
4748
// defer = true means we are transitioning to a new
4849
// Component. Register this new component to the list.
4950
if (defer) {
50-
addComponent(this.Component, map, this)
51+
addComponent(this.Component, this)
5152
}
5253
}
5354
})
@@ -73,36 +74,99 @@ function shimMethod (dir, methodName, fn) {
7374
* Remove a component view from a Component's hot list
7475
*
7576
* @param {Function} Component
76-
* @param {Object} map - hot component map
7777
* @param {Directive} view - view directive instance
7878
*/
7979

80-
function removeComponent (Component, map, view) {
80+
function removeComponent (Component, view) {
8181
var id = Component && Component.options.hotID
8282
if (id) {
83-
map[id].instances.$remove(view)
83+
map[id].views.$remove(view)
8484
}
8585
}
8686

8787
/**
8888
* Add a component view to a Component's hot list
8989
*
9090
* @param {Function} Component
91-
* @param {Object} map - hot component map
9291
* @param {Directive} view - view directive instance
9392
*/
9493

95-
function addComponent (Component, map, view) {
94+
function addComponent (Component, view) {
9695
var id = Component && Component.options.hotID
9796
if (id) {
9897
if (!map[id]) {
9998
map[id] = {
100-
Ctor: Component,
99+
Component: Component,
100+
views: [],
101101
instances: []
102102
}
103103
}
104-
map[id].instances.push(view)
104+
map[id].views.push(view)
105+
}
106+
}
107+
108+
/**
109+
* Create a record for a hot module, which keeps track of its construcotr,
110+
* instnaces and views (component directives or router-views).
111+
*
112+
* @param {String} id
113+
* @param {Object} options
114+
*/
115+
116+
exports.createRecord = function (id, options) {
117+
if (typeof options.el !== 'string' && typeof options.data !== 'object') {
118+
var add = function () {
119+
map[id].instances.push(this)
120+
}
121+
var remove = function () {
122+
map[id].instances.$remove(this)
123+
}
124+
options.created = options.created
125+
? [options.created, add]
126+
: add
127+
options.beforeDestroy = options.beforeDestroy
128+
? [options.beforeDestroy, remove]
129+
: remove
130+
map[id] = {
131+
Component: Vue.extend(options),
132+
views: [],
133+
instances: []
134+
}
135+
}
136+
}
137+
138+
/**
139+
* Update a hot component.
140+
*
141+
* @param {String} id
142+
* @param {Object|null} newOptions
143+
* @param {String|null} newTemplate
144+
*/
145+
146+
exports.update = function (id, newOptions, newTemplate) {
147+
var record = map[id]
148+
// force full-reload if an instance of the component is active but is not
149+
// managed by a view
150+
if (!record || (record.instances.length && !record.views.length)) {
151+
throw new Error('Root or manually-mounted instance modified. Full reload is required.')
152+
}
153+
var Component = record.Component
154+
// update constructor
155+
if (newOptions) {
156+
Component.options = Vue.util.mergeOptions(Vue.options, newOptions)
157+
}
158+
if (newTemplate) {
159+
Component.options.template = newTemplate
160+
}
161+
// handle recursive lookup
162+
if (Component.options.name) {
163+
console.log(Component.options)
164+
Component.options.components[Component.options.name] = Component
105165
}
166+
// reset constructor cached linker
167+
Component.linker = null
168+
// reload all views
169+
record.views.forEach(updateView)
106170
}
107171

108172
/**
@@ -111,7 +175,7 @@ function addComponent (Component, map, view) {
111175
* @param {Directive} view
112176
*/
113177

114-
exports.update = function (view) {
178+
function updateView (view) {
115179
if (!view._bound) {
116180
return
117181
}

lib/loader.js

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -137,31 +137,16 @@ module.exports = function (content) {
137137
'(function () {\n' +
138138
// shim the component directive so that it
139139
// registers the instances
140-
'var Vue = require("vue")\n' +
141140
'var hotAPI = require("' + hotReloadAPIPath + '")\n' +
142-
'hotAPI.install(Vue)\n' +
141+
'hotAPI.install(require("vue"))\n' +
143142
'if (!hotAPI.compatible) return\n' +
144-
'var map = Vue.config._hotComponents\n' +
145143
'var id = module.exports.hotID = ' + (scriptString || templateString) + '\n' +
146-
// store the record
147-
'if (typeof module.exports.el !== "string" && typeof module.exports.data !== "object") {\n' +
148-
'map[id] = { Ctor: Vue.extend(module.exports), instances: [] }\n' +
149-
'}\n' +
144+
// create the record
145+
'hotAPI.createRecord(id, module.exports)\n' +
150146
'module.hot.accept(' + JSON.stringify(accepted) + ', function () {\n' +
151-
'if (!map[id]) {\n' +
152-
'throw new Error("Root or manually-mounted instance modified. Full reload is required.")\n' +
153-
'}\n' +
154-
'var Ctor = map[id].Ctor\n' +
155-
// overwrite existing constructor's options
156-
(scriptString ? 'Ctor.options = Vue.util.mergeOptions(Vue.options, require(' + scriptString + '))\n' : '') +
157-
(templateString ? 'Ctor.options.template = require(' + templateString + ')\n' : '') +
158-
// handle recursive names
159-
'if (Ctor.options.name) Ctor.options.components[Ctor.options.name] = Ctor\n' +
160-
// reset linker
161-
'Ctor.linker = null\n' +
162-
// reload directive instances
163-
'map[id].instances.forEach(hotAPI.update)\n' +
164-
// re-enable transitions
147+
'var newOptions = ' + (scriptString ? 'require(' + scriptString + ')\n' : 'null\n') +
148+
'var newTemplate = ' + (templateString ? 'require(' + templateString + ')\n' : 'null\n') +
149+
'hotAPI.update(id, newOptions, newTemplate)\n' +
165150
'})\n' +
166151
'})()\n' +
167152
'}'

0 commit comments

Comments
 (0)