Vue數(shù)據(jù)變化后頁面更新詳細(xì)介紹
首先會通過module.hot.accept監(jiān)聽文件變化,并傳入該文件的渲染函數(shù):
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__); })
隨后執(zhí)行rerender方法,只貼重點(diǎn)部分代碼:
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) })
首先會給當(dāng)前的VueComponent的Options和實(shí)例里面的渲染函數(shù)替換為最新,然后執(zhí)行實(shí)例上的forceUpdate方法:
Vue.prototype.$forceUpdate = function () { var vm = this; if (vm._watcher) { vm._watcher.update(); } };
該方法是執(zhí)行watcher實(shí)例的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); } }
重點(diǎn)看 queueWatcher(this),將最新的watcher放入quene隊(duì)列并且將flushSchedulerQueue函數(shù)傳給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里面放入一個(gè)執(zhí)行回調(diào)的函數(shù)。并執(zhí)行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); };
該方法異步執(zhí)行flushCallbacks:
function flushCallbacks() { pending = false; var copies = callbacks.slice(0); callbacks.length = 0; for (var i = 0; i < copies.length; i++) { copies[i](); } }
開始執(zhí)行回調(diào)函數(shù)我們第一次放進(jìn)去的函數(shù)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'); } }
首先觸發(fā)watcher.before(),該方法是beforeUpdate的hook。然后執(zhí)行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方法,該方法會重新執(zhí)行render方法生成vnode,然后調(diào)用update方法更新節(jié)點(diǎn):
updateComponent = function () { vm._update(vm._render(), hydrating); };
總結(jié):
1.獲取監(jiān)聽文件最新的render和staticRenderFns并賦值給當(dāng)前的VueComponent和當(dāng)前vm實(shí)例。
2.使用$forceUpdate添加當(dāng)前的vm的watcher并在queue中,最后異步執(zhí)行flushSchedulerQueue,該函數(shù)遍歷quene執(zhí)行watcher的run方法,該方法會執(zhí)行vm實(shí)例的_update方法完成更新。
到此這篇關(guān)于Vue數(shù)據(jù)變化后頁面更新詳細(xì)介紹的文章就介紹到這了,更多相關(guān)Vue頁面更新內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue3中require報(bào)錯(cuò)require is not defined問題及解決
這篇文章主要介紹了vue3中require報(bào)錯(cuò)require is not defined問題及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06element-ui自定義表格如何給input雙向綁定數(shù)據(jù)
這篇文章主要介紹了element-ui自定義表格如何給input雙向綁定數(shù)據(jù),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10這15個(gè)Vue指令,讓你的項(xiàng)目開發(fā)爽到爆
這篇文章主要介紹了這15個(gè)Vue指令,讓你的項(xiàng)目開發(fā)爽到爆,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-10-10element-ui圖像組件、上傳組件以及分頁組件實(shí)現(xiàn)代碼
工作中碰到需要多圖上傳,在使用element-ui解決過程中碰到一些問題,在這里分享給大家,這篇文章主要給大家介紹了關(guān)于element-ui圖像組件、上傳組件以及分頁組件實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2024-02-02vue給數(shù)組中對象排序 sort函數(shù)的用法
這篇文章主要介紹了vue給數(shù)組中對象排序 sort函數(shù)的用法,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09