什么是响应式
我们先来看一下响应式意味着什么?我们来看一段代码:
m有一个初始化的值,有一段代码使用了这个值,那么在m有一个新的值时,这段代码可以自动重新执行。
let m = 100 //当m变成200,希望下面的代码自动执行一次
//一段代码
console.log(m)
console.log(m * 2)
console.log(m ** 2)
m = 200
上面的这样一种可以自动响应数据变量的代码机制,我们就称之为是响应式的。
响应式函数的设计
我们一开始的思路是封装一个响应式函数,并且设计一个需要响应的数组,将所有响应式函数存储进入数组中。
let reactiveFans = [] //需要响应的数组,全部放入这个数组
//封装一个响应式的函数 watchFn
function watchFn(fn){ //需要响应式的函数传给watchFn,这个函数值作为参数
reactiveFans.push(fn)
}
//对象的响应式
const obj = {
name:'kitty',
age:20
}
//当name发生变化,这些需要重新执行
// console.log(obj.name); //100行代码
// console.log(obj.age);
//将上面那段代码放到函数里面
//这样子或者
// function foo(){
// //100行代码
// console.log(obj.name);
// console.log(obj.age);
// console.log('Hello World');
// }
// watchFn(foo)
//这样子
watchFn(function(){
//100行代码
console.log(obj.name);
console.log(obj.age);
console.log('Hello World');
})
function bar(){
console.log('这个函数不需要任何响应式');
}
obj.name = 'kobe'
reactiveFans.forEach(fn =>{
fn()
})
但是这种设计从数据结构来考虑并不好。考虑替换掉数组。因为这个数组只能存name发生变化的函数,如果要存别的属性发生变化的函数就要用别的数组。
我们考虑定义一个类,当一个属性发生变化我们就重新new一个实例。
class Depend{
constructor(){
this.reactiveFans = []
}
addDepend(reactiveFn){
this.reactiveFans.push(reactiveFn)
}
//遍历,外界只要调用这个方法
notify(){
this.reactiveFans.forEach(fn=>{
fn()
})
}
}
const depend = new Depend()
//封装一个响应式的函数 watchFn
function watchFn(fn){ //需要响应式的函数传给watchFn,这个函数值作为参数
depend.addDepend(fn)
}
//对象的响应式
const obj = {
name:'kitty', //这个对应一个对象 dep对象
age:20 //这个对应一个对象,它们都有各自的数组 dep对象
}
watchFn(function(){
//100行代码
console.log(obj.name);
console.log(obj.age);
console.log('Hello World');
})
obj.name = 'kobe'
depend.notify()
如果要自动监听对象呢?这就需要使用Proxy了。
class Depend{
constructor(){
this.reactiveFans = []
}
addDepend(reactiveFn){
this.reactiveFans.push(reactiveFn)
}
//遍历,外界只要调用这个方法
notify(){
this.reactiveFans.forEach(fn=>{
fn()
})
}
}
const depend = new Depend()
//封装一个响应式的函数 watchFn
function watchFn(fn){ //需要响应式的函数传给watchFn,这个函数值作为参数
depend.addDepend(fn)
}
//对象的响应式
const obj = {
name:'kitty',
age:20
}
//自动监听的方法:Proxy Object.definepropery
const objProxy = new Proxy(obj,{
get(target,key,receiver){
return Reflect.get(target,key,receiver)
},
set(target,key,newValue,receiver){
Reflect.set(target,key,newValue,receiver)
depend.notify()
}
})
watchFn(function(){
//100行代码
console.log(objProxy.name);
console.log(obj.age);
console.log('Hello World');
})
watchFn(function(){
console.log(objProxy.name,"demo ------------");
})
objProxy.name = 'kobe'
//写在set里面,就不需要每次都notify()了
objProxy.name = "curry"
如果要监听多个对象,多个属性呢?也就是要做依赖收集管理。我们得用到一个数据结构WeakMap
class Depend{
constructor(){
this.reactiveFns = []
}
addDepend(reactiveFn){
this.reactiveFns.push(reactiveFn)
}
notify(){
this.reactiveFns.forEach(fn=>{
fn()
})
}
}
const obj = {
name:'harry',
age:18
}
const info = {
address:'南京市'
}
//获取depend函数
const targetMap = new WeakMap()
function getDepend(target,key){
let map = targetMap.get(target)
if(!map){
map = new Map()
targetMap.set(target,map)
}
let depend = map.get(key)
if(!depend){
depend = new Depend()
map.set(key,depend)
}
return depend
}
const objProxy = new Proxy(obj,{
get(target,key,receiver){
const depend = getDepend(target,key)
depend.addDepend(activeReactiveFn)
return Reflect.get(target,key,receiver)
},
set(target,key,newValue,receiver){
Reflect.set(target,key,newValue,receiver)
const depend = getDepend(target, key)
depend.notify()
}
})
//利用技巧传函数
let activeReactiveFn = null
//封装一个响应式函数
function watchFn(fn){
//只有函数执行了才知道用到了谁
activeReactiveFn = fn
fn()
activeReactiveFn = null //防止乱添加
}
watchFn(function(){
console.log("你好啊,这是测试Obj.name的");
console.log(objProxy.name); //调用函数后立马会执行get方法
})
watchFn(function(){
console.log("Hello啊,这是测试Obj.age的22222");
console.log(objProxy.age);
})
objProxy.name = '张三'
objProxy.age = 33
//数据结构推导
// const weakMap = new WeakMap()
// const objMap = new Map()
// const infoMap = new Map()
// objMap.set("name","nameDepend")
// objMap.set("age","ageDepend")
// infoMap.set("adress","addressDepend")
// weakMap.set(obj,objMap)
// // console.log(weakMap.get(obj).get("name"));
Vue3响应式的设计
//当前需要收集的响应式函数
let activeReactiveFn = null
/**
* Depend的优化
* 1》depend方法
* 2》使用Set而不是数组
*/
class Depend{
constructor(){
this.reactiveFns = new Set() //防止二次重新调用
}
// addDepend(reactiveFn){
// this.reactiveFns.push(reactiveFn)
// }
notify(){
this.reactiveFns.forEach(fn=>{
fn()
})
}
depend(){
if(activeReactiveFn){
this.reactiveFns.add(activeReactiveFn)
}
}
}
const obj = {
name:'harry',
age:18
}
const objProxy = reactive(obj)
const info = {
address:'南京市'
}
const infoProxy = reactive(info)
//
function reactive(obj){
return new Proxy(obj,{
get(target,key,receiver){
const depend = getDepend(target,key)
// depend.addDepend(activeReactiveFn)
depend.depend()
return Reflect.get(target,key,receiver)
},
set(target,key,newValue,receiver){
Reflect.set(target,key,newValue,receiver)
const depend = getDepend(target, key)
depend.notify()
}
})
}
//获取depend函数
const targetMap = new WeakMap()
function getDepend(target,key){
let map = targetMap.get(target)
if(!map){
map = new Map()
targetMap.set(target,map)
}
let depend = map.get(key)
if(!depend){
depend = new Depend()
map.set(key,depend)
}
return depend
}
//利用技巧传函数
//封装一个响应式函数
function watchFn(fn){
//只有函数执行了才知道用到了谁
activeReactiveFn = fn
fn()
activeReactiveFn = null //防止乱添加
}
watchFn(()=>{
console.log(objProxy.name,"----");
console.log(objProxy.age,"+++++");
})
watchFn(()=>{
console.log(infoProxy.address,"######");
// console.log(objProxy.age,"+++++");
})
objProxy.name = 'lebro' //它其实不应该执行两次
infoProxy.address = '上海市'
Vue2响应式的设计
//当前需要收集的响应式函数
let activeReactiveFn = null
/**
* Depend的优化
* 1》depend方法
* 2》使用Set而不是数组
*/
class Depend{
constructor(){
this.reactiveFns = new Set() //防止二次重新调用
}
// addDepend(reactiveFn){
// this.reactiveFns.push(reactiveFn)
// }
notify(){
this.reactiveFns.forEach(fn=>{
fn()
})
}
depend(){
if(activeReactiveFn){
this.reactiveFns.add(activeReactiveFn)
}
}
}
const obj = {
name:'harry',
age:18
}
const objProxy = reactive(obj)
const info = {
address:'南京市'
}
const infoProxy = reactive(info)
//Vue2实现的过程
function reactive(obj){
Object.keys(obj).forEach(key=>{
let value = obj[key]
Object.defineProperty(obj,key,{
get(){
const depend = getDepend(obj,key)
depend.depend()
return value
},
set(newValue){
value = newValue
const depend = getDepend(obj,key)
depend.notify()
}
})
})
return obj
}
//获取depend函数
const targetMap = new WeakMap()
function getDepend(target,key){
let map = targetMap.get(target)
if(!map){
map = new Map()
targetMap.set(target,map)
}
let depend = map.get(key)
if(!depend){
depend = new Depend()
map.set(key,depend)
}
return depend
}
//利用技巧传函数
//封装一个响应式函数
function watchFn(fn){
//只有函数执行了才知道用到了谁
activeReactiveFn = fn
fn()
activeReactiveFn = null //防止乱添加
}
watchFn(()=>{
console.log(objProxy.name,"----");
console.log(objProxy.age,"+++++");
})
watchFn(()=>{
console.log(infoProxy.address,"######");
// console.log(objProxy.age,"+++++");
})
objProxy.name = 'lebro' //它其实不应该执行两次
infoProxy.address = '上海市'