1. 概述

脚手架可以简单的理解为自动创建项目基础文件的工具,除了创建文件更重要的是提供给开发者一些约定和规范。通常在去开发相同类型的项目时,都会有一些相同的约定,其中会有相同的文件组织结构,相同的代码开发范式,相同的模块依赖,设置还有有一些相同的工具配置,最后甚至连一些基础代码都是一样的。

这样一来搭建新项目时有大量重复工作要做,脚手架工具就是解决这一类问题的。可以通过脚手架工具快速搭建特定类型的项目骨架,然后基于骨架进行后续的开发工作。类似eclipsevisual studio这这些IDE工具,他们创建项目的过程实际上就是一个脚手架的工作流程。

在前端项目创建过程当中,由于前端技术选型比较多样,又没有一个统一的标准,所以前端方向的脚手架一般不会集成在某一个IDE当中,都是以独立的工具存在的,并且相对会复杂一些,但是本质上,脚手架的目标是一样的,都是为了解决在创建项目过程中的一些复杂的工作。

目前市面上有很多成熟的脚手架工具,基本都是为特定项目服务的,例如create-react-appvue-cliangular。这些工具的实现方式大同小异,无外乎就是根据用户输入的信息创建对应的项目结构,最终生成项目的配置,不过他们一般只适用于自身所服务的那个框架项目。

还有一类就是以Yeoman工具为代表的通用型项目脚手架工具,可以根据一套模板生成对应的项目结构。这种类型的脚手架很灵活而且很容易扩展。

2. Yeoman

Yeoman官方的定义是一款用于创造现代化web应用的脚手架工具,不同于vue-cli工具Yeoman更像是一个脚手架的运行平台,可以通过Yeoman搭配不同的generator创建任何类型的项目。Yeoman的缺点在于他过于通用,不够专注,所以开发者更愿意使用像Vue-cli这样一类的脚手架。

yo就是yeoman工具模块的名字,可以使用yarn或者npm进行安装。

yarn global add yo

要想使用Yeoman创建项目必须要找到对应项目类型的generator。例如想要生成Node Module项目,可以使用generator-node模块,使用方式是先把他通过全局范围安装的方式安装到本地。

yarn global add generator-node;

有了这两款模块就可以使用yarn运行刚刚安装的generator-nodegengerator也就是生成器自动的创建一个全新的Node Module,可以先定位到项目所在的目录,也就是要执行命令的文件夹。

可以通过Yeoman命令yo加上刚刚安装的generator-node的生成器。运行特定的generator就是把包名字前面的generator-前缀去掉。

yo node

在这个过程Yeoman会提出一些问题,可以在命令行当中通过交互的方式把他填写进去,首先第一个是模块名字,会自动检查包名是否可用。填写完毕会在项目中创建一些基础文件,并且在项目根目录运行npm install安装项目必要的依赖。在目录中除了基本的文件外内部的一些基础代码基础的配置,都是提前配置好的,这也是脚手架工具的一个优势。

3. 子集生成器

有时候并不需要创建完整的项目结构,可能只是需要在已有的项目基础之上去创建一些特定类型的文件,例如给一个已经存在的的项目创建一个readme,又或是在一个原有项目之上添加某些类型的配置文件,比如eslinebabel,这些配置文件都有一些基础代码如果自己手动去写的话很容易配错,可以使用Yeoman提供的sub generator实现。

在项目目录下运行特定的sub generator命令生成对应的文件。这里使用generator-node提供的子集生成器cli生成一个cli应用所需要的文件,让模块变成cli应用。

yo node:cli

运行sub generator的方式就是在原有generator命令后面跟上sub generator的名字。会提示是否要重写package.json文件,原因是在添加cli支持的时候会添加一些新的模块和配置选择yes即可。完成过后会提示重写了package.json创建了lib\cli.js

package.json中出现了一个"bin": "lib/cli.js"以及新的dependencies,这些都是cli应用中需要的。有了这些就可以将cli代码模块作为一个全局的命令行模块去使用了,本地的模块可以通过yarn link到全局范围。

yarn link // 映射到全局
yarn // 安装依赖
my-module --help // 运行

并不是每一个generator都一个子集的生成器,所以在使用之前要通过所使用的generator的官方文档来明确这个generator下面有没有一个子集的生成器。

4. 自定义 Generator

不同的generator可以生成不同的项目,可以通过创建自己的generator生成自定义的项目结构。

一般generator基本结构

|—— generators   # 生成器目录
|    app/        # 默认生成器目录
        index.js # 默认生成器实现
|—— package.json # 模块包配置文件

首先创建generators文件夹,作为生成器的目录,再在里面存放app文件夹用于存放生成器对应的代码。如果需要提供多个的sub generator可以在app的同级目录添加新的生成器目录app2模块就有了一个叫做app2的子生成器。

除了特定的结构,还有个与普通npm不同的是Yeomangenerator的模块的名称必须是generator-<name>这样的一种格式,如果你在具体开发的时候没有去使用这样格式的名称,Yeoman在后续工作的时候就没有办法找到你所提供的这个生成器模块。

可以做下具体的演示,首先创建文件夹generator-simple作为生成器模块的目录。

mkdir generator-simple

在目录下通过yo init的方式创建一个package.json

yo init

还需要安装yeoman-generator的模块,这个模块提供了生成器的基类,基类中提供了一些工具函数可以在创建生成器的时候更加便捷。

