写点什么

从 Angular 转到 React,网易严选的前端工程化实践

  • 2020-10-23
  • 本文字数:5561 字

    阅读完需:约 18 分钟

从 Angular 转到 React,网易严选的前端工程化实践

前端日益发展,从最初的 HTML、CSS、JavaScript 三大基础,到后来的 jQuery、Backbone、AngularJS,再到现在的 Angular、React、Vue 三大框架流行,技术的演进既带来了更多的可能,也带来了一些问题。例如:团队如何高效合作、项目如何统一维护、代码如何规范等等。前端工程化的出现,就是为了解决这些日益突出的问题。它旨在制订规范化的前端工作流,并规范统一项目的模块化开发和前端资源,让代码的维护和互相协作更加容易更加方便。


今年我们团队由 Angular 技术栈转变成 React 技术栈,在这个大背景下,我们急需一套完善的工程化方案来帮助技术栈落地。在通过确定目标、定义规范、技术调研、开发实现等一系列步骤之后,制定了一套完善的工程化方案。它帮助解决开发流程中的问题,让开发更加专注业务本身,提高整个系统生产效率。


目标定义作为一个工程化方案,最终的目标是尽可能解决项目生命周期里遇到的问题,例如:


  • 规范保障


每个团队都会根据实践经验,总结出一套自己的规范(项目规范和流程规范)。让这一套规范在落地到实际的开发中时,除了人为的约束,更多的应该是通过工具约束。工程化就是把团队的经验沉淀到脚手架和开发套件中,让新项目或新成员可以复用这些经验。


  • 提效


一个好的工程化方案,是可以提升开发效率的,这个提效包括初始化项目,完备项目的 dev 环境,数据 mock,build 打包等一系列流程,既要让项目“搭建 - 开发 - 部署”更快捷高效,也要方便项目的后期维护和迭代。


  • 减少人力


在开发过程中可能会遇到一些重复人力的工作。例如 A、B 两个系统有一个相似的列表页,这个列表页不能作为组件抽离,但是又在多处能用到,这部分资源(代码模板)复用急需一个中间媒介来完成。假如资源 / 项目需要升级了,大多情况是资源开发者出一个升级文档,然后升级的人按照文档一步步修改。这是很重复的一份工作,因为假如你手上有十个项目,你就需要把相同的事情做十遍,这一步重复的工作我们希望可以通过工具来完成。


同时,我们的工程化方案应该符合以下四个特点:


  • 渐进式(技术演化能力)易于迭代

  • 扩展性(可伸缩、可插拔)易于部门共建

  • 易用性(贴合实际)易落地

  • 数据统计与分析

方案设计

基于以上的目标,下面来看一下整个工程化的方案设计。


首先,介绍整个方案的架构图:


架构图

可以看到整个架构图分为四层:

底层依赖

最底层依赖了规范、webpack、schematics、node、eslint,它是上次架构的基石。


  • 规范这里包括了项目规范和流程规范,是整套方案需要遵守的约定;因为这套规范是根据大量的实际业务总结出来的,所以它是贴合业务场景,便于整体方案落地的。

  • schematics 引入主要是为了解决上面说到的资源相关的问题,后面会详细讲到。

插件封装层

这一层主要是对上层需要实现的功能做一个划分,然后通过插件的形式实现。例如:


  • 将一系列模板抽离作为一个集合,然后将项目初始化时需要做的模板选择和模板处理放到 init 插件中完成,不同团队可以根据规范自定义插件;

  • 开发编译阶段需要的 webpack 和 server、build 脚本放在 @sharkr/scripts 插件中完成,不同团队可以根据规范自定义插件;

  • 前面说到的资源复用和资源升级问题,统称为文件处理相关,@sharkr/schematics-cli 插件提供这个能力;

  • @sharkr/eslint-config-react 插件提供 eslint 检查;

  • 还有一部分公共能力封装成 util 插件,方便共享。


这种插件的方式可以使工具具有很强的扩展性,在跟其他团队共建时,可以很方便的做一些自定义,但是又符合统一的规范。

统一命令层

第三层是统一命令层,这一层做的事情就是,最终对用户暴露一个统一的工具 @sharkr/cli ,在 cli 里去规范常用的一些命令,当然这些命令的具体实现都是调用下一层的插件。这些命令会应用到项目开发的生命周期中去。


在这一层中会添加埋点,用于收集命令、项目、使用包等相关情况,以便做数据的统计与分析,了解实际落地情况,也为未来迭代版本提供有效参考。

项目生命周期

项目生命周期,就是指项目从初始化,到开发(或后期迭代),再到 CICD 的这么一个周期。可以看到下一层的命令大多都可以对应到项目生命周期中,例如:


  • init 完成项目初始化

  • dev 可以使开发者快速的进入开发

  • add 可以给项目增加一个资源

  • generate 可以执行资源里定义的命令

  • update 帮助快速升级资源

  • lint 编码规范校验

  • test 对开发过程中的代码做一个测试

  • build 在 CI 阶段可以帮助完成编译打包


