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