之前在公司中,我负责设计和开发过一个和音视频多人互动业务相关的业务SDK模块,该模块作为一个独立的npm包来供不同的业务层应用去使用,我从最开始的项目创建到后续的迭代优化,我一步步对这个npm包的工程化方面进行了改进,虽然每个npm包的功能都有所差异,但是其工程化方面的演进思路感觉都是有不少共通之处的。
什么是项目工程化
工程化我的理解就是:把一个项目,用一些方式方法,不管是一些约定还是一些规范,只要是能让项目更加可控、质量更高、开发效率更高、流程更易于管理的手段方法,都可以视为工程化。
工程化包含哪些方面
前端项目的工程化中,包含的内容、覆盖的范围着实不小,包括但不限于:技术选项、项目环境、构建、打包、发布、git分支规范、lint、工程目录(目录结构)、代码风格、质量等等。
而且在不同的项目类型以及环境中,其涵盖的内容和关注的细节也有所差别,例如npm库可能需要额外关注版本控制、单元测试和文档。而web项目则更加关注于构建打包和发布,且这部分的复杂度要比npm包项目更高。
一个npm包的工程化重点
这一次主要以我自己开发过的一个npm包(针对音视频多人互动业务的底层封装)的开发过程来分享一下,我接下来会从整个npm包的研发和迭代过程中,去介绍在迭代过程中的项目工程化历程。这里的重点在于,去了解和思考一个npm库的工程化的演进思路,至于是什么npm包则不太重要。
开发背景
作为一个封装音视频多人互动业务的npm包,我需要针对互动业务的场景,封装出一个可以在不同应用中共用的互动sdk,且开发过程中,我需要并针对其npm提供的互动功能进行测试和验证,然后再将其发布到公司的npm仓库中,供其他应用层去使用。
工程化过程
通常这个过程主要分为两个阶段:
- 项目初始化:通常在立项之处需要考虑的事项,包括:
- 项目创建
- 技术选型
- 工程化迭代:在项目的迭代过程中,我们会遇到一些问题,我们想尽可能让项目更加规范、利于理解、利于维护或者提高质量等等,故会考虑增加一些工具或者规范来完善我们的项目
- 代码提交
- 测试
- 文档
项目初始化
通常我们在项目初始化阶段,就会考虑技术选型并将项目整个框架搭建起来,不过npm包类型的前端项目的创建,这里就不再多少说明了,你可以从创建个package.json开始,一步一步的搭建,也可以通过脚手架,例如vite。
用脚手架创建出来的项目,基本上在各个方面都帮你配置好了,不需要你操心,可以帮你省不少心。不过你也可以从下面步骤中了解不用脚手架搭建一个项目时的过程。
技术选型
技术选型本身是很重要的一环,这里需要说明一下,在不同项目、具体问题场景下,其选型没有固定的,只有是否合适以及自身的权衡,甚至于有些情况下在两个技术都可行的前提下,看你喜欢哪个甚至看你心情都可以。
对于当前的前端项目来说,一些常见的技术选型这里就不再赘述了,例如npm包管理器(yarn、pnpm等)、Typescript等,这些看你需求挑一个自己熟悉的即可,在没有特殊需求的情况下,这些没有太多差别。
打包
一般在为项目选择打包工具时,其项目本身的特性很大程度上都帮你选好了打包工具,而且打包工具有不少,常见的有webpack、rollup,不常见的有Parcel、gulp,也有迅速发展的esbuild,还有正在发展的Turbopack等。vite严格来说不止时一个打包工具,而是一个脚手架。
在选择上,每个人都有自己的偏好,每个项目都有自身的特点,而我在选择打包工具时,基于了如下考量:
- 该npm包本身作为一个业务sdk,其功能并没有太多特殊需求,基本上是js纯逻辑代码,自然对打包没有什么特殊要求。甚至于非常简单。
- 从生态、流行程度及其社区发展的角度考虑,可选的有:webpack、rollup,esbuild也不错,不过其生态没有前面两位那么庞大,而且作为一个纯逻辑的npm仓库,其打包任务本身就比较轻量,故esbuild的构建优势体现的并没有那么重要。
- webpack生态丰富、强大,对于我的npm项目来说,既没有图片、字体资源,也没有样式,使用webpack来说,有种杀鸡用牛刀的感觉,rollup本身完全可以胜任打包工作。
基于以上几点,我选择更为轻量但是也很强大的rollup进行打包。通常在选择打包工具时,普通的web项目(也包括带UI的组件)通常都会选用webpack(或者和一些其他打包工具结合使用,例如webpack + esbuild-loader),而纯逻辑的npm包则通常使用rollup,至于其他的打包工具并不会作为首选,除非你的团队中一开始已经尝试过并积累了一定经验。最终还是要看你的实际情况和成本去进行权衡。
ps:大部分情况下,中途其实是可以换的。很多时候你觉得当前的打包工具,够用,能满足你的需求,其实没有必要折腾,除非你主动的想要去尝试一些新东西,因为需要考虑成本问题。相比于一股脑全上了,我更加倾向于遇到某个问题或者瓶颈时,再考虑进行替换。
项目工具:代码lint
通常在一开始项目创建之处,会考虑到代码的标准性以及多人协作时代码风格的统一问题,故通常会为项目添加lint工具。
代码lint包括代码检查和代码风格统一:
- eslint:通过某些规则配置,分析和检查代码和语法中潜在的风险。而且他一般可以配合编辑器使用。但是,它并不是强制的。
- prettier:用于统一代码中的代码风格,例如:尾分好,单双引号,缩进等等。
这两样是前端项目常用的代码lint工具,基本上新建的项目都会带上它俩。同时,与lint相配合的就是husky和lint-staged,有lint进行检查,自然就有工具来帮我们将检查给执行下去,而这俩就是做这个工作的:
- husky:基于git钩子,在git代码提交时,运行指定命令,通常用来运行lint检查。如果lint检查不通过,则阻止git提交代码。
- lint-staged:如果一次lint针对的是项目中所有的文件时,那么其耗时随着项目规模的日益增长也会变得非常耗时。且对于项目中途才增加lint检查的旧项目来说,其lint可能会带来非常多的改动。故,这个库的作用就是从git中查找这次提交所改动的文件,将lint的检查,限定在这些文件中。
这里简单说明一下,虽然这两者在一个项目中并不是必须的但是在前端工程化中,很多都不是必须的,我们要明确引入他们的原因在于他们可以规范我们的代码,方便不同开发之间的协作,提高效率,提高质量,降低风险。这也是这些工具的意义。
工程化迭代
测试
作为一个成熟的npm包,为了让使用者用的放心,且也为了减少因为bug修复频繁的发布版本而导致你被diss,所以在发布到npm仓库时,在项目内部完成功能自测,就非常重要了。尤其我开发的这个项目,并非是独立的js逻辑,还包含了后台服务接口以及IM,同时其封装的业务逻辑还包含所谓的多人、多平台,故其测试的工作量和复杂性会更大。而随着npm包的开发优化,其测试部分也逐渐补充和完善。
该仓库的测试包含了以下几个部分:
- 自测 demo:在新开发的功能过程中,能够简单快速验证自测新开发的功能,保障其高质量
- 单元测试:在开发过程中,对一些纯粹的逻辑且包含较多验证分支的场景下,使用自动化的单元测试来高效率的保障其质量
- 功能测试:自动化的模拟自测,减少自测成本、覆盖更多的测试边界场景来高效的保障其高质量
自测demo
在初始时,我们开发npm包的功能后,对其进行验证的方式就是用自己编写的demo来做的,直到现在,在你开发完某个功能时,通常会自己在这个demo中去尝试一下这个新功能,保证其功能是正常的。
它从一个业务方的使用视角,将npm包本身的作为一个完整的SDK进行引用,并在此基础上模拟一个web端的应用,我们通常在这个demo中进行开发、自测和功能调试。
单元测试
自测demo是对功能手动进行验证,通常在开发新功能或者对已有功能进行修改时,才会在开发过程中使用自测demo来验证功能是否可用,而对于一些纯粹的逻辑代码且包含了比较多的分支和组合的场景下,其自动化的单元测试就派上用场了。
单元测试本身基本上大家都或多或少接触过,这里就不再花费大篇幅去说明了,而单元测试框架我通用用的是jest,你也可以尝试一下比较新的vitest,也许有不一样的收获。
功能测试
具体细节可以参考:npm包项目-基于puppeteer的功能测试
这三种测试方式的对比:
测试方式 | 使用时机 | 适用 | 特点 |
---|---|---|---|
自测demo | 开发新功能 、简单自测和功能调试 | 所有功能 | 编写简单、速度快 |
单元测试 | 开发新功能 | 纯逻辑功能 | 编写简单、速度快、耗费一定时间 |
功能测试 | 新功能开发完成 ,较为稳定后 | 所有功能 | 编写复杂、速度慢、耗费较多时间 |
multirepo到monorepo
参考:npm包项目-multirepo到monorepo的演进
版本控制
在multirepo时期,那时候只有一个package.json的包需要维护,基本上在开发完毕后,更新一下package.json中的version版本,然后提交并在鲸云上构建发布即可。
而到了monorepo项目架构下,如果仍然依赖人工的版本更新,则显得有些麻烦了,你需要找一个monorepo下的包依赖管理工具,尤其是当你的子包多了起来的时候,此时我们可以选择changeset来对包的依赖进行管理。毕竟它在pnpm官网的推荐文档里面。
它可以帮你做下面这些事情:
- 在你开发完毕后,利用changeset命令来自动更新你的包版本:主要版本、次要版本、补丁、beta、alpha、rc等
- 自动修改依赖包的版本号,并且可以帮你发布到npm仓库中
具体的使用方式参考:https://github.com/changesets/changesets/blob/main/packages/cli/README.md
文档编写
参考:npm包项目-文档编写
发布
由于公司的云端构建和发布比较成熟,故这个项目在发布部署方面并需要有较大的改动,通常都是直接通过云端的命令配置,完成仓库拉取、打包构建、发布npm的流程。整个流程都较为简单。
总结
整个npm项目,前前后后总共开发了一年多,从一开始项目创建到后续的完善优化,整个过程中我通常遵循一个原则,那就是适合的才是最好的,也许目前整个项目的工程化不是最好最完美的,但是它是通过针对当时实际中所遇到的问题而逐步优化过来的,项目并非一成不变,需求也不是一成不变,还是那句话,适合才是最好的。