Vue的响应式原理
2021-07-17 本文已影响0人
史蒂夫sdf
vue数据双向绑定是通过数据劫持(Object.defineProperty)+发布者-订阅者模式来实现的
function defineReactive(obj, key, val){
Object.defineProperty(obj, key, {
enumerable:true,//可枚举;改为false,for in时不会被遍历,但使用 "."依然可访问。
configurable:true,//可配置;改为false之后,不能删除修改(不可逆)。
writable:true,//可写入;改为false,当前属性变为只读。
get(){//读取key触发,方法内不能读取key,否则会无限循环调用自身。
console.log('get=>'+val)
return val;//val是闭包值
},
set(newVal){
console.log('set=>'+newVal);
val = newVal;//改变闭包值
}
})
}
发布者-订阅者模式
原始思路,一个商家,一个客户
let saler = {
production: '地瓜',
customerLists: [],
regist(callback){
this.customerLists.push(callback)
},
notify(){
this.customerLists.forEach(v=>{
v(this.production)
})
}
};
let customer = {
name: 'xiaoming',
readProduction(production){
console.log(this.name+' like '+production);
}
};
saler.regist(customer.readProduction);
saler.notify();
多个商家,多个客户
//Saler方法被生产者继承,每个生产者都是独立的
function Saler(prod){
this.production = prod;
this.customerLists = [];
}
Saler.prototype.regist = function regist(callback,context){
this.customerLists.push(callback.bind(context))
}
Saler.prototype.notify = function notify(){
this.customerLists.forEach(v=>{
v(this.production)
})
}
function Customer(name){
this.name = name;
}
Customer.prototype.readProduction = function readProduction(production){
console.log(this.name+' like '+production);
}
let saler0 = new Saler('红薯');
let customer0 = new Customer('xiaoqiang');
saler0.regist(customer0.readProduction,customer0);
saler0.notify();
let saler1 = new Saler('米粉');
let customer1 = new Customer('xiaoli');
saler1.regist(customer1.readProduction,customer1);
saler1.notify();
一个售卖中介,多个生产者,多个客户
// saler作为公共方法被每个生产者使用
let Saler = {
customerLists: [],
regist(callback,context){
this.customerLists.push(callback.bind(context))
},
notify(){
this.customerLists.forEach(v=>{
v(this.production)
})
}
}
function productor(prod){
this.production = prod;
this.__proto__ = Saler;
}
function Customer(name){
this.name = name;
}
Customer.prototype.readProduction = function readProduction(production){
console.log(this.name+' like '+production);
}
let saler0 = new productor('红薯');
let customer0 = new Customer('xiaoqiang');
saler0.regist(customer0.readProduction,customer0);
saler0.notify();
let saler1 = new productor('米粉');
let customer1 = new Customer('xiaoli');
saler1.regist(customer1.readProduction,customer1);
saler1.notify();
发布者-订阅者模式+数据劫持:数据劫持是数据修改时的动作,整个发布者和订阅者都要在这个动作内实现。
//一个订阅者应该对应一个发布者,没有所谓的中介
// saler销售中介作为公共方法被每个生产者使用
function Saler(){
this.customerLists = [];
}
Saler.prototype.regist = function regist(callback,context){
this.customerLists.push(callback.bind(context))
}
Saler.prototype.notify = function notify(){
this.customerLists.forEach(v=>{
v(this)
})
}
function productor(obj){
Object.assign(this,obj);
Saler.call(this);
this.__proto__ = new Saler();//这种继承方式使得每个生产者共用一个销售中介
}
function Customer(name){
this.name = document.getElementById(name);
}
Customer.prototype.readProduction = function readProduction(production){
console.log(this.name,production);
}
function defineItem(key, value, item, productor){
Object.defineProperty(item, key, {
enumerable: true,
configurable: true,
get(){
return value;
},
set(val){
value = val;
productor.notify();
}
})
}
function reactInit(obj,productor){
for(let i in obj){
if(typeof obj[i] === 'object'){
reactInit(obj[i],productor);
}else if(typeof obj[i] !== 'function'){
defineItem(i, obj[i], obj, productor);
}
}
}
function dataInit(obj){
let productor0 = new productor(obj);//初始化发布者对象
let customer0 = new Customer('app');//初始化订阅者对象view
productor0.regist(customer0.readProduction,customer0);//将订阅者的回调函数注册到发布者对象
reactInit(productor0,productor0);//初始化数据
customer0.readProduction(productor0);//初始化view
return productor0;//返回数据对象
}
let vm = dataInit({
production:'红薯',
p2:'番茄',
p3:{
p31:'里脊',
p32:'牛排'
}
})
v2 = dataInit({q:123456})
《JavaScript设计模式与开发实践》书中详细介绍了发布-订阅模式。
vue 的双向绑定原理及实现