Vue data中隨意改一個屬性,檢視都會更新?
阿新 • • 發佈:2021-12-17
- 面試官:看過 的原始碼沒?
- 候選者:看過。
- 面試官:那你說下
Vue data
中隨意更改一個屬性,檢視都會被更新嗎? - 候選者:不會。
- 面試官:why?
- 候選者:如果該屬性沒有被用到 template 中,就沒有必要去更新檢視,頻繁這樣效能不好。
- 面試官:那 Vue 中是如何去實現該方案的?
- 候選者:在例項初始化過程中,利用
Object.defineProperty
對 data 中的屬性進行資料監聽,如果在template http://www.cppcns.com
中被使用到的屬性,就被 Dep 類收集起來,等到屬性被更改時會呼叫notify更新檢視。 - 面試官:那你怎麼知道那些屬性是在 template 被用到的呢?
- 候選者:WTF。。。這個倒不是很清楚,您能解釋下嗎?
- 面試官:OK,那我就簡單解釋下:
先寫個簡單的 demo
,其中 data 中有 4 個屬性a,b,c,d,在模板中被利用到的屬性只有a,b。看看是不是隻有a,b才會呼叫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}}{{b}}</div>',});
在Vueinstance/state.裡面,會利用proxy把每個屬性都 代理一遍
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)) { // 代理物件的屬性 proxy(vm,`_data`,key) } } // observe data observe(data,true /* asRootData */)
利用defineReactive對data中的每個屬性進行劫持
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; // 重點在這裡,後續如果在模板中使用到的屬性,都會被執行reactiveGetter函式 // 被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 函式 setter.call(obj,newVal); } else { val = newVal; } childOb = !shallow && observe(newVal); // 如果我們在更改屬性時,就會呼叫notify 非同步更新檢視 dep.notify(); },});
執行$mount進行檢視掛載
if (vm.$options.el) { vm.$mount(vm.$options.el); }
$mount 是呼叫 Vue 原型上的方法,重點是最後一句 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 函式是否存在?如果不存在就解客棧析template模板
* Vue渲染頁面時,有兩個方式 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) {
// 如果模板不存在,就建立一個預設的html模板
templkyBNaQeAqFate = getOuterHTML(el);
}
}
// 重寫了Vue.prototype.$mount ,最終呼叫快取的mount方法完成對$mount的掛載
return mount.call(this,hydrating);
};
這裡mount呼叫了 mountComponent(this,hydrating) 方法,而 mountComponent是執行了 _render函式,最終_render是呼叫render 生成一個vnode。
const { render,_parentVnode } = vm.$options; vnode = render.call(vm._renderProxy,vm.$createElement);
最後一張圖可以看到是render
函式在渲染我們demo
裡面的template
模板,最終只有a,b兩個屬性才會被Dep類收集起來。
如果文中有錯誤的地方,麻煩各位指出,我會持續改進的。謝謝, 需要除錯原始碼的,這裡點選這裡,按照 readme操作即可。希望star下
到此這篇關於Vue data中隨意改一個屬性,檢視都會更新?的文章就介紹到這了,更多相關Vue data 內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!