Reference:
Vue3 Response principle juejin.cn/post/685666…
Vue3 response principle analysis juejin.cn/post/686439…
Github:github.com/vuejs/vue-n…
Proxy can solve the following problems with Object.defineProperty:
- It’s expensive to recursively traverse an object
- Array responsivity requires additional implementation
- New or deleted attributes cannot be listened on
- Map and set are not responsive
Create responsive data */function reactice(obj){}/* Declare the response function CB (dependent on responsive data) */function effect(cb){}/* Dependency collection: Establish data & CB mapping */function track(target,key){}/* Trigger update: Execute cb */ according to the mappingfunction trigger(target,key){}
Copy the code
Implementation:
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="Width = device - width, initial - scale = 1.0">
<title>Document</title>
</head>
<body>
<div id="app"></div>
<button id="btn">click </button>
<script src="./xiao-reactivity.js"></script>
<script>
const root = document.getElementById('app')
const btn = document.getElementById('btn')
const o = {
name:'kkb'.age:1
}
let obj = reactive(o)
effect(() = >{
// This function is notified of data changes
root.innerHTML = `
<h1>${obj.name}This year,${obj.age}At the age of < / h1 > `
})
btn.addEventListener('click'.() = >{
obj.age+=1
},false)
</script>
</body>
</html>
Copy the code
let targetMap = new WeakMap(a)let effectStack = []/ / storage effect
function reactive(data){
return new Proxy(data,{
get(target,key){
track(target,key)
return target[key]
},
set(target,key,value){
target[key] = value
trigger(target,key)
}
})
}
// Collect dependencies
// targetMap: WeakMap{
// target:Map{
// key: Set[cb1,cb2...]
/ /}
// }
function track(target,key){
const effect = effectStack[effectStack.length-1]
if(effect){
let depsMap = targetMap.get(target)
if(! depsMap){ depsMap =new Map()
targetMap.set(target,depsMap)
}
let deps = depsMap.get(key)
if(! deps){ deps =new Set()
depsMap.set(key,deps)
}
if(! deps.has(effect)){ deps.add(effect) } } }// Perform dependencies
function trigger(target,key){ targetMap.get(target)? .get(key)? .forEach(effect= >effect())
}
// Declare dependencies
function effect(fn){
if(effectStack.indexOf(fn)===-1) {// Without this, some effect statements call target.name and target.age, and the queue will have two identical effects
try{
effectStack.push(fn)// Push the task stack when the program executes to effect
return fn() // Effect is automatically read into the proxy object and stored in the dependency of the corresponding key, or not return, finally will be executed anyway, and there is no more code below
}finally{
effectStack.pop() // Clear the task stack}}}Copy the code