vue3.0和2.0对比

  • 2.0跟3.0的变化也挺大的,

    • 结构: 2.0用Flow ,3.0用 TypeScript。
    • 性能: 3.0优化了Virtual Dom的算法。
    • 响应式原理:2.0用 Object.defineProperty,3.0用Proxy

    Vue2.0和Vue3.0实现原理

    1. Vue 2.0

      Vue2.0实现MVVM(双向数据绑定)的原理是通过 Object.defineProperty 来劫持各个属性的setter、getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

      Vue官网也给出了解释:

 ![img](https://user-gold-cdn.xitu.io/2020/3/30/1712bd3f25858548?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)
  1. Vue 3.0 实现响应式基于ES6: Proxy

    Vue2.0和Vue3.0的差异如下:

    Vue2.0

  • 基于Object.defineProperty,不具备监听数组的能力,需要重新定义数组的原型来达到响应式。

  • Object.defineProperty 无法检测到对象属性的添加和删除 。

  • 由于Vue会在初始化实例时对属性执行getter/setter转化,所有属性必须在data对象上存在才能让Vue将它转换为响应式。

  • 深度监听需要一次性递归,对性能影响比较大。

    Vue3.0

  • 基于ProxyReflect,可以原生监听数组,可以监听对象属性的添加和删除。

  • 不需要一次性遍历data的属性,可以显著提高性能。

  • 因为Proxy是ES6新增的属性,有些浏览器还不支持,只能兼容到IE11 。

    Vue2.x实现响应式

    下面是基于Object.defineProperty ,一步步实现简单版Vue2.0。

  1. 由于Object.defineProperty 无法监听数组,所以数组类型实现响应式,需要处理。 判断如果是数组类型,就重写数组的原型方法(‘push’,’pop’,’shift’,unshift)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 重新定义数组原型,Object.defineProperty不具备监听数组的方法
    const oldArrayProperty = Array.prototype;
    const arrProto = Object.create(oldArrayProperty);
    ["push","pop","shift","unshift","splice"].forEach(
    methodName =>
    (arrProto[methodName] = function() {
    updateView();
    oldArrayProperty[methodName].call(this, ...arguments);
    })
    )
  2. 将传入的data属性进行深度监听,判断是对象还是数组。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    function observer(target){
    if(typeof target !== 'object' || target === null){
    return target
    }

    // 如果是数组类型,重写数组原型的方法("push","pop","shift","unshift","splice")
    if(Array.isArray(target)){
    target.__proto__ == arrProto;
    }

    // 如果是对象,遍历对象所有的属性,并使用Object.defineProperty把这些属性全部转为getter/setter
    for(let key in target){
    defineReactive(target,key,target[key])
    }
    }
  3. 核心API Object.defineProperty,将传入属性转为 getter/setter

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    function defineReactive(target, key, value){
    // 如果对象有更多的层级,再次调用observer监听方法,实现深层次的监听。
    observer(value);

    Object.defineProperty(target, key, {
    get(){
    return value;
    },
    set(newValue){
    // 设置值的时候也需要深度监听
    observer(value);

    if(newValue !== value){
    value = newValue;

    // 数据驱动视图,如果数据改变,就调用视图更新的方法。对应到Vue中是执行VDOM
    updateView();
    }
    }
    })
    }
  4. 数据更新会触发视图更新,这是MVVM的绑定原理,这就会涉及到Vue的 template 编译为 render 函数,在执行 Virtual Dom, Diff算法, Vnode等 这些东西了。

    1
    2
    3
    function updateView(){
    console.log('视图更新')
    }
  5. 使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const data = {
    name: "zhangsan",
    age: 20,
    info: {
    address: "北京" // 需要深度监听
    },
    nums: [10, 20, 30]
    };

    observer(data);

    Vue3.0实现响应式

    Vue3.0基于Proxy来做数据大劫持代理,可以原生支持到数组的响应式,不需要重写数组的原型,还可以直接支持新增和删除属性, 比Vue2.x的Object.defineProperty更加的清晰明了。

  6. 核心代码(非常少)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    const proxyData = new Proxy(data, {
    get(target,key,receive){
    // 只处理本身(非原型)的属性
    const ownKeys = Reflect.ownKeys(target)
    if(ownKeys.includes(key)){
    console.log('get',key) // 监听
    }
    const result = Reflect.get(target,key,receive)
    return result
    },
    set(target, key, val, reveive){
    // 重复的数据,不处理
    const oldVal = target[key]
    if(val == oldVal){
    return true
    }
    const result = Reflect.set(target, key, val,reveive)
    return result
    },
    // 删除属性
    deleteProperty(target, key){
    const result = Reflect.deleteProperty(target,key)
    return result
    }
    })
  7. 使用

    1
    2
    3
    4
    5
    6
    7
    8
    const data = {
    name: "zhangsan",
    age: 20,
    info: {
    address: "北京" // 需要深度监听
    },
    nums: [10, 20, 30]
    };

    直接这样就可以了,也不需要声明,Proxy直接会代理监听data的内容,非常的简单方便,唯一的不足就是部分浏览器无法兼容Proxy,也不能hack,所以目前只能兼容到IE11。

    全部源码

    1
    2
    可直接将代码复制到chrome浏览器的控制台,直接调试打印。
    复制代码
  8. Vue2.0

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    function defineReactive(target, key, value) {
    //深度监听
    observer(value);

    Object.defineProperty(target, key, {
    get() {
    return value;
    },
    set(newValue) {
    //深度监听
    observer(value);
    if (newValue !== value) {
    value = newValue;

    updateView();
    }
    }
    });
    }

    function observer(target) {
    if (typeof target !== "object" || target === null) {
    return target;
    }

    if (Array.isArray(target)) {
    target.__proto__ = arrProto;
    }

    for (let key in target) {
    defineReactive(target, key, target[key]);
    }
    }

    // 重新定义数组原型
    const oldAddrayProperty = Array.prototype;
    const arrProto = Object.create(oldAddrayProperty);
    ["push", "pop", "shift", "unshift", "spluce"].forEach(
    methodName =>
    (arrProto[methodName] = function() {
    updateView();
    oldAddrayProperty[methodName].call(this, ...arguments);
    })
    );

    // 视图更新
    function updateView() {
    console.log("视图更新");
    }

    // 声明要响应式的对象
    const data = {
    name: "zhangsan",
    age: 20,
    info: {
    address: "北京" // 需要深度监听
    },
    nums: [10, 20, 30]
    };

    // 执行响应式
    observer(data);
  9. Vue3.0

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    const proxyData = new Proxy(data, {
    get(target,key,receive){
    // 只处理本身(非原型)的属性
    const ownKeys = Reflect.ownKeys(target)
    if(ownKeys.includes(key)){
    console.log('get',key) // 监听
    }
    const result = Reflect.get(target,key,receive)
    return result
    },
    set(target, key, val, reveive){
    // 重复的数据,不处理
    const oldVal = target[key]
    if(val == oldVal){
    return true
    }
    const result = Reflect.set(target, key, val,reveive)
    console.log('set', key, val)
    return result
    },
    deleteProperty(target, key){
    const result = Reflect.deleteProperty(target,key)
    console.log('delete property', key)
    console.log('result',result)
    return result
    }
    })

    // 声明要响应式的对象,Proxy会自动代理
    const data = {
    name: "zhangsan",
    age: 20,
    info: {
    address: "北京" // 需要深度监听
    },
    nums: [10, 20, 30]
    };

作者:rickchen35141
链接:https://juejin.cn/post/6844904111813443598
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!