京东云开发者|关于“React 和 Vue 该用哪个”我真的栓Q


一、前言:我全都要

面对当今前端界两座大山一样的主流框架,React和Vue,相信很多小伙伴都或多或少都产生过这样疑问,而这样的问题也往往很让人头疼和犹豫不决:

  1. 业务场景中是不是团队用什么我就用什么?
  2. 如果选择了其中一个使用,那为什么不用另一个?
  3. 这两个框架各有什么优点和无法解决的问题?
  4. 最新版本的Vue3已经出了一段时间了,我要不要做组内第一个吃螃蟹的勇士?
  5. 我该依据什么样的因素决定使用哪个技术栈?

以上问题如果想不明白,很容易产生一个“算了不想了真麻烦,还是随大流好了,至少不会出错”的答案,其实种种疑问都指向了一个终极问题,那就是关于技术栈的选型
而技术栈选择的合适与否,往往对项目后续的开发有着极大的影响,甚至关系到业务落地的效率和效果。仅仅掌握业务逻辑的开发,已经完全不能满足个人发展了, 就好比一门武林绝学,招式用的再熟,也需要心法辅佐,所以也就引出了本文的主题:

  1. 旨在帮助那些对技术栈选择困难症的同学,并对React和Vue产生一定的认知
  2. 同时也适合那些只了解单一技术栈的小伙伴,可以拓展一下对不同框架的理解

二、正文:到底要啥

本文不会从正面回答上面列出的问题,技术栈的选择往往要依据现实情况从多方面考虑,所以我也将从以下几个方面分别阐述观点,各位读者可以结合自身情况和以下观点,决定React和Vue到底要用哪一个。而其实关于技术选型,很容易代入自己的主观意识,“好和坏”在同样优秀的框架面前更像是一种自我感受,但笔者会尽量从客观的角度去阐述,如果过程中观点出现冲突或有误,欢迎与我交流、指正。

  • 选型对象说明
  • 团队的适用性
  • 兼容性要求
  • 使用层面对比
  • 周边配套
  • 跨端处理
  • 设计思路
  • 性能对比
  • 心智模型
  • 社区生态
  • 开源代码许可协议

1. 选型对象说明

对比对象:React(hooks 版本)、Vue2、Vue3

关于对比对象的选择:

  • React有函数式组件的和类组件两种写法,鉴于 class 写法较老,且这种写法不利于构建工具的Tree-shaking,可能导致构建产物体积增加,而函数式组件的hooks 写法更符合未来的潮流, 所以类组件在此也不做详细的介绍,只选取函数式组件写法的React作为对比对象。
  • Vue2相较Vue3版本而言牢牢占据着大部分 Vue开发者的视野,但是因为Vue官方已经把Vue3作为默认的版本,所以在此同时把Vue2和Vue3作为对比对象。
  • 对比的内容不会涉及到具体的某个API的实现,也不会讲解大篇幅干涩的源码,过于详细的内容不是本文的重点,作为技术选型要从整体出发去考虑。

2. 团队的适用性

在这方面,其实选哪个框架取决于团队全体成员

  • 历史原因:如果你是以开发者的身份刚入职到一个新的环境,并且接手的是一个成熟的项目,处于正常迭代或者维护周期,那千万不要想着颠覆团队已有技术栈,技术栈切换就相当于重构
    而这种重构面临的首要影响就是投入和产出不成正比,相信文章的读者大多也都是扑在各个业务一线上,对业务方来说,采用什么样的技术去实现他们并不关心,并且切换技术栈带来的风险、开发人力和测试回归的成本都难以评估,除非带来巨大价值,否则这也是与我们合作的上下游都难以接受的。
  • 团队习惯:如果你是项目负责人,在抛开对框架本身进行对比的同时,要考虑的是团队成员对技术栈的熟悉程度,在大多数人都对某一项技术栈熟悉、而对另一项技术了解不深的情况下,那更为熟悉的技术栈带来的人效和产出质量,显然能帮助业务快速验证和试错。

注意:不熟悉某项技术,绝不能成为不使用这项技术的托词,从个人提升的角度考虑,学习新的技术栈可以帮助我们扩充思路和视野,如果要做的新项目周期不紧张,也预留了充足的时间,那么新的技术显然可以作为备选项之一。

3. 兼容性要求

  • PC端:React和Vue均不支持IE8,对于个别浏览器兼容模式使用IE内核也可能是不支持的,具体要看使用的内核版本(IE浏览器简直是前端界的噩梦),其他浏览器下可以放心大胆地使用了。
  • H5端:React和Vue 2.x均能使用。

注意:在移动端对于想要尝鲜Vue 3.x版本的同学来说要关注一下,Vue 3.x依赖收集是使用Proxy这个 API,而Proxy在 IOS 端最低支持 IOS10 版本,并且由于这个API具备更底层的对象监听能力,导致polyfill无法完全兼容,已实现的polyfill都是基于Object.defineProperty,并不能完整支持Proxy所有特性(比如数组长度的监测),所以如果业务环境对 IOS9 有兼容需求的情况下,就不要尝试了。

4.使用层面对比

框架引入

    • React和Vue都是渐进式框架,支持 script 标签直接使用,也支持在工程中通过模块化方式引入使用。

