Does changing data through array indexes in VUE change the view?
With this question in mind, write vUE responsive data principles with me to find out.
1. Export the vue constructor
import {initMixin} from './init';
function Vue(options) {
this._init(options);
}
initMixin(Vue); // Add the _init method to the prototype
export default Vue;
Copy the code
2. Initialize the VUE status in init
The init method is first mounted on the Vue prototype. The options passed in by the user are mounted on the VM’s $options
import { initState } from "./state";
export function initMixin(Vue) {
Vue.prototype._init = function (options) {
const vm = this;
vm.$options = options;
initState(vm);
};
}
Copy the code
3. Perform initialization operations based on different attributes
Initialize data
Initialize vm data (props, Data, computed, and watch) using initSate. Today we are going to focus on data initialization.
export function initState(vm) { // State initialization
const opts = vm.$options;
if (opts.data) {
initData(vm);
}
// if(opts.computed){
// initComputed();
// }
// if(opts.watch){
// initWatch();
// }
}
function initData(vm) {
let data = vm.$options.data;
// Data can be written in two ways: attributes and functions
// So we need to judge the data type before passing in the observation data
data = vm._data = isFunction(data) ? data.call(vm) : data;
// If data is a function, call this to the VM and immediately execute the data function, assigning the data object returned by the function to vm.\_data, otherwise return data directly
// The vm has nothing to do with data, and is associated with _data
observer(data);
}
Copy the code
export function observe(data) {
// Only if it is an object
if(! isObject(data)) {return;
}
// An object returns an instance of an Observer class to observe data
return new Observer(data);
}
Copy the code
4. Data hijacking
step1.hijacking every key of data.key
First use walk method to traverse data, redefine each attribute of data with defineProperty, add get and set attribute accessors.
One thing to note:
Add a new attribute to V. _data b=1. Because the data is traversed using an existing key on the original data.
step2[key]. Hijack data
Then, the data in data[key] is hijacked. The hijacking methods of array and object are different.
In the first case, observe all entries of the object when data[key] is the object
There are two points to note:
- If it’s bread in the object
object
And observe the object recursively. - If you assign an old value to a
object
Observe the new value as well.
import { isObject } from ".. /utils";
import { arrayMethods } from "./array";
// 1. If the data is an object, the object is constantly hijacked recursively
// 2. If it is an array, it hijacks the array's methods and checks for non-primitive data types in the array
class Observer {
constructor(data) { // Hijack all attributes of the object
Object.defineProperty(data,'__ob__', {value:this.enumerable:false // Set it to non-enumerable, so it cannot be iterated, so it can solve the stack overflow problem caused by iterating __ob__ multiple times.
})
// observeArray (observeArray, observeArray, observeArray, observeArray, observeArray, observeArray, observeArray, observeArray, observeArray, observeArray)
if(Array.isArray(data)){
// Array hijacking logic
// Rewrite the original array method, slice programming higher-order functions
data.__proto__ = arrayMethods;
// If the data in the array is of object type, you need to monitor the changes of the object
this.observeArray(data);
}else{
this.walk(data); // Object hijacking logic}}observeArray(data){ // Hijack recursion again for our arrays and objects in arrays
// [{a:1},{b:2}]
data.forEach(item= >observe(item))
}
walk(data) { / / object
Object.keys(data).forEach(key= >{ defineReactive(data, key, data[key]); }}})// Vue2 iterates through the object and redefines each attribute with a defineProperty
function defineReactive(data,key,value){ // Value can be an object
observe(value); // The default value of the user itself is the object set. Objects need to be handled recursively (poor performance).
Object.defineProperty(data,key,{
get(){
return value
},
set(newV){
// todo... Update the view
observe(newV); // If the user assigns a new object, the object needs to be hijackedvalue = newV; }})}export function observe(data) {
// Only if it is an object
if(! isObject(data)) {return;
}
if(data.__ob__){
return;
}
// The default outermost data must be an object
return new Observer(data)
}
Copy the code
The second case: when data[key] is an array: rewrite the array prototype method
Arr [3]=100. Vue directly listens to the array index, which causes serious performance consumption. So instead of using objectProperty, we override the array method to listen on the array
push shift unshift pop reverse sort splice
If the user calls the above seven methods, vUE overrides will be called, otherwise the original array methods will be used.
Why only these seven?
Because these seven methods change the array method, others like concat don’t change the original array.- The array does not monitor index changes, but if the data in the array is
object
Type, which requires the observe object change to the object. So ifarr:[{name:"chibaozi"}]
So it looks like thisVm.arr [0]. Name =" morning eat steamed stuffed bun"
It can be listened on by subscript modification. - The new data in the array is
object
Type, you also need to observe the added object.
let oldArrayPrototype = Array.prototype
export let arrayMethods = Object.create(oldArrayPrototype);
// arraymethods.__proto__ = array.prototype
let methods = [
'push'.'shift'.'unshift'.'pop'.'reverse'.'sort'.'splice'
]
methods.forEach(method= >{
// If the user calls the above seven methods, I will use my own rewrite, otherwise use the original array method
arrayMethods[method] = function (. args) { // args is the argument list arr.push(1,2,3)
oldArrayPrototype[method].call(this. args);/ / arr. Push (1, 2, 3);
let inserted;
let ob = this.__ob__; // Get an observer instance based on the current array
switch (method) {
case 'push':
case 'unshift':
inserted = args ; // This is the new content
break;
case 'splice':
inserted = args.slice(2)
default:
break;
}
// If there is new content to continue hijacking, I need to observe each item in the array, not the array
// Update operation.... todo...
if(inserted) ob.observeArray(inserted)
}
})
Copy the code
5. Data broker
We also need to delegate vm._data.key to vm.key, also via defineProperty.
function vmProxy(vm, source, key) {
Object.defineProperty(vm, key, {
get() {
return vm[source][key];
},
set(newV){ data[source][key] = newV; }}); }function initData(vm) { //
let data = vm.$options.data; // vm.$el vue checks for attributes that start with $
// Vue2 will data hijack all data in data object.defineProperty
// The vm has nothing to do with data, and is associated with _data
data = vm._data = isFunction(data) ? data.call(vm) : data;
// The user goes to vm. XXX => vm._data.xxx
for(let key in data){ // vm.name = 'xxx' vm._data.name = 'xxx'
proxy(vm,'_data',key);
}
observe(data);
}
Copy the code
summary
Now, I’m sure you have the answer. Changing the index and length of an array in VUe2 is not monitored.
In Vue2, each attribute of a data Object is iterated internally through the defineReactive method, and the attribute is hijacked using Object.defineProperty. (only hijacks existing attributes). For performance reasons, object.defineProperty is not used to intercept each entry of the array. Arrays are implemented by overriding the array method. You need to change the array by pop Splice shift unshift Reverse sort to trigger the watcher update. If the array is an object data type, it will also be recursively hijacked. Vue3 uses proxy instead for reactive data. Internal dependency collection means that each attribute has its own DEP attribute to store the watcher it depends on. When the attribute changes, the watcher of its own object is notified to update.
After a deeper understanding of the reactive principle, we can also summarize some methods of vUE code optimization:
- Do not nest too many layers of the object hierarchy. Because multiple layers of objects in VUE2 are hijacked recursively, the object hierarchy is too deep and performance is poor.
- Don’t put anything in data that doesn’t need to be responsive.
- You can use Object.freeze () to freeze data
Next article will be compiled by handwriting template, heh heh.