前端工程化-yeoman、plop

为什么要工程化?

  1. 部署上线机械执行,比如代码和资源文件的压缩、部署过程需要手动上传代码到服务器
  2. 如果使用新的特性,但是兼容可能会有问题。比如使用less/sass/postcss之类去增强css编程性,但是运行环境不能直接支持
  3. 可以统一代码风格,质量保证。比如:eslint、ts
  4. 整体依赖后端项目,比如要等待后端接口支持后才能进行调试

工程化流程

创建项目->编码->预览/测试->提交->部署

创建项目阶段可以用脚手架搭建;
编码环节:格式化代码、校验代码风格、编译/构建/打包
预览环节:web server/mock;hmr;source map
提交环节:git hooks;lint-staged;持续集成
部署:CI/CD;自动发布

脚手架工具

脚手架用于自动的创建项目基本结构、提供项目规范和约定。比如要有相同的组织结构、相同的开发范式、相同的模块依赖、相同的工具配置、相同的基础代码。(create-react-app/vue-cli…)

Yeoman

使用yeoman搭建不同的generator创建任意类型的项目,过于通用,不够定制化。

使用

全局安装yeoman和对应的generator,通过yo运行generator

yarn global add generator-node

cd path
yo node

sub generator

如果不需要创建完整的项目结构,只需要在原有的项目基础上创建特定的配置文件等,如eslint、webpack等。

使其变成cli应用

yo node:cli

将模块注册到全局范围,使其在全局都能被使用

yarn link

运行命令行测试

[模块名] --help

yeoman常规使用步骤

  1. 明确需求
  2. 找到合适的generator
  3. 全局范围内安装找到的generator
  4. 通过yo运行对应的generator
  5. 通过命令行交互填写选项
  6. 生成所需的项目结构

    自定义generator

    解决:官方提供的脚手架不够定制化。

注意:yeoman的generator名称必须为:generator-<name>这种格式。

文件目录:

generators
--app
----index.js  // generator的核心入口
--package.json

步骤:

  1. yarn init

  2. yarn add yeoman-generator 提供了一些基类,是我们在创建生成器的时候更加便捷

  3. 编写入口文件:

    const Generator=require('yeoman-generator')
    
    module.exports=class extends Generator {
     writing(){
         // yeoman 自动在生成文件阶段调用此方法
         // 我们这里尝试往项目目录中写入文件
         // 参数:文件路径,文件内容
         this.fs.write(this.destinationPath('temp.txt'),Math.random().toString()) // 扩展了node的fs
     }
    }

    当我们在外部使用这个脚手架时,就会看到temp.txt中的内容

    yo sample

    对应文件夹中就会有temp.txt

    根据模板创建文件

    在生成器的app目录下添加目录:templates,内部可以用ejs模板标记输出的数据,如:

    <%- title %>
    
    其他的ejs语法也支持
    <% if (success) { %> 哈哈哈 <% }%>

    我们更改启动文件index.js:

    module.exports=class extends Generator {
     writing(){
         // 通过模板方式写入文件到目标目录
    
         // 模板文件路径
         const tml=this.templatePath('foo.txt')
         // 输出的目标路径
         const output=this.destinationPath('foo.txt')
         // 模板数据上下文
         const context={title:'hello world',success:true}
         this.fs.copyTpl(tml,output,context)
     }
    }

    用模板的方式要比用fs的方式手写更快

    接受用户输入数据

    我们常会看到很多脚手架会通过用户交互的方式来定制功能,比如:用户输入一些参数来展示渲染文件。
    prompting这个成员函数就是用来生成交互实例的

    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=>{ // 用户交互结果
             // answer以对象形式出现 :{name: ‘zhangsan’ }
             this.answers=answers
         })
     }
     writing(){
         // 模板文件路径
         const tml=this.templatePath('foo.txt')
         // 输出的目标路径
         const output=this.destinationPath('foo.txt')
         // 模板数据上下文
         const context=this.answers
         this.fs.copyTpl(tml,output,context)
     }}

    发布Generator

    和npm public一样

    Plop – 一个小而美的脚手架工具

    创建项目中特定类型的小工具,类似于yeoman中的sub generator,不会独立使用,plop可集成到项目中,自动化的创建同类型的项目文件。

比如我们在项目中经常会创建相同类型的文件,比如每个组件都有相同结构的三个文件:js、css、test.js.

使用

先要建立plop的入口文件,他需要导出一个函数:

// plopfile.js
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', // name为命令行交互中的name
                templateFile:'plop-templates/component.hbs'
            }
        ]
    })
}

plop-templates为指定模板目录,component.hbs为模板文件:

// component.hbs
import React from 'react'

export default ()=>(
    <div className="{{name}}">
        <h1>{{name}} Component</h1>
    </div>
)

如上,可以用的方式把交互行中的数据写入到模板中。再终端中运行

yarn plop component

就可以按照命令行的提示生成指定文件啦,是不是特别方便。

脚手架的工作原理

node中安装 inquirer 模块,根据prompts指令渲染模板。