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