前端开发系列113-工程化篇之Yeoman脚手架generator创建

这篇文章将以实例的方式来教会如何从零开始创建属于我们自己的generator。

点击获取本文示例的generator

generator创建准备

这里我们一切从零开始,在创建自己的generator之前需要做一些准备工作,比如准备好yo命令行工具,比如对生成器生成的项目结构和目录文件有清晰的规划等。

yo命令行工具

在安装了NodeJS和npm的前提下,可以通过下面的命令来安装yo命令行工具,并检查安装是否成功。

$ npm install -g yo

$ yo --version

generator-generator的安装

创建generator可以完全从零开始,也可以使用Yeoman官方提供的generator引导,这里我们选择使用Yeoman官方推荐的方式来处理。

$ mkdir YeomanTest && cd YeomanTest/ 创建新的目录并进入

$ npm install -g generator-generator 安装Yeoman引导generator

列出具体的执行情况

wendingding$ mkdir YeomanTest
wendingding$ cd YeomanTest/
wendingding$ pwd
/Users/文顶顶/Desktop/Yeoman/YeomanTest
wendingding$ npm install -g generator-generator + generator-generator@4.0.2

updated 1 package in 117.639s

   ╭─────────────────────────────────────╮
   │                                     │
   │   Update available 5.5.1 → 6.1.0    │
   │     Run npm i -g npm to update      │
   │                                     │
   ╰─────────────────────────────────────╯

执行Yeoman官方的引导generator,并处理交互式配置部分,下面列出具体的执行情况。

wendingding$ yo generator
? Your generator name generator-wendingding
Your generator must be inside a folder named generator-wendingding
I'll automatically create this folder.
? Description 博客文章测试创建生成器
? Project homepage url http://www.wendingding.com
? Author's Name 文顶顶
? Author's Email 18681537032@163.com
? Author's Homepage http://www.wendingding.com
? Package keywords (comma to split) wendingding
? Send coverage reports to coveralls Yes
? GitHub username or organization flowerField
? Which license do you want to use? Apache 2.0
   create package.json
   create README.md
   create .editorconfig
   create .gitattributes
   create .gitignore
   create generators/app/index.js
   create generators/app/templates/dummyfile.txt
   create __tests__/app.js
   create .travis.yml
   create .eslintignore
   create LICENSE
I'm all done. Running npm install for you to install the required dependencies.
If this fails, try running the command yourself.

在执行generator-generator这个生成器的过程中,会询问项目名称、作者、使用协议、主页地址等等信息,依次选择填空即可。

注意:按照约定,Yeoman generator的名字必须以“generator-”的前缀开头,这是因为所有的generator其实都是全局安装的node模块,所以Yeoman其实是完全依靠文件系统来对这些生成器进行查找操作的。

当上面的命令执行完毕后,会发现在当前的路径下面生成了generator-wendingding目录,进入到generator-wendingding目录,使用tree命令查看当前目录结构,显示如下:

.
├── LICENSE
├── README.md
├── __tests__
├── generators
├── app
│    ├── index.js
│    └── templates
│        └── dummyfile.txt
├── node_modules
├── package-lock.json
└── package.json

上面目录结构中虽然有很多文件,但我们真正需要关注的应该是generators路径下面的app/index.js文件以及templates目录,其中index文件对应是generators的组装指令部分,templates路径用于存放项目所有的模板文件。

项目模板文件准备

上面这些准备工作完成之后,接下来我们开始着手分析目标项目的文件结构,即我们使用自己创建的这个脚手架来搭建项目,其结构目录应该是怎样的?需要包含哪些文件等等。任何时候,明确知道你的目标,知道自己正在做什么至关重要。

下面试着给出目标项目的文件结构。

.
├── Gruntfile.js
├── bower.json
├── build
├── dist
├── package.json
└── src
    ├── css
    │   └── style.css
    ├── index.html
    ├── js
    │   └── index.js
    ├── libs
    │   └── jquery
    └── template

我们可以看到该项目应该包含bulidsrc以及dist三个目录,其中src目录中需要创建名为cssjslibstemplate的文件夹,分别用来保存样式文件、脚本文件、依赖的框架以及模板文件等。

除了这些必要的文件外,假设目标项目需要使用bower来进行依赖管理,使用Grunt来进行自动化构建,所以自然还应该拥有Gruntfile.js、bower.json以及package.json文件。

假设目标项目中一定会使用到jQuery框架,可能会使用到bootstrap框架。

现在我们可以开始分析生成器中应该包含项目模板文件了,也就是在generators/templates路径中应该包含哪些文件。

固定文件

index.jsstyle.css创建空文件即可。
Gruntfile.js文件因为内容固定不变,所以选择直接从旧项目中拷贝。
.jshintrc文件用于js文件语法检查,内容也是固定不变的。
.bowerrc文件用于重置Bower下载包的安装路径,内容为{"directory": "src/libs/"}

灵活文件

