尤雨溪:回应『Angular有哪些地方比Vue更优秀?』

1,370 阅读11分钟
原文链接: zhuanlan.zhihu.com

最近 @大漠穷秋 老师发布了一篇对比 Angular 和 Vue 的文章。框架之间的对比虽然是老生常谈,但也确实是绕不过去的话题,Vue 本身的文档里也直接就有和其他框架的对比。同为开源的技术方案,比较本身其实没有任何问题,但在写 Vue 与其他框架的比较的时候,我们尽力做到两点:

1. 确保事实的准确性。有的就是有,没有就是没有,不确定的就不说,弄错了一定改。

2. 确保语气的中立性。别人的缺点指出但不嘲讽,优点大方承认。

之前 @汪志成 对 Vue 跟 Angular 的比较文案提出了意见,我们也对应地进行了修订。也欢迎社区继续进行监督和反馈 —— 比较的目的不是扭曲大家的认知,而是为了帮助大家做出自己的判断。

现在说回来大漠老师的这篇文章,很遗憾,以上两点都不及格。

先说事实。

CLI/工具链

首先两个框架 CLI 的定位不一致。vue-cli 不是一个打包工具,它只是一个 scaffold,也就是初始化工具。真正负责打包的是初始化之后项目内的 webpack 配置和 npm 脚本。从一开始 vue-cli 就是这样的设计意图,项目真正的工具链在项目模板里面而不是 CLI 里面。

相比之下 @angular/cli 是一个全包式的命令行工具,一切都是通过 `ng` 来执行,但这不代表 `ng` 有的命令 Vue 就没有对应的功能 —— 比如在 vue-cli 生成的项目里面:

  • npm run dev 对应 ng serve
  • npm run build 对应 ng build
  • npm run lint 对应 ng lint
  • npm run unit 对应 ng test
  • npm run e2e 对应 ng e2e

除了 i18n 之外,@angular/cli 有的 Vue 都有。『很多日常开发必备的功能都需要开发者自己去下载配置第三方的Node模块』这句话是一个事实上的错误。看起来大漠老师连 vue-cli 生成的项目都没跑过就急着写文章了呢。

其次,CLI 命令/参数多 = 更优秀?并没有这样的道理。如果我们仔细看看文中的截图,ng build 的多个参数,其实就是对应不同的底层 webpack 配置。说实话,我相信不仅仅是我,对于很多其他开发者而言,更宁可直接阅读 webpack 的文档来调整真正的 webpack 配置(并且可以 commit 进项目),而不是去额外学习一套由 Angular 封装的抽象,因为实际生产中需求千变万化,完全被 CLI 封装的配置不利于二次开发。

实事求是地说,@angular/cli 确实有一些值得学习的地方,比如 ng serve 对 SSL 的支持。我们也会在新版本的 vue-cli 中持续吸收改进,但连 vue-cli 能做什么都没弄清楚就拿玩具来打比方,只能贻笑大方了。

异步加载模块

我不知道为什么大漠老师又截了个不知哪里的旧中文文档的图,还没截全 —— 最新的关于异步加载的文档是下面这两个链接:

Vue 将一个组件(以及其所有依赖)改为异步加载,所需要的只是把:

import Foo from './Foo.vue'

改成

const Foo = () => import('./Foo.vue')

就这么简单。这里注意三点:

  1. 当这样分割的时候,该组件所依赖的其他组件或其他模块都会自动被分割进对应的 chunk 里,不存在大漠所暗示的『手动改 500 个组件』这样的情况。况且两边代码分割的功能都是 webpack 提供的,我真不知道大漠老师是真的不懂还是故意误导。
  2. 所谓的路由级别的分割,只需要把这个组件作为路由组件就可以了,甚至连路由配置表都不用改。
  3. 更重要的是这样的异步组件并不一定只能用在路由层面 —— 任何你要用到一个组件的地方,都可以用异步组件无缝替换之,这种灵活性是 Angular 的 loadChildren 根本无法比拟的。比如一个动态的长表单页面,你甚至可以根据用户目前的输入来动态抓取表单接下来的部分(这个用例还是 wepback 的维护者 Sean Larkin 发现并用在生产中的)。

单元测试和集成测试

Vue的单元测试需要你自己去安装配置 Karma + Jasmine

—— 事实错误。vue-cli 的 webpack 模板内置了开箱即用的 Karma + Jasmine 配置,自带了一个初始测试用例,npm run unit 即可。这又双一次证明大漠根本没有跑过 vue-cli 生成的项目。

Vue 的单测不仅仅支持 Karam + Jasmine - 事实上社区有广泛的单测反馈,对于 Jest, Ava 都有实践,我们正在开发中的官方的单测工具库 vue-test-utils (由社区最流行的单测库 avoriaz 的开发者开发)会进一步简化常见的组件单测断言需求,并且还会有和所有主流 test runner 的整合指南。

在集成测试方面,Vue就只能是0分了,因为压根什么都没做。

—— again,事实错误。vue-cli 的 webpack 模板内置了开箱即用的 Nightwatch + Selenium E2E 测试配置,自带了一个初始测试用例,npm run e2e 即可。这又双叒一次证明大漠根本没有跑过 vue-cli 生成的项目。

从对集成测试的支持大家应该可以看出来,在那些技术含量不太高的地方,Vue确实可以抄袭得有模有样,但是一旦技术门槛提高,Vue就没法抄了,或者说抄得没那么快了。

—— 集成测试这种东西,有什么技术门槛可言,还需要抄么?顺便说一句,vue-cli 初始化的项目可是早比 @angular/cli 正式发布前就已经自带集成测试了...

