# 前言

伴随着前端技术日新月异的发展,前端开发中前后端分离,工程化,自动化等现代化的开发模式越来普及,前端项目也引入了编译,构建,单元测试等现代软件工程化的标准环节。这样大提高了前端的开发效率和业务交付能力。

# Yeoman构建脚手架源码

Lerna 和 Jsdoc 包管理脚手架:github.com/ge-tbms/gen…

TS SDK以及配合Jest测试脚手架:github.com/ge-tbms/gen…

React+SCSS+Mobx 开发环境脚手架: github.com/ge-tbms/gen…

Rax 组件构建脚手架(实现页面和组件分别生成):github.com/ge-tbms/gen…

# 开箱即用的源码

源码地址:github.com/dkypooh/fro…

# NPM Script搭建开发工作流

npm 允许在 package.json里面,使用 scripts 字段定义脚本命令

{
    "scripts": {
        "start": "node start.js"
    }
}

脚本语法

npm run-script <command> [--silent] [-- <args>...]

命令行下需要使用 npm run <comand> 方式执行脚本

$ npm run start
## 等同于
$ node start.js

# 执行原理

# 配置参数

npm scripts 参数传递的命令行分割符是 '--', 即可将后续参数添加到 process.env.argv 数组中。例如:

$ npm run build -- --name hello

# npm scripts 组合命令

npm脚本需要执行多个任务,首先需要明确它们的执行顺序,然后把他们组合起来。 如果是并行执行(即同时的平行执行),可以使用 & 符号。

$ npm run build-js & npm run build-css

如果是串行执行(即只有前一个任务成功,才执行下一个任务),可以使用 && 符号。

$ npm run eslint && npm run build && npm run publish

从代码检查,到代码构建,最后到发布。

# 默认命令

默认命令可以省略掉 run

npm start 是 npm run start
npm stop 是 npm run stop的简写
npm test 是 npm run test的简写
npm restart 是 npm run stop && npm run restart && npm run start的简写

# npm钩子

npm提供了两种钩子,pre和post,分别代表操作前和操作后

{
    "scripts": {
        "prebuild": "echo 1",
        "build": "echo 2",
        "postbuild": "echo 3"
    }
}

用户执行 npm run build 的时候,会自动按照下面的顺序执行。

$ npm run prebuild && npm run build && npm run postbuild
## 最终输出
$ 123

# 默认钩子

  • prepublish,postpublish
  • preinstall,postinstall
  • preuninstall,postuninstall
  • preversion,postversion
  • pretest,posttest
  • prestop,poststop
  • prestart,poststart
  • prerestart,postrestart

# Yeoman Generator搭建开发工作流

Yeoman,它不只是一个工具,而是一个工作流。它其实包括了三个部分yo、grunt、bower,分别用于项目的启动、文件操作、包管理。

# 打造一个自己的集成工具流

源码地址:github.com/dkypooh/fro…

# 安装及步骤

可以同时安装 Yeoman(yo) 和 Generator(generator-generator)构建器脚本

$ npm i -g yo generator-generator

# 生成文件目录

$ yo generator

# 标准Generator目录

templates 是模板目录,例如 React 工程脚手架, index.js 入口文件,维护 Yoeman 各个生命周期。

├── LICENSE
├── README.md
├── __tests__
│   └── app.js
├── generators
│   └── app
│       └── templates   // 模板目录
│       ├── index.js    // 入口文件
└── package.json

# 生命周期回调

image-20210214201341362

  • initializing - Your initialization methods (checking current project state, getting configs, etc)

    • 初始化方法(检验当前项目状态、获取configs、等)
  • prompting - Where you prompt users for options (where you’d call this.prompt())

    • 人机交互,获取用户选项
  • configuring - Saving configurations and configure the project (creating .editorconfig files and other metadata files)

    • 保存配置(创建 .editorconfig 文件)
  • default - If the method name doesn’t match a priority, it will be pushed to this group

    • 如果函数名称如生命周期钩子不一样,则会被放进这个组
  • writing - Where you write the generator specific files (routes, controllers, etc)

    • 写generator特殊的文件(路由、控制器、等)
  • conflicts - Where conflicts are handled (used internally)

    • 冲突后处理办法
  • install - Where installations are run (npm, bower)

    • 选择安装依赖(npm、bower)
  • end - Called last, cleanup, say good bye, etc

    • 安装结束、清除文件、设置good bye文案、等

