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