乐趣区

关于前端:为什么我们放弃了-VueVue-和-React-深度对比

原文:为什么咱们放弃了 Vue?Vue 和 React 深度比照
经作者受权转载

我应用 Vue 和 React 曾经很长一段时间了,两个框架上实际代码量都在 10 万行以上。不得不说两者都是很 nice 的,帮忙开发者缩小很多工作量,这类框架是现代化前端开发必备的。然而 Vue 和 React 两者之间的抉择并不像抉择苹果或香蕉一样简略,两者在工程实际上的差距让咱们逐步放弃了 Vue。本文以不一样的角度对两者进行深度比照。

常见摇晃问题、观点

首先,我简短谈谈常见比照我的项目、观点的认识,这些局部内容能够通过一些文章或者 Vue 官网比照文档查到,次要目标是帮忙小白解决入门摇晃问题。我会依据长期实践教训间接下结论,如果你拥护,欢送评论区留言 battle,反正我不会答复你这类问题。

Vue 或 React 文档更丰盛?

两者都有丰盛的文档(包含中文文档),Vue 文档、React 中文,所以不必放心你四六级都过不了,看不懂文档,这都是有眼就行的事~当然,如果你提前懂点 javascript 相干常识也是大大滴好,ES6 语法更佳,能够在这里跟阮老师学习,收费的电子书。文档和继续进阶不是你在两个框架间做抉择的起因。

Vue 的话须要记住各种指令,还有属性细节,免不了常常查文档。React 绝对简略,记住:“函数入口是 props,进口是 html”就行了。

React 学习门槛高?

这个也不是你抉择框架的起因,如果这个也能够作为起因的话,我感觉是因为你懒,给本人找了借口。据我本人学习、实际总结,两个框架都很简略,有手就行,有脑就会,不见得会 React 就比 Vue 牛逼很多。两者都提供了相应脚手架,不便用户应用:

Vue

npm install -g @vue/cli
vue create my-project

React

npx create-react-app my-app
cd my-app
npm start

傻瓜式应用,有限 div 就完事了。

大我的项目用 React,小我的项目用 Vue

这是我以前在华为的时候,外部探讨两个框架比照时下的一个论断。
怎么说呢?这个就是万精油论断,没有参考意义。你如何定义一个我的项目是大我的项目?超过 xx 万行代码?后端 API 超过 xxx 个?无论什么我的项目,都有做“大”的可能,只有失常经营,你就得继续保护,补充新增的需要。框架的可继续演进更为重要。
当然我能够这么跟你说,React 适不适宜“小”我的项目我不晓得,然而 Vue 不适宜“大”我的项目,业务代码超过 5 万行之后问题显著,前面会具体说这点。

XX 大公司也在用 Vue,咱们追随就行了

很多老手在入门一些框架,或者选型组件、计划时会看看哪些大公司曾经用了,防止本人踩坑。当然啦,我也这么做滴。然而大公司可能只是“尝鲜”、“实验性”应用,这些我的项目对他们来说无关紧要,选型失败了,压力给到开发工程师,996 重构就行了,而你,╮(╯▽╰)╭ 我的项目和屎一样也得保护上来。

举个选型谬误的例子,看看大公司怎么援救的。

以前在华为做硬件我的项目的时候,用的原理图软件,那叫一个辣鸡,用着用着总想砸掉电脑。历史问题,选型谬误,但无奈很多我的项目、根底库都在下面,只能硬着头皮搞,迁徙的老本太高,软件厂商程度太差,然而华为牛逼啊,把那个软件大改特改,各种外部数据库都集成在下面,各种自开发的辅助工具,还是能够开发出很牛逼的产品,部门做的单板间断 11 年寰球第一。

如果你感觉你能搞定选型谬误带来的问题,或者你在华为,那当我没说。(PS:以前还用 tcl 写脚本呢,你也能够试试蛋不蛋疼)

Vue 模板简略,React jsx 有学习老本

同上。两者都很简略,一学就会。连这点东东都叫学习老本,我只能说:“我不是针对你,我是说在座的各位都是 ……”
(Vue 的模板有很多工程实际问题,前面详说。)

性能比照

能够看看这个第三方基准测试,两者都挺快的。不过咱们实际过程中发现有差别,大列表渲染、大量数据加载,不做进一步优化的话 Vue 显著比 React 慢。TaskHub 这个网站咱们以前就是用 Vue 写的,起初间接迁徙到 React 前端性能大大晋升,用户体验有显著差距(数据结构、后盾不变)。

深度比照

