前端工程化的发起者,脚手架的本质作用就是创建项目基础结构、提供项目规范和约定。

脚手架应用场景

在前端工程中,会有一些相同的约定:

  • 相同的组织结构
  • 相同的开发范式
  • 相同的模块依赖
  • 相同的工具配置
  • 相同的基础代码

脚手架就是解决上述约定的工具,通过创建项目骨架自动的执行工作。例如:IED创建项目的过程就是一个脚手架的过程

与IED不同的是,由于前端技术选型比较多样,又没有一个统一的标准,所以前端脚手架不会集成在某一个IDE中,一般都是以一个独立的工具存在,相对会复杂一些。

常用脚手架工具

  1. 为特定类型服务的脚手架

根据信息创建对应的项目基础结构,但只适用于自身所服务的框架的项目。例如:

  • create-react-app
  • vue-cli
  • angular-cli
  1. 通用型脚手架工具Yeoman

会根据一套模板生成一个对用的项目结构,这中脚手架工具很灵活,非常容易扩展。

  1. 用于创建特定类型文件脚手架Plop

Plop是在项目开发过程中,创建一些特定类型的组件,例如创建一个组件/模块所需要的文件,这些文件一般都是由特定结构组成的,有相同的结构。

通用脚手架工具剖析

Yeoman简介

Yeoman是最老牌、最强大、最通用的脚手架工具,是创建现代化应用的脚手架工具,不同于vue-cli,yeoman更像是一个脚手架的运行平台,我们可以通过Yeoman搭建不同的Generator,去创建任何类型的项目。缺点是,在框架开发的项目中,Yeoman过于通用不够专注。

Yeoman基本使用

  • 在全局范围安装Yeoman: npm install yo —global or yarn global add yo
  • Yeoman要搭配对应的Generator才能使用,需要安装对应的Generator:npm install generator-node —global or yarn global add generator -node
  • 通过Yo运行对应的Generator
  • 通过命令行交互填写选项
  • 生成你所需要的项目结构

Sub Generator

有时候我们可能不需要创建一个完整的项目结构,而是在已有项目的基础上,创建一些项目文件,如README.md,或者是创建一些特定类型的文件,如ESLint、Babel配置文件。我们可以使用Sub Generator

  • 使用generator-node的子生成器cli生成器,生成一个cli应用所需要的文件 yo node:cli
  • 将生成的模块作为一个全局的命令行模块使用 yarn link 到全局范围
  • 使用 my-module —help
  • 使用之前要安装相应的依赖 yarn init

注意:不是所有的生成器都有子生成器

自定义Generator

基于Yeoman搭建自己的脚手架

创建Generator模块

  • Generator本质上就是一个NPM模块
  • generator基本结构

1.png
可以添加其他生成器
2.png

  • 模块名称必须是 generator-
  • 具体操作
    • 新建文件夹,命名为 generator- 格式
    • yarn init 生成 package.json 文件
    • 添加 yeoman-generator 依赖
    • 按照项目结构要求创建文件 generator/app/index.js ```javascript //此文件作为Generator的核心入口 //需要导出一个继承自 Yeoman Generator 的类型 //Yeoman Generator 在工作时会自动调用我们在此类型中定义的一些生命周期方法 //我们在这些方法中可以通过调用父类提供的一些工具方法实现一些功能,例如文件写入

const Generator = require(‘yeoman-generator’)

module.exports = class extends Generator { prompting() { // Yeoman 在询问用户环节会自动调用此方法 // 在此方法中可以调用父类的 prompt() 方法发出对用户的命令行询问 return this.prompt([ { type: ‘input’, name: ‘name’, message: ‘Your project name’, default: this.appname // appname 为项目生成目录名称 } ]) .then(answers => { // answers => { name: ‘user input value’ } this.answers = answers }) } writing () { //Yeoman 自动在生成文件阶段调用此方法 //我们这里尝试在项目目录中写入文件 // this.fs.write( // this.destinationPath(‘temp.txt’), // Math.random().toString() // )

  1. // 模板文件路径
  2. const tmpl = this.templatePath('foo.txt')
  3. // 输出目标路径
  4. const output = this.destinationPath('foo.txt')
  5. //模板数据上下文
  6. const context = { title: 'Hello zxb', success: false }
  7. this.fs.copyTpl(tmpl, output, context)
  8. }

}

  1. - yarn link 把模块链接到全局范围
  2. - 使用创建的模块
  3. - 根据模块创建文件
  4. - app目录下添加templates目录,将我们需要的生成文件添加到templates目录作为模板,内部可以使用 EJS 模板标记输出数据。
  5. - index.js 中可以通过模块方式写入文件到目标目录。
  6. - 需要三个参数
  7. > const tmpl = this.templatePath('foo.txt')// 模板文件路径
  8. > const output = this.destinationPath(foo.txt)// 输出目标路径
  9. > const context = { title: 'Hello zxb', success: false }//模板数据上下文
  10. > this.fs.copyTpl(templ,output,context) // 这个方法会自动把模板文件映射到生成的输出文件
  11. > // 相对于手动创建每一个文件,模板的方式大大提高了效率
  12. - 接收用户输入数据
  13. - 通过命令行交互的方式去询问我们的用户从而去得到
  14. **Vue Generator案例**
  15. ```javascript
  16. const Generator = require('yeoman-generator')
  17. module.exports = class extends Generator {
  18. prompting () {
  19. return this.prompt([
  20. {
  21. type: 'input',
  22. name: 'name',
  23. message: 'Your project name',
  24. default: this.appname
  25. }
  26. ])
  27. .then(answers => {
  28. this.answers = answers
  29. })
  30. }
  31. writing () {
  32. // 把每一个文件都通过模板转换到目标路径
  33. const templates = [
  34. '.browserslistrc',
  35. '.editorconfig',
  36. '.env.development',
  37. '.env.production',
  38. '.eslintrc.js',
  39. '.gitignore',
  40. 'babel.config.js',
  41. 'package.json',
  42. 'postcss.config.js',
  43. 'README.md',
  44. 'public/favicon.ico',
  45. 'public/index.html',
  46. 'src/App.vue',
  47. 'src/main.js',
  48. 'src/assets/logo.png',
  49. 'src/components/HelloWorld.vue',
  50. 'src/store/actions.js',
  51. 'src/store/getters.js',
  52. 'src/store/index.js',
  53. 'src/store/mutations.js',
  54. 'src/store/state.js',
  55. 'src/utils/request.js',
  56. 'src/views/About.vue',
  57. 'src/views/Home.vue'
  58. ]
  59. templates.forEach(item => {
  60. // item => 每个文件
  61. this.fs.copyTpl(
  62. this.templatePath(item),
  63. this.destinationPath(item),
  64. this.answers
  65. )
  66. })
  67. }
  68. }