通过这整个架构可以看到,我们的工具可以介入到项目生命周期里的所有环节,那么这些环节有的一些问题和痛点,也可以通过工具去解决。下面将会从项目生命周期的角度讲解一下我们工程化的一个具体方案。

项目初始化

首先,在项目初始化过程中需要解决的一个问题就是:帮助规范落地。


把规范分为项目规范和流程规范,流程规范上面也介绍了,通过工具做了一个约束,那么项目规范怎么在初始化的时候落地呢?


我们的做法是,将这部分规范落地到模板当中,用户通过 @sharkr/cli 去 init 项目,选择合适的模板,init 插件完成模板处理,然后就初始化了一个符合规范的项目。


这里提到选择合适模板,init 插件完成模板处理,那 init 插件是怎么做的呢?

init 插件

每个团队都会有一些常用的业务场景,例如我们 B 端,会维护好几个模板,纯 web 的、带 node server 的、应用于微应用的,那么这一系列模板作为一个集合,配套的会出一个相应的 init 插件,这个插件可以完成这系列模板的初始化处理。


执行 init 命令时,cli 调用对应的 init 插件,用户根据提示输入项目相关配置项,init 插件根据配置项处理模板。


假如说其他团队也有自己的模板,那么同样的,他们也可以根据规范提供模板配套的 init 插件供 cli 调用,方便共建。


这是插件的一个优势,可以方便扩展自定义的部分,但是它也带来了一个小问题。这部分插件不是很稳定,可能经常需要更新,它的频繁更新会给用户带来影响。这就需要有一个较好的插件更新机制,方便迭代。

插件更新

先来看一下最初采用的插件更新机制,如下图:



  • 用户执行 sr init myapp,调用 @sharkr/cli

  • @sharkr/cli 检查 init 插件版本

  • 发现版本不是最新的,提示用户 update @sharkr/cli

  • 用户全局 update @sharkr/cli

  • 再次执行 sr init myapp, 调用 @sharkr/cli

  • @sharkr/cli 调用最新的 init 处理


这种方式需要用户经常手动更新,不是很友好,所以在后来方案设计时,改用了 npx 的方式调用 init 插件。


npx 是 npm5.2 版本中新增的命令,它能临时下载一个模块并且运行它,运行之后再删除这个模块。



  • 用户执行 sr init myapp,调用 @sharkr/cli

  • @sharkr/cli 使用 npx 调用最新的 init 插件处理


通过方案二,可以看到使用 npx 的方式调用插件,可以使用户在任何时候使用 init 命令都能调用最新的 init 插件完成项目初始化。


说完了项目初始化,再来看一下开发 / 迭代过程中会遇到哪些问题。

开发 / 迭代

完成项目初始化进入开发阶段,这个阶段应该是关注业务本身、减少重复工作、高效快速的。

dev 环境

开始项目开发遇到的第一个问题就是 dev 环境。需要 webpack 配置编译代码、需要一个本地 server、需要一个数据转发、需要 eslint 配置规范编码等。


这一部分可以通过 @sharkr/scripts 提供 webpack 配置和 server 脚本,在模板里集成调用,用 koa 起一个本地服务,koa 中间件完成转发等。@sharkr/eslint-config-react 定义编码规范。在这个环节规范了流程和配置,有效的保障项目质量。


有了一个完备的 dev 环境,再来看一下前面说到的资源复用和资源 / 项目升级。

资源复用和资源 / 项目升级

资源复用


简单把资源分为两类,一类是有固定输入输出,可抽离的,例如时间控件,下拉列表等;还有一类是无固定输入输出,不可抽离,但是又具有某些共性的,例如 B 端常见的列表页、详情页,这一部分资源没有一个很好的复用方式。


对于第一类资源,可以采用封装组件的形式完成资源共享:



对于第二类资源,以前的做法基本是 Ctrl+c、Ctrl+v 来复用,这种做法不够高级,还效率不高,所以在 cli 中提供了另外一种做法,那就是将代码模板抽离到一个 schematic 包里,然后通过 @sharkr/cli 来安装这个包,并且执行里面的 schematic。



资源 / 项目升级


同样的,假如项目或者是资源需要升级,就需要在项目中升级依赖,可能还需要修改配置、甚至调整目录结构、修改 api 调用等。这种升级以前的做法是,出一份升级文档,升级者按照文档一步步修改项目完成升级。假如有几十个项目,就需要几十个人做相同的事几十遍,这无疑是一个大的人力消耗,作为一个提效工具,就是需要将这部分重复的工作通过代码完成。



把升级文档里做的事情编写成 schematic 包


