Vue数据变化后页面更新详细介绍
作者:Young soul2
这篇文章主要介绍了Vue在数据发生变化后是如何更新页面的,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
首先会通过module.hot.accept监听文件变化,并传入该文件的渲染函数:
module.hot.accept(/*! ./App.vue?vue&type=template&id=472cff63&scoped=true& */ "./App.vue?vue&type=template&id=472cff63&scoped=true&", __WEBPACK_OUTDATED_DEPENDENCIES__ => { /* harmony import */ _App_vue_vue_type_template_id_472cff63_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./App.vue?vue&type=template&id=472cff63&scoped=true& */ "./App.vue?vue&type=template&id=472cff63&scoped=true&"); (function () { api.rerender('472cff63', { render: _App_vue_vue_type_template_id_472cff63_scoped_true___WEBPACK_IMPORTED_MODULE_0__.render, staticRenderFns: _App_vue_vue_type_template_id_472cff63_scoped_true___WEBPACK_IMPORTED_MODULE_0__.staticRenderFns }) })(__WEBPACK_OUTDATED_DEPENDENCIES__); })
随后执行rerender方法,只贴重点部分代码:
record.Ctor.options.render = options.render record.Ctor.options.staticRenderFns = options.staticRenderFns record.instances.slice().forEach(function (instance) { instance.$options.render = options.render instance.$options.staticRenderFns = options.staticRenderFns // reset static trees // pre 2.5, all static trees are cached together on the instance if (instance._staticTrees) { instance._staticTrees = [] } // 2.5.0 if (Array.isArray(record.Ctor.options.cached)) { record.Ctor.options.cached = [] } // 2.5.3 if (Array.isArray(instance.$options.cached)) { instance.$options.cached = [] } // post 2.5.4: v-once trees are cached on instance._staticTrees. // Pure static trees are cached on the staticRenderFns array // (both already reset above) // 2.6: temporarily mark rendered scoped slots as unstable so that // child components can be forced to update var restore = patchScopedSlots(instance) instance.$forceUpdate() instance.$nextTick(restore) })
首先会给当前的VueComponent的Options和实例里面的渲染函数替换为最新,然后执行实例上的forceUpdate方法:
Vue.prototype.$forceUpdate = function () { var vm = this; if (vm._watcher) { vm._watcher.update(); } };
该方法是执行watcher实例的update方法:
Watcher.prototype.update = function () { /* istanbul ignore else */ if (this.lazy) { this.dirty = true; } else if (this.sync) { this.run(); } else { queueWatcher(this); } };
function queueWatcher(watcher) { var id = watcher.id; if (has[id] != null) { return; } if (watcher === Dep.target && watcher.noRecurse) { return; } has[id] = true; if (!flushing) { queue.push(watcher); } else { // if already flushing, splice the watcher based on its id // if already past its id, it will be run next immediately. var i = queue.length - 1; while (i > index$1 && queue[i].id > watcher.id) { i--; } queue.splice(i + 1, 0, watcher); } // queue the flush if (!waiting) { waiting = true; if (!config.async) { flushSchedulerQueue(); return; } nextTick(flushSchedulerQueue); } }
重点看 queueWatcher(this),将最新的watcher放入quene队列并且将flushSchedulerQueue函数传给nextTick。
function nextTick(cb, ctx) { var _resolve; callbacks.push(function () { if (cb) { try { cb.call(ctx); } catch (e) { handleError(e, ctx, 'nextTick'); } } else if (_resolve) { _resolve(ctx); } }); if (!pending) { pending = true; timerFunc(); } // $flow-disable-line if (!cb && typeof Promise !== 'undefined') { return new Promise(function (resolve) { _resolve = resolve; }); } }
在callbacks里面放入一个执行回调的函数。并执行timerFunc():
timerFunc = function () { p_1.then(flushCallbacks); // In problematic UIWebViews, Promise.then doesn't completely break, but // it can get stuck in a weird state where callbacks are pushed into the // microtask queue but the queue isn't being flushed, until the browser // needs to do some other work, e.g. handle a timer. Therefore we can // "force" the microtask queue to be flushed by adding an empty timer. if (isIOS) setTimeout(noop); };
该方法异步执行flushCallbacks:
function flushCallbacks() { pending = false; var copies = callbacks.slice(0); callbacks.length = 0; for (var i = 0; i < copies.length; i++) { copies[i](); } }
开始执行回调函数我们第一次放进去的函数flushSchedulerQueue:
function flushSchedulerQueue() { currentFlushTimestamp = getNow(); flushing = true; var watcher, id; // Sort queue before flush. // This ensures that: // 1. Components are updated from parent to child. (because parent is always // created before the child) // 2. A component's user watchers are run before its render watcher (because // user watchers are created before the render watcher) // 3. If a component is destroyed during a parent component's watcher run, // its watchers can be skipped. queue.sort(sortCompareFn); // do not cache length because more watchers might be pushed // as we run existing watchers for (index$1 = 0; index$1 < queue.length; index$1++) { watcher = queue[index$1]; if (watcher.before) { watcher.before(); } id = watcher.id; has[id] = null; watcher.run(); // in dev build, check and stop circular updates. if (has[id] != null) { circular[id] = (circular[id] || 0) + 1; if (circular[id] > MAX_UPDATE_COUNT) { warn$2('You may have an infinite update loop ' + (watcher.user ? "in watcher with expression \"".concat(watcher.expression, "\"") : "in a component render function."), watcher.vm); break; } } } // keep copies of post queues before resetting state var activatedQueue = activatedChildren.slice(); var updatedQueue = queue.slice(); resetSchedulerState(); // call component updated and activated hooks callActivatedHooks(activatedQueue); callUpdatedHooks(updatedQueue); // devtool hook /* istanbul ignore if */ if (devtools && config.devtools) { devtools.emit('flush'); } }
首先触发watcher.before(),该方法是beforeUpdate的hook。然后执行watcher.run()方法:
Watcher.prototype.run = function () { if (this.active) { var value = this.get(); if (value !== this.value || // Deep watchers and watchers on Object/Arrays should fire even // when the value is the same, because the value may // have mutated. isObject(value) || this.deep) { // set new value var oldValue = this.value; this.value = value; if (this.user) { var info = "callback for watcher \"".concat(this.expression, "\""); invokeWithErrorHandling(this.cb, this.vm, [value, oldValue], this.vm, info); } else { this.cb.call(this.vm, value, oldValue); } } } };
该方法watcher.get方法,该方法会重新执行render方法生成vnode,然后调用update方法更新节点:
updateComponent = function () { vm._update(vm._render(), hydrating); };
总结:
1.获取监听文件最新的render和staticRenderFns并赋值给当前的VueComponent和当前vm实例。
2.使用$forceUpdate添加当前的vm的watcher并在queue中,最后异步执行flushSchedulerQueue,该函数遍历quene执行watcher的run方法,该方法会执行vm实例的_update方法完成更新。
到此这篇关于Vue数据变化后页面更新详细介绍的文章就介绍到这了,更多相关Vue页面更新内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!