原本想简略写写,没想到后面写了那么多了,╮(╯▽╰)╭,上面是重头戏,写写实际过程中发现的问题,两个框架的解决思路。如果你还是小白,上面的一些货色可能没接触过,能够看下这篇文章:【译】通过创立雷同的 APP,比照 React 和 Vue,亲自实现一下,理解基础知识。

市场占比

相干 npm 下载量见上图,市场曾经用脚投票了。看到这里,如果你只想晓得选型论断,你能够走了。如果你还说 xx 大公司在用 Vue,跟着就行。能够这么说吧,大公司更多用的是 React,用 Vue 更多的目标是保留相干技术栈能力,多一个抉择,防止 React license 事件再次发生。

  • React 的许可协定到底产生了什么问题?
  • Facebook 认怂 React 专利,但问题仍旧没有解决?

当然,尤大也在这里说过,看 npm 下载量没用,理论应用应该参考 devTool 的下载量。然而 … 为啥我关上的很多网站上面这个标都是亮的?

开发生态

主观来说,作为外围团队成员,显然咱们会更偏爱 Vue,认为对于某些问题来讲用 Vue 解决会更好。如果没有这点信念,咱们也就不会终日为此忙活了。然而在此,咱们想尽可能地偏心和精确地来形容所有。其余的框架也有显著的长处,例如 React 宏大的生态系统

by Vue 官网

生态上的差距是显著的,这点 Vue 官网也抵赖的,很多人因为生态这点迁徙到 React,不过我自己不是很在意,Vue 生态也不差,如果说你用了 React 生态的货色就感觉很牛逼,你的竞争对手也会用,这点并不能给你产品带来多大增值,竞争力还是要靠本人手码进去的好。上面简略带过:

UI 组件

两者的周边 UI 库都挺丰盛的,React 略微多一点,不过这不是选型的要害,本人手写 UI 库也不是什么难事,偶然封装一下原生标签也是很简略的。以前用 Vue 的时候还没有太多 UI 库,手动写了一个性能比拟全的 UI 库,用 rollup 打包,也就 2 万行代码左右,有手就行。

dom 相干的第三方库

Vue 和 React 都有 ref 能够操作 dom,本人封装一下不是什么难事。能够找找有没他人封装好的,拿来主义。

  • Vue:拜访子组件实例或子元素
  • React:Refs and the DOM

小程序(划重点)

有小程序开发教训的同学都晓得,小程序原生开发是很蛋疼的,通常须要借助框架封装,代码转换。常见的有几个框架:

  • Taro(React 技术栈,举荐应用)
  • wepy(Vue 技术栈,强烈不举荐应用)
  • uni-app(Vue 技术栈,能够应用)

这些小程序开发框架都是基于 Vue 或者 React 的二次封装,简化小程序开发。

vue 的一些周边库和 Vue 强绑定,而不是以一个独立 js 库的模式存在。导致代码难以复用,相干 Bug、问题也带到了二次开发的框架中。

这种强依赖导致的问题会给当前我的项目降级、迁徙带来很多问题。
比方 vuex 作为 Vue 官网举荐的状态治理计划,只能在 Vue 下面应用,不能在 React 下面应用。Redux 状态治理在 React 上用的多,这个却能用在 Vue 下面。
相似的问题很多,你会发现 React 周边的货色能够用于 Vue,Vue 的货色不能用在 React 上。

如果你感觉这个问题不重大,当你把 Vue 代码迁徙到小程序 wepy 框架时发现,wepy 不反对 Vuex (bug 异样多),状态治理只能用 redux,欲哭无泪。
同样的问题,如果你用的是 React 相干技术栈,React 迁徙到 Taro 小程序框架异样简略,而且还能一次性生成微信小程序、支付宝小程序、字节跳动小程序等,代码复用率高。

APP 生态

weex、rn 这块我没有比拟好的实践经验,两者用于生产计划都要慎重考虑。rn 比 weex 成熟这点是明确的。

逻辑代码组织

Vue 三种组件写法比照(Js 局部)

Object API 29 lines

import Vue, {PropOptions} from 'vue'

interface User {
  firstName: string
  lastName: number
}

export default Vue.extend({
  name: 'YourComponent',

  props: {
    user: {
      type: Object,
      required: true
    } as PropOptions<User>
  },

  data () {
    return {message: 'This is a message'}
  },

  computed: {fullName (): string {return `${this.user.firstName} ${this.user.lastName}`
    }
  }
})

Class API 17 lines

import {Vue, Component, Prop} from 'vue-property-decorator'

interface User {
  firstName: string
  lastName: number
}