通过 @sharkr/cli 来安装这个包,并且执行里面的 schematic


这么一看,好像很简单,不管是资源复用,还是项目升级,都写成 schematic 包就好了,那么 schematic 到底是什么呢?

schematics

简介


schematics 是 ngCli 团队开发的一个强大和通用的工作流程工具,开发者可以将变换应用于项目中,例如:创建新组件、添加配置项、修改现有项目,或者更新你的代码来修复更新依赖时带来的 break change。


原理


schematics 如它名字一样,可以理解为一个描述了具体操作的原理图。


schematics 的输入是一个树,包含一个基础区域(一组已经存在的文件)和一个临时区域(要应用于基础区域的更改列表)),schemtics 描述了对 Tree 的修改,并将这些修改合并到临时区域的更改列表中,再往外输出一个新的 Tree。


在整个操作完结,并得到确认后,链条中所有描述的修改才会真正被应用。


更多 schematics 知识:


https://angular.io/guide/schematics


优势


和常规的 JavaScript 脚本工具相比,它的优势在哪呢?


  • 开发便捷


提供了丰富而强大的通用能力,帮助开发者快速开发:在 code generate 领域,可以通过使用其模板能力,构建各种类型的动态场景代码模板,快速生成代码。同时还提供了丰富的 util,提供包括 ast、git 初始化、TslintFixTask tslint 处理等能力。


  • 易于调试


schmatics 由于其虚拟树的设计,在开发阶段支持干运行,并不会对文件系统执行任何直接操作,方便开发者在项目中进行调试,安全无污染。


  • 可扩展性和可重用性


从其原理可以看出,schmatics 的整体设计,遵循了函数式范式,schematics 不会产生副作用(副作用只会被记录在缓存区中),具备原子性。schematics 可以自由 Compose 成新的 schematics。


  • 测试友好


schmatics 提供了完备的测试支持库,测试用例书写没有障碍。


schematics 封装


既然 schematics 处理文件很友好,而且刚好能解决文件处理问题,于是我们就引入了 schematics 完成文件处理,并对它做了一层封装。



  • 最底层主要依赖 @angular-devkit/schematics 和 @angular-devkit/core 提供 schematics 基础能力;

  • 将公共能力提取到 @schematics/util 方便开发调用;

  • 提供 schematics 开发模板,方便开发新建 schematics 包;封装 schematics 的 cli,也就是说它也可以单独调用命令;

  • 资源(通用资源、项目改造资源、模板升级资源等)会做一些整合,这些资源集合将作为物料维护在物料海,将来会跟我们正在开发的物料平台对接;

  • 向上暴露所有的命令,最终在 @sharkr/cli 作为统一出口。


下面再来看下,开发和使用一个 schematics 包需要做点什么呢?



首先,对于开发者来说,需要做以下三步:


  • 根据 schematics 规范开发一个 schematics 包

  • 定义配置项(可选)

  • 定义 schema 命令


其次,对于用户来说,需要做以下三步:


  • 使用 @sharkr/cli 调用 schematics 包

  • 输入配置(可选)

  • 完成项目修改


那么 @sharkr/cli 做了什么了?


  • @sharkr/cli 调用 @sharkr/schematics 插件

  • schematics 插件解析出自定义配置项,生成用户会话

  • 根据用户会话拿到 option,传入 option 调用 Rule 函数

  • 修改文件放到暂存区

  • 确认修改后更新物理文件


这里 Rule 函数是需要开发者实现的,任何你想写成文档的都能通过代码方式写在这个 Rule 函数里。


schematics 实践


下面来看一个实际的例子:前段时间我们的项目都需要完成服务上云,为了实现上云,需要调整下 CI 脚本,还要往项目里放一些环境配置文件,涉及到多处文件修改和增加,所以我们写了一个 @sharkr/ng-cloud-add 的 schematics 包达到快速改造项目的目的。以下是效果:



核心代码写起来跟写文档类似:



以上就讲完了在开发 / 迭代过程中遇到的问题和解决方案,最后再看一下 CICD 阶段做了什么。

CI

这部分跟 dev 环境类似,也需要提供 webpack 配置、build 脚本、还约束了 build 打包后的目录规范、配置部署相关文件。


价值与后续计划 价值通过介绍以上的一些方案设计,可以发现这套工具可以让开发更专注业务本身,初始化项目之后就可以直接进入开发;重复的操作可以通过 schematics 完成,省去重复的人力成本;项目 / 资源升级可以更加快速推进;规范和文档沉淀到工具中供使用,非常有利于规范的遵守。


后续计划接下来长远的规划将会从以下两点展开:


  • 与资源平台结合。目前放在 schematics 包里的资源没有一个很好的展示平台,后续将会对接物料平台,让资源更容易被使用。

  • vscode 可视化扩展能力。为了让开发在开发过程中更加快捷高效,后续会结合 vscode 的插件能力,让添加资源更容易。

