美文网首页
vue3X 和 vue2X响应式原理对比-简要实现

vue3X 和 vue2X响应式原理对比-简要实现

作者: 云高风轻 | 来源:发表于2021-08-04 09:08 被阅读0次

    1. 前言

    理解响应式有助于理解代码逻辑
    Vue3 基于proxy
    Vue2 基于Object.defineProperty


    2. 什么是数据响应式

    所谓的数据响应式就是能够使数据的变化可以被检测
    并对这种变化做出响应的机制

    我们常见的MVVM框架中要解决的一个核心问题就是连接数据层视图层,
    通过数据驱动应用,数据变化,视图更新,
    需要对数据做响应式处理,这样一旦数据发生变化就可以立即做出``更新处理`

    响应式

    数据变化可侦测, 从而对使用数据的地方进行更新


    3.Vue中的应用

    通过数据响应式 加上虚拟DOMpatch算法,可以使我们只需要操作数据,完全不用接触繁琐的DOM操作,从而大大提升开发效率,降低开发难度


    4. 简要对比

    4.1 Vue2

    基于 Proxy的数据响应式 Vue 2的响应式系统使用 Object.definePropertygettersetter

    Vue2的数据响应式会根据数据类型做不同的处理:

    对象就采用 Object.defineProperty()的定义方式来拦截数据,当数据被访问或者发生变化时,我们感知并作出响应;
    数组则通过覆盖数组原型的方法,扩展它的7个变更方法,使这些方法可以额外的做更新通知,从而做出响应

    这种机制很好的解决了数据响应式的问题,但也存在缺点:

    1.比如初始化的时候递归遍历会造成性能损失
    2.新增或删除属性时需要用户使用Vue.set/delete这样的特殊api才能生效
    3.对于es6中的Map,Set这些数据结构不支持等问题


    4.2 Vue3

    Vue 3将使用 ES2015 Proxy作为 其观察机制,这将会带来如下变化:

    1.组件实例初始化的速度提高 100%
    2.使用 Proxy 节省以前一半的内存开销,加快速度,但是存在低浏览器版本的不兼容
    3.为了继续支持IE11,Vue 3 将发布一个支持旧观察者机制和新 Proxy 版本的构建
    4.编程体验是一致的,不需要使用特殊的api


    5.Vue2实现数据的监听的简要核心代码

    5.1 简要代码

        if(typeof obj !== "object" || obj == null){
            return
        }
        const keys = Object.keys(obj)
        for(let i = 0;i < keys.length;i++){
            const key = keys[i]
            defineReactive(obj,key,obj[key])
        }
        console.log("变了",obj)
    }
    
    function defineReactive(obj,key,val){
        observe(obj)
        Object.defineProperty(obj,key,{
            get (){
                return val
            },
            set(v){
                val = v
                update()
            }
        })
    }
    function update(){
        console.log(obj.yzs)
    }
    const obj = {}
    defineReactive(obj,"yzs","名字")
    obj.yzs = "幸福一家人"
    
    

    5.2 运行

    1.可以作为 nodejs来运行
    2.也可以在前端页面引入查看浏览器控制台结果


    5.3 分析

    1.拦截每个key从而可以侦测数据的变化
    2.只能对于对象支持比较好 , 数组的话就得单独写
    3.遍历每个key成本高:内存大,速度慢
    4.新增或删除 属性无法监听 需要使用特殊的API
    5.Vue.set(obj,"yzs","幸福一家人")
    6.Vue.delete(obj,"幸福一家人")
    7.不支持 Map,Set,Class等数据结构


    6. Vue3响应式简要代码

    6.1 核心代码

    function  reactive(obj) {
        return new Proxy(obj,{
            get(target,key){
                console.log("get 的key",key);
    
                return target[key]
            },
            set(target,key,val){
                // notify 通知
                console.log("set 的key",key);
    
                target[key] = val
            },
            deleteProperty(target,key){
                // notify 通知
                console.log("delete 的key",key);
                delete target[key]
            }
        })
    }
    const state = reactive({
        name:"yzs"
    })
    state.yzs
    state.yzs = "yzs001"
    delete state.yzs
    state.age = 31
    state.age
    

    6.2 分析

    代理整个对象,从而侦测数据变化

    1.语言级别的支持 对象数组 都可以监听
    2.Proxy原理就是 在对象外面套一层壳,这个壳就是Proxy ,
    属于懒处理 不访问不进行处理
    例如:不恰当的列子,Vue2就是全员检测 Vue3就是只针对出门的进行检测
    3.es6的proxy数据响应式,很好的解决了以上问题

    4.上面的代码其实只能检测到 单层对象对象里面嵌套的话检测不到,我把代码贴到下边,有兴趣的可以看看


    7. 嵌套对象监听

    //代理整个对象,从而侦测数据变化
    
    function  reactive(obj) {
        return new Proxy(obj,{
            get(target,key){
                console.log("get 的key",key);
    
                // 依赖手机
                track(target,key)
                return  typeof target[key] === "object"
                 ?  reactive(target[key])
                 :  target[key]
            },
            set(target,key,val){
                // notify 通知
                console.log("set 的key",key);
                trigger(target,key)
                target[key] = val
            },
            deleteProperty(target,key){
                // notify 通知
                console.log("delete 的key",key);
                delete target[key]
            }
        })
    }
    
    // 临时存储副作用函数
    const effectStack = []
    // 1. 依赖 收集函数
    // 包装 fn
    // 立即执行 fn
    // 返回 fn
    function effect(fn) {
        const e = createReactiveEffect(fn)
        e()
        return e
    }
    function createReactiveEffect(fn) {
        const effect = function () {
            try {
                effectStack.push(fn)
                return fn()  
            } 
            catch (error) {
                
            } finally{
                effectStack.pop()
            }
        }
        return effect
    
    }
    // 保存依赖关系的数据结构 
    const targetMap = new WeakMap()
    // 弱引用 不去影响垃圾回收机制
    // 2. 依赖收集:建立 target/key 和 fn 之间的映射关系
    function track(target,key) {
        // 1. 获取当前的副作用函数
       const effect = effectStack[effectStack.length - 1]
       if(effect){
        // 2. 取出 target/key 对应的map
        let depMap = targetMap.get(target)
        if(!depMap){
            depMap = new Map()
            targetMap.set(target,depMap)
        }
        // 3. 获取key 对应的 set
        let deps = depMap.get(key)
        if(!deps){
            deps = new Set()
            depMap.set(key,deps)
        }
        // 4. 存入set
        deps.add(effect)
       }
    }
    // 3. 触发更新函数: 当某个响应数据发生变化,根据 target key 获取对应的 fn并执行他们
    function trigger(target,key) {
        // 1. 获取 target/key 对应的set 并遍历执行他们
        const depMap = targetMap.get(target)
        if(depMap){
            const deps = depMap.get(key)
            if(deps){
                deps.forEach(dep =>dep());
            }
        }
    }
    
    const state = reactive({
        name:"yzs",
        children:{
            age:3
        }
    })
    // state.children.age
    // state.children = {num:2}
    effect(()=>{
        console.log("effect-1",state.name);
    })
    effect(()=>{
        console.log("effect-2",state.name,state.children.age);
    })
    state.name = "yzs001"
    state.children.age = 30
    

    参考资料

    vue3


    初心

    我所有的文章都只是基于入门,初步的了解;是自己的知识体系梳理;
    如果能帮助到有缘人,非常的荣幸,一切为了部落的崛起;
    共勉

    相关文章

      网友评论

          本文标题:vue3X 和 vue2X响应式原理对比-简要实现

          本文链接:https://www.haomeiwen.com/subject/culyvltx.html