摘要:原型方法通過原型方法方法來掛載實例。當響應(yīng)式屬性發(fā)生變化時,會通知依賴列表中的對象進行更新。此時,對象執(zhí)行方法,重新渲染節(jié)點。在執(zhí)行過程中,如果需要讀取響應(yīng)式屬性,則會觸發(fā)響應(yīng)式屬性的??偨Y(jié)響應(yīng)式屬性的原理
vue實例 初始化 完成以后,接下來就要進行 掛載。
vue實例掛載,即為將vue實例對應(yīng)的 template模板,渲染成 Dom節(jié)點。
原型方法 - $mount? 通過原型方法 $mount方法 來掛載vue實例。
? 掛載vue實例時,經(jīng)歷一下幾個重要步驟:
生成render函數(shù);
生成vue實例的監(jiān)聽器watcher;
執(zhí)行render函數(shù),將vue實例的template模板轉(zhuǎn)化為VNode節(jié)點樹;
執(zhí)行update函數(shù),將VNode節(jié)點樹轉(zhuǎn)化為dom節(jié)點樹;
// 掛載Vue實例 Vue$3.prototype.$mount = function(el, hydrating) { // el為dom元素對應(yīng)的選擇器表達式,根據(jù)選擇器表達式,獲取dom元素 el = el && query(el); ... // this->Vue實例,或者是組件實例對象 var options = this.$options; // 解析模板,將模板轉(zhuǎn)換為render渲染函數(shù) if(!options.render) { // 一般是組件實例的構(gòu)造函數(shù)的options中會直接有template這個屬性 var template = options.template; if(template) { if(typeof template === "string") { if(template.charAt(0) === "#") { template = idToTemplate(template); /* istanbul ignore if */ if("development" !== "production" && !template) { warn( ("Template element not found or is empty: " + (options.template)), this ); } } } else if(template.nodeType) { template = template.innerHTML; } else { { warn("invalid template option:" + template, this); } return this } } else if(el) { // 獲取dom節(jié)點的outerHTML template = getOuterHTML(el); } // template,模板字符串 if(template) { /* istanbul ignore if */ if("development" !== "production" && config.performance && mark) { mark("compile"); } // 將html模板字符串編譯為渲染函數(shù) var ref = compileToFunctions(template, { // 換行符 shouldDecodeNewlines : shouldDecodeNewlines, // 分割符 delimiters : options.delimiters, // 注釋 comments : options.comments }, this); // 獲取渲染函數(shù) var render = ref.render; // 獲取靜態(tài)渲染函數(shù) var staticRenderFns = ref.staticRenderFns; // 將渲染函數(shù)添加到Vue實例對象的配置項options中 options.render = render; // 將靜態(tài)渲染函數(shù)添加到Vue實例對象的配置項options中 options.staticRenderFns = staticRenderFns; /* istanbul ignore if */ if("development" !== "production" && config.performance && mark) { mark("compile end"); measure(((this._name) + " compile"), "compile", "compile end"); } } } return mountComponent.call(this, el, hydrating) }; // 掛載vue實例 function mountComponent(vm, el, hydrating) { vm.$el = el; ... updateComponent = function() { vm._update(vm._render(), hydrating); }; // 給Vue實例或者是組件實例創(chuàng)建一個監(jiān)聽器, 監(jiān)聽updateComponent方法 vm._watcher = new Watcher(vm, updateComponent, noop); ... return vm; }
? watcher 對象在構(gòu)建過程中,會作為觀察者模式中的 Observer,會被添加到 響應(yīng)式屬性的dep對象的依賴列表 中。
? 當響應(yīng)式屬性發(fā)生變化時,會通知依賴列表中的watcher對象進行更新。
? 此時,watcher 對象執(zhí)行 updateComponent 方法,重新渲染 dom節(jié)點。
watcher? 在 掛載vue實例 時, 會為 vue實例 構(gòu)建一個 監(jiān)聽者watcher。
// vm => 當前監(jiān)聽者對應(yīng)的vue實例 // expOfFn => 需要監(jiān)聽的表達式 // cb => 監(jiān)聽回調(diào)方法 // options => 選項,比如deep、immediate // vm.$watch("message", function(newValue) {...}) var Watcher = function Watcher(vm, expOrFn, cb, options) { this.vm = vm; vm._watchers.push(this); ... // 監(jiān)聽器訂閱的Dep對象實例 this.deps = []; this.newDeps = []; // 存儲監(jiān)聽器已經(jīng)訂閱的Dep對象實例的id,防止重復(fù)訂閱。 // 每一個Dep對象實例都有一個ID this.depIds = new _Set(); this.newDepIds = new _Set(); // 監(jiān)聽器監(jiān)聽的表達式 this.expression = expOrFn.toString(); // 初始化getter,getter用于收集依賴關(guān)系 if(typeof expOrFn === "function") { // expOfFn 為 函數(shù) // 對應(yīng):給vue實例建立watcher this.getter = expOrFn; } else { // epOfFn 為 字符串 // 對應(yīng):在vue實例中建立監(jiān)聽 // 比如 vm.$watch("message", function() {...}) this.getter = parsePath(expOrFn); } this.value = this.lazy ? undefined : this.get(); }; // 執(zhí)行watcher的getter方法,用于收集響應(yīng)式屬性dep對象 和 watcher的依賴關(guān)系 // 在vue實例掛載過程中, getter = updateComponent Watcher.prototype.get = function get() { // 將Dep.target設(shè)置為當前Watcher對象實例 pushTarget(this); var value; var vm = this.vm; ... value = this.getter.call(vm, vm); ... return value }; // watcher 更新 Watcher.prototype.update = function update() { ... this.get() ... }; // 將watcher添加到dep屬性的依賴列表中 Watcher.prototype.addDep = function addDep(dep) { var id = dep.id; if(!this.newDepIds.has(id)) { this.newDepIds.add(id); this.newDeps.push(dep); if(!this.depIds.has(id)) { dep.addSub(this); } } };
? 在掛載vue實例時,watcher對象會在構(gòu)建過程中會執(zhí)行 updateComponent 方法。
? 執(zhí)行 updateComponent 方法分為兩個過程:
執(zhí)行 render 方法,返回 VNode節(jié)點樹;
執(zhí)行 update 方法,將 VNode節(jié)點樹 渲染為 dom節(jié)點樹。
? 執(zhí)行 render 方法時,會觸發(fā)響應(yīng)式屬性的 getter 方法,將 watcher 添加到 dep對象的依賴列表中。
render? render 方法是 vue實例 在掛載時由 template 編譯成的一個 渲染函數(shù)。
tempalte:{{message}}render: // 執(zhí)行render, 需要讀取響應(yīng)式屬性message,觸發(fā)message的getter方法 (function anonymous() { with(this){return _c("div",{attrs:{"id":"app"}},[_v(_s(message))])} }) // _s, 將this.message轉(zhuǎn)化為字符串 // _v, 生成文本節(jié)點對應(yīng)的VNode // _c, 生成"div"元素節(jié)點對應(yīng)的Vnode
? render 函數(shù)返回 VNode節(jié)點 , 用于 渲染Dom節(jié)點。
? 在執(zhí)行過程中,如果需要讀取 響應(yīng)式屬性,則會觸發(fā) 響應(yīng)式屬性 的 getter。
? 在 getter 方法中, 將 watcher 對象添加到 響應(yīng)式屬性dep對象的依賴列表 中。
修改響應(yīng)式屬性? 修改 響應(yīng)式屬性時,會觸發(fā)響應(yīng)式屬性的 setter 方法。此時,響應(yīng)式屬性的 dep對象執(zhí)行 notify 方法,遍歷自己的 依賴列表subs, 逐個通知subs中的 watcher 去更新。
? watcher 對象執(zhí)行 update 方法,重新調(diào)用 render 方法生成 Vnode節(jié)點樹,然后 對比新舊Vnode節(jié)點樹 的不同,更新dom樹。
總結(jié)? 響應(yīng)式屬性 的原理:
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/101402.html
摘要:問題為什么修改即可觸發(fā)更新和的關(guān)聯(lián)關(guān)系官方介紹的官網(wǎng)文檔,對響應(yīng)式屬性的原理有一個介紹。因此本文在源碼層面,對響應(yīng)式原理進行梳理,對關(guān)鍵步驟進行解析。 描述 ?我們通過一個簡單的 Vue應(yīng)用 來演示 Vue的響應(yīng)式屬性: html: {{message}} js: let vm = new Vue({ el: #ap...
摘要:由于是需要兼容的后臺系統(tǒng),該項目并不能使用到等技術(shù),因此我在上的經(jīng)驗大都是使用原生的編寫的,可以看見一個組件分為兩部分視圖部分,和數(shù)據(jù)部分。 在公司里幫項目組里開發(fā)后臺系統(tǒng)的前端項目也有一段時間了。 vue這種數(shù)據(jù)驅(qū)動,組件化的框架和react很像,從一開始的快速上手基本的開發(fā),到后來開始自定義組件,對element UI的組件二次封裝以滿足項目需求,期間也是踩了不少坑。由于將來很長一...
摘要:引言上篇文章介紹原型,這篇文章接著講繼承,嘔心瀝血之作,大哥們點個贊呀明確一點并不是真正的面向?qū)ο笳Z言,沒有真正的類,所以我們也沒有類繼承實現(xiàn)繼承有且僅有兩種方式,和原型鏈在介紹繼承前我們先介紹下其他概念函數(shù)的三種角色一個函數(shù),有三種角色。 showImg(https://segmentfault.com/img/bVbo4hv?w=1800&h=1000); 引言 上篇文章介紹原型,...
摘要:插件開發(fā)前端掘金作者原文地址譯者插件是為應(yīng)用添加全局功能的一種強大而且簡單的方式。提供了與使用掌控異步前端掘金教你使用在行代碼內(nèi)優(yōu)雅的實現(xiàn)文件分片斷點續(xù)傳。 Vue.js 插件開發(fā) - 前端 - 掘金作者:Joshua Bemenderfer原文地址: creating-custom-plugins譯者:jeneser Vue.js插件是為應(yīng)用添加全局功能的一種強大而且簡單的方式。插....
摘要:雖然計算屬性在大多數(shù)情況下更合適,但有時也需要一個自定義的偵聽器。當某個屬性發(fā)生變化,觸發(fā)攔截函數(shù),然后調(diào)用自身消息訂閱器的方法,遍歷當前中保存著所有訂閱者的數(shù)組,并逐個調(diào)用的方法,完成響應(yīng)更新。 雖然目前的技術(shù)棧已由Vue轉(zhuǎn)到了React,但從之前使用Vue開發(fā)的多個項目實際經(jīng)歷來看還是非常愉悅的,Vue文檔清晰規(guī)范,api設(shè)計簡潔高效,對前端開發(fā)人員友好,上手快,甚至個人認為在很多...
閱讀 1581·2023-04-26 02:50
閱讀 3581·2023-04-26 00:28
閱讀 1962·2023-04-25 15:18
閱讀 3246·2021-11-24 10:31
閱讀 1029·2019-08-30 13:00
閱讀 1028·2019-08-29 15:19
閱讀 1801·2019-08-29 13:09
閱讀 3008·2019-08-29 13:06