总结

本篇分享主要从四个方面对工程化实践做了一个阐述:


  • 开发规范的落地依赖模板收敛项目规范,cli 工具收敛流程规范的方式,并在初始化项目时将模板应用于实际项目场景。

  • cli 工具的扩展依赖于插件式,对于自定义的部分提供相应插件即可扩展,而插件的更新依赖 npx 调用的方式,解决用户需要频繁更新问题。

  • 方便快速初始化项目,提供完备的 dev 环境和 build 脚本等提升了开发效率。

  • 资源复用和资源 / 项目升级统称为文件处理,这部分借用了 angular 里的 schematics 功能来完成。在这部分还讲了什么是 schematics 以及它在 cli 里做了什么。


最后特别感谢同事波哥,给了我非常多的指导和帮助!


作者简介


扑扑香,2016 年毕业,网易严选业务研发部前端开发,先后参与过企业邮、线下店、数据产品等多个项目开发,目前致力于效率工具开发。


2020-10-23 15:293401

评论 3 条评论

发布
用户头像
从一个工程化实现完整的 Angular 转到 React, 然后再去寻求工程化落地, 工作量真的这么不饱和吗?
2021-01-13 13:03
回复
同问
2022-06-29 23:57
回复
用户头像
写的这些内容和 Angular 转 React 有毛线关系。
2020-12-18 11:14
回复
没有更多了
发现更多内容

TIME的新封面:元宇宙将改变一切

智捷云

元宇宙 Metaverse 智捷云 智捷云科技

Spring Cloud 与 K8s 的微服务设计

Damon

7月月更

关于数据产品经理的三个小的知识点

松子(李博源)

数据中台 数据产品经理 数据产品

在 Kyma 云原生平台上开发并部署 Node.js 应用

汪子熙

Kubernetes 云原生 SAP Kyma 7月月更

阿里JAVA架构师面试136题含答案:JVM+spring+分布式+并发编程!

程序知音

Java 程序员 java面试 后端技术 八股文

图文并茂,讲解TCP和UDP协议的原理以及区别

程序知音

程序员 网络 TCP/IP 后端技术 底层原理

多商户系统的直播功能用过吗?用过的朋友扣个 666!

CRMEB

一文读懂Elephant Swap的LaaS方案的优势之处

西柚子

黄东旭:TiDB的优势是什么?

TiDB 社区干货传送门

人物访谈

SENSORO 付刘伟:创新技术服务为基层政府筑造数字底座

SENSORO

人工智能 大数据 物联网 城市大脑 数字政府

C# 窗体应用DataGridView,使用数据库(Sql和MySQl)对DataGridView绑定数据源,获取数据

IC00

C# 7月月更

面向高性能计算场景的存储系统解决方案

Baidu AICLOUD

异构计算 云原生存储 AI加速

算法题每日一练---第5天:星系炸弹

知心宝贝

算法 前端 后端 7月月更

如何做好研发精益需求管理

思码逸研发效能

研发管理 研发效能 科技

SeekTiger的Okaleido有大动作,生态通证STI会借此爆发?

鳄鱼视界

万物皆可柯里化的 Ramda.js

掘金安东尼

前端 函数式编程 7月月更

NFT数字藏品系统搭建—app开发

开源直播系统源码

软件开发 数字藏品 NFT数字藏品系统

NFT卡牌链游系统Dapp开发搭建

薇電13242772558

NFT 链游

妙啊!美团 OCTO 分布式服务治理系统,这描述也太清晰了

冉然学Java

Java 分布式 美团 #技术干货# Java 开发

敏捷 ? DevOps ?

FunTester

NFT挖矿分红系统开发模式定制

开发微hkkf5566

CSS 炫酷文本过渡动画

南城FE

CSS css3 前端 7月月更

面试突击67:说一下 TCP/IP 协议?以及每层的作用?

王磊

Java 面试

实时计算基本概念解析

五分钟学大数据

实时计算 7月月更

转转微服务框架的连接管理

转转技术团队

微服务 RPC 服务治理

专注B2B跨境支付的背后,XTransfer的风控基础设施是如何炼成的?

XTransfer技术

LeetCode-88. 合并两个有序数组(java)

bug菌

Leet Code 7月月更

优博讯助力深圳打造全球“鸿蒙欧拉之城”

Geek_2d6073

转转监控系统的内部原理及实践 审核中

转转技术团队

监控 Prometheus

开幕在即 | “万物互联,使能千行百业”2022开放原子全球开源峰会OpenAtom OpenHarmony分论坛

OpenHarmony开发者

OpenHarmony

JAVA编程规范之日志规约

源字节1号

后端技术

从 Angular 转到 React,网易严选的前端工程化实践_大前端_扑扑香_InfoQ精选文章