学到Vue3X和Vue2X响应式原理对比

本文详细比较了Vue3基于Proxy的响应式机制与Vue2基于Object.defineProperty的实现,讨论了两者的优势、局限及改进,包括性能提升、API简化与兼容性处理。通过实例和代码展示,帮助理解响应式在实际开发中的应用和改进。
今天和大家分享下Vue3X和Vue2X响应式原理对比

1.首先我们要知道:

  1. Vue3 基于proxy
  2. Vue2 基于Object.defineProperty

2.什么是数据响应式

“响应式”,是指当数据改变后,Vue 会通知到使用该数据的代码。
例如,视图渲染中使用了数据,数据改变后,视图也会自动更新。

代码示例

<div id="citing">{{ name }}</div>
//创建一个 Vue 组件:
var vm = new Vue({
  el: '#citing',
  data: {
    name: 'lax'
  }
})
代码执行后,页面上对应位置会显示:lax。

如果想改变显示的名字,只需要执行:

vm.name = 'zs'
这样页面上就会显示修改后的名字了,并不需要去手动修改 DOM 更新数据。
我们常见的MVVM框架中要解决的一个核心问题就是连接数据层和视图层,
通过数据驱动应用,数据变化,视图更新,
需要对数据做响应式处理,这样一旦数据发生变化就可以立即做出``更新处理`

3.Vue中的运用

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

4.简要对比

4.1 Vue2中
基于 Proxy的数据响应式 Vue 2的响应式系统使用 Object.defineProperty的 getter和 setter。
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实现数据的监听的简要核心代码

    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,"lax","名字")
obj.lax = "改变就是好事"

在前端页面引入查看浏览器控制台结果
1.拦截每个key从而可以侦测数据的变化
2.只能对于对象支持比较好 , 数组的话就得单独写
3.遍历每个key成本高:内存大,速度慢
4.新增或删除 属性无法监听 需要使用特殊的API
5.Vue.set(obj,"lax","改变就是好事")
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:"lax"
})
state.lax
state.yzs = "lax001"
delete state.lax
state.age = 22
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:"lax",
    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 = "lax001"
state.children.age = 22

参考资料

vue3云高风轻的笔记

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值