Vue?data中隨意改一個(gè)屬性,視圖都會(huì)更新嗎?
- 面試官:看過 Vue 的源碼沒?
- 候選者:看過。
- 面試官:那你說下
Vue data
中隨意更改一個(gè)屬性,視圖都會(huì)被更新嗎? - 候選者:不會(huì)。
- 面試官:why?
- 候選者:如果該屬性沒有被用到 template 中,就沒有必要去更新視圖,頻繁這樣性能不好。
- 面試官:那 Vue 中是如何去實(shí)現(xiàn)該方案的?
- 候選者:在實(shí)例初始化過程中,利用
Object.defineProperty
對(duì) data 中的屬性進(jìn)行數(shù)據(jù)監(jiān)聽,如果在template
中被使用到的屬性,就被 Dep 類收集起來,等到屬性被更改時(shí)會(huì)調(diào)用notify更新視圖。 - 面試官:那你怎么知道那些屬性是在 template 被用到的呢?
- 候選者:WTF。。。這個(gè)倒不是很清楚,您能解釋下嗎?
- 面試官:OK,那我就簡(jiǎn)單解釋下:
先寫個(gè)簡(jiǎn)單的 demo
,其中 data 中有 4 個(gè)屬性a,b,c,d,在模板中被利用到的屬性只有a,b??纯词遣皇侵挥衋,b才會(huì)調(diào)用Dep收集起來呢?
new Vue({ el: '#app', data() { return { a: 1, b: 2, c: 3, d: 4, }; }, created() { console.log(this.b); this.b = 'aaa'; }, template: '<div>Hello World{{a}}{}</div>', });
在Vueinstance/state.js里面,會(huì)利用proxy把每個(gè)屬性都 代理一遍
const keys = Object.keys(data) const props = vm.$options.props const methods = vm.$options.methods let i = keys.length while (i--) { const key = keys[i] if (props && hasOwn(props, key)) { process.env.NODE_ENV !== 'production' && warn( `The data property "${key}" is already declared as a prop. ` + `Use prop default value instead.`, vm ) } else if (!isReserved(key)) { // 代理對(duì)象的屬性 proxy(vm, `_data`, key) } } // observe data observe(data, true /* asRootData */)
利用defineReactive對(duì)data中的每個(gè)屬性進(jìn)行劫持
observe(data, true /* asRootData */); // observe const keys = Object.keys(obj); for (let i = 0; i < keys.length; i++) { defineReactive(obj, keys[i]); } // defineReactive Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter() { const value = getter ? getter.call(obj) : val; // 重點(diǎn)在這里,后續(xù)如果在模板中使用到的屬性,都會(huì)被執(zhí)行reactiveGetter函數(shù) // 被Dep類 收集起來 if (Dep.target) { console.log(`${key} 屬性 被Dep類收集了`) dep.depend(); if (childOb) { childOb.dep.depend(); if (Array.isArray(value)) { dependArray(value); } } } return value; }, set: function reactiveSetter(newVal) { const value = getter ? getter.call(obj) : val; /* eslint-disable no-self-compare */ if (newVal === value || (newVal !== newVal && value !== value)) { return; } if (setter) { // 這里是處理computed set 函數(shù) setter.call(obj, newVal); } else { val = newVal; } childOb = !shallow && observe(newVal); // 如果我們?cè)诟膶傩詴r(shí),就會(huì)調(diào)用notify 異步更新視圖 dep.notify(); }, });
執(zhí)行$mount進(jìn)行視圖掛載
if (vm.$options.el) { vm.$mount(vm.$options.el); }
$mount 是調(diào)用 Vue 原型上的方法, 重點(diǎn)是最后一句 mount.call(this, el, hydrating)
Vue.prototype.$mount = function ( el?: string | Element, hydrating?: boolean ): Component { el = el && query(el); const options = this.$options; // resolve template/el and convert to render function /** * 查看render 函數(shù)是否存在?如果不存在就解析template模板 * Vue渲染頁面時(shí),有兩個(gè)方式 1. template,2. render,最終所有的模板類的都需要使用render去渲染 */ if (!options.render) { let template = options.template; if (template) { if (typeof template === 'string') { if (template.charAt(0) === '#') { template = idToTemplate(template); /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && !template) { warn( `Template element not found or is empty: ${options.template}`, this ); } } } else if (template.nodeType) { template = template.innerHTML; } else { if (process.env.NODE_ENV !== 'production') { warn('invalid template option:' + template, this); } return this; } } else if (el) { // 如果模板不存在,就創(chuàng)建一個(gè)默認(rèn)的html模板 template = getOuterHTML(el); } } // 重寫了Vue.prototype.$mount ,最終調(diào)用緩存的mount方法完成對(duì)$mount的掛載 return mount.call(this, el, hydrating); };
這里mount調(diào)用了 mountComponent(this, el, hydrating) 方法,而 mountComponent是執(zhí)行了 _render函數(shù),最終_render是調(diào)用render 生成一個(gè)vnode。
const { render, _parentVnode } = vm.$options; vnode = render.call(vm._renderProxy, vm.$createElement);
最后一張圖可以看到是render
函數(shù)在渲染我們demo
里面的template
模板,最終只有a, b兩個(gè)屬性才會(huì)被Dep類收集起來。
如果文中有錯(cuò)誤的地方,麻煩各位指出,我會(huì)持續(xù)改進(jìn)的。謝謝, 需要調(diào)試源碼的,這里點(diǎn)擊這里,按照 readme操作即可。希望star下
到此這篇關(guān)于Vue data中隨意改一個(gè)屬性,視圖都會(huì)更新?的文章就介紹到這了,更多相關(guān)Vue data 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解vue.js+UEditor集成 [前后端分離項(xiàng)目]
本篇文章主要介紹了詳解vue.js+UEditor集成 [前后端分離項(xiàng)目] ,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-07-07Vue項(xiàng)目總結(jié)之webpack常規(guī)打包優(yōu)化方案
這篇文章主要介紹了vue項(xiàng)目總結(jié)之webpack常規(guī)打包優(yōu)化方案,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-06-06vue單頁應(yīng)用加百度統(tǒng)計(jì)代碼(親測(cè)有效)
這篇文章主要介紹了vue單頁應(yīng)用加百度統(tǒng)計(jì)代碼的解決方法,需要的朋友參考下吧2018-01-01解決ant design vue 表格a-table二次封裝,slots渲染的問題
這篇文章主要介紹了解決ant design vue 表格a-table二次封裝,slots渲染的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-10-10