This is the 10th day of my participation in the More text Challenge. For more details, see more text Challenge
One, foreword
The first part mainly introduces the observation of object data changes, involving the following points:
The deep observation processing is realized when the old attribute value of the object changes to object and array.
Combined with the realization principle, the reason why the new attribute of the object cannot be observed and how to realize the data observation are explained.
In this section, the observation of array data changes (array, new object, array, normal value)
Second, array, new object, array, ordinary value observation problem
1. Problem analysis
Does adding an object, array, or normal value to an array ARR trigger an update?
let vm = new Vue({
el: '#app'.data() {
return { arr: [{ name: "Brave" }, 100]}}}); vm.arr.push({a:100});
vm.arr[2].a = 200;
Copy the code
Processing for array types as of the current version:
-
Overrides methods on the array chain to hijack seven prototype methods that cause changes to the original array;
-
Recursively call observe for each item in the array, making the array type realize recursive observation;
Since observe only deals with object types, normal values in the array are not observed;
Although we have implemented the data hijacking of an array, we have not implemented the specific logic after the data hijacking:
// src/Observer/array.js
let oldArrayPrototype = Array.prototype;
export let arrayMethods = Object.create(oldArrayPrototype);
let methods = [
'push'.'pop'.'shift'.'unshift'.'reverse'.'sort'.'splice'
]
methods.forEach(method= > {
arrayMethods[method] = function () {
console.log('Method ='; + method)
// Hijack to array change, processing logic has not been implemented}});Copy the code
So, adding content to an array can trigger data hijacking, but the actual logic behind the hijacking is not implemented yet
In vue 2.x, adding an object to an array and modifying the properties of the new object trigger updates.
2. Thinking analysis
Overwrite the push method logic:
Because of the inconsistent number of incoming parameters for the seven methods, for example, push can be passed multiple parameters
3. Code implementation
When the parameter of push is an object type, you need to observe again
// src/observe/array.js
methods.forEach(method= > {
// Current external call: arr.push
arrayMethods[method] = function (. args) {
console.log('Method ='; + method)
// AOP:before native method extensions...
// Call the array native method logic (bound to the current call context)
oldArrayPrototype[method].call(this. args)// AOP::after native method extensions...
// If the new attribute is a property, continue to observe
// Splice push unshift
let inserted = [];
switch (method) {
// arr.splice(0,0,100) if the splice method is used to add, there must be a third parameter, from which the content is added
case 'splice': // Modify delete add
inserted = args.slice(2); // The splice method adds data from the third argument
case 'push': // Add forward
case 'unshift': // Add backward
inserted = args // Push and unshift parameters are added
break;
}
// Walk through the inserted array to see if it needs to be hijacked}});Copy the code
When the parameter of push is an object type, continue to observe it.
Question 1
Array deep hijack of the observeArray method in the Observer class
Because there is no export, it is not accessible in SRC /observe/array.js’s methods.forEach
The VM is not available in the Observer class,
So add a custom attribute associated with the current this: value.ob = this;
Value: adds a custom attribute to an array or object __ob__ = this,
This: an instance of the current Observer class on which the observeArray method exists;
In SRC/observeArray, we call the observeArray method in the methods. ForEach in SRC/observeArray.
// src/observe/index.js
class Observer {
constructor(value) {
// value: adds a custom attribute to an array or object __ob__ = this,
// this: is an instance of the current Observer class on which the observeArray method exists;
value.__ob__ = this;
if (isArray(value)) {
value.__proto__ = arrayMethods;
this.observeArray(value);
} else {
this.walk(value); }}}Copy the code
The __ob__ array is added and the push method is called, so you can get ob from the __ob__ attribute
// src/observe/array.js
methods.forEach(method= > {
arrayMethods[method] = function (. args) {
oldArrayPrototype[method].call(this. args)let inserted = null;
let ob = this.__ob__; // get ob from the __ob__ attribute
switch (method) {
case 'splice':
inserted = args.slice(2);
case 'push':
case 'unshift':
inserted = args
break;
}
// observeArray: Iterates through the inserted array and calls the observe method to continue the deep observation of the object on the new Observer
if(inserted)ob.observeArray(inserted);// Inserted has a value, which is an array}});Copy the code
So, when you push an object or an array into an array, you continue to use the observeArray method to make the object or array responsive
Question 2
Running will result in an infinite loop
// src/observe/index.js
class Observer {
constructor(value) {
value.__ob__ = this;
if (isArray(value)) {
value.__proto__ = arrayMethods;
this.observeArray(value);
} else {
this.walk(value); }}walk(data) {
Object.keys(data).forEach(key= >{ defineReactive(data, key, data[key]); }); }}Copy the code
In the Observer class, since value.ob = this; This code
If value is an object, it goes to this.walk(value); Method to continue the loop through the object’s properties,
In this case, the attribute __ob__ is looping out, and __ob__ is an object with __ob__ on it
So, after defining the property __ob__ in the walk loop, its value is still an object, creating an infinite recursion loop
If value is an object then we go to the walk method, we loop through all the properties in the value object,
The __ob__ attribute is looping out, and ob is the current instance, which is actually an object, and will continue to be observed, resulting in an infinite loop
__ob__ cannot be traversed, otherwise it will be defineProperty and will loop endlessly.
Frozen: Properties that are frozen cannot be modified, but they can still be traversed
You need to define the __ob__ attribute with defineProperty and configure the OB attribute to be non-enumerable
// src/observe/index.js
class Observer {
constructor(value) {
// value.__ob__ = this; // Can be traversable enumerations, resulting in an infinite loop
// Define the __ob__ attribute to be unenumerable, to prevent the object from continuously defining eProperty when entering the walk, which causes an infinite loop
Object.defineProperty(value, '__ob__', {
value:this.enumerable:false // Cannot be enumerated
});
if (isArray(value)) {
value.__proto__ = arrayMethods;
this.observeArray(value);
} else {
this.walk(value); }}}Copy the code
After execution, the problem is solved:
Three, the end
This paper mainly introduces the observation of array data changes:
- Implement the concrete logic that has overridden the prototype method after the array data changes have been hijacked;
- Analysis of the observation situation when the array data changes;
At this point, the data hijacking is complete
Next, the process of data rendering