Vue3 VS Angular

1,001 阅读15分钟

从架构设计出发,使用难度为ReactJS > Vue.js > Angular

Vue3 渐进式JavaScript 框架

一、创建Vue3工程

1.1 基于vue-cli创建

vue-cli已处于维护模式,官方推荐基于Vite创建项目

// 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --- version
// 安装或者升级@vue/cli
npm i -g @vue/cli
//执行创建命令
vue create vue_test

1.2 基于vite创建

vite是新一代前端构建工具,优势如下:

  • 轻量快速的热重载(HMR),能实现极速的服务启动
  • 对TypeScript、JSX、css等支持开箱即用
  • 真正的按需编译,不再等待整个应用编译完成

搭建一个vite项目 (点击查看官网

// 创建命令
npm create vue@latest

二、API设计

2.1 选项式API

我们在Vue2中常常会需要在特定的区域(data,methods,watch,computed...)编写负责相同功能的代码。随着业务复杂度越来越高,代码量会不断的加大;由于相关业务的代码需要遵循option的配置写到特定的区域,导致后续维护非常的复杂,代码可复用性也不高。同一逻辑被分割;代码量多时,反复横跳,开发体验差;mixin命名冲突

2.2 Composition Api

Vue3.0带来了一个全新的特性,显然我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起。

2.1.1 .啰嗦的setup
  1. 模板使用必须return出去
  2. 变量不是响应式的
  3. 定义方法必须加function
  4. setup执行的时机比beforeCreate还要早
  5. this是undefined,vue3弱化this了
<script>
setup() {
    let name = '张三' //不是响应式的
    let age = 20 // 不是响应式的
    function changeName() {
        name = zhang-san
    }
    return {name, age,changeName}
}
</script>
2.1.2 setup语法糖
<script lang="ts" setup>
    let name = '张三'
    function changeName() {
        name = zhang-san
    }
</script>
// 指定组件名时,还需要非setup的script
<script lang="ts">
    export default {
        name: 'Person'
    }
</script>

简化操作,需要安装插件支持

<script lang="ts" setup name="Person123">
    let name = '张三'
    function changeName() {
        name = zhang-san
    }
</script>
// 1.安装setup插件
npm i vite-plugin-vue-setup-extend -D
// 2.vite配置文件配置
vite.config.js
import VueSetupExtend from 'vite-plugin-vue-setup-extend'

export default defineConfig({
  plugins: [
    vue(),
    VueSetupExtend()
  ]
})

Angular类中声明即可在模板中使用

export class LoginComponent {
    name: '张三'
    age: 20
    changeName() {
        this.name = zhang-san
    }
}

vue3 支持选项式,强烈不推荐混合写。

三、响应式系统

Proxy

Vue 3 中则使用了 Proxy 来创建响应式对象,仅将 getter / setter 用于 ref。

Rxjs

RxJS 是一个用于处理异步事件流的库。VueUse 库提供了 @vueuse/rxjs 扩展来支持连接 RxJS 流与 Vue 的响应性系统。

与信号 (signal) 的联系

cn.vuejs.org/guide/extra…

我们也在探索一种新的、受 Solid 启发的、名为 Vapor Mode 的编译策略,它不依赖于虚拟 DOM,而是更多地利用 Vue 的内置响应性系统。

const [count, setCount] = createSignal(0)

count() // 访问值
setCount(1) // 更新值
const count = signal(0)

count() // 访问值
count.set(1) // 设置值
count.update((v) => v + 1) // 通过前值更新

四、TypeScript

TypeScript 这样的类型系统可以在编译时通过静态分析检测出很多常见错误。这减少了生产环境中的运行时错误,也让我们在重构大型项目的时候更有信心。通过 IDE 中基于类型的自动补全,TypeScript 还改善了开发体验和效率。

Vue3 本身就是用 TypeScript 编写的,并对 TypeScript 提供了一等公民的支持。所有的 Vue 官方库都自带了类型声明文件,开箱即用。

五、三方依赖

5.1 axios

Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。

5.2 Pinia

Pinia 起始于 2019 年 11 月左右的一次实验,其目的是设计一个拥有组合式 API 的 Vue 状态管理库。

5.3 vxe-table

是一个基于 vue 的 PC 端表单/表格组件,可以实现增删改查、虚拟列表、虚拟树、懒加载、快捷菜单、数据校验、打印导出等功能。支持配置式表格和表单,提供多种边框、样式、渲染器、插件等选项,面向现代浏览器,高效的简洁 API 设计,模块化表格、按需加载、扩展接口,为单行编辑表格而设计,支持增删改查及更多扩展,强大的功能的同时兼具性能。

5.4 Animate.css

Animate.css库的原理基于CSS的关键帧(keyframes)和动画(animations)属性。这些是CSS3中引入的功能,允许开发者在网页上创建复杂的动画效果,而不需要使用JavaScript或任何其他脚本语言。

5.5 Monorepo

Monorepo 是一种项目代码管理方式,指单个仓库中管理多个项目,有助于简化代码共享、版本控制、构建和部署等方面的复杂性,并提供更好的可重用性和协作性。Monorepo 提倡了开放、透明、共享的组织文化,这种方法已经被很多大型公司广泛使用,如 Google、Facebook 和 Microsoft 等。

5.5.1 Monorepo演进

阶段一:单仓库巨石应用, 一个 Git 仓库维护着项目代码,随着迭代业务复杂度的提升,项目代码会变得越来越多,越来越复杂,大量代码构建效率也会降低,最终导致了单体巨石应用,这种代码管理方式称之为 Monolith。

阶段二:多仓库多模块应用,于是将项目拆解成多个业务模块,并在多个 Git 仓库管理,模块解耦,降低了巨石应用的复杂度,每个模块都可以独立编码、测试、发版,代码管理变得简化,构建效率也得以提升,这种代码管理方式称之为 MultiRepo

阶段三:单仓库多模块应用,随着业务复杂度的提升,模块仓库越来越多,MultiRepo这种方式虽然从业务上解耦了,但增加了项目工程管理的难度,随着模块仓库达到一定数量级,会有几个问题:跨仓库代码难共享;分散在单仓库的模块依赖管理复杂(底层模块升级后,其他上层依赖需要及时更新,否则有问题);增加了构建耗时。于是将多个项目集成到一个仓库下,共享工程配置,同时又快捷地共享模块代码,成为趋势,这种代码管理方式称之为 MonoRepo

Vue需要借助lerna等工具自行搭建,Angular自带library功能。

六、不推荐

6.1 mixin

cn.vuejs.org/guide/reusa…

在 Vue 2 中,mixins 是创建可重用组件逻辑的主要方式。尽管在 Vue 3 中保留了 mixins 支持,但对于组件间的逻辑复用,使用组合式 API 的组合式函数是现在更推荐的方式。mixins 有三个主要的短板:

  1. 不清晰的数据来源:当使用了多个 mixin 时,实例上的数据属性来自哪个 mixin 变得不清晰,这使追溯实现和理解组件行为变得困难。这也是我们推荐在组合式函数中使用 ref + 解构模式的理由:让属性的来源在消费组件时一目了然。
  2. 命名空间冲突:多个来自不同作者的 mixin 可能会注册相同的属性名,造成命名冲突。若使用组合式函数,你可以通过在解构变量时对变量进行重命名来避免相同的键名。
  3. 隐式的跨 mixin 交流:多个 mixin 需要依赖共享的属性名来进行相互作用,这使得它们隐性地耦合在一起。而一个组合式函数的返回值可以作为另一个组合式函数的参数被传入,像普通函数那样。

基于上述理由,我们不再推荐在 Vue 3 中继续使用 mixin。

6.2 Vuex

cn.vuejs.org/guide/scali…

由于 Pinia 在生态系统中能够承担相同的职责且能做得更好,因此 Vuex 现在处于维护模式。它仍然可以工作,但不再接受新的功能。对于新的应用,建议使用 Pinia。

6.3 选项式API

cn.vuejs.org/guide/extra…

选项式 API 确实允许你在编写组件代码时“少思考”,这是许多用户喜欢它的原因。然而,在减少费神思考的同时,它也将你锁定在规定的代码组织模式中,没有摆脱的余地,这会导致在更大规模的项目中难以进行重构或提高代码质量。在这方面,组合式 API 提供了更好的长期可维护性。

6.4 基于Class的API

cn.vuejs.org/guide/extra…

基于 Class 的 API 非常依赖 ES 装饰器,在 2019 年我们开始开发 Vue 3 时,它仍是一个仅处于 stage 2 的语言功能。我们认为基于一个不稳定的语言提案去设计框架的核心 API 风险实在太大了,因此没有继续向 Class API 的方向发展。在那之后装饰器提案果然又发生了很大的变动,在 2022 年才终于到达 stage 3。

Angular 是一个基于 TypeScript 构建的开发平台

作为一个平台,Angular 包括:

  • 一个基于组件的框架,用于构建可伸缩的 Web 应用
  • 一组完美集成的库,涵盖各种功能,包括路由、表单管理、客户端-服务器通信等
  • 一套开发工具,可帮助你开发、构建、测试和更新代码

一、创建工程

1.1 Angular CLI

Angular CLI 是开发 Angular 应用的最快、最简单和推荐的方式。Angular CLI 能简化许多任务。

// 安装脚手架
npm install -g @angular/cli
// 创建应用
ng new my-app
命令详情
ng build把 Angular 应用编译到一个输出目录中。
ng serve构建你的应用并启动开发服务器,当有文件变化时就重新构建。
ng generate基于原理图(schematic)生成或修改某些文件。
ng test在指定的项目上运行单元测试。
ng e2e构建一个 Angular 应用并启动开发服务器,然后运行端到端测试。

1.2 基于esbuild的构建系统

@angular-devkit/build-angular 包中有一个名为 browser-esbuild 的新构建器,它存在于 Angular CLI 生成的应用程序中。该构建是现有 browser 构建器的直接替代品,可提供已稳定的浏览器应用程序构建系统。

Angular CLI 中 Vite 目前仅仅用在开发服务器范围内。当前的开发服务器进程使用新构建系统在内存中生成应用程序的开发构建,并将结果传递给 Vite 为应用程序提供服务。Vite 的使用很像基于 Webpack 的开发服务器,封装在 Angular CLI dev-server builder 中,目前无法直接配置。

二、知识要点

2.1 组件

组件是构成应用的砖块。组件包括三个部分:带有 @Component() 装饰器的 TypeScript 类、HTML 模板和样式文件。

2.2 依赖注入

依赖注入让你可以声明 TypeScript 类的依赖项,而无需操心如何实例化它们,Angular 会为你处理这些琐事。这种设计模式能让你写出更加可测试、也更灵活的代码。

2.3 Angular路由

你可以通过显示或隐藏与特定组件相对应的部分来更改用户看到的内容,而不用去服务器获取新页面。用户执行应用程序任务时,他们需要在定义好的不同视图之间移动。

2.4 Angular表单

Angular 提供了两种不同的方法来通过表单处理用户输入:响应式表单和模板驱动表单。 两者都从视图中捕获用户输入事件、验证用户输入、创建表单模型、修改数据模型,并提供跟踪这些更改的途径。

2.5 HTTP API

Angular 给应用提供了一个 HTTP 客户端 API,也就是 @angular/common/http 中的 HttpClient 服务类。

2.6 Rxjs

RxJS 是一个用于处理异步事件流的库。Angular深度绑定。

2.7 动画

Angular 主要的动画模块是 @angular/animations@angular/platform-browser。当你使用 CLI 创建新项目时,这些依赖会自动添加到你的项目中。

2.8 Angular 库开发

多个项目集成到一个仓库下,共享工程配置,又快捷地共享模块代码,成为趋势。中大型项目,多模块项目,更适合用 MonoRepo 方式管理代码,在开发、协作效率、代码一致性方面都能受益。

ng g lib my-lib

2.9 独立组件

独立组件提供了一种简化的方式来构建 Angular 应用程序。独立组件、指令和管道旨在通过减少对 NgModule 的需求来简化创作体验。现有应用程序可以选择性地以增量方式采用新的独立风格,而无需任何重大更改。

2.10 信号

Angular 信号(Signal)是一个体系,可以精细地跟踪你的状态在整个应用程序中的使用方式和位置(细粒度),从而允许框架优化渲染更新(高性能),提供基于Signals的组件。

Angular 基于 Zone.js 响应式 全局自动变更检测带来的开发体验其实是最棒的,但是经过 Angular 这十年的实践还是有很多问题:

  • 第一就是采用 Zone.js 是一个黑盒,需要不断的维护浏览器新的 API,而且有一些 ES 特性是模拟不了,比如 async await,维护成本很高,另外就是需要提前加载 Zone.js 打补丁,会增加构建体积和运行时间,如果使用了一些第三方库,频繁绑定浏览器事件容易出现性能问题,所以需要 ngZone runOutsideAngular 避免性能陷阱

  • 全局变更检测策略每次都要从根组件向所有子组件检测,性能不好,为了提升性能引入了 OnPush 组件,使用了 OnPush 组件后只有该组件的输入参数引用变化或者当前组件内部触发的事件会触发变更检查,否则就需要手动通过 markForCheck 或者 detectChanges 触发变更

  • 第三就是单向数据流,Angular 假设数据流和 Dom 顺序是相同的,所以从组件树往下检查,但是在前端应用场景不总是如此,比如表单验证状态,需要等所有表单项都验证一遍后才可以确定表单最终的状态,一旦出现这种子组件更新了已经检测完毕的父组件数据会报 ExpressionChangedAfterItHasBeenCheckedError 错误,这就是 Angular 的表单 ngModel 为什么是 Promise 微任务的原因

  • 随着应用越来越大,性能问题就变得重要,这种变更检查无法做更细粒度的视图更新

三、三方依赖

3.1 Ag-Grid

功能齐全且高度可定制的高性能企业级JavaScript数据表格。

那么为什么 Angular 在国内用户偏少呢?

  1. Angular 正式版发布较晚,很多产品的技术选型已经确定。Angular 在 2016 年年底才发布,这时候正是其它框架的发力阶段,而 Angular 此时还存在很多不确定因素,因此无法在市场中脱颖而出也是可以理解的。
  2. Angular 选用的技术栈过于超前。Angular 在发布之初就搭配了 TS 和 RxJS,这种技术选型在当时看来过于超前,毕竟那时候 TS 都被喷的体无完肤。哪怕就现在来说,仍然有很多人不了解 TS 和 RxJS。这也是很多人觉得 Angular 门槛高的主要原因。
  3. 国内前端开发者的心态问题。必须承认一点,国内的前端开发人员大多还是初中级水平,大家只想快速完成需求,不是很关心代码规范、设计模式这些东西。想想 Vue 只是升个级就出现了一大片“学不动了”的怨念,所以谁还愿意花时间学 Angular 呢?
  4. 精通 Angular 的布道者太少。 Angular 最大的难度不在于使用层面,而是它的源码劝退了很多人,读不懂源码就很难魔改,毕竟国内大厂还是有很多定制化需求的。回过头来想想,国内 TS 和 RxJS 的布道者也很少。这一点 Angular 和 Vue 真的没法比,毕竟 Vue 的开发者亲自在知乎做推广。

Vue和React都很好解决了组件实现的问题,但没有解决设计的问题,虽然Vue和React更易上手但如果没有良好的架构设计,后面的维护工作将面临更多挑战。

随着项目代码越来越多,我们可以清晰的发现使用Angular开发应用更便于维护,这不仅仅是TypeScript的功劳,也因为Angular作为一个框架,其Component,Module,Service等功能架构设计意图是完备的,在面对变化时更容易找到内置的方案,避免在项目膨胀的过程中对整个工程的架构设计的冲击。

因为Angular的架构清晰,像对熟悉类似spring这样同样架构清晰的Java工程师来说自然更觉得亲切。

技术没有好坏之分,只有适合与否。短平快的项目,Vue、React更合适;长期的项目缺乏设计能力就使用Angular。