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

简介: 理解响应式有助于理解代码逻辑Vue3 基于proxyVue2 基于Object.defineProperty

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




相关文章
|
18天前
|
JavaScript 前端开发 开发者
Vue是如何劫持响应式对象的
Vue是如何劫持响应式对象的
19 1
|
18天前
|
JavaScript 前端开发 API
介绍一下Vue中的响应式原理
介绍一下Vue中的响应式原理
26 1
|
21天前
|
监控 JavaScript 算法
深度剖析 Vue.js 响应式原理:从数据劫持到视图更新的全流程详解
本文深入解析Vue.js的响应式机制,从数据劫持到视图更新的全过程,详细讲解了其实现原理和运作流程。
|
23天前
|
JavaScript
Vue 双向数据绑定原理
Vue的双向数据绑定通过其核心的响应式系统实现,主要由Observer、Compiler和Watcher三个部分组成。Observer负责观察数据对象的所有属性,将其转换为getter和setter;Compiler解析模板指令,初始化视图并订阅数据变化;Watcher作为连接Observer和Compiler的桥梁,当数据变化时触发相应的更新操作。这种机制确保了数据模型与视图之间的自动同步。
|
1月前
|
缓存 JavaScript 搜索推荐
Vue SSR(服务端渲染)预渲染的工作原理
【10月更文挑战第23天】Vue SSR 预渲染通过一系列复杂的步骤和机制,实现了在服务器端生成静态 HTML 页面的目标。它为提升 Vue 应用的性能、SEO 效果以及用户体验提供了有力的支持。随着技术的不断发展,Vue SSR 预渲染技术也将不断完善和创新,以适应不断变化的互联网环境和用户需求。
62 9
|
18天前
|
JavaScript 前端开发 API
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
46 0
|
2月前
|
API
vue3知识点:响应式数据的判断
vue3知识点:响应式数据的判断
29 3
|
3月前
|
缓存 JavaScript 前端开发
「offer来了」从基础到进阶原理,从vue2到vue3,48个知识点保姆级带你巩固vuejs知识体系
该文章全面覆盖了Vue.js从基础知识到进阶原理的48个核心知识点,包括Vue CLI项目结构、组件生命周期、响应式原理、Composition API的使用等内容,并针对Vue 2与Vue 3的不同特性进行了详细对比与讲解。
「offer来了」从基础到进阶原理,从vue2到vue3,48个知识点保姆级带你巩固vuejs知识体系
|
2月前
|
缓存 JavaScript UED
优化Vue的响应式性能
【10月更文挑战第13天】优化 Vue 的响应式性能是一个持续的过程,需要不断地探索和实践,以适应不断变化的应用需求和性能挑战。
37 2
|
2月前
|
JavaScript 前端开发 网络架构
如何使用Vue.js构建响应式Web应用
【10月更文挑战第9天】如何使用Vue.js构建响应式Web应用