背景
日常开发中,一个项目的代码往往不只在一个项目仓库下,一些功能独立、复用性强的组件或者工具函数等,可抽离成单独的通用组件,通过依赖包的形式安装进来,其他同学也可以很方便地在其他项目中调用。比如业务项目叫my-project,抽离的组件库@cute/hello发布到npm上,项目中通过npm install @cute/hello --save,就可以直接引用组件库了。
调用组件库是一件很快乐的事情,只需要调调API就能实现。但是如果组件库有bug,你想在组件库下调试完毕后,在真实有问题的项目环境去开发调试,却无从下手。有人想到可以先npm publish发布一个测试的beta包,再在项目中npm install,但这样一来一回频繁调试的时候就浪费了很多时间,况且第三方组件库不允许你去操作发布测试的。怎么能做到组件库开发和调试时像内嵌在项目的代码一样流畅,无额外感知成本呢?那么这就涉及到组件库的本地调试了。
下面会先介绍三种常见组件库本地调试方法,最后会总结一种自动化调试方案:使用nodemon+yalc实现自动化调试本地组件库。想直接看自动化调试的可以跳到最后面。
1、 常用的npm link方式
2、 修改项目package.json依赖路径
3、 复制构建的组件库产物到项目库的node_modules下
4、 自动化调试方案:nodemon+yalc
说明:文中所讲的组件库、library、依赖包、开源库等词汇在此均指同一概念,即抽象出来的通用组件。
以往的调试方式
大致来看,会有几种常见的调试方式,但是都有其局限性。
1. 常用的npm link方式
通过npm link的方式建立组件库和项目之间的联系,这也是较常用的方式。每当组件库代码变更后,通过在组件库发布代码所在目录执行以下
npm run build // 执行构建
cd dist // 进入构建后结果目录
npm link // 将 library 链接到全局
然后到项目所在目录重新绑定并重启服务。
npm link @cute/hello
npm run start // 对应到具体执行脚本
npm link后输出和项目绑定link示例如下,通过执行发现,组件库npm link后会在发布代码目录下也生成一个node_modules会用来存放组件库自身依赖的dependencies。my-project执行npm link @cute/hello后会把组件库发布目录下的代码都同步到项目的node_modules下了。
➜ cute-hello git:(xxx) cd dist
➜ dist git:(xxx) npm link
npm notice created a lockfile as package-lock.json. You should commit this file.
added 4 packages from 6 contributors and audited 4 packages in 4.75s
found 0 vulnerabilities
/usr/local/lib/node_modules/@cute/hello -> /Users/user/cute-hello/dist
➜ my-project git:(xxx) npm link @cute/hello
/Users/user/my-project/node_modules/@cute/hello -> /usr/local/lib/node_modules/@cute/hello -> /Users/user/cute-hello/dist
my-project 与本地组件库 npm link 后,它的 node_modules 中的 @cute/hello 代码是本地组件库发布目录 link 后的代码。
每次组件库代码更改后,在组件库进行 npm run build 操作后,项目执行 npm link 后,它会去 link 的地址去找资源文件(也就是组件库),所以它的资源文件不在项目下,webpack不会对其做预编译,所以如果组件库本身还依赖其他的依赖库,需要每次构建后在组件库执行npm link,它才会自动生成组件库自身依赖并注入 node_modules 中 ,这样项目库才能找到组件库的依赖,不然会导致实际构建或者运行时报错,项目库会找不到组件库自身的依赖。
调试完毕后解除link操作,项目重新 npm install 依赖
npm uninstall -g @cute/hello // 解除组件全局link
整个调试流程如下图。
操作成本:
- 每次组件库修改需要build构建
- 组件库有自身依赖时构建后需执行npm link和项目重启服务
- 结束调试后解除link,安装正确的生产依赖包
- 组件库和项目库都有调试成本
适用场景:
- 在一些命令行工具库里面尤其好用,完全零调试成本,全局的命令行工具甚至不需要在项目库安装依赖包就能调试;
- 组件库自身没有依赖的简单情况下
2. 修改项目package.json依赖路径
修改 package.json 中组件库为本地组件库路径,需要在项目库重新执行 npm install 安装依赖。但是项目库中安装依赖,实际上是把依赖安装到组件库下了,项目下并没有把组件库自身依赖安装到项目下。
// package.json
"dependencies": {
"@cute/hello": "file:/Users/user/cute-hello/dist",
}
可以看到 package-lock.json 中组件自动引用本地相对路径
// package-lock.json
"@cute/hello": {
"version": "file:../cute-hello/dist",
"requires": {
...
},
}
每次组件库代码更改后,在组件库进行npm run build构建,这里同样会面临像npm link那样的尴尬处境,项目会去组件库找组件的自身依赖,如果没有找到则会启动错误,所以组件库每次构建完你都需要在项目重新执行npm install安装组件库的自身依赖。
调试完毕提交代码及上线前需要手动把package.json更新回正确的依赖包地址。
整个调试流程如下图。
操作成本:
每次组件库修改需要build构建
项目重新执行npm install
结束调试后重新安装正确的生产依赖包
上线前需要手动修改package.json的正确依赖包地址
适用场景:
组件库自身没有依赖的简单情况下
3. 复制组件库产物到项目库node_modules下
在组件库构建后,将dist下的构建产物复制到项目 node_modules 的@cute/hello目录下,组件库下执行命令:
cp -r dist /Users/user/my-project/node_modules/@cute/hello
每次组件库代码更改后,都需要在组件库进行npm run build操作,而且项目的node_modules下组件库复制完可能不会触发热更新,需要删掉项目下组件库再执行cp命令,那么命令改成先删除再复制,这样就可触发项目的热更新了。
rm -rf /Users/user/my-project/node_modules/cute/hello && cp -r dist /Users/user/my-project/node_modules/@cute/hello
整个调试流程如下图。
操作成本:
每次组件库修改需要build构建
每次需要在组件库执行cp命令
结束调试后重新安装正确的生产依赖包
只需要在组件库下执行一些操作命令,项目库无额外操作成本
适用场景:
- 基本都能适用
自动化调试流程:nodemon+yalc
自动化调试流程依赖【文件监控开源库 nodemon】和【本地依赖管理开源库 yalc】,大致思路是用nodemon监听文件修改,然后自动执行组件库打包,再使用yalc进行组件库发布。
nodemon 是一个用来监视应用程序中文件更改并自动重启服务的开源库,仅用于开发环境(推荐),他不影响项目代码,可以理解为是把node替换成nodemon执行命令。
yalc 是一个类似于本地化 npm 的解决方案,它在本地环境中创建了一个共享的 library 存储库( ~/.yalc ),当你需要本地依赖时可以快速从存储库中拉取资源进行使用。它与 npm link 不同就是 npm link 中组件库和项目引用的依赖是映射到一个地方的,当项目库启动时会在依赖下去寻找其他依赖,如果找不到相关依赖,应用就会异常;如果组件库的编译规则和项目的编译规则不匹配,也同样会出问题。而 yalc 就是相当于将文件复制进依赖目录从而能正常运行。
更多详情可以看以下链接
● yalc: https://github.com/whitecolor/yalc
● nodemon: https://github.com/remy/nodemon
自动化流程
自动化流程通过使用nodemon监听文件修改后,自动执行组件库打包,再使用 yalc push 进行组件库发布,项目只要 yalc add [package] 后,后续就能直接热更新下预览变更了。具体实现流程下面会介绍。
首先,全局安装nodemon和yalc:
npm install -g yalc // 全局安装
npm install -D nodemon // 组件库安装
// 或
yarn global add yalc
yarn global add nodemon -D
组件库单独执行 yalc publish 时会把当前组件库下相关文件存储到本地共享的全局存储中,除了不会发布到真实远端仓库外,都和npm真实发包无异。更好用的是 yalc publish --push 或者简称 yalc push ,它会将你的依赖发布到本地存储库(更新状态),并将所有更改传播到现有通过 yalc 安装的依赖中,下面会用到。
dist git:(xxx) ✗ yalc push
@cute/hello@2.0.0-beta.2 published in store.
在组件库的 package.json 中添加 nodemon 脚本命令,这里可以新建一个 nodemon.json 的配置文件去配置详细的信息(具体配置可以去官网查看)。nodemon 启动监听,当监听文件变化时自动 build 构建,并将库 yalc push 发布到缓存中。
如果当前构建后,package.json 中 private: true 时,直接 yalc push 会报错:Will not publish package with private: true
use --private flag to force publishing. 需要使用 yalc push --private 强制发布。
{
"scripts": {
"build": "node scripts/dist.js",
"build:watch": "nodemon --config 'nodemon.json' -x 'npm run build && cd dist && yalc push --private'"
},
}
// nodemon.json
{
"restartable": "rs",
"ignore": [
".git",
"node_modules/**/node_modules",
"package.json",
"nodemon.json",
"dist",
],
"verbose": true,
"execMap": {
"js": "node --harmony"
},
"events": {
"restart": "osascript -e 'display notification \"App restarted due to:\n'$FILENAME'\" with title \"nodemon\"'"
},
"watch": ["src"],
"env": {
"NODE_ENV": "development"
},
"ext": "js,json,css,scss,vue"
}
上面组件库的准备工作完成后,项目下只要执行 yalc add @cute/hello ,在项目根目录下就会自动生成一个 yalc.lock 文件和 .yalc 目录,.yalc 目录类似 node_modules 那样,它存放着从存储库中拉取下来的依赖,yalc.lock 记录当前被替换的组件库版本信息,这些信息用来后面还原依赖包用。同时 my-projecy 中的 node_modules 的组件库也被替换成 yalc 缓存中的组件库了。
my-project git:(xxx) yalc add @cute/hello
Package @cute/hello@2.0.0-beta.2 added ==> /Users/user/my-project/node_modules/@cute/hello
package.json的组件库地址自动注入了 file:.yalc/@cute/hello 。
"dependencies": {
"@cute/hello": "file:.yalc/@cute/hello",
}
项目根目录下自动生成了 yalc.lock 文件和 .yalc 目录:
# yalc.lock
{
"version": "v1",
"packages": {
"@cute/hello": {
"signature": "519aa4086b04e080b8d9731188dd162d",
"file": true,
"replaced": "@cute/hello@2.0.0-beta.1"
}
}
}
此时,就可以快乐地进行组件库和项目库之间的调试了,不需要额外的操作成本,所改代码即所视。
调试完毕后,要提交代码部署到公测环境时,执行 yalc remove --all(如果只引了一个依赖包也可直接 yalc remove [package],yalc remove --all 会将当前所有从 yalc 存储中拉取的都还原),即可一键还原安装包现场,上面说到的package.json会自动还原了依赖包版本、.yalc、yalc/lock都自动去掉了,还原了开发调试前的现场,但是node_modules中的对应的组件库也去掉了,所以完成调试后本地只需要重新npm install安装正常版本即可后续开发。
➜ my-project git:(xxx) ✗ yalc remove --all
Removing installation of @cute/hello in /Users/user/my-project
总结
总结一下,自动化调试流程就是在组件库使用nodemon监听文件变更后,自动执行 build 构建和 yalc push,项目中通过 yalc add [package] 从本地存储中拉取依赖包进行调试,调试完成后 yalc remove --all 一键恢复调试前现场。
其实以往的调试方式都可以加 nodemon 命令去监听达到自动化调试的目的,但是却有一些局限性(如下表),纵观来看 yalc 的方式更加通用且逻辑清晰,组件库和项目库不需要花太多时间去理会中间的执行过程,只需要像 npm 安装依赖一样,在项目中通过 yalc 引入多个组件库进行调试,调试完毕后 yalc remove --all 即可清除所有调试库。
调试方式 \ 对比 | nodemon改造 | 局限 |
---|---|---|
npm link | 组件库nodemon监听文件变化->build构建->npm link | 项目需要重启服务;编译规则不匹配,也可能会出问题 |
cp复制构建产物 | 组件库nodemon监听文件变化->build构建->scripts中添加cp命令 | 如果有多个项目在用组件库,需要写多套cp命令调试 |
修改package.json依赖库地址 | 组件库nodemon监听文件变化->build构建->自动进入项目库执行npm install | 项目库每次install很繁琐,调试完成后项目的依赖地址需要手动恢复,这样一点也不cool |
更多阅读
● npm link:https://docs.npmjs.com/cli/v8/commands/npm-link
● yalc: https://github.com/whitecolor/yalc
● nodemon: https://github.com/remy/nodemon