Jsx VS Template

    • React:
      采用的 Jsx 在写法上更为灵活多变,属于在 Js 中增加了 HTML 语法,组件的实现思路是All in Js,开发过程中拥有 Js 完整的生态。同时开发工具对 JSX 的支持相比于现有可用的其他Vue模板也比较先进 (比如,linting、类型检查、编辑器的自动完成)。
    • Vue:
      整体思路是 Template 模板语法,跟 Jsx 相比,它是在 HTML 中增加了对部分 Js 语法的支持,在灵活度上不如 Jsx,本质是模板语法无法穷举所有 Js 能力,所以笔者认为Vue内部使用的 slot、directive 等,也恰恰是对模板语法不够灵活所做出的一种补充,使模板语法也能完成跟 Jsx 同样的事情。
      模板语法也有优点,它更接近原生 HTML,对于新手上手更友好,并且在Vue3中,它从模版层面进行静态分析,对静态模版做标记,从而提升 diff 的效率
      值得一提的是,与React一样,Vue 在技术上也支持 render 函数和 Jsx,但只是不是默认的而已。

那么你可能会有疑问,为什么 Template 不去适配所有的 Js 语法?这里举一个例子:Taro

Taro1.x 和 Taro2.x 采用了穷举所有 Jsx 语法的方式,去生成不同平台的代码,导致每次 Jsx 或 Js 语法有更新,这两个版本的 Taro 编译器都要同步去做适配,这是一种重编译时的方案,对 Jsx 的支持其实非常痛苦,所以 Taro3 索性采取了重运行时、轻编译时的重构,以获得编译器对 Jsx 更有好的支持。
并且还有另一个原因是,我们假如 Template 支持了所有 Js 能力,那么势必又导致了 Template 语法变得复杂,也可能和原本统一的 Ecma 规范割裂(层出不穷的小程序就是一个典型的例子,相当于规范之中又出规范,生态之外再造生态),造成了学习成本增加和沉重的编译器。

    • 共性也是有的,都是 DSL,对底层而言,虽然两者采用了不一样的方式实现,但最终都会被编译为渲染函数去执行。

    • 下图是 Jsx 语法示例:

    • 下图是 Template 语法示例:

SFC

    • HTML:React是 Jsx,Vue 是默认的 Template,在这里不在赘述区别,同时需要指出的是,Vue3 相较Vue2而言,Template 下可以允许存在多个根节点,可以减少一些不必要的 DOM 层级。
    • JS:React 组件本身就是 JS 文件,形式采用函数组件和类组件,编程范式上更贴近面向对象 + 官方推崇的函数式。Vue2组件是 Options Api,通过一个个配置项去实现生命周期、状态声明和逻辑开发。Vue3对于部分逻辑处理和Vue2有很大区别,setup 模式下,已经和React越来越趋同了,编程范式是面向过程 + 函数式,官方命名为 Composition Api,可以使同一个功能逻辑更加集中。
    • CSS:React的 CSS 使用方式是直接通过 Import 导入,Vue文件中有专门处理样式的 Style 标签,值得一提的是,Vue3内置状态驱动的动态 CSS,详细可查看官方文档(https://cn.vuejs.org/api/sfc-css-features.html)。
    • 其他思考:React 的函数式组件和Vue3Composition Api,在 ESM 模块规范下对Tree-shaking 更友好,更容易减少构建体积。

组件使用

    • React组件仅需引入即可使用。
    • Vue的组件引入后需要全局或局部注册,且组件内的 Props 的要显式声明。

逻辑复用

    • React的复用主要体现在高阶组件、render props 以及 hooks,但是也有他们对应的不足。高阶组件层级过深时容易带来 props 的命名冲突、来源不明确的问题,并且额外的组件实例会有更多的内存消耗。hooks 的引入,使React的逻辑抽离更容易,完全修复了命名冲突,来源准确,且无额外开销,可以贯彻函数式编程的理念。但是与此同时,因为 hooks 中可能保留着组件状态,也意味着每次React的更新,如果不进行手动优化,不论前后数据是否有变化,每个 hook 都会重新执行,这也是底层架构上额外带来的问题。
    • Vue的组件复用主要是是用 Mixin、Extend、插槽和Vue3的 Function API。Mixin:它和React的高阶组件带来的问题十分类似,响应式数据命名冲突,以及逻辑来源不明确。插槽:主要功能点是组件复用,它解决了数据命名冲突的问题,同时数据来源准确,但是也存在着额外组件实例带来的内存消耗Function API:目前看来是较为优秀的一种逻辑复用方式,没有以上列出的所有问题,虽然和React的 hooks 十分相像,但是本质不同,Vue可以追踪到数据变化,也仅在组件实例化时执行一次。

样式隔离方案

    • React:CSS moduleCSS in JSBEM 命名规范
    • Vue:官方支持 Style scopedBEM 命名规范

TS支持

    • React本身就是 Js 和 Jsx,并且 TS 专门开了后门给做了支持(Jsx 其实一开始没有类型支持,Tsx 的开发体验完全来自于 TS 专门针对 Jsx 制定的一整套推导机制),所以Tsx 的类型支持也很完善
    • Vue2.x来自尤雨溪本人的回答是“因为当初API的设计根本就没有考虑类型系统”,2.x 跟 TS 的整合需要借助 vue-class-component 使用类组件进行开发,所以目前 2.x 版本的Vue对 TS 的支持度较React仍有差距,但是最近随着Vue2.7的发布,可以使 Vue2.x 用上大部分 Vue3 的写法,也使 Vue2.x 就具备了兼容 TS 的能力。
    • Vue3,根据来自官方的建议,IDE 支持需用