@Component
export default class YourComponent extends Vue {@Prop({ type: Object, required: true}) readonly user!: User

  message: string = 'This is a message'

  get fullName (): string {return `${this.user.firstName} ${this.user.lastName}`
  }
}

Function API 25 lines

import Vue from 'vue'
import {computed, value} from 'vue-function-api'

interface User {
  firstName: string
  lastName: number
}

interface YourProps {user?: User}

export default Vue.extend({
  name: 'YourComponent',

  setup ({user}: YourProps) {const fullName = computed(() => `${user.firstName} ${user.lastName}`)
    const message = value('This is a message')

    return {
      fullName,
      message
    }
  }
})
写法 长处 毛病
Object API Vue 官网写法,不便 Vue 间接解决组件 1. 代码长、缩进多,组件简单时难以理清逻辑,不好进行宰割
2. 混入较多 Vue 的概念,老手学习老本高
Class API 相干概念能够用 class 的思路了解,能够更好地形容 Vue 的混入、data、computed,生命周期钩子等概念。Vue 3.0 将原生反对 class 写法 用到了润饰器语法个性,目前还在试验阶段(typescript 能够应用 helper 函数解决兼容问题,问题不大)
Function API 无状态(局部场景),更好的单元测试、并行化 函数式写法很容易写出回调天堂,导致代码可读性、可维护性差,目前纯正 function api 写法较少见

React 两种组件写法比照(Js 局部)

class 组件 34 lines

import React, {Component} from 'react';

interface P {}

interface S {}

class Index extends Component<P, S> {constructor(props: Readonly<P>) {super(props);
    this.state = {};}

  static defaultProps = {};

  componentDidMount() {}

  componentDidUpdate(prevProps: Readonly<P>) {}

  componentWillUnmount() {}

  render() {
    return (
      <div>
      </div>
    );
  }
}

export default Index;

函数组件 15 lines

import React, {FC} from "react";

interface Props {}

const Index: FC<Props> = (props) => {
  // js 代码

  return (<div></div>);
};

Index.defaultProps = {};

export default Index;

在 js 逻辑局部两者写法没故障,都须要用到框架特定的生命周期钩子,Vue 的 class 写法最为简洁(3 种比照),React 的 function 写法最为清晰(全副写法比照)。这部分不是抉择要害,怎么写是集体爱好。

组件内状态治理

Vue 应用的是数据对象(data),React 应用的是状态对象(不可变 state),这点两个框架的设计不同,如下的问题解决思路也不同。

  1. 我如何批改数据?

    • Vue 间接 this 援用数据对象,间接批改。
    • React 应用 setState 办法批改
  2. 框架如何发现数据被批改?

    • Vue 应用 es5 新办法 Object.defineProperty,劫持 setter、getter 实现数据监听。
    • React,你用了 setState,它通过这个函数就晓得哪些数据变动了。
  3. 我如何发现数据被批改?

    • Vue:应用 watcher,或者 computed 属性发现
    • React:componentWillUpdate、componentDidUpdate 中能够监听变动,或者函数组件的依赖局部插入
  4. 框架何时渲染批改的数据,我如何晓得曾经渲染好了?

    • Vue:在适当的时候渲染,你通过应用 watcher,或者 computed 属性发现
    • React:setState 调用后在适当的时候从新渲染,并调用相干生命周期钩子

在组件状态治理性能上两者都没有太多槽点,如果要说的话就是 Vue watcher 写多了代码一堆缩进,比拟难看,React 也没好多少。

Vue 的数据对象相比 React 的状态对象在代码收缩的时候差距就来了。代码少的时候 Vue 的写法更为简洁,但组件状态很多,须要明确数据更新逻辑时,React 简略的 setState({}, callback),就搞定了,Vue 有点让人摸不到头脑。

Vue 我的项目解决 bug 和疑难杂症三大定理

  1. 没有什么是 deep watch 解决不了的,有就加 immediate
  2. 事件相干,dom 相干记得 nextTick
  3. 切实不行,就用 setTimeout

(来自某个师兄)

React 的不可变(immutable)状态在利用简单时体现出的通明、可测试性更佳。

以上内容比照下来,感觉两者都 OKOK 的,性能也健全,Vue 生态差一点,然而能够本人入手饥寒交迫。上面几点是咱们真正弃用 Vue 的起因。

沃苏艾德布耀布耀德说过:同样的问题,在语言层面上的解决方案才是最佳解决方案。语言生命周期长于框架生命周期

模板语法 VS JSX

上下文失落

