vue-hot-reload-api
Advanced tools
Comparing version
266
index.js
var Vue // late bind | ||
var map = Object.create(null) | ||
var shimmed = false | ||
var map = window.__VUE_HOT_MAP__ = Object.create(null) | ||
var installed = false | ||
var isBrowserify = false | ||
/** | ||
* Determine compatibility and apply patch. | ||
* | ||
* @param {Function} vue | ||
* @param {Boolean} browserify | ||
*/ | ||
exports.install = function (vue, browserify) { | ||
if (shimmed) return | ||
shimmed = true | ||
if (installed) return | ||
installed = true | ||
@@ -20,81 +13,13 @@ Vue = vue | ||
exports.compatible = !!Vue.internalDirectives | ||
exports.compatible = Number(Vue.version.split('.')[0]) >= 2 | ||
if (!exports.compatible) { | ||
console.warn( | ||
'[HMR] vue-loader hot reload is only compatible with ' + | ||
'Vue.js 1.0.0+.' | ||
'[HMR] You are using a version of vue-hot-reload-api that is ' + | ||
'only compatible with Vue.js core ^2.0.0.' | ||
) | ||
return | ||
} | ||
// patch view directive | ||
patchView(Vue.internalDirectives.component) | ||
console.log('[HMR] Vue component hot reload shim applied.') | ||
// shim router-view if present | ||
var routerView = Vue.elementDirective('router-view') | ||
if (routerView) { | ||
patchView(routerView) | ||
console.log('[HMR] vue-router <router-view> hot reload shim applied.') | ||
} | ||
} | ||
/** | ||
* Shim the view directive (component or router-view). | ||
* | ||
* @param {Object} View | ||
*/ | ||
function patchView (View) { | ||
var unbuild = View.unbuild | ||
View.unbuild = function (defer) { | ||
if (!this.hotUpdating) { | ||
var prevComponent = this.childVM && this.childVM.constructor | ||
removeView(prevComponent, this) | ||
// defer = true means we are transitioning to a new | ||
// Component. Register this new component to the list. | ||
if (defer) { | ||
addView(this.Component, this) | ||
} | ||
} | ||
// call original | ||
return unbuild.call(this, defer) | ||
} | ||
} | ||
/** | ||
* Add a component view to a Component's hot list | ||
* | ||
* @param {Function} Component | ||
* @param {Directive} view - view directive instance | ||
*/ | ||
function addView (Component, view) { | ||
var id = Component && Component.options.hotID | ||
if (id) { | ||
if (!map[id]) { | ||
map[id] = { | ||
Component: Component, | ||
views: [], | ||
instances: [] | ||
} | ||
} | ||
map[id].views.push(view) | ||
} | ||
} | ||
/** | ||
* Remove a component view from a Component's hot list | ||
* | ||
* @param {Function} Component | ||
* @param {Directive} view - view directive instance | ||
*/ | ||
function removeView (Component, view) { | ||
var id = Component && Component.options.hotID | ||
if (id) { | ||
map[id].views.$remove(view) | ||
} | ||
} | ||
/** | ||
* Create a record for a hot module, which keeps track of its construcotr, | ||
@@ -114,4 +39,3 @@ * instnaces and views (component directives or router-views). | ||
map[id] = { | ||
Component: null, | ||
views: [], | ||
Ctor: null, | ||
instances: [] | ||
@@ -130,7 +54,6 @@ } | ||
function makeOptionsHot (id, options) { | ||
options.hotID = id | ||
injectHook(options, 'created', function () { | ||
injectHook(options, 'init', function () { | ||
var record = map[id] | ||
if (!record.Component) { | ||
record.Component = this.constructor | ||
if (!record.Ctor) { | ||
record.Ctor = this.constructor | ||
} | ||
@@ -140,3 +63,4 @@ record.instances.push(this) | ||
injectHook(options, 'beforeDestroy', function () { | ||
map[id].instances.$remove(this) | ||
var instances = map[id].instances | ||
instances.splice(instances.indexOf(this), 1) | ||
}) | ||
@@ -163,141 +87,37 @@ } | ||
/** | ||
* Update a hot component. | ||
* | ||
* @param {String} id | ||
* @param {Object|null} newOptions | ||
* @param {String|null} newTemplate | ||
*/ | ||
exports.update = function (id, newOptions, newTemplate) { | ||
var record = map[id] | ||
// force full-reload if an instance of the component is active but is not | ||
// managed by a view | ||
if (!record || (record.instances.length && !record.views.length)) { | ||
console.log('[HMR] Root or manually-mounted instance modified. Full reload may be required.') | ||
if (!isBrowserify) { | ||
window.location.reload() | ||
} else { | ||
// browserify-hmr somehow sends incomplete bundle if we reload here | ||
return | ||
function tryWrap (fn) { | ||
return function (id, arg) { | ||
try { fn(id, arg) } catch (e) { | ||
console.error(e) | ||
console.warn('Something went wrong during Vue component hot-reload. Full reload required.') | ||
} | ||
} | ||
if (!isBrowserify) { | ||
// browserify-hmr already logs this | ||
console.log('[HMR] Updating component: ' + format(id)) | ||
} | ||
var Component = record.Component | ||
// update constructor | ||
if (newOptions) { | ||
// in case the user exports a constructor | ||
Component = record.Component = typeof newOptions === 'function' | ||
? newOptions | ||
: Vue.extend(newOptions) | ||
makeOptionsHot(id, Component.options) | ||
} | ||
if (newTemplate) { | ||
Component.options.template = newTemplate | ||
} | ||
// handle recursive lookup | ||
if (Component.options.name) { | ||
Component.options.components[Component.options.name] = Component | ||
} | ||
// reset constructor cached linker | ||
Component.linker = null | ||
// reload all views | ||
record.views.forEach(function (view) { | ||
updateView(view, Component) | ||
}) | ||
// flush devtools | ||
if (window.__VUE_DEVTOOLS_GLOBAL_HOOK__) { | ||
window.__VUE_DEVTOOLS_GLOBAL_HOOK__.emit('flush') | ||
} | ||
} | ||
/** | ||
* Update a component view instance | ||
* | ||
* @param {Directive} view | ||
* @param {Function} Component | ||
*/ | ||
exports.rerender = tryWrap(function (id, fns) { | ||
var record = map[id] | ||
record.Ctor.options.render = fns.render | ||
record.Ctor.options.staticRenderFns = fns.staticRenderFns | ||
record.instances.slice().forEach(function (instance) { | ||
instance.$options.render = fns.render | ||
instance.$options.staticRenderFns = fns.staticRenderFns | ||
instance._staticTrees = null // reset static trees | ||
instance.$forceUpdate() | ||
}) | ||
}) | ||
function updateView (view, Component) { | ||
if (!view._bound) { | ||
return | ||
} | ||
view.Component = Component | ||
view.hotUpdating = true | ||
// disable transitions | ||
view.vm._isCompiled = false | ||
// save state | ||
var state = extractState(view.childVM) | ||
// remount, make sure to disable keep-alive | ||
var keepAlive = view.keepAlive | ||
view.keepAlive = false | ||
view.mountComponent() | ||
view.keepAlive = keepAlive | ||
// restore state | ||
restoreState(view.childVM, state, true) | ||
// re-eanble transitions | ||
view.vm._isCompiled = true | ||
view.hotUpdating = false | ||
} | ||
/** | ||
* Extract state from a Vue instance. | ||
* | ||
* @param {Vue} vm | ||
* @return {Object} | ||
*/ | ||
function extractState (vm) { | ||
return { | ||
cid: vm.constructor.cid, | ||
data: vm.$data, | ||
children: vm.$children.map(extractState) | ||
} | ||
} | ||
/** | ||
* Restore state to a reloaded Vue instance. | ||
* | ||
* @param {Vue} vm | ||
* @param {Object} state | ||
*/ | ||
function restoreState (vm, state, isRoot) { | ||
var oldAsyncConfig | ||
if (isRoot) { | ||
// set Vue into sync mode during state rehydration | ||
oldAsyncConfig = Vue.config.async | ||
Vue.config.async = false | ||
} | ||
// actual restore | ||
if (isRoot || !vm._props) { | ||
vm.$data = state.data | ||
} else { | ||
Object.keys(state.data).forEach(function (key) { | ||
if (!vm._props[key]) { | ||
// for non-root, only restore non-props fields | ||
vm.$data[key] = state.data[key] | ||
} | ||
}) | ||
} | ||
// verify child consistency | ||
var hasSameChildren = vm.$children.every(function (c, i) { | ||
return state.children[i] && state.children[i].cid === c.constructor.cid | ||
exports.reload = tryWrap(function (id, options) { | ||
makeOptionsHot(id, options) | ||
var record = map[id] | ||
var newCtor = Vue.extend(options) | ||
record.Ctor.options = newCtor.options | ||
record.Ctor.cid = newCtor.cid | ||
newCtor.release() | ||
record.instances.slice().forEach(function (instance) { | ||
if (instance.$parent) { | ||
instance.$parent.$forceUpdate() | ||
} else { | ||
console.warn('Root or manually mounted instance modified. Full reload required.') | ||
} | ||
}) | ||
if (hasSameChildren) { | ||
// rehydrate children | ||
vm.$children.forEach(function (c, i) { | ||
restoreState(c, state.children[i]) | ||
}) | ||
} | ||
if (isRoot) { | ||
Vue.config.async = oldAsyncConfig | ||
} | ||
} | ||
function format (id) { | ||
return id.match(/[^\/]+\.vue$/)[0] | ||
} | ||
}) |
{ | ||
"name": "vue-hot-reload-api", | ||
"version": "1.3.2", | ||
"version": "2.0.0", | ||
"description": "hot reload api for *.vue components", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
4
100%0
-100%8
Infinity%4718
-37.35%106
-61.03%1
Infinity%