package.json文件中项目名称、作者以及开源协议等需要用户配置
bower.json文件的项目名称、作者、开源协议以及依赖框架等需要用户配置

可选文件

bootstrap框架相关的部分为可选文件,需要根据用户配置进行处理。

依赖文件

jQuery框架相关的部分为依赖文件,在组装指令部分通过在代码中调用方法来下载和安装。

根据上面的分析,我们在generators/templates准备了多个模板文件,下面列出文件结构以及主要文件的具体内容:

.
└── app
    ├── index.js
    └── templates
        ├── Gruntfile.js
        ├── bower.json
        ├── css
        │   └── style.css
        ├── index.html
        ├── js
        │   └── index.js
        └── package.json

package.json文件内容

{
  "name": "<%= appName %>",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "<%= appAuthor %>",
  "license": "<%= appLicense %>",
  "devDependencies": {
    "grunt": "^1.0.2",
    "grunt-contrib-concat": "^1.0.1",
    "grunt-contrib-cssmin": "^2.2.1",
    "grunt-contrib-jshint": "^1.1.0",
    "grunt-contrib-uglify": "^3.3.0",
    "grunt-contrib-watch": "^1.0.0"
  }
}

bower.json文件内容

{
  "name": "<%= appName %>",
  "description": "\"测试使用\"",
  "main": "js/index.js",
  "authors": [
    "<%= appAuthor %>"
  ],
  "license": "<%= appLicense %>",
  "keywords": [
    "generator-wendingding",
    "yeoman-generator"
  ],
  "homepage": "https://github.com/flowerField/generator-wen",
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ],
  "dependencies": {
    "jquery": "^3.3.1"<% if(isIncludeBootstrap) { %>,
    "bootstrap": "^4.1.1" <% } %>
  }
}

index.html文件内容

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title><%= appName %></title>
    <link rel="stylesheet" href="css/style.css">
    <script type="text/javascript" src="js/index.js"></script>
  </head>
  <body>

  </body>
</html>

Gruntfile.js文件内容

