{T}

构建流水线

在现代前端开发中,构建是项目交付前不可或缺的一环。它涵盖了从源代码到生产环境代码的转换过程,主要包括:

  • 源代码预编译:处理 Sass、Less、TypeScript 等需要转换的语言。
  • 代码优化:合并与压缩 JavaScript 和 CSS 文件,减少网络请求。
  • 资源处理:优化图片、生成雪碧图,提升加载性能。
  • 版本管理:为静态资源添加哈希版本号,解决浏览器缓存问题。
  • CDN 部署:将静态资源上传到内容分发网络,加速全球访问。

虽然许多项目的构建流程已经预先配置好,但理解其背后的原理和考量,对于提升开发效率和解决复杂问题至关重要。

本章将引导你使用 npm script 和命令行工具,从零开始为一个典型的前端项目搭建构建流水线,让你深入理解每个环节的实现细节。

准备工作

在开始之前,请确保你已具备以下基础:

  • 熟悉基本的命令行操作。
  • 了解 npm script 的基本用法。
  • 项目中已安装 Node.js 和 npm。

项目设置

我们从一个简单的项目结构开始,它代表了典型的前端资源引用关系:

bash
client/
├── images/
│   └── schedule.png
├── index.html
├── scripts/
│   └── main.js
└── styles/
    └── main.css

资源依赖关系如下:

  • index.html 引用了 main.cssmain.js
  • main.cssindex.html 可能引用了 images/ 目录下的图片。

为了确保构建过程的正确性,我们必须遵循以下顺序:

  1. 图片优化:压缩图片以减少文件大小。
  2. 样式处理:编译 Less/Sass 并压缩 CSS。
  3. 脚本处理:编译和压缩 JavaScript。
  4. 资源版本化
    • 为图片添加版本号,并更新 CSS/JS 中的引用。
    • 为 CSS/JS 添加版本号,并更新 HTML 中的引用。

构建流水线详解

我们将逐步实现每个构建环节,并最终将它们串联成一条完整的流水线。

1. 初始化构建环境

我们约定将构建产物输出到 dist 目录,并保持与 client 目录相同的结构。每次构建前,需要清空旧的产物。

利用 npm 的 pre 钩子,我们可以轻松实现这一步。在 package.json 中添加 prebuild 脚本:

json
"scripts": {
  "prebuild": "rm -rf dist && mkdir -p dist/{images,styles,scripts}"
}

2. 组织构建脚本

为了保持 package.json 的整洁,我们使用 scripty 将复杂的构建命令拆分到独立的文件中。

首先,创建用于存放构建脚本的目录:

bash
mkdir -p scripts/build
touch scripts/build.sh scripts/build/{images,styles,scripts,hash}.sh
chmod -R a+x scripts

注意:必须为脚本文件添加可执行权限,否则 scripty 将无法执行。

3. 图片优化

我们使用 imagemin-cli 来压缩图片。

首先,安装依赖:

bash
npm install imagemin-cli --save-dev

然后,在 scripts/build/images.sh 中添加:

bash
imagemin client/images/* --out-dir=dist/images

最后,在 package.json 中配置 build:images 命令:

json
"scripts": {
  "build:images": "scripty"
}

4. 样式处理

我们使用 lessc 编译 Less 文件,并用 cssmin 压缩 CSS。

安装依赖:

bash
npm install less cssmin --save-dev

scripts/build/styles.sh 中添加,利用管道(|)和重定向(>)实现流式处理:

bash
for file in client/styles/*.css
do
  lessc $file | cssmin > dist/styles/$(basename $file)
done

package.json 中配置 build:styles 命令:

json
"scripts": {
  "build:styles": "scripty"
}

5. 脚本处理

对于 ES6+ 代码,我们使用 uglify-es 进行压缩。

安装依赖:

bash
npm install uglify-es --save-dev

scripts/build/scripts.sh 中添加:

bash
for file in client/scripts/*.js
do
  ./node_modules/uglify-es/bin/uglifyjs $file --mangle > dist/scripts/$(basename $file)
done

提示uglify-es 提供了丰富的命令行选项,如 sourcemap 生成和代码混淆,详情请查阅其官方文档

package.json 中配置 build:scripts 命令:

json
"scripts": {
  "build:scripts": "scripty"
}

6. 资源版本化与引用替换

为静态资源添加版本号是确保缓存更新的关键。我们使用 hashmark 生成哈希版本号,并用 replaceinfiles 更新文件引用。

安装依赖:

bash
npm install hashmark replaceinfiles --save-dev

scripts/build/hash.sh 中添加:

bash
# 1. 为图片资源添加版本号,并更新 CSS 中的引用
hashmark -c dist -r -l 8 '**/*.{png,jpg}' '{dir}/{name}.{hash}{ext}' | replaceinfiles -S -s 'dist/**/*.css' -d '{dir}/{base}'

# 2. 为 CSS 和 JS 添加版本号,并更新 HTML 中的引用
hashmark -c dist -r -l 8 '**/*.{css,js}' '{dir}/{name}.{hash}{ext}' | replaceinfiles -S -s 'client/index.html' -d 'dist/index.html'

package.json 中配置 build:hash 命令:

json
"scripts": {
  "build:hash": "scripty"
}

7. 整合构建流水线

现在,我们将所有步骤串联起来,形成一个完整的构建命令。

package.json 中添加 build 脚本:

json
"scripts": {
  "prebuild": "rm -rf dist && mkdir -p dist/{images,styles,scripts}",
  "build": "scripty",
  "build:images": "scripty",
  "build:styles": "scripty",
  "build:scripts": "scripty",
  "build:hash": "scripty"
}

scripts/build.sh 中按顺序执行所有构建步骤:

bash
for step in 'images' 'scripts' 'styles' 'hash'
do
  npm run build:$step
done

现在,运行 npm run build 即可触发完整的构建流水线。

验证构建产物

构建完成后,dist 目录将包含优化后的生产代码。

你可以通过以下方式验证构建结果:

  1. 检查 dist 目录:确认所有静态资源都已添加哈希版本号。
  2. 查看 dist/index.html:确认资源引用已更新为带版本号的路径。
  3. 本地启动服务:运行 http-server dist,并在浏览器中打开 http://127.0.0.1:8080,检查页面是否正常渲染。

常见问题与排错指南

  • scripty 报错:请确保所有 scripts/build/ 下的脚本文件都具有可执行权限 (chmod a+x)。
  • uglify-es 无法识别 ES6 语法:确保你调用的是 node_modules/uglify-es/bin/uglifyjs,而不是全局安装的 uglifyjs
  • 文件路径错误:仔细检查脚本中的输入和输出路径是否正确。

总结与展望

通过本章的学习,你已经掌握了如何使用 npm script 和命令行工具搭建一个完整、自动化的前端构建流水线。这不仅加深了你对构建过程的理解,也为你将来定制更复杂的构建流程奠定了基础。

你认为这个构建流水线还有哪些可以改进的地方?欢迎留言分享你的想法。


本节用到的代码见 GitHub。建议边看边动手练习,注意切换到正确的分支 12-use-npm-script-as-build-pipeline