# 构建React开发脚手架

我们以 Mobx状态管理 这章我们一起搭建React + Mobx + SCSS工程环境为脚手架模板(app/templates)。通过 yeoman 集成成通用脚手架工具。视频案例:

# 生成器安装

$ npm i -g generator-react-mobx-scss

# 入口文件初始化

Generatorindex.js 文件,需要集成 yeoman-generator 基类

var Generator = require('yeoman-generator');

module.exports = class extends Generator {};

# 生命周期节点实现

# Initializing

初始化获取用户名信息

// 初始化获取用户名信息
initializing() {
    try {
      this.username = process.env.USER || process.env.USERPROFILE.split(require('path').sep)[2]
    } catch (e) {
      this.username = ''
    }
}

# Prompting

获取基本配置信息

// 获取基本配置信息
return this.prompt([
    // 项目名称
    {
    type: 'input',
    name: 'name',
    message: 'Your project name',
    // 项目描述
    {
        type: 'input',
        name: 'description',
        message: 'Your project description',
        default: ''
    },
    // 用户名(默认系统)
    {
        type: 'input',
        name: 'username',
        message: 'Your name',
        default: this.username
    },
    // 邮箱信息
    {
        type: 'input',
        name: 'email',
        message: 'Your email',
        default: ''
    },
    // npm 镜像选择
    {
        type: 'list',
        name: 'registry',
        message: 'Which registry would you use?',
        choices: [
            'https://registry.npm.taobao.org',
            'https://registry.npmjs.org'
        ]
    }
]).then(answers => {
    this.answers = answers
    this.obj = {answers: this.answers}
})

# Writing

模板文件复制到项目目录,同时动态插入配置信息

const _ = require('lodash')

this.fs.copy(this.templatePath('static', '*'), this.destinationPath('static'))
this.fs.copyTpl(this.templatePath('src'), this.destinationPath('src'), this.obj, {
    interpolate: /<%=([\s\S]+?)%>/g
});
this.fs.copy(this.templatePath('index.js'), this.destinationPath('index.js'))
this.fs.copy(this.templatePath('babelrc'), this.destinationPath('.babelrc'))
this.fs.copy(this.templatePath('gitignore'), this.destinationPath('.gitignore'))
this.fs.copy(this.templatePath('eslintrc'), this.destinationPath('.eslintrc'))
this.fs.copy(this.templatePath('editorconfig'), this.destinationPath('.editorconfig'))
this.fs.copyTpl(this.templatePath('webpack.config.js'), this.destinationPath('webpack.config.js'))
// 动态插入配置信息, 生成Package.json文件
this.fs.copyTpl(this.templatePath('package.json_vm'), this.destinationPath('package.json'), this.obj)
// 动态插入配置信息, 生成模板ReadME.md
this.fs.copyTpl(this.templatePath('readme.md'), this.templatePath('readme.md'), this.obj) 

# Install

可以选择 npm 或者 yarn 安装依赖。

// 语法结构:npmInstall(pkgs?: string|string[], options?: object, spawnOptions?: object): void;
// undefined为全部安装
install() {
    this.npmInstall(undefined, {
      registry: this.answers.registry
    })
}

# End

end() {
    this.log.ok('Project ' + this.answers.name + ' generated!!!')
    this.spawnCommand('npm', ['start'])
}

# 结语

React组件 章节学习中,大家也接触到了 npm scripts 的使用,本章节系统化的带大家一起学习 npm 脚本以及 npm hooks 来管理项目周期。同时带大家一起学习了 Yeoman 基本原理和生命周期。最后以 react-mobx-scss 项目为模板,一起开发了 generator-react-mobx-scss 脚手架生成器,读者可以在自己平时的项目中使用此脚手架生成器。

# 参考文献

阅读全文