一、Yarn脚本执行顺序的基本规则
Yarn作为JavaScript生态中常用的包管理工具,它的脚本执行顺序其实就像做菜的步骤清单。当你运行yarn install或yarn add时,Yarn会按照特定顺序执行package.json中定义的脚本。
举个实际例子(技术栈:Node.js):
// package.json示例
{
"scripts": {
"preinstall": "echo '准备安装依赖...'",
"install": "echo '正在安装主依赖...'",
"postinstall": "echo '依赖安装完成!'",
"pretest": "echo '准备运行测试...'",
"test": "echo '运行测试用例'",
"posttest": "echo '测试结果分析完成'"
}
}
当你运行yarn test时,控制台会依次输出:
- 准备运行测试...
- 运行测试用例
- 测试结果分析完成
这种自动化的前后钩子机制,就像三明治的夹心层,主命令是肉片,pre/post脚本就是面包片。
二、生命周期钩子的完整执行链条
Yarn的脚本执行顺序远比表面看到的复杂。整个生命周期就像地铁线路图,有主干线也有支线。以下是完整的执行链条示例:
// 完整生命周期示例(技术栈:Node.js)
{
"scripts": {
"preprepare": "echo '准备阶段前奏'",
"prepare": "echo '正式准备阶段'",
"postprepare": "echo '准备阶段收尾'",
"prepack": "echo '打包前检查'",
"pack": "echo '执行打包'",
"postpack": "echo '打包后处理'",
"prepublishOnly": "echo '发布前专属检查'",
"prepublish": "echo '传统发布前脚本'"
}
}
当运行yarn publish时,实际执行顺序是:
- prepublishOnly
- prepublish
- prepare
- prepack
- pack
- postpack
- publish
- postpublish
这个顺序就像多米诺骨牌,前一个触发后一个,环环相扣。需要注意的是,从Yarn 2.x开始,部分传统钩子如prepublish的行为有所变化。
三、典型应用场景与实战示例
让我们看几个实际开发中的典型用例(技术栈:Node.js):
// 实战场景示例
{
"scripts": {
// 1. 自动构建场景
"prebuild": "rimraf ./dist",
"build": "webpack --config webpack.prod.js",
"postbuild": "node ./scripts/notify.js",
// 2. 多环境部署
"predocker:build": "npm run build",
"docker:build": "docker build -t my-app .",
"postdocker:build": "docker image prune -f",
// 3. 数据库迁移
"premigrate": "node ./scripts/check-env.js",
"migrate": "knex migrate:latest",
"postmigrate": "node ./scripts/log-migration.js"
}
}
注释说明:
- 构建场景:先清理旧文件 → 执行构建 → 发送通知
- Docker部署:先构建应用 → 构建镜像 → 清理缓存
- 数据库迁移:检查环境 → 执行迁移 → 记录日志
这些场景展示了如何利用pre/post脚本构建自动化工作流,就像工厂的流水线,每个环节自动衔接。
四、常见问题与解决方案
在实践中我们常遇到这些问题:
- 脚本执行顺序错乱
// 问题示例(技术栈:Node.js)
{
"scripts": {
"preserve": "echo 'A'",
"serve": "echo 'B' & echo 'C'", // 并行执行导致混乱
"postserve": "echo 'D'"
}
}
解决方案是使用&&替代&确保串行执行:
"serve": "echo 'B' && echo 'C'"
- 环境变量继承问题 pre/post脚本默认继承父进程环境,但要注意:
# 错误用法
"prestart": "export NODE_ENV=development",
"start": "node app.js" # 这里NODE_ENV不会生效
# 正确做法(使用cross-env)
"prestart": "cross-env NODE_ENV=development",
"start": "cross-env-shell \"node app.js\""
- 循环触发陷阱
// 危险示例
{
"scripts": {
"prebuild": "npm run test", // 可能导致无限循环
"build": "echo 'building...'"
}
}
应该改为独立任务:
"ci": "npm run test && npm run build"
五、进阶技巧与最佳实践
- 条件式执行
// 条件执行示例(技术栈:Node.js)
{
"scripts": {
"predeploy": "if [ \"$ENV\" = \"production\" ]; then echo '校验生产环境'; fi",
"deploy": "node deploy.js"
}
}
- 多项目协同 Monorepo中的特殊处理:
// lerna + yarn workspace示例
{
"scripts": {
"postinstall": "lerna bootstrap --hoist",
"build": "lerna run build --stream"
}
}
- 性能优化技巧
// 缓存优化示例
{
"scripts": {
"postinstall": "node ./scripts/skip-if-exists.js || heavy-operation"
}
}
其中skip-if-exists.js内容:
// 检查缓存是否存在
if(fs.existsSync('./.cache')) process.exit(0)
else process.exit(1)
六、技术对比与选型建议
与npm脚本的对比:
- Yarn的执行速度更快(并行安装依赖)
- Yarn的--ignore-scripts选项更严格
- Yarn 2+的Plug'n'Play模式会改变传统node_modules行为
与pnpm的对比:
- pnpm的依赖结构更节省空间
- pnpm的pre/post脚本执行逻辑略有不同
- pnpm对monorepo支持更原生
选型建议:
- 小型项目:任意选择
- 大型项目:Yarn workspace + Lerna
- 极致性能:pnpm
七、总结与注意事项
关键要点总结:
- pre/post脚本就像书挡,总是成对出现
- 生命周期钩子顺序是固定的"准备-执行-收尾"
- 复杂场景可以组合多个钩子
注意事项:
- 避免在脚本中做耗时操作(如大文件拷贝)
- 生产环境记得使用--production跳过devDependencies
- 团队协作时保持脚本跨平台兼容
- Yarn 2+版本变化较大,需要特别测试
最后记住,这些脚本就像烹饪食谱,合理的顺序安排能让你的项目"大餐"更加美味可口!
评论