本片文章讲解什么是前端工程化,以及前端工程化和工具之间的关系
技术是为了解决问题而存在的,为什么会有工程化呢?
前端目前面临的有哪些问题呢:
- 想要使用 ES6+新特性,但是兼容问题
- 想要使用 Less Sass PostCSS 增强 CSS 的编程性但是运行环境不能直接支持
- 想要使用模块化的方式提高项目的可维护性,但是运行环境不能直接支持
- 部署上线前需要手动压缩代码,手动上传代码到服务器
- 多人协作开发,无法硬性统一大家的代码风格,代码质量无法保证
- 部分功能需要等待后台服务接口提前完成
前端工程化主要解决的问题:
- 传统语言或语法的弊端
- 无法使用模块化/组件化
- 重复的机械式工作
- 代码风格统一、质量保证
- 依赖后端服务接口支持
- 整体依赖后端项目
工程化表现: 一切以提高效率、降低成本、质量保证为目的的手段都属于【工程化】
一切重复的工作都应该被自动化,注意 工程化 ≠ 某个工具 (比如webpack等工具)工具不是工程化的核心,工程化的核心是对项目整体的规划或架构,而工具只是帮助我们落地实现这种规划或架构的手段。
目前一些成熟的工程化集成工具:create-react-app、vue-cli、angular-cli、gatsby-cli
工程化与 Node:工程化归功于 Node.js 前端用到的工具几乎都是用 node.js 开发的
下面会通过 5 个方面来讲解如何落实前端工程化
- 脚手架工具开发
- 自动化构建系统
- 模块化打包
- 项目代码规范化
- 自动化部署
脚手架工具开发
脚手架工具,前端工程化的发起者。脚手架的本质作用:创建项目基础架构、提供项目规范和约定
- 相同的组织结构
- 相同的开发范式
- 相同的模块依赖
- 相同的工具配置
- 相同的基础代码
IDE 创建项目的过程就是一个脚手架的工作流程,而前端脚手架,前端技术选型多样,没有一个统一的标准,前端的脚手架的工具没有集成到开发工具中。
脚手架工具的本质作用:
- 创建项目的基础结构
- 实现项目构建任务
- 提供项目约定
常用的脚手架工具
-
React 项目 → create-react-app
-
Vue 项目 → vue-cli
-
Angular 项目 → angular-cli
-
Yeoman 通用的脚手架工具
-
Plop 在项目过程中组件/模块所需要的文件
Yeoman
下面注重关注一下 Yeoman 通用的脚手架工具
Yeoman可以创建任何类型的项目,而 Yeoman 过于通用在使用某个框架的情况下开发项目各倾向于框架的脚手架工具,Yeoman的生成项目是通过Generator插件来生成的,不同的项目有不同的Generator,如果在Yeoman上没有找到满意的Generator插件,也可以自己定义Generator插件来生成想要的项目。
yarn global add yo 全局安装 Yeoman,基于 generator 来创建不同的项目
yarn global add generator-node 输入该命令来安装 node 模块
这样就可以创建一个 node 项目了
在项目的目录下,输入命令: yo node 配置项目信息,生成如下的项目结构:
Yeoman 的Generator也会有子Generator
Yeoman Sub Generator 有时候不需要创建完整的项目结构,可能是在已有的项目上创建特定类型的文件比如创建 readme,添加一些配置文件 ESlint babel 配置文件,手动配置很容易配错,可以通过生成器帮助我们自动配置,Yeoman 提供了 sub Generator 在 node generator 提供了 node:cli 的工具
运行命令:
yo node:cli 在已有的项目基础之上,重写了 package.json 和多了 cli.js(什么是 cli?CLI就是一个脚手架命令行工具)
然后执行: yarn link link 到全局范围注意生成的名字”hello“
然后运行 yarn 命令安装项目中的依赖
运行 hello —help 如果运行出现权限的问题执行: sudo chmod -R 777 <项目路径> 即可以解决
执行的结果如下:
Yeoman 的使用总结:
- 明确需求
- 找到合适的Generator
- 全部范围安装找到的Generator
- 通过Yo运行对应的Generator
- 通过命令交互填写选项
- 生成需要的项目结构
比如安装 webapp 项目的结构,首先需要安装 yarn global add generator-webapp generator
然后在项目的目录下运行命令: yo webapp 就可以自动生成 webapp 的项目结构
通用脚手架工具剖析
如何自定义 Generator 基于 Yeoman 搭建自己的脚手架呢?
自定义 Generator
Generator 本质上就是一个 npm 模块,Generator 基本结构如下:
generators/ 生成器目录
app/ 默认生成器目录
index.js 默认生成器实现
+component/ 其他生成器目录(sub generator)
+index.js 其他生成器实现
package.json 模块包配置文件
` generator- Generator 的名称必须遵循这个格式 ,创建一个Generator项目,命名为:generator-sample yarn add yeoman-generator 安装基类生成器模块 然后在 index.js 的入口文件编写代码:
//作为 Generator 的核心入口
//需要导出一个继承自 Yeoman Generator 的类型
//Yeoman Generator 在工作时自动调用我们在此类型的一些生命周期方法
const Generator = require('yeoman-generator');
module.exports = class extends Generator{
writing(){
//尝试往项目目录写入文件 高度封装的 fs 模块
this.fs.write(this.destinationPath('temp.txt'),Math.random().toString());
}
}
运行: yarn link 将生成器连接到全局,然后我们借助自己定义的Generator来生成文件,创建一个项目my-object ,到项目的目录运行: yo sample 就会自动生成一个文件.
在项目的目录下回生成一个文件
- 接收用户数据与模板创建文件
上述中只是简单生成了一个文件,一般我们的项目中肯定有html等文件,原理是一样的,从Generator 模板中创建一个html文件代码如下:通过promting()方法会提示用户,比如提示用户输入项目名,在writing()方法中就可以得到用户输入的信息。
//作为Generator的核心入口
//需要导出一个继承自Yeoman Generator的类型
//Yeoman Generator 在工作时自动调用我们在此类型的一些生命周期方法
const Generator = require('yeoman-generator');
module.exports = class extends Generator{
prompting(){
//接收用户的数据 存储然后在writing中读取用户的输入信息
return this.prompt([
{
type:'input',//用户输入的方式接收用户的信息
name:'name',//得到结果的一个键
message:'Your project name',//给用户的提示
default:this.appname,//appname 为项目生成目录名称
}
]).then(answers=>{
//得到用户输入信息的对象 在writing中使用它
console.log(answers);
this.answers = answers;
});
}
writing(){
//尝试往项目目录写入文件 高度封装的fs模块
// this.fs.write(this.destinationPath('temp.txt'),Math.random().toString());
//加载模板 通过模板方式写入文件到目标目录
//模板路径
const tmpl = this.templatePath('bar.html');
//输出目标路径
const output = this.destinationPath('bar.html');
//模板数据上下文
const context = this.answers;
//写入到项目的目录
this.fs.copyTpl(tmpl,output,context);
}
}
准备的模板文件如下:name是用户输入的项目名
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= name %></title>
</head>
<body>
<h1><%= name %></h1>
</body>
</html>
完成之后运行: yran link 连接到全局,当然你也可以发布到npm中,然后安装到全局即可。 然后在指定的项目目录下运行: yo sample
生成的bar.html可以看到<%=name%> 变成了test 正式用户输入的项目名
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>test</title>
</head>
<body>
<h1>test</h1>
</body>
</html>
Yomen这个通用的脚手架工具的出现,对前端的开发影响非常大,以前的前端开发项目需要一个一个的手动的创建/复制基础的架构目录和文件,脚手架的出现可以使项目创建更加高效、保证质量、以及项目约束。
Vue Generator 实现
为了更加深入的理解脚手架工具,下面通过实现Vue Generator 创建Vue项目的脚手架的实现(PS:真实的项目开发还是使用Vue官方提供的Vue-cli来创建Vue项目) 首先要准备好模板文件
如下代码:其实实现的方式很简单,就是讲准备好的模板文件全部拷贝到指定的目录中去
const Generator = require('yeoman-generator');
module.exports = class extends Generator {
//监听用户的输入
prompting() {
return this.prompt([
{
type: 'input',
name: "name",
message: "Your project name",
default: this.appname,
}
]).then(answers => {
this.answers = answers;
});
}
writing() {
const templates = [
'babel.config.js',
'package.json',
'postcss.config.js',
'README.md',
'public/favicon.ico',
'public/index.html',
'src/App.vue',
'src/main.js',
'src/router.js',
'src/assets/logo.png',
'src/components/HelloWorld.vue',
'src/store/actions.js',
'src/store/getters.js',
'src/store/index.js',
'src/store/mutations.js',
'src/store/state.js',
'src/utils/request.js',
'src/views/About.vue',
'src/views/Home.vue'
];
templates.forEach(item=>{
const template = this.templatePath(item);
const outpath = this.destinationPath(item);
const context = this.answers;
this.fs.copyTpl(template, outpath, context);
});
}
}
发布 Generator
yarn publish 进行发布 如果你的 npm 有配置淘宝的镜像源,在 npm 淘宝镜像会存在问题
需要修改发布的命令:
yarn publish --registry= https://registry.yarnpkg.com
这样就可以发布 npm 的官方镜像了. 关于 Yeoman Generator
- 每个 Generator 是一个单独的 npm 模块
- 一般 Generator 用于生成项目,Sub Generator 用于补充生成项目中的文件
- Generator 模块必须继承自 yeoman-generator
- 一般通过 Yeoman 实现一个自定义脚手架实际上是开发一个 Generator
Plop 小型的脚手架工具
Plop主要用于创建项目中的特定类型文件的小工具,Plop 一般用于创建项目中同类型的文件,Plop 可以提高开发过程中的效率。
plop 的使用 yarn add plop --dev 安装 plop 模块
比如项目中要用到很多组件,为了提高开发效率就需要一个通用的组件的模板来快速的生成
准备组件模板:
component.hbs(组件模板类)
console.log('123');
component.css.hbs(组件的CSS)
.{{name}} {
}
component.test.hbs(组件的测试)
console.log('123'+{{name}});
之后就需要创建一个 plopfile.js 的文件
/* Plop日寇文件 需要导出一个函数 */
module.exports = plop=>{
//component是任务名称
plop.setGenerator('component',{
description:"create a component",
prompts:[
{
type:'input',
name:'name',
message:'component name',
default:'My Component',
}
],
actions:[
{
type:'add',
path:'src/component/{{name}}/{{name}}.js',
templateFile:'plop-templates/component.hbs',
},
{
type:'add',
path:'src/component/{{name}}/{{name}}.css',
templateFile:'plop-templates/component.css.hbs',
},
{
type:'add',
path:'src/component/{{name}}/{{name}}.test.js',
templateFile:'plop-templates/component.test.hbs',
}
]
});
}
然后在命令行运行: yarn plop component 执行 plop cli 命令行工具,可以看到在项目中生成了对应的文件
在项目中生成了对应的文件
Plop 脚手架工具非常好用,在项目开发阶段可以通过Plop来创建比如:一些通用的组件逻辑等代码可以在项目开发阶段提高开发效率,强烈推荐👍
Polp的使用流程如下:
- 将 plop 模块作为项目开发依赖安装
- 在项目根目录下创建一个 plopfile.js 文件
- 在 plopfile.js 中定义脚手架任务
- 编写用于生成特定类型文件的模板
- 通过 Plop 提供的 CLI 运行脚手架任务
脚手架的工作原理
脚手架的工作过程:
1 创建js文件:文件头必须要是:!/usr/bin/env node
2 通过命令行交互询问用户问题 需要安装inquirer 模块 yarn add inquirer
3 根据用户回答的结果生成文件
如下代码:其实原理很简单 在脚手架工具准备好模板然后通过node最原始fs模块对模板文件进行处理
#!/usr/bin/env node
/* Node CLI 应用入口文件必须要有一个这样的文件头 如果是Linux或者MacOS系统还需要
修改此文件的读写权限755 具体使用:chmod 755 cli.js 实现修改 */
// console.log('cli working');
//脚手架的工作过程:
//1 通过命令行交互询问用户问题 需要安装inquirer 模块 yarn add inquirer
//2 根据用户回答的结果生成文件
const inquirer = require('inquirer');
const path = require('path');
const fs = require('fs');
const ejs = require('ejs');
//通过命令行交互询问用户问题
inquirer.prompt([
{
type:"input",
name:'name',
message:'Project name?'
}
]).then(answer=>{
// console.log(answer);
//模板路径
const tmplDir = path.join(__dirname,"templates");
//目标目录
const destDir = process.cwd();
//将模板下的文件全部转换到目标目录
fs.readdir(tmplDir,(err,files)=>{
if (err) {
throw err;
}
files.forEach(file=>{
//通过模板引擎渲染文件 添加模板引擎模块 yarn add ejs
ejs.renderFile(path.join(tmplDir,file),answer,(err,result)=>{
if (err) {
throw err;
}
// console.log(result);
//将渲染的结果写入目标文件路径
fs.writeFileSync(path.join(destDir,file),result);
})
});
});
});
模板文件如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%=name %></title>
</head>
<body>
</body>
</html>