Vue3 responsive principle

 Responsive principle

Vue2 uses Object.defineProperty Vue3 uses Proxy

Shortcomings of 2.0

The object can only hijack the set data, and the new data needs Vue.Set(xxx) array can only operate seven methods, modifying a certain value cannot be hijacked.

Implementation of reactive and effect

export const reactive = <T extends object>(target:T) => {
    return new Proxy(target,{
        get (target,key,receiver) {
          const res  = Reflect.get(target,key,receiver) as object


          return res
        },
        set (target,key,value,receiver) {
           const res = Reflect.set(target,key,value,receiver)


           return res
        }
    })
}

 The responsive principle of Vue3 relies on the core API of Proxy, through which certain operations of objects can be hijacked.

effect track trigger

Implement the effect side effect function

let activeEffect;
export const effect = (fn:Function) => {
     const _effect = function () {
        activeEffect = _effect;
        fn()
     }
     _effect()
}

 Use a global variable active to collect the current side effect function, and call it when initializing

implement track

const targetMap = new WeakMap()
export const track = (target,key) =>{
   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)
   }

   deps.add(activeEffect)
}

After the execution is completed, we get a data structure as follows 

 

implement the trigger

export const trigger = (target,key) => {
   const depsMap = targetMap.get(target)
   const deps = depsMap.get(key)
   deps.forEach(effect=>effect())
}

 When we make an assignment, we will call set and then trigger the side effect function of collection

import {track,trigger} from './effect'


export const reactive = <T extends object>(target:T) => {
    return new Proxy(target,{
        get (target,key,receiver) {
          const res  = Reflect.get(target,key,receiver) as object

          track(target,key)

          return res
        },
        set (target,key,value,receiver) {
           const res = Reflect.set(target,key,value,receiver)
 
           trigger(target,key)

           return res
        }
    })
}

 Add these two methods to reactive

test code

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>

    <div id="app">

    </div>

    <script type="module">
        import { reactive } from './reactive.js'
        import { effect } from './effect.js'
        const user = reactive({
            name: "小满",
            age: 18
        })
        effect(() => {
            document.querySelector('#app').innerText = `${user.name} - ${user.age}`
        })

        setTimeout(()=>{
            user.name = '大满很吊'
            setTimeout(()=>{
                user.age = '23'
            },1000)
        },2000)

    </script>
</body>

</html>

Recursive implementation of reactive

import { track, trigger } from './effect'

const isObject = (target) => target != null && typeof target == 'object'

export const reactive = <T extends object>(target: T) => {
    return new Proxy(target, {
        get(target, key, receiver) {
            const res = Reflect.get(target, key, receiver) as object

            track(target, key)

            if (isObject(res)) {
                return reactive(res)
            }

            return res
        },
        set(target, key, value, receiver) {
            const res = Reflect.set(target, key, value, receiver)

            trigger(target, key)

            return res
        }
    })
}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>

    <div id="app">

    </div>

    <script type="module">
        import { reactive } from './reactive.js'
        import { effect } from './effect.js'
        const user = reactive({
            name: "小满",
            age: 18,
            foo:{
                bar:{
                    sss:123
                }
            }
        })
        effect(() => {
            document.querySelector('#app').innerText = `${user.name} - ${user.age}-${user.foo.bar.sss}`
        })

        setTimeout(()=>{
            user.name = '大满很吊'
            setTimeout(()=>{
                user.age = '23'
                setTimeout(()=>{
                    user.foo.bar.sss = 66666666
                },1000)
            },1000)
        },2000)

    </script>
</body>

</html>

 

 

Guess you like

Origin blog.csdn.net/qq1195566313/article/details/127563892