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

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


初心

我所有的文章都只是基于入门,初步的了解;是自己的知识体系梳理;
如果能帮助到有缘人,非常的荣幸,一切为了部落的崛起;
共勉
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351

推荐阅读更多精彩内容