- 响应式原理
Vue 遍历data对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。
官方地址
手撸简化版VUE响应式实现源码
模拟vue中的data数据
const data = {
name: '路灯',
age: 18,
son: {
name: "小灯",
age: 1
},
arr: [1, 2, 3]
}
- 遍历data,使用Object.defineProperty设置每一个属性
function observer(data){
if(typeof data === 'object'){
for(key in data){
defineReactive(data, key, data[key])
}
}
}
function defineReactive(obj, key, value){
Object.defineProperty(obj, key, {
get(){
return value;
},
set(newVal){
value = newVal;
render();
}
})
}
function render(){
console.log('渲染了')
}
observer(data);
此时,如果修改data中的name,age都会打印'渲染了'(用render函数模拟VUE更新视图)。初步实现了,更改数据,视图会响应式更新。但是修改son.name不会渲染,所以需要递归遍历data所有子对象。
- 递归遍历data所有子对象
function observer(data){
if(Array.isArray(data)){
return ;
}
if(typeof data === 'object'){
...
}
}
function defineReactive(obj, key, value){
observer(obj[key]);
Object.defineProperty(obj, key, {
...
})
}
VUE并不对数组属性进行监听,尤雨溪觉得:性能代价和获得的用户体验收益不成正比。所以出现了通过索引修改数组视图并不会更新现象。但是VUE重写了一些数组方法,调用这些方法修改数组会更新视图。
- 数组变异方法
如果属性是个数组,那么就让这个数组的原型等于一个新对象(arrayPrototype)。这个新原型对象里的push,pop等方法会触发渲染。
function observer(data){
if(Array.isArray(data)){
data.__proto__ = arrayPrototype;
return ;
}
重写数组7种方法,这7种方法会触发渲染。
const arrayPrototype = Object.create(Array.prototype);
['pop', 'push', 'shift', 'unshift', 'splice', 'sort', 'reserve'].forEach(method => {
arrayPrototype[method] = function(){
Array.prototype[method].apply(this, arguments);
render();
}
})
经过上面,本来数组对象的隐式原型(_proto_)是 Array.prototype,现在是一个新对象,新对象的原型是Array.prototype。这个新原型对象重写了数组7个方法。
总结
以上在VUE源码的基础上简化,留其思想,以供学习。
VUE源码位置:src/core/observer
本文源码:
const data = {
name: '路灯',
age: 18,
son: {
name: "小灯",
age: 1
},
arr: [1, 2, 3]
}
const arrayPrototype = Object.create(Array.prototype);
['pop', 'push', 'shift', 'unshift', 'splice', 'sort', 'reserve'].forEach(method => {
arrayPrototype[method] = function(){
Array.prototype[method].apply(this, arguments);
render();
}
})
function observer(data){
if(Array.isArray(data)){
data.__proto__ = arrayPrototype;
return ;
}
if(typeof data === 'object'){
for(key in data){
defineReactive(data, key, data[key])
}
}
}
function defineReactive(obj, key, value){
observer(obj[key]);
Object.defineProperty(obj, key, {
get(){
return value;
},
set(newVal){
if(value == newVal) return;
value = newVal;
render();
}
})
}
function render(){
console.log('渲染了')
}
observer(data);