为什么要上V3

秦篆原创技术杂谈vue大约 5 分钟

本文是公司内小组讨论使用的,切勿用作实际应用考虑 基于前端技术趋同这个趋势,谈谈为什么我们要使用vue3,以及vue2和vue3的优缺点。

开篇

V3刚刚发布的时候,vue社区曾经出现过一篇文章,文意直指从vue2升级至vue3是一个极度错误的决定。 原文链接open in new window, 彼时尤大还亲自下场和该文作者论证过一些问题。 文章指出以下几点问题:

  1. vue3移除了event api,也就是on,on,off,$once等等,致使从前使用了event bus的项目需要重写这种以vue实例作为事件总线通信工具的结构。
  2. 在一些大型项目的编译程序上,转v3后会遇到相当体量的breaking change,编译过程中修复报错比较困难。
  3. 原来的选项式api向现在的组合式api的转变,让该文作者认为,vue3没有坚持vue自己的风格,转变向了react的类似风格。
  4. 以及其它的一些生态系统和使用反馈的问题,这里就不一一赘述了。

尤大大针对以上问题的回复是这样的:

  1. vue会为所有的版本提供相同的api核心,以达到完全兼容的目的,目前阻碍用户进行升级的内容实际上是第三方的框架,如nuxt等。

    tip

    事实上,几乎所有的api都能在更版本的vue迭代中找到替代方案,如event bus需求,可以使用mitt等方法实现。

  2. 实际使用过 Composition API + < script setup> 的用户在真是开发中的反馈非常积极,证明这是一个有价值的补充, 现在他们中的许多人更喜欢它而不是 Options API。
  3. 我们当然可以更好地处理新 API 的引入,但仅仅因为存在争议,并不意味着它是错误的或者不必要的。实际上,引入大的、新的想法的行为,势必会让那些喜欢呆在舒适区的人感到不安,但如果我们迎合这种心态,就永远不会取得真正的进展。

自从vue3发布大版本3.2之后,逐渐趋于稳定。社区环境中关于vue的内容也逐渐转向vue3而非vue2,同样的,vue研发团队也不再为vue2进行重大功能的开发和更新。这就使得 vue生态发生了改变。

曾经我也是认为vue2的选项式api有着vue自己的特色,但是在使用了setup语法糖之后,☀️嗯真香~。新的组合式api让其它语言的开发者明显降低了进入vue的精力,也让vue开发者更容易接触到其它框架的内容。

接下来说一说vue2和vue3的优缺点

vue2👊vue3

vue2

vue2是一个非常成功也非常值得肯定的框架,一度在git上超越同等架构如react数万个star。应用场景相当广泛,学习成本之低远远超越任何一种js框架,使得vue在众多前端框架中脱颖而出。

我认为vue在2.x版本后能够如此迅速增长用户的原因有三点:

  1. 创新性的响应式数据。
  2. 组合式api。
  3. SFC的文件写法,摒弃了传统的文件分家思想。

以及vue传统的router和vuex。

vue2的缺点也不少:

  1. 相信很多开发者在使用vue2进行开发的时候都遇到了数据更行但是dom无法更新的情况。
  2. 当同一个组件中逻辑过多时,选项式api往往会将同一种逻辑分装在data,method,以及其它的watch等模块中,造成维护困难。
  3. 渲染消耗资源其实是相对大的,比起原生的js来说,往往会消耗更多的资源去处理dom。

vue3

快!自由!这是我对vue3的第一认知。
为什么快?vue3做了一系列的重新设计:

  1. 首当其冲的就是diff算法的优化diff算法解读
  2. 直接改变了响应式数据的劫持方式

    提示

    vue2通过Object.defineProperty(obj, prop, descriptor)的方法去劫持数据,在数据对象上直接定义了一个新的属性或者修改对象属性,等于是重新包装了一次对象,对访问和写入做了拦截。 这样会造成的问题就是:

    1. 递归遍历数据对象属性,消耗大
    2. 新增/删除属性,数据无响应;需要额外方法实现(Vue.set/Vue.delete、this.set/get/$delete)
    3. 数组修改需要额外方法实现(Vue.set),或者通过重写的push/pop/shift/unshift/splice/sort/reverse方法实现

    vue3则通过Proxy(代理)方式,代理了对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。 然后通过通过Reflect(反射)机制,对数据对象的源头进行操作。
    一个响应式的实现实例

    function reactive(target = {}) {
    if (typeof target !== "object" || target == null) {
        return target
    }
    // 代理配置
    const proxyConf = {
        get(target, key, receiver) {
          //只监听对象本身(非原型)属性
          const ownKeys = Reflect.ownKeys(target)
          if (ownKeys.includes(key)) {
            //如果是本身的属性就监听,如果是对象原型的属性就不监听
            console.log("get", key)
          }
          const result = Reflect.get(target, key, receiver)   
          //(惰性)深度监听-->提升性能   
          return reactive(result)  
        },
    
        set(target, key, val, receiver) {
          // 重复的数据不处理   
          if (val === target[key]) {
            return true   
          }
          // 监听是否是新增的key
    
          const ownKeys = Reflect.ownKeys(target)  
          if (ownKeys.includes(key)) {
            console.log("已有的key", key)
          } else {
            console.log("新增的key", key)
          }
    
          const result = Reflect.set(target, key, val, receiver)
          console.log("set", key, val)
          return result //通过return的值可以看出是否设置成功
        },
    
        deleteProperty(target, key) {
          const result = Reflect.deleteProperty(target, key)
          console.log("delete property", key)
          return result //是否删除成功
        },  
    }
    
    // 生成代理对象 
    const observed = new Proxy(target, proxyConf)
      return observed
    
    }
    

    顺便看一下vue3中的响应式实现源码:
    reactive

    可以看到,reactive方法return了createReactiveObject函数,所以去看createReactiveObject。

    createReactiveObject
    createReactiveObject
  3. 按需编译,动态引入。
  4. 新增的片段等特性。 诸如以上的内容等等。
上次编辑于:
贡献者: luolj