Memories
In the previous Vue responsive principle - understood Observer, Dep, Watcher simply explain Observer
, Dep
, Watcher
the relationship between the three.
In Observer
pseudo-code, we simulate the following code:
class Observer {
constructor() {
// 响应式绑定数据通过方法
observe(this.data);
}
}
export function observe (data) {
const keys = Object.keys(data);
for (let i = 0; i < keys.length; i++) {
// 将data中我们定义的每个属性进行响应式绑定
defineReactive(obj, keys[i]);
}
}
export function defineReactive () {
// ...省略 Object.defineProperty get-set
}
复制代码
Today we'll learn more Observer
there is also done something.
How to change Array listening?
data
If the data is how to do an array? We found that Object.defineProperty
the array is responsive technology is flawed.
Although we can listen to the change of the index.
function defineReactive (obj, key, val) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: () => {
console.log('我被读了,我要不要做点什么好?');
return val;
},
set: newVal => {
if (val === newVal) {
return;
}
val = newVal;
console.log("数据被改变了,我要渲染到页面上去!");
}
})
}
let data = [1];
// 对数组key进行监听
defineReactive(data, 0, 1);
console.log(data[0]); // 我被读了,我要不要做点什么好?
data[0] = 2; // 数据被改变了,我要渲染到页面上去!
复制代码
But defineProperty
can not detect a change in the length of the array, that is accurate by changing the length and the increased length can not be monitored. This situation can not trigger any change.
data.length = 0; // 控制台没有任何输出
复制代码
And the cost of any indices of listening is also relatively high, consolidation of a number of other factors, Vue used another program to deal with.
First, we observe
need to transform the look, add a separate deal with an array.
// 将data中我们定义的每个属性进行响应式绑定
export function observe (data) {
const keys = Object.keys(data);
for (let i = 0; i < keys.length; i++) {
// 如果是数组
if (Array.isArray(keys[i])) {
observeArray(keys[i]);
} else {
// 如果是对象
defineReactive(obj, keys[i]);
}
}
}
// 数组的处理
export function observeArray () {
// ...省略
}
复制代码
Then the next we should consider Array
changes in how to listen?
Vue
Very simple and crude solution to this problem array, the array is to be able to change the method to do some hands and feet.
We know that there are many ways to change the array, for example such as push
the method it. push
There is Array.prototype
, if we can on
Able to intercept the prototype push
method is not something you can do it?
Object.defineProperty
Objects in the attribute descriptor existing in two main forms: data descriptor and access descriptor . Access descriptor is getter-setter function described by the attribute, that is, we used to make responsive target binding. Object.defineProperty-MDN
While we can not use Object.defineProperty
the array responsive handling, that is getter-setter
, but there are other functions can be for our use. Is data descriptor , data descriptor is an attribute having a value, the value may be written to, may not be written.
value
Corresponding to the attribute value. JavaScript can be any valid value (numeric, objects, functions, etc.). The default is undefined .
writable
If and only if the property
writable
istrue
time,value
in order to be assignment operator change. The default is false .
So we just put the method on a prototype, were value
re-assigned.
The following code in the process of re-evaluation, we can get to all method names and parameters.
function def (obj, key) {
Object.defineProperty(obj, key, {
writable: true,
enumerable: true,
configurable: true,
value: function(...args) {
console.log('key', key);
console.log('args', args);
}
});
}
// 重写的数组方法
let obj = {
push() {}
}
// 数组方法的绑定
def(obj, 'push');
obj.push([1, 2], 7, 'hello!');
// 控制台输出 key push
// 控制台输出 args [Array(2), 7, "hello!"]
复制代码
With the above code that we can know that the user uses the stereotype of the array of methods and parameters we can intercept, the intercept of the notification process can do some changes.
Vue monitor Array trilogy
Next, take a look at Vue
is how to achieve it -
The first step: first get the native Array
prototype method, because the method still need a native to help us achieve changes in the array after interception.
Step Two: The Array
prototype method is used Object.defineProperty
to do some interception.
The third step: the need intercepted Array
type of data transformation prototype after prototype point.
We will carry out reform under the code, or in the process of intercepting the parameters you want passed to the method developer's native ensure that the array is changed in accordance with the developer's idea, then we'll do an updated view of such an operation.
const arrayProto = Array.prototype // 获取Array的原型
function def (obj, key) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
value: function(...args) {
console.log(key); // 控制台输出 push
console.log(args); // 控制台输出 [Array(2), 7, "hello!"]
// 获取原生的方法
let original = arrayProto[key];
// 将开发者的参数传给原生的方法,保证数组按照开发者的想法被改变
const result = original.apply(this, args);
// do something 比如通知Vue视图进行更新
console.log('我的数据被改变了,视图该更新啦');
this.text = 'hello Vue';
return result;
}
});
}
// 新的原型
let obj = {
push() {}
}
// 重写赋值
def(obj, 'push');
let arr = [0];
// 原型的指向重写
arr.__proto__ = obj;
// 执行push
arr.push([1, 2], 7, 'hello!');
console.log(arr);
复制代码
After being changed arr
.
Vue source parsing
array.js
Vue
In array.js
the rewrite of methodsToPatch
the seven methods and prototype rewritten exposed to.
// Object.defineProperty的封装
import { def } from '../util/index'
// 获得原型上的方法
const arrayProto = Array.prototype
// Vue拦截的方法
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
];
// 将上面的方法重写
methodsToPatch.forEach(function (method) {
def(arrayMethods, method, function mutator (...args) {
console.log('method', method); // 获取方法
console.log('args', args); // 获取参数
// ...功能如上述,监听到某个方法执行后,做一些对应的操作
// 1、将开发者的参数传给原生的方法,保证数组按照开发者的想法被改变
// 2、视图更新等
})
})
export const arrayMethods = Object.create(arrayProto);
复制代码
observer
During the data observer
time-bound, we first determine whether hasProto
, if present __proto__
, directly to value
the __proto__
point prototype after the rewrite. If you can not use __proto__
, it looks like some browser vendors did not materialize. Then direct circulation arrayMethods
to the body of these methods it is directly attached to the value
body as well. After all invoke a method is to go to find itself, when it can not find its own methods of closing, only to find the prototype.
// 判断是否有__proto__,因为部分浏览器是没有__proto__
const hasProto = '__proto__' in {}
// 重写后的原型
import { arrayMethods } from './array'
// 方法名
const arrayKeys = Object.getOwnPropertyNames(arrayMethods);
// 数组的处理
export function observeArray (value) {
// 如果有__proto__,直接覆盖
if (hasProto) {
protoAugment(value, arrayMethods);
} else {
// 没有__proto__就把方法加到属性自身上
copyAugment(value, arrayMethods, )
}
}
// 原型的赋值
function protoAugment (target, src) {
target.__proto__ = src;
}
// 复制
function copyAugment (target, src, keys) {
for (let i = 0, l = keys.length; i < l; i++) {
const key = keys[i]
def(target, key, src[key]);
}
}
复制代码
By the above code, we found that there is no direct modification Array.prototype
, but directly arrayMenthods
assigned to value
the __proto__
. Because it does not pollute the global Array, arrayMenthods
only data
the Array
effect.
to sum up
Because the cost of an array of listening and brought some problems, Vue
use the program instead of rewriting the prototype. Some methods to intercept the array, in this process, and then do notice changes and other operations.
Some code in this article are Vue
the source simplified in order to facilitate understanding. Understand the thinking, it is easy to understand the source code.
The series is written ~
- 1. Vue responsive principle - be appreciated Observer, Dep, Watcher
- 2. Responsive principle - how to listen Array of change
Github blog please share ~
Reproduced in: https: //juejin.im/post/5cf606d6f265da1b8e708ba6