Vue 的单文件组件,应用 <template><script> 对代码进行宰割,间接导致的问题就是上下文失落。
举个例子,你封装了一些罕用的函数,在 Vue 文件中 import 进来。你这个函数能在 template 中间接应用吗?

// filter.js 文件
export function isNickname(value) {return /^[\s\S]{1,50}$/.test(value);
}
<template>
  <div>
    {{a}}
    <button @click="a = isNickname('abc')">Test</button>
    {{b}}
  </div>
</template>

<script>
// eslint-disable-next-line no-unused-vars
import {isNickname} from '../fn/filter';


export default {
  name: 'HelloWorld',
  props: {msg: String},
  data: () => {
    return {
      a: false,
      b: 1,
      c: 1,
    }
  },
  methods: {isNickname1() {return isNickname('abc');
    }
  }
}
</script>

<style scoped></style>

上述代码会报错:

[Vue warn]: Property or method "isNickname" is not defined on the instance but referenced during render

所以你只能将办法定义在 methods 中,再援用进来。模板语法并不知道你有 isNickname 这个函数,简略的操作多了 3 行代码。

模板语法不是图灵齐备的,必须转换为 js 代码 (render 函数),放在 component 语境下才行。
相似的例子还有很多,你会发现,你写的代码与 Vue 强绑定了,哪天 Vue 外围库崩了,你代码也崩了。Vue 外围库降级了,周边依赖库也得跟着降级。

模板宰割

好的代码组织能将常变与不变的局部进行宰割解耦

Vue 的模板重大限度了这一点。
举个例子,前端有个下拉菜单,性能一直减少,而且对于不同的人要显示不同菜单(权限治理)。在 Vue 中,为了实现 html 代码(绑定在 template 中)的宰割,你只能再搞一个组件。在 React 中,能够间接这样写:

const menu = <div>abc<div>;

可独自做一个组件(低开销函数组件),也可当做变量,放在以后代码中。绝对灵便很多。

JSX 手写 render 渲染函数自带上面的劣势

  • 残缺的 js 性能来构建视图页面,能够应用长期变量、js 自带的控制流、以及间接援用以后 js 作用域中的值
  • 开发工具对 jsx 的反对比现有 vue 模板先进(linting、typescript、编译器主动补全)

JSX 能够用于 Vue 能够用于 React,就像 Redux 一样。这种语言是与框架解耦的。

“尽管模板语法有那么多问题,然而 Vue 也反对 JSX 呀。”

我猜到你会这么说,但就像下面所说的,既然我肯定要用 JSX/TSX、Redux 了,那我为什么不必 React?

“ 基于 HTML 的模板使得将已有的利用逐渐迁徙到 Vue 更为容易 ”

不会更容易,只会更麻烦。
首先,上面会说到的 template 中无奈很好 linting、type 推断,代码迁徙过来很多 bug 无奈及时发现。其次代码迁徙很大部分都是 js 逻辑的迁徙(这个更重要),迁徙到 vue 中,你须要填鸭式拆分原先代码,放到 computed、menthods 中,工作量不小且代码和 Vue 强绑定。最初,原代码 class、@click 这些货色,有现代化的编辑器,批量 replace 成 className、onClick 不是很简略的事件吗?

Typescript、linting 反对

这点更是致命,Typescript 已成为咱们前端开发必须。类型检测、推断对于代码重构十分重要,哪天后端字段改了,前端能够很不便设配改变,明确晓得代码改变点在哪,构建前就能发现大量谬误。

而 Vue 的模板不反对 typescript(官网还在加强),在模板上反对要很多“hack”操作,原始框架更为简单。
Vue.extend 对象中编写代码很难有比拟好的 ts 推断,为了更好的反对 Typescript,咱们以前都是应用 Vue 的 Class 写法(参考上文)。前端配合后盾改变接口,然而很多未提前查看出的谬误都呈现在模板代码中。

可测试性、重构

Vue 须要新建一个.vue 文件

<template>
  <div>
    {{hello}}
  </div>
</template>

<script>
export default {
  name: 'Test',
  props: {hello: String},
  created() {console.log(this.hello);
  }
}
</script>

<style scoped></style>

React 操作都在 jsx 环境下执行,放的地位随便,写法比模板更容易测试,迭代:

function Test(props: { hello: string}) {console.log(props);
  return <div>{props.hello}</div>
}

Vue 与 React 测试老本的差距显著。React 手起刀落,一个函数就搞定了,要测试什么内容清晰可见。如果要重构 hello 字段,Vue 要记得 template 中的代码也要手动改,React 间接 typescript 类型、属性重构就行了,编辑器自动化。

简单状态、Action 治理