TypeScript

原文直接说了说 TypeScript 有多好,言下之意这是只有 Angular 才有的东西?事实上:

这里我不否认 Angular 是一个 TS 为主导的框架,所以默认的 workflow 跟 TS 的整合会更紧密,但单说 TS 配置完成后的开发体验,并不构成本质的差别。

AOT & Treeshaking

随着你的项目规模越来越大,你会越来越体会到有AOT是多么的重要,而Vue在这一点上根本无能为力,开发者只能自求多福了。

—— again,事实错误。Vue 从 2.0 一开始就对模板编译策略进行了专门的设计:框架本身的 dist 文件分为包含编译器和不包含编译器的版本,前者可以直接在浏览器里编译,后者则专门用于构建时 AOT 编译,并且剥去了编译器所占的尺寸。vue-cli 初始化的项目默认就是 AOT 的状态 —— 请注意,从 Vue 2.0 快一年前发布的时候就已经是这样了,早于 @angular/cli 支持 AOT 的时间。

Vue 在构建时还有很多其他优化,细节可以看我 7 月在 JSConf 演讲的末尾部分。

再说说 Treeshaking。上面的 slides 里面也有提到 Treeshaking 的原理,这里谈几点:

  1. 直到 webpack 3.3 之前,treeshaking 都依赖 rollup。框架本身层面,Vue 的 dist 文件就是用 rollup 打包的 flat ES module(注意,这又是一个 Vue 2.0 发布时就采用的设计,React 后来也采用了同样的构建,而 Igor Minar 提到过 Angular 也想采用同样的发布构建),保证了框架本身尺寸的最小化。
  2. 应用层面的 Treeshaking,@angular/cli 也是要用 rollup 才能达成,而 webpack 3.3 之后终于支持真正的 treeshaking(文档里叫 scope hoisting)—— 任意使用 webpack 的项目只要启用 ModuleConcatenationPlugin 就可以获得 类似 rollup treeshaking 的效果。对于 vue-cli 的模板来说,默认启用就是举手之劳,现阶段只是希望等这个功能更稳定一些。

团队

我很早就说过了,用『一个人』来贬低一个项目是很短视的行为。Ruby 最早只有 Matz,Python 最早只有 Guido,Linux 最早只有 Linus,任何项目,尤其是独立开源项目,都有一个发展的过程。有爹如 Angular 1,下场又如何呢?

Vue 的 contributor graph 有一些细节,不说估计很容易就被忽略了。2.0 的代码在初始开发阶段只有我一个人写,所以绝大部分早期的 commit 都是我的,而且保留了所有细粒度的 commit,比如改个缩进啥的。到后期正式发布之后,commit 就开始强调可读性,一个 feature 一般都是 rebase 成几个比较阶段性的 commit,大部分 PR 也都是 squash 成了一个单独的 commit,所以 contributor 的 commit 会偏少。

还是用数据说话。如果只看近期的状态,这个网站有个指标挺有意思:最近的 100 个 commit 来自多少个 contributor?

Vue 是 23 个,Angular 是 38 个。Angular 更多,但很明显 Vue 也绝不是只有我一个人。

结语

说了这么多,我们可以看到原文里几乎每一个技术比较点都经不起推敲,并且可以很明显的看出,作者对于 Vue 生态缺乏足够的了解,为了贬低而贬低的意图太过明显,甚至把一些在 Vue 当中更早落地的技术细节看做是在抄袭 Angular...

在我以前的一个回答中 (尤雨溪:什么才是你心目中的前端圈?) 我说过希望中文前端圈能少一些戾气,所以我这里尽量只谈技术上的事实,并且:

希望 Vue 的用户和支持者不要对大漠或 Angular 进行不理性的攻击。

希望 Vue 的用户和支持者不要对大漠或 Angular 进行不理性的攻击。

希望 Vue 的用户和支持者不要对大漠或 Angular 进行不理性的攻击。

重要的话说三遍。这篇文章不是为了撕逼,只是为了澄清一些技术上的事实。对自己要求更高一点的同学,可以不要站队。把自己跟一个框架捆绑不是什么好事,你的自信应该来自于你驾驭框架的能力,而不是被框架驾驭的能力。

大漠在另一个回答下面(知乎用户:为什么vue的高仿项目层出不穷,而React和angular却很少?) 也写过这样的一段话:

我曾经问过我的老板和Angular团队的负责人这样一个问题:在中国国内的网络上经常出现针对Angular的讨论,有一些内容非常偏激,甚至可以说【恶毒】,作为Angular在国内推广的负责人,我可以去和这些人针锋相对吗?我的老板是这样跟我说的:“战胜”竞争对手不是Angular团队的目标,我们的目标是帮助开发者和企业更好地开发面向未来的WEB应用。很平常的一句话,但是给我留下的印象非常深刻,因为我看到了一个全新的角度,原来他们是这样去思考问题的。反观国内的很多技术讨论区,很多情况下会非常快速地陷入人身攻击和胡扯的境地,给人的感觉就像泼妇骂街一样。尤其是前端社区,越来越像娱乐圈。所有的参与者,包括提问题的人,请你们安静一秒钟,仔细想想,你自己想要什么?在口水战中战胜对手能帮助你提高编码技巧,还是能帮助你提高认识世界的高度?

在那个充满戾气的问题下面看到这样一段话,当时我是很佩服大漠的。然而... 现在这两篇文章,真让我怀疑大漠是被人盗号了...

如果你没有被盗号,摸着你的良心问问,你,脸不脸红?