作者:陈盛靖
一、背景
在页面开发过程中,发现两个小程序的工程目录下都有大量相同的全局components,每次修改都需要同时修改两个项目的组件代码,因此想要将这部分抽离出来。最常见的方式是抽离成npm包进行复用,但是无意间看到了这篇文章(juejin.cn/post/734119...),于是想到是否可以使用远程组件的方式来进行复用。
二、远程组件调研
1.原理
远程组件原理其实就是从服务端动态获取JS代码文件,下载到本地并注册执行的。
而基于远程组件,也可以设计一套带有版本控制的发布流程:
2.优缺点
优点
- 可以做到技术栈无关,不同框架的应用都可以使用
- 可以动态更新版本,并且用户对于更新无感知;
- 如果可以快速升级和回退组件版本,引入组件的应用代码无需重新发布;
- 直接引用url,减少应用打包体积;
缺点
- 如果组件服务崩溃,可能导致组件加载错误,从而使整个业务不可用
- url资源加载相对耗时,首屏加载可能时间较长
- 可能存在请求拦截从而导致执行恶意脚本的风险
可以看到,使用远程组件的好处有很多,尽管有一些不足,但是只要做好优化和防范策略是可以避免或减少使用远程组件带来的问题的影响的。那么接下来,就是对远程组件方案的可行性进行调研。
3. 可行性调研
远程组件的注册,需要js解释器完成。在web端,我们可以直接使用script来加载组件js脚本,但是我所在业务线的小程序是以Taro为框架,并且小程序的主要平台是钉钉和微信。可惜的是,出于安全考虑,这两个平台都对其做了限制,具体的文档说明如下:
钉钉: open.dingtalk.com/document/or...
微信: developers.weixin.qq.com/miniprogram...
因此,我不得不放弃了远程组件的方案。
三、npm包
1. npm的调试问题
这是最常见的公共代码复用的方式,这里不过多介绍。然而,npm也并非没有缺点,其中最令人在意的是它的调试体验问题,例如:
- 每次更新都需要重新执行link命令。
- npm link时可能由于依赖关系问题导致link失败;
- npm unlink后,node_moudles下对应npm包被删除,需要重新安装;
那么,有没有其他的调试方法,能够更加方便的去调试npm包呢?
2. 调试方法探索
a. monorepo
什么是monorepo
monorepo 是一种项目代码管理方式,指单个仓库中管理多个项目,有助于简化代码共享、版本控制、构建和部署等方面的复杂性,并提供更好的可重用性和协作性(juejin.cn/post/721588...)。
monorepo调试npm包
我们通过将组件库代码和应用保管到一个git仓库下,这样在调试时,我们可以直接引用组件库的代码,不需要使用npm link调试;同时,也减少了工程的维护成本(因为少了一个项目工程)。
然而,这种方式也存在缺点,比如:
- 业务代码和组件开发的代码是耦合在一起的,一旦出现问题,不易回溯;
- 目前lib库发布时change log是基于git提交的生成的,由于业务开发和组件代码git记录混合,也会导致生成的change log存在问题;
- 对于需要引用的工程不可能都将其加入monorepo里;而对于非monorepo下的应用,则依然需要使用npm link的方法进行调试,调试体验问题并没有完全解决;
- 当代码出现问题时,项目代码和不得不和组件代码一起回退;
由于公司内部已经存在较为完善工程体系及其CI/CD流程,如果要实现基于monorepo的开发一套新流程,需要解决的问题可能相当的多,而实际带来的收益可能并不高。考虑到ROI(投入产出比),因此这一方案也被放弃。
b. 修改编译配置
那么,真的没有更好的方法去调试npm包了吗?
其实,npm link调试的问题主要是无法实时编译以及调试步骤繁琐。只要有办法在开发中实现监听组件代码变更和自动编译,就能解决这两个问题。幸运的是,在Taro中,我们可以通过修改编译的配置项(taro-docs.jd.com/docs/config...),来更改实际打包的依赖文件路径。
但是使用这种方法,当修改组件代码时,webpack并不能监听到代码变更。为了解决这个问题,我们可以通过webpack server实现对代码的热更新;而kone(公司内部的工程化工具)支持动态编译的能力,直接运行kone build就能做到实时监听,具体调试方法如下:
步骤一,打包npm包
首先,我们需要使用打包工具生成build打包文件,并开启热更新。
步骤二,配置alias
接着,在config/dev.js
下增加alias配置
java
module.exports {
alias: {
'@guming/my-module': path.resolve(__dirname, '..', 'packages/my-module/build/esm/index.d.ts'),
}
}
其中,my-module为需要调试的npm包,packages/my-module/build/esm/index.d.ts为npm项目build的入口文件。
步骤三,配置paths
为了让编辑器(VS Code)不报错,并继续使用自动路径补全的功能,需要在项目根目录下的 jsconfig.json
或者 tsconfig.json
中配置 paths
让编辑器认得我们的别名(taro-docs.jd.com/docs/config...)。
通过简单改动几个编译的配置项,就能方便快捷地对npm包进行调试。
可能遇到的问题
由于修改了依赖的引用路径,可能会导致相关的eslint校验结果发生变化,例如import/order(juejin.cn/post/722292...)的校验。
import/order可以让我们在代码中,根据自定义的排序规则,有序地引用依赖文件。当我们修改了本地的依赖引用路径时,会导致依赖的引用顺序发生变化;如果此时该规则在被作为远端也存在校验,就可能导致远程规则的校验和本地的规则的校验发生不一致的情况。而其他与路径相关的校验规则也可能发生同样的问题。
一个解决方法是,不修改 jsconfig.json
或者 tsconfig.json
中的paths
配置。根据Taro官网文档的描述,这样可能会引起其他问题(例如vscode报错),但是目前使用中并没有遇到过。
四、总结
方案对比
本文主要讲述了远程组件和npm包两种组件复用方式的探索过程。远程组件相对于传统的npm包,拥有许多优点,但是由于平台限制等原因,最终选择了抽离npm包的方式,并针对Taro项目中如何更方便地调试npm包提供了一种解决方法。
实际上,远程组件和npm包两种方案各有其优缺点,具体采用何种方式以及如何实现,还是要根据自己的业务实际情况综合考虑。