你好,我是李思嘉,前端效率工程化这门课我们会讨论一个前端项目从开发到构建和部署这一系列工作流程的效率问题。在开发效率篇里,我们会讨论一系列影响开发效率的流程和工具。工欲善其事必先利其器,第一课时,我们首先从开发一个新项目时最基础的准备工作讲起。
当你准备开发一个新项目时,在进入到实际业务编码前,通常需要做很多的基础准备工作,这里会遇到的问题有:
而脚手架工具,正是为了解决这些问题而诞生的。
因此,对于一个熟练的前端工程师来说,要掌握的基本能力之一就是通过技术选型来确定所需要使用的技术栈,然后根据技术栈选择合适的脚手架工具,来做项目代码的初始化。一个合适的脚手架,可以为开发人员提供反复优化后的开发流程配置,高效地解决开发中涉及的流程问题,使得工程师能够快速上手,并提升整个开发流程的效率和体验。当然,前提是建立在选择对了脚手架工具并深入掌握其工作细节的基础上。
那么下面我们先来谈谈脚手架工具究竟是什么。
说到脚手架(Scaffold) 这个词,相信你并不陌生,它原本是建筑工程术语,指为了保证施工过程顺利而搭建的工作平台,它为工人们在各层施工提供了基础的功能保障。
而在软件开发领域,脚手架是指通过各种工具来生成项目基础代码的技术。通过脚手架工具生成后的代码,通常已包含了项目开发流程中所需的工作目录内的通用基础设施,使开发者可以方便地将注意力集中到业务开发本身。
那么对于日常的前端开发流程来说,项目内究竟有哪些部分属于通用基础设施呢?让我们从项目创建的流程说起。对于一个前端项目来说,一般在进入开发之前我们需要做的准备有:
正如下面简单的示例项目模板,经历了上面这些步骤后我们的项目目录下就新增了这些相关的文件:
package.json 1) npm 项目文件
package-lock.json 2) npm 依赖 lock 文件
public/ 3) 预设的静态目录
src/ 3) 源代码目录
main.ts 3) 源代码中的初始入口文件
router.ts 3) 源代码中的路由文件
store/ 3) 源代码中的数据流模块目录
webpack/ 4) webpack 配置目录
common.config.js 4) webpack 通用配置文件
dev.config.js 4) webpack 开发环境配置文件
prod.config.js 4) webpack 生产环境配置文件
.browserlistrc 5) 浏览器兼容描述 browserlist 配置文件
babel.config.js 5) ES 转换工具 babel 配置文件
tsconfig.json 5) TypeScript 配置文件
postcss.config.js 5) CSS 后处理工具 postcss 配置文件
.eslintrc 7) 代码检查工具 eslint 配置文件
jest.config.js 7) 单元测试工具 jest 配置文件
.gitignore 8) Git 忽略配置文件
README.md 8) 默认文档文件
而通过脚手架工具,我们就能免去人工处理上的环节,轻松地搭建起项目的初始环境,直接进入到业务开发中。接下来我们就先来看一下前端领域的几个典型脚手架工具,了解这几个脚手架所代表的不同设计理念,接着我们会重点分析两个代表性脚手架工具包内的技术细节,以便在工作中更能得心应手地使用和优化。
[图:logo-yeoman]
Yeoman 是前端领域内较早出现的脚手架工具,它由 Google I/O 在 2012 年首次发布。Yeoman 提供了基于特定生成器(Generator)来创建项目基础代码的功能。时至今日,在它的网站中能找到超过 5600 个不同技术栈的代码生成器。
作为早期出现在前端领域的脚手架工具,它没有限定具体的开发技术栈,提供了足够的开放性和自由度,但也因此缺乏某一技术栈的深度集成和技术生态。随着前端技术栈的日趋复杂化,人们更倾向于选择那些以具体技术栈为根本的脚手架工具,而 Yeoman 则更多用于一些开发流程里特定片段代码的生成。
[图:logo-create-react-app]
Create React App(后简称 CRA )是 Facebook 官方提供的 React 开发工具集。它包含了 create-react-app 和 react-scripts 两个基础包。其中 create-react-app 用于选择脚手架创建项目,而 react-scripts 则作为所创建项目中的运行时依赖包,提供了封装后的项目启动、编译、测试等基础工具。
正如官方网站中所说的,CRA 带来的最大的改变,是将一个项目开发运行时的各种配置细节完全封装在了一个 react-scripts 依赖包中,这大大降低了开发者,尤其是对 webpack 等构建工具不太熟悉的开发者上手开发项目的学习成本,也降低了开发者自行管理各配置依赖包的版本所需的额外测试成本。
但事情总有两面性,这种近乎黑盒的封装在初期带来便利的同时,也为后期的用户自定义优化带来了困难。虽然官方也提供了 eject 选项来将全部配置注入回项目,但大部分情况下,为了少量优化需求而放弃官方提供的各依赖包稳定升级的便利性,也仍不是一个好的选择。在这种矛盾之下,在保持原有特性的情况下提供自定义配置能力的工具 react-rewired 和 customize-cra 应运而生。
[图:logo-vue-cli]
正如 Create-React-App 在 React 项目开发中的地位, Vue 项目的开发者也有着自己的基础开发工具。Vue CLI 由 Vue.js 官方维护,其定位是 Vue.js 快速开发的完整系统。完整的 Vue CLI 由三部分组成:作为全局命令的 @vue/cli、作为项目内集成工具的 @vue/cli-service、作为功能插件系统的 @vue/cli-plugin-。
Vue CLI 工具在设计上吸取了 CRA 工具的教训,在保留了创建项目开箱即用的优点的同时,提供了用于覆盖修改原有配置的自定义构建配置文件和其他工具配置文件。
同时,在创建项目的流程中,Vue CLI 也提供了通过用户交互自行选择的一些定制化选项,例如是否集成路由、TypeScript 等,使开发者更有可能依据这些选项来生成更适合自己的初始化项目,降低了开发者寻找模板或单独配置的成本。
除了技术栈本身的区别之外,以上三种脚手架工具,实际上代表了三种不同的工具设计理念:
刚上手开发项目时,我们通过上述脚手架提供的开箱即用的能力可以很容易地上手开发项目,但是往往在开发过程中遇到问题时又需要回过头来查询文档,看脚手架中是否已有相应解决方案。而如果我们对该脚手架足够熟悉,就能减少这类情况下所花费的时间,提升开发效率。所以在这里,我们先来聊一下该如何了解一个脚手架。
要了解一个脚手架,除了学会如何使用脚手架来创建项目外,我们还需要了解它提供的具体功能边界,提供了哪些功能、哪些优化。这样我们才能在后续的开发中更得心应手,后续的优化也更有的放矢。
还是以上面的 CRA 和 Vue CLI 为例,除了通过脚手架模板生成项目之外,项目内部分别使用 react-scripts 和 vue-cli-service 作为开发流程的集成工具。接下来,我们先来对比下这两个工具在开发与生产环境命令中都使用了哪些配置项,其中一些涉及效率的优化项在后面的课程中还会详细介绍。
从下面表格中我们可以发现,在一般源文件的处理器使用方面,两个脚手架工具大同小异,对于 babel-loader 都采用了缓存优化,Vue 中还增加了多线程的支持。在样式和其他类型文件的处理上 Vue 默认支持更多的文件类型,相应的,在 CRA 模板下如果需要支持对应文件就需要使用 customize-cra 等工具来添加新处理模块。
在与构建核心功能相关的方面(html、env、hot、css extract、fast ts check),两者使用的插件相同,而在其他一些细节功能上各有侧重,例如 React 的 inline chunk 和 Vue 的 preload。
(第三方工具)
两者在代码优化配置中相同的部分包括:都使用 TerserPlugin 压缩JavaScript, 都使用 splitChunks 做自动分包 (参数不同)。CSS 的压缩分别采用上面表格中的 OptimizeCssAssetsWebpackPlugin 和 OptimizeCssNanoPlugin 。react-scripts 中还开启了 runtimeChunk 以优化缓存。
在 resolve 和 resolve loader 部分,值得一提的是两者都使用 PnpWebpackPlugin(pnp) 来加速使用 Yarn 作为包管理器时的模块安装和解析,感兴趣的同学可以 进一步了解,我们在后面构建和部署的篇章中也会再次谈到。
通过上述几方面的对比,我们就对这两个典型脚手架工具提供的构建集成能力有了一个大概的了解。这有助于我们在使用具体工具时快速定位问题的边界,同时在使用其他脚手架工具和模板时,我们也可以参照和借鉴上面的最佳实践方案。下一步,我们再来讨论定制专属脚手架模板的问题。
虽然官方提供的默认脚手架模板已经代表了对应技术栈的通用最佳实践,但是在实际开发中,我们还是时常需要对通过这些脚手架创建的模板项目进行定制化,例如:
通过将这些实际项目开发中所需要做的定制化修改输出为标准的脚手架模板,我们就能在团队内部孵化出更符合团队开发规范的开发流程。一方面最大程度减少大家在开发中处理重复事务的时间,另一方面也能减少因为开发风格不一导致的团队内项目维护成本的增加。接下来,我们就结合上面提到的三个脚手架工具来分别看下如何定制专属的脚手架模板。
脚手架模板在 Yeoman 中对应的是生成器 (Generator)。作为主打自由制作和分享脚手架生成器的开源工具, Yeoman 为制作生成器提供了丰富的 API 和 详细的文档。在这里,我们简单概述一下,一个基本的复制已有项目模板的生成器包含了:
基本目录结构如下所示:
generator-[name]/
package.json
generators/
app/
templates/...
index.js
其中 app/index.js 的核心逻辑如下:
var Generator = require('yeoman-generator')
module.exports = class extends Generator {
writing() {
this.fs.copyTpl(
this.templatePath('.'),
this.destinationPath('.'))
}
install() {
this.npmInstall()
}
}
writing 和 install 是 Yeoman 运行时上下文的两个阶段,在例子中,当我们执行下面的创建项目命令时,依次将生成器中模板目录内的所有文件复制到创建目录下,然后执行安装依赖。
在完成生成器的基本功能后,我们就可以通过在生成器目录里 npm link ,将对应生成器包挂载到全局依赖下,然后进入待创建项目的目录中,执行 yo 创建命令即可。 (如需远程安装,则需要先将生成器包发布到 npm 仓库中,支持发布到 @scope/generator-[name] 。)
至此,制作 Yeoman 的生成器来定制项目模板的基本功能就完成了。除了基本的复制文件和安装依赖外, Yeoman 还提供了很多实用的功能,例如编写用户交互提示框或合成其他生成器等,可供开发者定制功能体验更完善的脚手架生成器。
为 create-react-app 准备的自定义模板在模式上较为简单。作为一个最简化的 CRA 模板,模板中包含如下必要文件:
具体目录结构如下所示:
cra-template-[template-name]/
README.md (for npm)
template.json
package.json
template/
README.md (for projects created from this template)
gitignore
public/
index.html
src/
index.js (or index.tsx)
在使用时,同样还是需要将模板通过 npm link 命令映射到全局依赖中,或发布到 npm 仓库中,然后执行创建项目的命令。
npx create-react-app [app-name] --template [template-name]
相比 CRA 模板而言,Vue 的模板中变化最大的当属增加了 meta.js/json 文件,用于描述创建过程中的用户交互信息以及用户选项对于模板文件的过滤等。
[template-name]/
README.md (for npm)
meta.js or meta.json
template/
此外,Vue 的 template 目录中包含了复制到项目中的所有文件,并且在相关文件中还增加了 handlebars 条件判断的部分,根据 meta.js 中指定用户交互结果选项来将模板中带条件的文件转换为最终生成到项目中的产物。如以下代码所示:
template/package.json
...
"dependencies": {
"vue": "^2.5.2"{{#router}},
"vue-router": "^3.0.1"{{/router}}
},
...
meta.js
...
prompts: {
...
router: {
when: 'isNotTest',
type: 'confirm',
message: 'Install vue-router?',
},
...
}
使用自定义模板创建项目的命令为:
npm install -g @vue/cli-init
vue init [template-name] [app-name]
这样就完成了脚手架的定制工作。有了定制化后的脚手架,我们就可以在之后的创建项目时直接进入到业务逻辑的开发中,而不必重复地对官方提供的标准化模板进行二次优化。
使用脚手架工具是提升开发效率的第一项内容。通过今天的学习,我们了解了脚手架的使用场景,了解了 3 个典型脚手架工具的特点,并分析了 React 和 Vue 官方提供的脚手架工具中的构建集成技术细节。最后,对于希望将业务中使用的更具定制化的基础代码转变为新的脚手架模板的同学,我们也了解了如何在不同工具环境下创建和使用自定义模板。
课程最后,我想请你来回想一下:你在项目开发中使用的是哪一种脚手架工具和模板?使用的理由是?你可以将答案写在留言区与大家一起讨论。
下个课时我们将要学习的是一个大家一直在使用但是很少了解其中细节的技术点:热更新技术。