发布generator(实际就是发布npm模块)


plop

一个小而美的脚手架工具,创建特定类型的脚手架工具。

Plop的基本使用

  • 安装 yarn add plop —dev
  • 新建plopfile.js——定义任务 ```javascript //Plop 入口文件,需要导出一个函数 接受一个plop对象 ,用于创建生成器任务

module.exports = plop =>{ plop.setGenerator(‘component’,{ description:’create a component’, prompts:[ { type:’input’, name:’name’, message:’componet name’, default:’MyComponent’ } ], actions:[ { type:’add’,//添加文件 path:’src/com/{{name}}//{{name}}.vue’, templateFile:’plop-templates/components.hbs’ } ] }) }

  1. **总结**
  2. - plop模块作为项目开发依赖安装
  3. - 在项目根目录下创建一个plopfile.js文件
  4. - plopfile,js文件中定义脚手架任务
  5. - 编写用于生成特定类型文件的模板
  6. - 通过Plop提供的CLI运行脚手架任务
  7. <a name="Ikcia"></a>
  8. ## 脚手架工作原理
  9. 脚手架的工作原理就是在启动脚手架之后,回自动地去询问一些预设问题,通过回答的结果结合一些模板文件,生成项目的结构。
  10. **实操**
  11. - 创建文件夹cli-test
  12. - yarn init
  13. - 修改package.json 增加`"bin": "cli.js",`
  14. - 创建cli.js文件<br />
  15. ```javascript
  16. #!/usr/bin/env node
  17. // Node CLI 应用入口文件必须要有这样的文件头
  18. // 如果是 Linux 或者 macOS 系统下还需要修改此文件的读写权限为 755
  19. // 具体就是通过 chmod 755 cli.js 实现修改
  20. // 脚手架的工作过程:
  21. // 1. 通过命令行交互询问用户问题
  22. // 2.根据用户问答的结果生成文件
  23. const fs = require('fs')
  24. const path = require('path')
  25. const inquirer = require('inquirer')
  26. const ejs = require('ejs')
  27. inquirer.prompt([
  28. {
  29. type: 'input',
  30. name: 'name',
  31. message: 'Project name?'
  32. }
  33. ])
  34. .then(anwsers => {
  35. //console.log(anwsers)
  36. //根据用户回答的结果生成文件
  37. //根据目录
  38. const tmplDir = path.join(__dirname, 'templates')
  39. //目标目录
  40. const destDir = process.cwd()
  41. //将模板下的文件全部转换到目标目录
  42. fs.readdir(tmplDir, (err, files) => {
  43. if (err) throw err
  44. files.forEach(file => {
  45. // 通过模板引擎渲染文件
  46. ejs.renderFile(path.join(tmplDir, file), anwsers,(err, result)=>{
  47. if (err) throw err
  48. // 将结果写入目标文件路径
  49. fs.writeFileSync(path.join(destDir, file), result)
  50. })
  51. })
  52. })
  53. })
  • npm link
  • 命令行运行 cli-test**