//包装函数
module.exports = function (grunt) {
    // 项目配置信息
    grunt.config.init({
        pkg:grunt.file.readJSON("package.json"),
        //代码合并
        concat:{
            options:{
                stripBanners:true,
             banner:'/*项目名称:<%=pkg.name%> 项目版本:<%=pkg.version%> 项目的作者:<%=pkg.author%> 更新时间:<%=grunt.template.today("yyyy-mm-dd")%>*/\n'
            },
            target:{
                src:["src/js/*.js"],
                dest:'build/js/index.js'
            }
        },
        //js代码压缩
        uglify:{
            target:{
                src:"build/js/index.js",
                dest:"build/js/index.min.js"
            }
        },
        //css代码压缩
        cssmin:{
            target:{
                src:"src/css/style.css",
                dest:"build/css/style.min.css"
            }
        },
        //js语法检查
        jshint:{
            target:['Gruntfile.js',"dist/js/index.js"],
        },
        //监听 自动构建
        watch:{
            target:{
                files:["src/js/*.js","src/css/*.css"],
                //只要指定路径的文件(js和css)发生了变化,就自动执行tasks中列出的任务
                tasks:["concat","jshint","uglify","cssmin"]
            }
        }
    });
    //通过命令行安装插件(省略...)
    //从node_modules路径加载插件
    grunt.loadNpmTasks("grunt-contrib-concat");
    grunt.loadNpmTasks("grunt-contrib-uglify");
    grunt.loadNpmTasks("grunt-contrib-cssmin");
    grunt.loadNpmTasks("grunt-contrib-jshint");
    grunt.loadNpmTasks("grunt-contrib-watch");
    //注册任务:在执行$ grunt命令的时候依次执行代码的合并|检查|压缩等任务并开启监听
    grunt.registerTask("default",["concat","jshint","uglify","cssmin","watch"]);
};
注意:上面部分文件中很多地方使用模板语法来传递参数,Yeoman所用的模板语言是EJS,具体用法请参考[EJS官网](http://www.embeddedjs.com/)

组装指令

处理完上面这些工作之后,接下来就是最最核心的部分了,我们需要在app/index.js文件中编写组装指令,这部分代码控制着这个生成器应该怎么执行,包括交互式配置的具体内容、如何复制文件以及框架依赖和Node模块下载等内容。

下面列出该示例中的index.js文件内容

'use strict';
const Generator = require('yeoman-generator');
const chalk = require('chalk');
const yosay = require('yosay');
const mkdirp = require('mkdirp');

module.exports = class extends Generator {
  prompting() {
    this.log(
      // yosay(`Welcome to the transcendent ${chalk.red('generator-wen')} generator!`)
      yosay(`欢迎使用\n${chalk.red('generator-wen')} !\n Author:文顶顶`)
    );

    const prompts = [
      {
        type    : 'input',
        name    : 'appName',
        message : '请输入项目名称:',
        default : this.appname        //appname是内置对象,代表工程名,这里就是ys
     },
     {
       type    : 'input',
       name    : 'appAuthor',
       message : '请输入作者姓名:',
       default : '文顶顶'
    },
    {
        type: 'list',
        name: 'appLicense',
        message: '请选择使用的license:',
        choices: ['MIT', 'ISC', 'Apache-2.0', 'AGPL-3.0']
      },
      {
        type    : 'confirm',
        name    : 'isIncludeBootstrap',
        message : '是否需要使用bootStrap框架?',
        default : false
     },

    ];

    return this.prompt(prompts).then(props => {
      // To access props later use this.props.someAnswer;
      this.props = props;
    });
  }

  writing() {
    mkdirp("build");
    mkdirp("dist");
    mkdirp("src/template");

    this.fs.copyTpl(
      this.templatePath('index.html'),
      this.destinationPath('src/index.html'),
      {appName: this.props.appName}
    );

    this.fs.copy(
      this.templatePath('css/style.css'),
      this.destinationPath('src/css/style.css')
    );

    this.fs.copy(
      this.templatePath('js/index.js'),
      this.destinationPath('src/js/index.js')
    );

    this.fs.copy(
      this.templatePath('.bowerrc'),
      this.destinationPath('.bowerrc')
    );

    this.fs.copy(
      this.templatePath('Gruntfile.js'),
      this.destinationPath('Gruntfile.js')
    );

    this.fs.copy(
      this.templatePath('.jshintrc'),
      this.destinationPath('.jshintrc')
    );

    this.fs.copyTpl(
      this.templatePath('package.json'),
      this.destinationPath('package.json'),
       {appName: this.props.appName,appAuthor:this.props.appAuthor,appLicense:this.props.appLicense}
    );

    this.fs.copyTpl(
      this.templatePath('bower.json'),
      this.destinationPath('bower.json'),
       {appName: this.props.appName,appAuthor:this.props.appAuthor,appLicense:this.props.appLicense,isIncludeBootstrap:this.props.isIncludeBootstrap}
    );
  }

  install() {
    //this.installDependencies();
    this.bowerInstall();
  }
};

上面的代码大概由三部分组成,第一部分为prompting函数用来处理安装提示,第二部分为writing函数用来设置模板文件的复制操作,第三部分为install函数用来处理框架依赖和node包的安装。

generator的发布和测试

项目模板文件和组装指令都准备好了后,我们就可以发布自己的generator了,可以先通过$ npm link命令以软连接的方式生成一个全局的npm包,测试使用。

具体的执行细节如下

wendingding:generator-wendingding wendingding$ npm link
up to date in 3.897s
/usr/local/lib/node_modules/generator-wendingding -> /Users/文顶顶/Desktop/Yeoman/YeomanTest/generator-wendingding
wendingding:generator-wendingding wendingding$

测试·使用自己创建的generator来生成初始化项目

随便找个目录新建文件夹,使用$ yo wendingding命令即可完成项目的初始化工作。

wendingding:YeomanTest wendingding$ mkdir Demo
wendingding:YeomanTest wendingding$ cd Demo/
wendingding:Demo wendingding$ yo wendingding

     _-----_     ╭──────────────────────────╮
    |       |    │         欢迎使用           │
    |--(o)--|    │      generator-wen !      │
   `---------´   │      Author:文顶顶        │
    ( _´U`_ )    ╰──────────────────────────╯
    /___A___\   /
     |  ~  |     
   __'.___.'__   
 ´   `  |° ´ Y `

? 请输入项目名称: Demo
? 请输入作者姓名: 文顶顶
? 请选择使用的license: Apache-2.0
? 是否需要使用bootStrap框架? Yes
   create bower.json
   create package.json
   create src/index.html
   create src/css/style.css
   create src/js/index.js
   create .bowerrc
   create Gruntfile.js
   create .jshintrc
bower invalid-meta  for:/Users/文顶顶/Desktop/Yeoman/YeomanTest/Demo/bower.json
bower invalid-meta  The "name" is recommended to be lowercase, can contain digits, dots, dashes
bower cached        https://github.com/jquery/jquery-dist.git#3.3.1
bower validate      3.3.1 against https://github.com/jquery/jquery-dist.git#^3.3.1
bower cached        https://github.com/twbs/bootstrap.git#4.1.1
bower validate      4.1.1 against https://github.com/twbs/bootstrap.git#^4.1.1
bower install       jquery#3.3.1
bower install       bootstrap#4.1.1

jquery#3.3.1 src/libs/jquery

bootstrap#4.1.1 src/libs/bootstrap
wendingding:Demo wendingding$ tree -L 3
.
├── Gruntfile.js
├── bower.json
├── build
├── dist
├── package.json
└── src
    ├── css
    │   └── style.css
    ├── index.html
    ├── js
    │   └── index.js
    ├── libs
    │   ├── bootstrap
    │   └── jquery
    └── template

9 directories, 6 files

如果需要把这个生成器发布到社区,可以参考官网的说明。

posted on 2022-12-17 13:31  文顶顶  阅读(42)  评论(0编辑  收藏  举报

导航