响应式原理
在使用vue时,需要修改设计属性值的时候,页面的数据直接更新。
使用js中 Object.defineProperty()这个方法实现
//上述方法需要的参数
Object.defineProperty(对象,'设置什么属性名',{
writeable // 可编辑
enumerable //控制属性是否可枚举 是不是可以被for in 取出来
set() //赋值触发
get() //取值触发
})
将对象转换为响应式
var o = {
name:'zs',
age:23,
gender:'nan'
}
function defineReactive(target,key,value,enumerable){
//函数内部就是一个局部作用域,这个value就只在函数内使用变量
Object.defineProperty(target,key,{
configurable:true,
enumerable:!enumerable,
get(){
console.log(`读取o的 ${key} 属性`)
return value;
},
set(newVal){
console.log(`修改o的 ${key} 属性`)
value = newVal
}
})
}
//将对象转换为响应式的
let keys = Object.keys(o)
for(let i = 0;i<keys.length;i++){
defineReactive(o,keys[i],o[keys[i]],true)
}
打开浏览器在控制台中输入 o.age = 40
age的值就变成40啦
监听对象的方法是get和set,但是对于数组,data.list.push(1) 进行push操作的时候并不会触发get() set()方法,所以我们要重写数组的方法,将数组改为响应式
// 首先定义一组数组 使用数组时的一些方法
let ARRAY_METHOD = [
'push',
'pop',
'shift',
'unshift',
'reverse',
'sort',
'splice'
]
let array_method = Object.create(Array.prototype);
ARRAY_METHOD.forEach(method =>{
array_method[method] = function(){
console.log('拦截的' + method + '方法')
// 将数组进行响应式化
for(let i = 0;i<arguments.length;i++){
reactify(arguments[i])
}
//调用原来的方法
let res = Array.prototype[method].apply(this,arguments);
return res;
}
})
function reactify(o){
let keys = Object.keys(o);
for(let i = 0;i<keys.length;i++){
let key = keys[i]; // 属性名
let value = o[key]
if(Array.isArray(value)){
value.__proto__ = array_method; //数组就可以响应式了
for(var j = 0;j<value.length;j++){
reactify(value[j]) //递归
}
}else{
// 对象或值类型
defineReactive(o,key,value,true)
}
}
}
function defineReactive(target,key,value,enumerable){
if(typeof value === 'object' && value != null && !Array.isArray(value)){
// 是非数组的引用类型
reactify(value)
}
Object.defineProperty(target,key,{
configurable:true,
enumerable:!enumerable,
get(){
console.log(`读取${key}属性`)
return value
},
set(newVal){
console.log(`修改${key}属性`)
value = newVal
}
})
}
let o = {
name:'zs',
age:22,
course:[
{name:'数学'},
{name:'英语'},
{name:'语文'}
],
obj:{
obj:'coding'
}
};
reactify(o)
这一波操作之后对数组进行添加或者修改最后数组里的值都是响应式的