- Interviewer: Have you read the source code for Vue?
- Candidate: Yes.
- Interviewer: Would you say that if you change a property randomly in Vue Data, the view will be updated?
- Candidate: No.
- Interviewer: Why?
- Candidate: If the property is not used in the template, there is no need to update the view frequently, which is not good performance.
- Interviewer: How is the solution implemented in Vue?
- Candidate: During instance initialization, the
Object.defineProperty
Data listens for attributes in data. If the attributes are used in the template, they are collected by the Dep class and called when the attributes are changednotify
Update the view. - Interviewer: So how do you know which attributes are used in template?
- Candidate: WTF… That’s not very clear. Could you explain it to me?
- Interviewer: OK, let me explain briefly:
Let’s start with a simple demo where data has four attributes A, B, C, and D, and only a and B are used in the template. What if only A and B call Dep to collect it?
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>'});Copy the code
- The Vue
instance/state.js
Inside, will useproxy
Proxy each property
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)) {// Properties of the proxy object
proxy(vm, `_data`, key)
}
}
// observe data
observe(data, true /* asRootData */)
Copy the code
- using
defineReactive
Hijacks each property in 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;
// The important point here is that all subsequent properties used in the template will be implemented with the reactiveGetter function
// is collected by the Dep class
if (Dep.target) {
console.log(`${key}Property is collected by the Dep class)
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) {
// Here is handling a computed SET function
setter.call(obj, newVal);
} else{ val = newVal; } childOb = ! shallow && observe(newVal);// If we change properties, we call notify asynchronously to update the viewdep.notify(); }});Copy the code
- perform
$mount
The view is mounted
if (vm.$options.el) {
vm.$mount(vm.$options.el);
}
Copy the code
$mount
Is to call the method on the Vue prototype, focusing on the last sentencemount.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
/** * check whether the render function exists. Render */ template */ template */ Vue */ template */
if(! options.render) {let template = options.template;
if (template) {
if (typeof template === 'string') {
if (template.charAt(0) = = =The '#') {
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) {
// Create a default HTML template if the template does not existtemplate = getOuterHTML(el); }}Prototype.$mount (); // Vue. Prototype.$mount ()
return mount.call(this, el, hydrating);
};
Copy the code
- here
mount
Call themountComponent(this, el, hydrating)
Method, andmountComponent
Is to perform the_render
Delta function, and finally_render
Is to callrender
To generate avnode
.
const { render, _parentVnode } = vm.$options;
vnode = render.call(vm._renderProxy, vm.$createElement);
Copy the code
The last one you can see isrender
The function is in rendering our demotemplate
Templates, ultimately onlya, b
It takes two properties to beDep
Class collected.
If there are mistakes in the article, please point out that I will continue to improve. Thank you, need to debug the source code, here click here, according to the readme operation. Hope under star. thank you