工程化的定义和主要解决的问题
前端工程化是遵循一定的标准和规范通过工程去提高效率降低成本的一种手段,近些年被广泛的关注和探讨,因为前端要求不断提高,业务逻辑越来越复杂,从传统的网站到现在H5,移动app,桌面应用以及小程序,前端工程化就变成了前端工程师必备的手段之一。
技术是为了解决问题而存在的,日常问题举例:
- 想要使用ES6+新特性,但是兼容有问题。
- 想要使用less/sass/postcss增加css的编程性,但是运行环境不能直接支持
- 想要使用模块化的方式提高项目的可维护性,但运行环境不能直接支持
- 部署上线前需要手动压缩代码及资源文件,部署过程需要手动上传代码到服务器
- 多人写作开发,无法硬性统一大家的代码风格,从仓库中pull回来的代码质量无法保证
- 部分功能开发时需要等待后端服务接口提前完成
主要 解决的问题:
工程化表现
一切以提高效率、降低成本、质量保证为目的的手段都属于【工程化】
一切重复的工作都应该被自动化
工程化不等于某个工具
比如webpack工具很全面,webpack只是实现工程化的工具而已,不是工程化的核心
一些成熟的工程化集成
工程化与node.js
node 驱动了 工程化进步
主要内容:
- 脚手架工具开发
- 自动化构建系统
- 模块化打包
- 项目代码规范化
- 自动化部署
脚手架工具
脚手架的本质作用:创建项目基础结构、提供项目规范和约定
- 相同的组织结构
- 相同的开发范式
- 相同的模块依赖
- 相同的工具配置
- 相同的基础代码
举个例子:IDE创建项目的过程就是一个脚手架的工作流程
常用的脚手架工具
React项目–》create-react-app
Vue.js项目–》vue-cli
Angular项目–》angular-cli
都是根据信息创建对应的项目基础结构
• Yeoma:通用创建项目脚手架
• Plop: 用于创建特定类型的文件
Yeoman简介
可以通过 Yeoman 定制自己的脚手架
Yeoman基本使用
全局环境安装 yo
yarn global add yo
安装对应的 generator ( 本次演示的是一个 node 项目)
yarn global add generator-node
通过 yo 运行 generator
yo node
// 做一系列选择,回答一些问题...
// 最后生成项目
Sub Generator
有些generator 还可以支持 sub generator
有时不一定要生成一整个项目, 还可以生成 eslint配置文件、格式化代码配置文件、等等
举个例子
yo node:cli // generator-node 提供的一个sub generator
Yeoman使用步骤总结
Yeoman是一款通用的脚手架工具,几乎可以通过它去创建任何有项目,使用yeoman的步骤如下
如果你选择的命令是yo webapp,最终生成了web应用的基础结构
自定义Gennerator
基于Yeoman搭建自己的脚手架
不同的Gennerator可以用来生成不同的项目,也就是说可以创造自己的gennerator去生成自定义的项目结构,市面上的gennerator都是通用的,而在实际开发过程中会出现一部分基础代码甚至业务代码在相同类型项目时还是会重复的,这时候就可以把公共部分都放到脚手架中去生成,让脚手架工具发挥更大的价值。
例如在创建vue.js的时候,官方默认的脚手架工具只会创建一个最基础的项目骨架,不包含经常要用到的模块,例如axios、vue-router,vuex等,需要在每次创建完手动的去引入这些模块并且去编写一些基础的代码,如果把这些也放到脚手架中就不存在这个问题了。
创建Generator模块
创建一个Generator实际上就是创建一个npm模块
工程里手动创建一个generators文件夹
创建的index.js用来做为Generator的核心入口,
需要导出一个继承自Yeoman Generator的类型
Yeoman Generator在工作时会自动调用我们在此类型中定义的一些生命周期方法
在这些方法中可以通过调用父类提供的一些工具方法实现一些功能,例如文件写入
在index.js里面写
这样一个简单的Generator就完成了
把他链接到全局范围,变成全局模块包,这样就能找到generator-sample
文件,最终生成temp.txt,里面就是我们生成的一个随机数
根据模板创建文件
在app文件夹下创建一个目录templates/foo.txt
Foo.txt是一个模板文件,内部可以使用EJS模板标记输出数据
例如:<%=title%>
其他的EJS语法也支持
<%if(success){%>
哈哈哈
<%}%>
有了模板之后index.js里就不需要借助fs.write来写入文件了,可以通过
通过模板方式写入文件到目标目录
相对于手动创建每一个文件,模板的方式大大提高了效率
接收用户输入数据
vue Generator 案例
- 准备一个 generator-lt-vue 项目
a. 安装 (yarn init + yarn add yeoman-generator)
b. 创建目录 /generators/app/index.js /generators/app/templates (同上一部分) - 模板素材准备
a. 用 vue-cli 创建一个vue 模板(这个 模板 index.html 有很多EJS模板变量,由于我们案例没那些变量可以先删除)
vue create hello-world
b. 将准备好的模板文件全部复制到 templates 文件夹
- 写我们的generator (/generators/app/index.js )
a. 提供输入询问(项目名是啥啊?)
b. 可以遍历 templates 文件夹并获取所有文件
c. 将所有文件复制到目标项目
const fs = require("fs");
const path = require("path");
const Generator = require("yeoman-generator");
module.exports = class extends Generator {
async prompting() {
this.answer = await this.prompt([
{
type: "input",
name: "name",
message: "your project name",
default: this.appname, // 默认是项目文件夹名
},
]);
}
writing() {
// 通过模板方式西写入文件到目标
// 递归找到所有的 template 文件路径
const fileArray = [];
travel(
path.join(__dirname, "templates"),
// callback
(pathname, next) => {
fileArray.push(pathname);
next(); // 一定要调用这个才可以持续递归
},
// finish 函数
() => {
const newfileArray = clipAbsolutePathToRelative(fileArray, __dirname);
newfileArray.map((filePath) => {
this.fs.copyTpl(
this.templatePath(filePath),
this.destinationPath(filePath),
this.answer
);
});
}
);
}
// 小知识点提示
// 直接附加到Generator原型的每种方法都被视为一项任务。每个任务都由Yeoman环境运行循环按顺序运行。
// 实现不自动执行的 私有方法的方式: https://yeoman.io/authoring/running-context.html
};
// 异步遍历文件夹下所有文件, 尤其要注意必须在会调用接着 next() 才可以持续递归
function travel(dir, callback, finish) {
fs.readdir(dir, function (err, files) {
(function next(i) {
if (i < files.length) {
var pathname = path.join(dir, files[i]);
fs.stat(pathname, function (err, stats) {
if (stats.isDirectory()) {
travel(pathname, callback, function () {
next(i + 1);
});
} else {
callback(pathname, function () {
next(i + 1);
});
}
});
} else {
finish && finish();
}
})(0);
});
}
// 把绝对路径裁切成相对路径
function clipAbsolutePathToRelative(pathArray, dirPath) {
return pathArray.map((pathItem) =>
pathItem.replace(`${dirPath}\\templates\\`, "")
);
}
- 将generator-lt-vue 导入全局
a. yarn link - 创建一个新的目录
a. mkdir yo-vue - 执行 我们自定义的 generator-lt-vue 生成模板
a. yo generator-lt-vue
7.在 yo-vue 项目下 yarn install --> yarn serve 最后可以正常使用
发布 Generator
发布 generator 就是发布一个 npm 模块
执行: npm publish
注意: npm publish 不能使用 淘宝镜像
可以这样用 ( 重新设置源)
• yarn publish --registry=https"//registry.yarnpkg.com
Plop
一个小而美的脚手架工具
主要用于在项目中 创建特定类型的小工具, 类似 yeoman subGenerator
主要用于在在项目中创建同类型的文件
举个例子:
写一个react 组件(Header)重复的3个文件, 并且文件基础代码都是一致的。
• Header.jsx
• Header.css
• Header.test.js
• 这时候用 plop 就可以 自动化创建组件标准文件,显著提升效率
Plop具体使用
- 将 plop 模块作为项目开发依赖安装
a. yarn add plop --dev - 在项目根目录下创建一个plopfile.js 文件
- 在 plopfile,js 文件中定义脚手架任务
- 编写用于特定类型的文件的模板
- 通过Plop 体用的cli 运行脚手架任务
案例: 使用Plop 为React 添加一个标准的组件模板 - 用 npx create-react-app my-app 创建 React 项目
- 安装 Plop 依赖
a. npm install --save-dev plop - 根目录创建 plopfile.js
文档地址: https://github.com/plopjs/plop
npx create-react-app my-app
npm install --save-dev plop
plopfile.js
module.exports = function (plop) {
// create your generators here
plop.setGenerator("component", {
description: "this is a skeleton plopfile",
prompts: [
{
type: "input",
name: "name",
message: "your Component name:",
default: "MyComponent",
store: true,
},
], // array of inquirer prompts
actions: [
{
type: "add",
path: "src/components/{{name}}/{{name}}.js",
templateFile: "plop-templates/component.hbs",
},
{
type: "add",
path: "src/components/{{name}}/{{name}}.css",
templateFile: "plop-templates/component.css.hbs",
},
{
type: "add",
path: "src/components/{{name}}/{{name}}.test.js",
templateFile: "plop-templates/component.test.hbs",
},
], // array of actions
});
};
- 根目录创建对应模板文件
a. hbs 是 Handlebars.js 模板引擎
b. 官网: https://handlebarsjs.com/guide/
// component.hbs
import React from 'React'
export default () => {
return <div class="{{name}}">
<h1>{{name}}</h1>
</div>
}
// component.css.hbs
.{{name}}{
color: red;
}
//component.test.hbs
import React from 'react';
import { render } from '@testing-library/react';
import {{name}} from './{{name}}';
test('renders learn react link', () => {
const { getByText } = render(<{{name}} />);
const linkElement = getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});
- 使用
a. 项目根目录运行
ⅰ. yarn plop component(这个是自己定义的名字)
yarn plop component
... 回答一些问题
... 创建成功 如下
? your Component name: Login
√ ++ \src\components\Login\Login.js
√ ++ \src\components\Login\Login.css
√ ++ \src\components\Login\Login.test.js
6.结果
脚手架的工作原理
• 就是问点问题, 根据问题定制如何将模板文件复制渲染出来
案例: 实现一个基础的脚手架
• 问问题 + 复制模板
NodeJs 交互式命令行工具
提供错误回调
询问操作者问题
获取并解析用户输入
检测用户回答是否合法
管理多层级的提示
npm install inquirer --save
高效的嵌入式 JavaScript 模板引擎
npm install ejs --save
注意: 写脚本一定要在文件顶部写一段注释
#!/usr/bin/env node
// node Cli 引用入口文件必须要有这样的文件头
// 如果是 linux 或者 macOS 系统还要修改此文件读写为 755
// 具体就是 通过 chmod 755 cli.js 实现
要全局执行怎么办?
• npm link 或 yarn link