因为如今开发中,前端能够负责的范围愈来愈大,早已不是仅限制于网页。像 App、小程序、甚至桌面应用,均可以使用前端技术来开发。css
因此原来经过前端写 Demo,后端套数据的模式早就没法支持现代多元化前端开发。html
前端工程化就是在这个背景下成为了受人重视的技术,而且是目前前端开发必备的技术之一。前端
技术是为了解决问题而存在的,前端工程化解决了不少问题。好比 ES6 新语法、Less/Sass/PostCss 等 CSS 工具和模块化。它们都是没法被运行环境直接支持的。可是使用前端工程化之后,就能够将它们转换为运行环境支持的代码。vue
还有一些部署项目时所须要作的操做,好比压缩代码及资源文件,将代码上传至服务器等。若是让人来手动处理这些任务,会很容易出现操做失误。这种状况下,让机器自动来完成这些任务更为合理。node
多人协做时,代码风格、代码质量都须要获得保证。react
开发过程当中须要等待后端接口提早完成。webpack
这些都是前端工程化负责的范畴。git
总结起来,前端工程化解决的问题主要有 6 个部分。web
传统语言或语法的弊端。(ES6+语法、TypeScript、Sass)vue-cli
没法使用模块化/组件化(ES Modules、Components)
重复的机械式工做(Build、Publish)
代码风格统1、质量保证(git、ESLint)
依赖后端服务接口支持(Mock)
总体依赖后端项目(DevServer)
一切以提升效率、下降成本、质量保证为目的的手段都属于工程化。
一切重复的工做都应该被自动化。
一个常规前端项目的总体环节大约有 5 步:
建立项目-编码-预览/测试-提交-部署
1.建立项目阶段:
借助脚手架工具自动建立项目目录结构和特定类型的文件。
2.编码阶段:
借助工具实现代码格式化、校验代码风格,还可使用新的特性来编写源代码,经过编译/构建/打包生成运行环境支持的运行时代码。
3.预览/测试阶段
开发过程,须要 Mock 服务,须要使用基础的 Web 服务器来托管项目,好比 Nginx、Apatch 等。但这些服务都不提供热更新的体验,因此须要借助现代化的工具来提升开发体验。
在编译转换后,若是发生异常,须要定位源代码的位置。这是就须要使用 Source Map 技术来映射运行代码和源代码。
4.代码提交阶段
使用 git hooks 对项目总体进行检查,包括代码风格的检查和项目质量的检查,确保不会将有问题的代码提交到 git 仓库中,这样 git 仓库的代码永远都是可用的。除此之外,还能够对 git log 的格式进行限制,这样在代码分支合并和回滚时有很大的价值。
主要借助的工具备 Lint-staged 和持续集成等。
5.部署阶段
CI/CD、自动发布。
能够经过一行命令自动的将项目打包上传至 ftp 服务器。
如今有些工具很是强大,像 Webpack,以致于不少人认为前端工程化就是指 webpack。
实际上,工具不是工程化的核心,工程化的核心是对项目的总体规划和架构。工具只是实现这种规划和架构的一种手段。
一个项目的工程化,首先要规划出一个项目总体的工做流架构。
如文件组织结构(按做用分层、按业务分层)、代码开发范式(语法、规范、标准、语言,如 ES6+、TypeScript 等)、先后端分离方式(Ajax 和中间层)。
规划完工做流总体架构之后,再来考虑使用哪些工具来实现这套规划。
能够借鉴目前业界成熟的工程化方案中的思路,如 create-react-app、vue-cli、angluar-cli、gatsby-cli 等。
它们并非一个简单的项目生成脚手架,而是工程化工具的集成。
目前前端的工程化很大程度上都归功于 Node.js。
Node.js 是继 Ajax 后有一次颠覆式的前端工业革命,没有 Node.js 就没有今天的前端。
目前绝大多数工程化工具都是由 Node.js 开发的,因此前端工程化是由 Node.js 强力驱动的。
总之,工程化是为了解决问题而存在的。
脚手架工具是前端工程化的发起者,它是自动帮助咱们建立项目基础结构、提供项目规范和约定的工具。
在开发相同类型的项目时,会有不少相同的东西,包括:
组织结构
开发范式
模块依赖
工具配置
基础代码
...
这些相同的东西不须要人为建立,可使用脚手架工具快速建立项目骨架。而后基于这个骨架来开发。
好比使用大型的 IDE,如 IDEA 和 Visual Studio 等来建立项目的过程就是一个脚手架的工做流程。
前端的脚手架和服务端项目、移动端项目不太同样。由于技术选型过于多样化,因此不会集中在某一个特定的 IDE 中,目前流行的作法是在命令行中来完成项目的建立。
React 项目: create-react-app
Vue 项目: vue-cli
Angular 项目: angular-cli
它们根据用户选择的信息建立对应的项目基础结构,不过它们只能服务于自己的框架,并不通用。
另外一类是通用型脚手架工具,表明像 Yeoman。优势是灵活易扩展。
还有一类是在项目开发过程当中会使用到的,好比 Plop。它能够建立某种特定类型的文件。好比某个模块或是某个组件的基础代码。
yeoman 是最老牌、最强大、最通用的脚手架工具之一,基于 node.js 开发。
不一样于常规的脚手架工具,yeoman 更像是一个脚手架的运行平台。
咱们能够本身定义 Generator 来建立属于本身的项目脚手架。
Yeoman 的缺点就是它的优势,由于不够专一,因此在专一于某项框架的脚手架建立过程当中不如 vue-cli 这类工具。
npm i -g yo
复制代码
安装完 yo 以后,能够查看版本号来确认安装是否成功。
yo --version
复制代码
只安装 yo 是不够的,它只是一个 Generator 运行模块,还须要安装特定的 Generator。
好比一个 node 项目,就须要安装generator-node
。
npm i -g generator-node
复制代码
安装完成以后,就能够建立 node 项目了。
命令是 yo 加上 generator 的名字。(node 包名去掉 generator-)
yo node
复制代码
在项目建立过程当中,yeoman 会提问一些问题,经过用户提交的答案来建立不一样的项目结构。
每一个 generator 均可以提供 sub generator,用于生成特定的文件或者基础代码。但也不是每个 generator 都提供了 sub generator,具体能够去 generator 的官方文档查看。
运行 sub generator 也比较简单,就是 yo 加上 generator 的名字,再加上冒号(:),最后加上 sub generator 的名字。
好比在 node.js 项目中建立一个命令。
yo node:cli
复制代码
大体上分为 6 步。
由于官方的 generator 在某些场景下做用会很是局限,因此咱们有必要自定义本身的 generator。
generator 本质上就是一个 npm 模块,只须要符合特定的结构便可。
generator 的名字必须是 generator-<name>
的格式。
generator 须要有一个生成器的基类。
总体的的操做步骤以下。
建立项目根目录。
mkdir generator-sample
复制代码
初始化项目。
npm init -y
复制代码
安装 yeoman-generator。
npm i yeoman-generator
复制代码
按照项目结构要求,建立特定的文件夹。
入口文件是 generators/app/index.js
。
这个文件须要导出一个继承自 Yeoman Generator 的 class。Yeoman Generator 在工做时会自动调用这个 class 中的生命周期函数。
在这个导出的类中,能够调用父类提供的工具方法实现一些具体的功能。
好比在 writing 生命周期函数中能够建立一些文件。
const Generator = require("yeoman-generator");
module.exports = class extends Generator { writing() { // 模版文件路径 const tmpl = this.templatePath("foo.txt"); // 输出目标路径 const output = this.destinationPath("foo.txt"); // 模板数据上下文 const context = { title: "Hello", success: false }; this.fs.copyTpl(tmpl, output, context); } }; 复制代码
操做文件可使用父类中提供的 this.fs 模块下的方法。这个 fs 和 nodejs 原生的 fs 不同,是高度封装过的,因此功能更增强大。
若是一个项目所须要的文件过多,挨个文件建立会很麻烦,这时候使用模板的方式来建立文件更加方便。
在 generators/app 目录下建立 templates 文件夹,该文件夹下的文件都是模板文件。能够被 this.templatePath 方法获取到。
模板文件彻底遵循 EJS 语法,即尖括号百分号的写法。
<%= name %>
复制代码
EJS 模板官网:https://ejs.co/
最终使用 this.fs.copyTpl 方法将模板拷贝到目标目录下。
相对于手动建立每一个文件,模板的方式大大提升了效率。
模版中的动态数据须要用户来输入,通常会在命令行中发起询问。
promting 生命周期函数中能够调用父类的 prompt 方法来发起询问,这个询问返回的是一个异步函数。
const Generator = require("yeoman-generator");
module.exports = class extends Generator { prompting() { // prompt 的参数是一个数组,能够发起多个问题 // 每一个问题都是以一个对象的形式体现 // type 是问题的类型,input 表明须要用户来输入,除此之外还有单选、多选等。 // name 是问题的 key // message 是问题的提示 // default 是用户不作输入时的默认值 return this.prompt([ { type: "input", name: "name", message: "Your project name", default: this.appname, }, ]).then((answers) => { // answers 会以一个对象的形式返回 // { name: 'user input value' } // 将这个对象挂载到 this.answers this.answers = answers; }); } writing() { // 模版文件路径 const tmpl = this.templatePath("foo.txt"); // 输出目标路径 const output = this.destinationPath("foo.txt"); // 将上下文对象替换为用户输入对象 const context = this.answers; this.fs.copyTpl(tmpl, output, context); } }; 复制代码
首先能够经过 vue-cli 生成一个基础的 vue 项目骨架,而后将这个骨架做为基础模板来使用。须要被变量替换的地方都使用 EJS 的语法替换掉。
在 writing 生命周期函数中经过遍历的方式将模板中全部文件都调用一遍 this.fs.copyTpl。
具体步骤就不演示了。
generator 的发布流程和 npm 普通的包是同样的,经过 npm publish
命令去发布。
若是想要被 yeoman 官方收录,那么就须要在关键词中添加 yeoman-generator。
除了 yeoman 这类大型的脚手架工具外,还有不少小而美的脚手架工具,好比 Plop。
Plop 经常使用于在项目开发过程当中建立某类类型的文件,优势相似于 Yeoman 中 sub generator 的概念。
好比在 React 项目的开发过程当中,开发一个组件须要建立 1 个目录,该目录下须要 3 个文件。
. |____Footer | |____Footer.tsx | |____Footer.scss | |____Footer.test.ts 复制代码
若是咱们手动的建立这些文件,并编写通用的代码,过程会很是繁琐,因此可使用 Plop 来实现这个功能。
首先将 plop 添加到项目中。
npm install --save-dev plop
复制代码
在项目根目录下建立 plopfile.js 文件,它是 plop 工做所须要的入口文件。
一个简单的 plopfile.js 配置以下:
// 导出一个函数
// 该函数须要接收一个 plop 对象参数 module.exports = (plop) => { // 调用 setGenerator 方法 // 第一个参数是命令名,第二个参数是配置对象 plop.setGenerator("component", { description: "create a component", // 说明 // 询问 prompts: [ { type: "input", name: "name", message: "component name", default: "MyComponent", }, ], // 动做 actions: [ { type: "add", // 添加文件 // 使用 {{ }} 语法拿到上面询问的值 path: "src/components/{{name}}/{{name}}.tsx", // 模板的文件路径 templateFile: "plop-templates/component.hbs", }, // 添加 scss 文件和 test 文件 { type: "add", // 添加文件 // 使用 {{ }} 语法拿到上面询问的值 path: "src/components/{{name}}/{{name}}.scss", // 模板的文件路径 templateFile: "plop-templates/component.scss.hbs", }, { type: "add", // 添加文件 // 使用 {{ }} 语法拿到上面询问的值 path: "src/components/{{name}}/{{name}}.test.ts", // 模板的文件路径 templateFile: "plop-templates/component.test.ts.hbs", }, ], }); }; 复制代码
上面这种 hbs 文件遵循 handlebars 的语法,具体参考:https://handlebarsjs.com/
模板通常存放在根目录下的 plop-templates 文件夹下。
最后就能够经过命令运行 plop 脚本。
npx plop component
复制代码
总结一下,plop 的使用步骤共有 5 步。
脚手架的工做原理相对简单,大体上都是在启动时询问用户一些问题,经过获得的答案配合模板生成对应的文件。
开发脚手架的思路:
经过命令行交互询问客户问题。
根据用户回答的结果生成文件。
建立脚手架项目文件夹。
mkdir sample-scaffolding
复制代码
初始化项目。
npm init -y
复制代码
在 package.json 中添加 bin 字段,定义 cli 的入口文件。
{
"bin": "cli.js" } 复制代码
建立 cli.js。
touch cli.js
复制代码
由于须要在控制台对用户发起询问,因此要借助一个 nodejs 的库,inquirer。
npm i --save-dev inquirer
复制代码
除此以外,还须要借助模板引擎来给模板文件注入变量,因此须要借助 ejs。
npm i ejs
复制代码
如今能够在 cli.js 中编写逻辑。
#!/usr/bin/env node
const fs = require("fs"); const path = require("path"); const inquirer = require("inquirer"); const ejs = require("ejs"); // 执行命令后,直接发起询问。 inquirer .prompt([ { type: "input", name: "name", message: "Project name?", }, ]) .then( // 返回值 anwsers 是一个对象 (anwsers) => { // 模板目录 const tmplDir = path.join(__dirname, "templates"); // 目标目录 const destDir = process.cwd(); // 将模板目录下的全部模板文件经过模板引擎转换后复制到目标目录中 fs.readdir(tmplDir, (err, files) => { if (err) throw err; // 遍历全部模板文件 files.forEach((file) => { // 经过 ejs 渲染文件 ejs.renderFile(path.join(tmplDir, file), anwsers, (err, result) => { if (err) throw err; // 将 ejs 渲染后的文件写入目标文件中 fs.writeFileSync(path.join(destDir, file), result); }); }); }); } ); 复制代码
第一行的 #!/usr/bin/env node
是告诉系统,该文件经过 node 来执行。
接下来建立模板文件夹。
mkdir templates
复制代码
建立模板文件 index.html 和 style.css。
index.html 内容:
<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title><%= name %></title> <link rel="stylesheet" href="./style.css" /> </head> <body> hello, world! </body> </html> 复制代码
style.css 内容:
body {
background-color: #ddffdd; color: #060606; } 复制代码
将命令连接到全局。
npm link
复制代码
到这里,简单的脚手架 demo 就开发完成了。
如今开始测试,到其余目录建立一个项目目录。
mkdir test-project
复制代码
进入该目录,执行脚手架命令。
sample-scaffolding
复制代码
此时命令行会出现询问,输入项目名,就能够看到生成后的文件了。
? Project name? hello
复制代码
脚手架工具是前端工程化的第一块内容,到此讲解结束,接下来会发布另外一篇文章,关于前端工程化第二块的内容,也是前端工程化的核心部分,自动化构建。敬请期待。
- END -