yarn add yeoman-generator

安装完工具过后通过编辑器打开目录,在目录下按照项目结构要求,创建generators文件夹,在这个目录下创建app目录,在目录中创建index.js作为generator的核心入口文件。

index.js需要导出一个继承自Yeoman Generator的类型,Yeoman Generator在工作时会自动调用此类型中定义的一些生命周期方法,可以在这些方法中通过调用父类提供的工具方法实现一些功能,例如文件写入。

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

module.exports = class extends Generator {

    writing() { 
        // Yeoman自动生成文件阶段调用此方法
        // 我们尝试往项目目录中写入文件
        this.fs.write(this.destinationPath('temp.txt'), '123'); // 这里的fs模块与node中的fs不同,是高度封装的模块功能更强大一些,
    }

}

this.destinationPath是父类中方法用来获取绝对路径,this.fs是父类中模块。

这时一个简单的generator就已经完成了,通过npm link的方式把这个模块连接到全局范围,使之成为一个全局模块包,这样Yeoman在工作的时候就可以找到自己写的这个generator-simple模块

yarn link

可以通过Yeoman运行这个生成器具体的操作方式是yo simple

yo simple

5. 根据模板创建文件

很多时候需要自动创建的文件有很多,而且文件的内容也相对复杂,这种情况可以使用模板创建文件。在生成器的目录下添加template目录,然后将需要生成的文件都放入到template目录中作为模板。模板中完全遵循ejs模板引擎的语法。

有了模板过后在生成文件之时就不用再借助于fswrite方法去写入文件,而是借助于fs中的copytemplate方式,使用的时候有三个参数,分别是模板文件的路径,输出文件的路径,模板数据的上下文。

模板文件路径可以借助this.templatePath('foo.txt')自动获取当前template目录下的文件路径。输出路径仍旧是用this.destinationPath('foo.txt'),模板数据上下文只需要定义一个对象就可以了用于传入ejs

const tmpl = this.templatePath('foo.txt');

const output = this.destinationPath('foo.txt');

const context = { title: 'yd'}

this.fs.copyTpl(tmpl, output, context);

运行

yo simple

6. 接收用户输入数据

generator中想要发起一个命令行交互询问,可以通过实现generator类型中的promting方法。

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

module.exports = class extends Generator {
    prompting() {
    }
}

可以调用父类提供的promit方法发出对用户的命令行询问。这个方法返回promise,在调用的时候对他return,这样Yeoman在调用的时候就有更好的异步流程控制。

这个方法接受数组参数,数组的每一项都是一个问题对象,可以传入类型typenamemessagedefault

[
    {
        type: "input", // 提问类型
        name: "name", // 得到结果的一个键
        message: "message", // 命令行提示的话
        default: this.appname // appname为项目生成目录文件夹的名字,会作为问题的默认值
    }
]

promise执行过后我们会得到一个返回值,返回值就是问题的结果。会以对象的形式出现。对象的键就是输入的name,值就是用户输入的内容,可以将这个值挂载在全局this中方便日后使用

const Generator = require('yeoman-generator');
module.exports = class extends Generator {
    prompting() {
        return this.prompt([
            {
                type: "input", // 提问类型
                name: "projectName", // 得到结果的一个键
                message: "message", // 命令行提示的话
                default: this.appname // appname为项目生成目录文件夹的名字,会作为问题的默认值
            }
        ]).then(answers => {
            this.answers = answers;
        })
    }
}

有了this.answers就可以在writing的时候传入模板引擎,使用这个数据作为模板数据的上下文。

const context = this.answers;
this.fs.copyTpl(tmpl, output, context);

7. 案例演示

首先打开命令行窗口然后通过mkdir创建一个全新的gengerator目录。

mkdir generator-vue
cd generator-vue

通过yarn init初始化package.json,然后安装Yeoman的依赖。

yarn init
yarn add yeoman-generator

新建generator主入口文件generators/app/index.js

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

module.exports = class extends Generator {
    prompting() {
        return this.promit([
            {
                type: 'input',
                name: 'name',
                message: 'Your project name',
                default: this.appname
            }
        ]).then(answer => { // 获取到用户输入的数据
            this.answer = answer;
        })
    }
    writing() {

    }
}

创建templates目录,把项目的结构copytemplates当中作为模板,有了模板过后需要把项目结构里面一些可能发生变化的地方通过模板引擎的方式修改。

通过数组循环的方式批量生成每一个文件,把每一个文件通过模板转换,生成到对应的路径。

writing() {
    const templates = [
        '.browserslistrc',
        'src/views/Home.vue'
    ]
    templayes.forEach(item => {
        this.copyTpl(this.templatePath(item),
        this.destinationPath(item),
        this.answer);
    })
}

generator-vue通过link的方式定义到全局。

yarn link

在全新的目录使用该generator

yo vue;

会提示输入项目名称,输入之后就可以看到模板被拉去到了新的目录。

8. 发布

Generator实际上就是一个npm的模块,发布generator就是发布npm的模块。只需要将已经写好的generator模块通过npm publish命令发布成一个公开模块就可以了。

转载须知

如转载必须标明文章出处文章名称文章作者,格式如下:

转自:【致前端 - zhiqianduan.com】 yeoman创建脚手架工具  "隐冬"
请输入评论...