全局状态治理计划选型是很重要的,毕竟 95% 以上的 API 对接代码都在这里,这部分代码占全局代码很大一部分比例,是否复用、重构、测试成为抉择的要害。

Vue 举荐的计划只有强耦合的 Vuex(Redux 迁徙到 Vue 等不算在内)
React 周边计划有 Redux、Mobx 等。这些库不会与 React 有太强的耦合(能够独立存在)。
两个框架的状态治理思维差不多,都是单向数据流、单例模式(Vuex & Redux)。

Vuex

Vuex 的源码不多,能够看这里。能够看到代码中有很多和 Vue 强绑定的东东,脱离了 Vue,这货色就没法用了。你可能会说我就用 Vue,什么 React 不去用不就完了?思考以下场景:

  • 项目经理要把 Vue 的代码迁徙反对小程序,忽然!有的框架不反对 Vuex,脑袋嗡嗡叫
  • 项目经理说要设配 APP 端,忽然!一堆 Bug!脑袋嗡嗡叫
  • 项目经理脑抽,要把 React 我的项目迁徙到 Vue,忽然!redux!用的还是 saga!脑袋嗡嗡叫
  • 状态治理呈现竟态问题!卧槽要写一堆烂码去解决。新人看了脑袋嗡嗡叫

怎么办?!!!这部分的代码比 Vuex 源码都多?
这些问题都是状态治理库和框架强绑定导致的,框架上的问题也会影响到周边库。

if (version >= 2) {Vue.mixin({ beforeCreate: vuexInit})
  } else {
    // override init and inject vuex init procedure
    // for 1.x backwards compatibility.
    const _init = Vue.prototype._init
    Vue.prototype._init = function (options = {}) {
      options.init = options.init
        ? [vuexInit].concat(options.init)
        : vuexInit
      _init.call(this, options)
    }
  }

能够看到,Vue 外围降级,这些随同的库也得降级、测试。在非浏览器环境下运行时,因为 Vue(或类 Vue 框架)的初始化等机制须要改变,会导致相干库,如 Vuex 不可用,多了一个代码分支,相干代码无奈复用、测试、重构负担重。

Redux

Redux 是 React 上比拟罕用的状态治理计划,其设计思维非常简单(见上图),能够独立应用,相干代码容易迁徙到不同平台。衍生出周边异步计划也很多:

  • redux-thunk
  • redux-promise
  • redux-saga
  • redux-observable

选型能够参考这里 & 这里,咱们用 saga 比拟多,解决竟态问题等比较简单,起步多看看文档就能够,也不难。上面这张图能够帮忙你了解几个计划之间的关系,利弊衡量。


相干插件也很丰盛,参考:Redux Middleware。你会发现很多你想要的货色 Vuex 都木有!

“既然这样,我在 Vue 上用 Redux 就行了”

也行,毕竟这样当前迁徙到 React 会简略点。

万恶之源 this 指针

写过 React 函数组件的同学都晓得,相比 class 组件,函数组件少了 this 指针,代码简化、清晰不少。而这个问题在 Vue 上更为严重。全局 this!

有人感觉这是长处,方便使用。等你代码量下来了再来谈话。

当我的项目多人合作的时候,或者承接某某祖传代码,你不全局搜寻,你都不晓得 this 下面挂了羊头还是狗肉。

  • this.ajax
  • this.http
  • this.message
  • this.wtf……

正如一位网友评论:

那货色就是全局作用域。拿“容许在全局作用域上轻易放货色很不便”作为长处的话,和“容许随地大小便会很不便”有什么区别……

写 C 语言的老手都晓得全局变量不要随便用,这满天飞的 this,张三读不懂,李四看不懂,IDE 也不懂。而且这是官网举荐写法,╮(╯▽╰)╭(说全局不太精确,应该说是组件作用域)

想起我以前写的 Vue UI 库,叫 SUE-UI,sue~很快的样子。为了防止当前和其余插件抵触,插件应用都是:

  • this.$su_message
  • this.$su_modal
  • this.$su_toast

往事不堪回首啊!

最初

框架性能上,临时没有发现 Vue 做的来 React 做不来的事件,反过来也一样,两个框架都能满足性能需要。
工程实际上,因为耦合性、代码组织灵活性、平滑降级、测试、重构让咱们最终放弃了 Vue。

在 Vue 中你操作的是定义好的对象,React 中你操作的是一个函数。所谓前端开发,实质就是在编写上面几个函数。

S = async(A1)
S = sync(A2)
UI = f(S)

显然,React 对此的形象更为彻底。
(完)

退出移动版