前端工程化
一切以提高效率、降低成本、质量保证为目的的手段都属于工程化
前端工程化主要解决的问题
- 传统语言或语法的弊端
- 无法使用模块化/组件化
- 重复的机械工作
- 代码风格统一、质量保证
- 依赖后端服务接口支持
- 整体依赖后端项目
脚手架工具
常用脚手架工具
- create-react-app - React项目
- vue-cli - Vue.js项目
- angular-cli - Angular项目
- Yeoman
- Plop
Yeoman
可用于创建任何类型的通用前端工程脚手架
-
使用步骤总结
- 明确需求
- 找到合适的generator,并全局安装
- 通过yo运行generator,进行命令行交互填写相应的信息,最终生成所需的项目工程结构
-
基础使用
- 安装
yarn global add yo
- 安装指定类型的generator
-
yarn global add generator-node
node工程
-
- 通过yo运行generator
yo node
- 安装
-
Sub Generator
子集生成器,用于生成特定的文件
- 例如生成cli相关的配置文件
yo node:cli
- 例如生成cli相关的配置文件
-
自定义generator
基于yeoman搭建自己的脚手架
创建generator本质上是创建一个npm模块
-
generator基本结构
-
generator命名约定
generator-<name>
新建generator目录
使用
yarn init -y
初始化-
yarn add yeoman-generator
添加依赖- 提供了一些创建generator的工具,使创建generator更加便捷
-
创建generators/app/index.js相应的目录结构及文件
index.js文件作为generator的核心入口
需要导出一个继承自yeoman generator的类型
yeoman generator在执行过程中会自动调用我们在此类型中定义的一些生命周期方法
-
可以在这些方法中通过调用父类的一些工具方法来实现一些功能,例如:文件写入
// index.js // generator-sample const Generator = require('yeoman-generator') module.exports = class extends Generator { writing () { this.fs.write( this.destinationPath('tmp.txt'), Math.random().toString() ) } }
使用
yarn link
命令将本地模块链接到全局使用yo执行相应的generator,例如
yo sample
-
使用ejs模板创建文件
-
在app目录下创建templates目录,并放置相应的模板文件
这是一个模板文件 模板文件的名字是 <%= title %> <% if (success) { %> world <% } %>
-
使用
this.fs.copyTpl(from, to, context)
命令根据模板创建文件const Generator = require('yeoman-generator') module.exports = class extends Generator { writing () { // this.fs.write( // this.destinationPath('tmp.txt'), // Math.random().toString() // ) const tmpl = this.templatePath('foo.txt') // 获取模板文件路径 const out = this.destinationPath('foo.txt') // 输出路径 const context = { title: 'hello', success: true } //模板数据上下文 this.fs.copyTpl(tmpl, out, context); } }
-
执行generator创建文件,得到foo.txt
// foo.txt 这是一个模板文件 模板文件的名字是 hello world
-
-
接收用户输入
通过实现prompting() 方法来实现,在此方法中,调用父类的prompt(),返回一个promise对象
prompt()方法接收一个问题对象数组
-
问题对象question包含type、name、message、default等属性
- type: input 用户输入方式
- name: answers对象的key
- message: 交互提示信息
- default: 交互问题的默认值
-
prompt().then(answers => {}) 获得问答结果对象 { name: '......' }
const Generator = require('yeoman-generator') module.exports = class extends Generator { prompting () { return this.prompt([ { type: 'input', name: 'name', message: 'project name ?', default: this.appname } ]).then(answers => { this.answers = answers }) } writing () { // this.fs.write( // this.destinationPath('tmp.txt'), // Math.random().toString() // ) const tmpl = this.templatePath('bar.html') // 获取模板文件路径 const out = this.destinationPath('bar.html') // 输出路径 const context = this.answers //模板数据上下文 this.fs.copyTpl(tmpl, out, context); } }
-
执行generator
-
-
Vue Generator案例
创建generator-vue目录,并
yarn init -y
初始化创建generators\app\index.js 目录结构及文件
创建generators\templates目录,并将所有模板文件放入
-
修改index.js文件
// generators\app\index.js const Generator = require('yeoman-generator') module.exports = class extends Generator { prompting () { return this.prompt([ { type: 'input', name: 'name', message: 'project name ?', default: this.appname } ]).then(answers => { this.answers = answers }) } writing () { const templates = [ // ... 所有模板文件的路径 ] templates.forEach(t => { this.fs.copyTpl( this.templatePath(t), this.destinationPath(t), this.answers ) }) } }
-
关于模板冲突
- 需要自动添加的模板文件中,可能存在也是用了ejs模板语法的文件,此时需要对其对应的模板语法进行转译,即<%%
-
发布generator
- 将模块托管到一个仓库(gitee/github/gitlab等)
- 通过
npm publish
或yarn publish
命令发布成一个公开的模块 - 如果修改过npm或yarn的源,例如淘宝的镜像,那么在发布时会提示错误,需要在publish命令后面使用
--registry=
参数指定原镜像地址npm publish --regishtry=https://registry.npmjs.org
yarn publish --registry=https://registry.yarnpkg.com
Plop
一款小而美的脚手架工具
用于创建项目中特定类型的文件
类似于Yeoman的sub generator
-
基本使用
将plop作为开发依赖进行安装
yarn add plop --dev
-
创建plopfile.js文件
plop工作的入口文件
-
需要导出一个函数,该函数接收一个plop对象,提供创建生成器任务的工具
// plopfile.js // 用于创建component module.exports = plop => { 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}}.js', templateFile: 'plop-templates/component.hbs' } ] }) }
创建plop-templates目录,用于存放plop模板文件 (handlebars模板文件)
-
通过plop提供的CLI运行脚手架任务
-
yarn plop component
执行指定生成器任务
-
脚手架工作原理
-
创建一个node cli模块
- 创建模块目录
- 使用
yarn init -y
初始化 - 修改
package.json
文件,添加bin: cli.js
- 创建cli.js
- Node CLI 应用入口文件
- 必须包含文件头
#!/usr/bin/env node
使之在node环境下执行 - 如果是linux或macOS系统还需修改文件读写权限
chmod 755 cli.js
- 执行
yarn link
-
脚手架工作过程
- 通过命令行交互询问用户问题
- 根据用户回答的结果生成文件
-
cli.js示例
#!/usr/bin/env node // Node CLI 应用入口文件必须包含文件头 // 如果是linux或macOS系统还需修改文件读写权限 chmod 755 cli.js // 脚手架工作过程 // 1、通过命令行交互询问用户问题 // 2、根据用户回答的结果生成文件 const path = require('path') const inquirer = require('inquirer') const { readdir, writeFileSync } = require('fs') const ejs = require('ejs') inquirer.prompt([ { type: 'input', name: 'name', message: 'project name' } ]).then(answers => { // 模板目录 const tmplDir = path.join(__dirname, 'templates') // 目标目录 const destDir = process.cwd() // 读取模板并生成文件 readdir(tmplDir, (err, files) => { if (err) throw err files.forEach(file => { // ejs模板引擎渲染文件 ejs.renderFile(path.join(tmplDir, file), answers, (err, result) => { if (err) throw err writeFileSync(path.join(destDir, file), result) }) }) }) })
- 辅助工具
- inquirer 用于命令行交互
- ejs 模板
- 辅助工具