使用Yeoman快速搭建项目脚手架

2,883 阅读5分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

PS:此文实现的generator-vue-3(创建vue3+ts+eslint项目):github地址npm地址

前言

说到脚手架,大家应该都不陌生。我们最熟悉的,创建react技术栈项目的create-react-app,创建vue技术栈项目的vue-cli,都是帮助我们从0到1快速搭建项目的脚手架。

那既然已经有了成熟的脚手架,我们为什么还要搭建自己项目的脚手架呢?

通常,一个团队的项目,会有一些定制化配置:

  1. 项目发布的ci脚本
  2. eslint等规范配置
  3. env不同环境的配置
  4. 登录代理配置 ......

如果每次新建项目都要重新配置,或把原来的项目复制一份再改改。那既不能保证大家项目技术栈的一致性,也影响了大家的开发效率。那就思考,我们能不能自己写个模板,像vue-cli脚手架一样自动创建定制化项目呢?

今天我就和大家分享一下如何定制一套自己的项目脚手架,提升开发效率。

什么是yeoman

yeoman官网

简介

yeoman是现代化前端项目的脚手架工具,用于生成包含指定框架结构的工程化目录结构。它是整个前端自动化工厂的第一站。

可以理解为,yeoman是一个脚手架的运行框架,它定义了一个脚手架在运行过程中所要经历的各个阶段(先读取用户输入,然后生成项目文件,最后安装依赖),我们所需要的就是在生命周期的对应阶段,填充对应的操作代码即可。

生命周期

yeoman提供了各个阶段的生命周期,按运行顺序优先级如下:

  1. initializing - 初始化阶段 (检查当前项目状态, 获取配置, 等等)
  2. prompting - 用户输入阶段 (where you’d call this.prompt())
  3. configuring - 保存配置并配置项目 (创建.editorconfig文件和其他元数据文件)
  4. default - 默认执行阶段
  5. writing - 依据模板进行新项目结构的写操作 (路由, 控制台, 等等)
  6. conflicts - 处理冲突阶段 (内部使用)
  7. install - 安装依赖阶段 (npm, bower)
  8. end - 结尾阶段(清理多余文件等等)

遵循这些优先级准则,您的生成器将与其他友好共存。

了解了yeoman的生命周期,接下来我们看一下yeoman如何生成脚手架。

什么是generator

generator-xxx生成器

yeoman相当于是一个脚手架的运行框架,想要生成某个脚手架模板的项目,则需要找到对应命名为generator-xxx的生成器。yeoman 通过yo命令调用generator-xxx 即可生成(generate)脚手架项目。

也就是说需要通过yeoman调用generator来生成项目的脚手架,都需要命名为generator-xxx(xxx为脚手架名称,通过yo xxx进行调用)

generator-generator

yeoman 提供了 generator-generator, 可以帮助我们快速生成一个脚手架模板。

如何生成脚手架模板

安装yeoman和generator

全局安装yo(yeoman)generator-generator(生成脚手架模板)

npm install yo generator-generator -g

生成脚手架模板

yo generator

执行上述命令时,如下图,需要输入你的generator-name,发布后,可以通过调用yo name命令生成此脚手架的项目。 image.png

image.png 执行完后,就可生成如以下目录的脚手架模板项目:

.
├── generators/                     // generator主要代码目录
│   └── app/
│       ├── index.js                // generator配置文件,yeoman生命周期
│       └── templates/              // 模板文件,可以是自己配置后的项目
│           └── dummyfile.txt
├── .editorconfig
├── .eslintignore
├── .gitattributes
├── .gitignore
├── .travis.yml
├── .yo-rc.json
├── LICENSE
├── README.md
├── package.json
└── __tests__/                      // 测试用例
    └── app.js

如何定制脚手架

template模板

template模板目录:/generators/app/templates

此文件夹下可放置模板文件或模板项目,通过app/index.js中的生命周期阶段,对模板文件或模板项目中的字段进行重写或者替换,从而生成新的项目。

下图中的templates中为定制后的vue模板项目 image.png package.json中红框是等待用户输入后,填充的内容 image.png

配置文件

配置文件目录:/generators/app/index.js

提供了yeoman-generator基础库,可在此基础上进行扩展和填充。

index.js

"use strict";
const Generator = require("yeoman-generator");
const chalk = require("chalk");
const yosay = require("yosay");
const path = require("path");
const mkdirp = require("mkdirp");
// 5.0.0版本需要动态引入install
const _ = require("lodash");
_.extend(Generator.prototype, require("yeoman-generator/lib/actions/install"));

module.exports = class extends Generator {
  // 向用户展示交互式问题收集关键参数
  prompting() {
    // Have Yeoman greet the user.
    this.log(
      yosay(
        `Welcome to the stunning ${chalk.red("generator-vue-3")} generator!`
      )
    );

    const prompts = [
      {
        type: "input",
        name: "namespace",
        message: "Please input your project namespace,such as @baidu:",
        default: ""
      },
      {
        type: "input",
        name: "name",
        message: "Please input project name:",
        default: "vue"
      },
      {
        type: "input",
        name: "description",
        message: "Please input project description:",
        default: "a vue project"
      },
      {
        type: "input",
        name: "author",
        message: "Author's Name",
        default: ""
      },
      {
        type: "input",
        name: "email",
        message: "Author's Email",
        default: ""
      },
      {
        type: "input",
        name: "license",
        message: "License",
        default: ""
      }
    ];

    return this.prompt(prompts).then(props => {
      // To access props later use this.props.someAnswer;
      this.props = props;
      if (this.props.namespace) {
        this.props.fullName = this.props.namespace + "/" + this.props.name;
      } else {
        this.props.fullName = this.props.name;
      }
    });
  }

  // 未匹配任何生命周期方法的非私有方法均在此环节*自动*执行
  default() {
    if (path.basename(this.destinationPath()) !== this.props.name) {
      this.log(`\nYour generator must be inside a folder named
        ${this.props.name}\n
        I will automatically create this folder.\n`);

      mkdirp(this.props.name);
      this.destinationRoot(this.destinationPath(this.props.name));
    }
  }

  // 依据模板进行新项目结构的写操作
  writing() {
    this.log("\nWriting...\n");

    this.__writingCopy(["package.json"], {
      name: this.props.name,
      fullName: this.props.fullName,
      description: this.props.description,
      author: this.props.author,
      email: this.props.email,
      license: this.props.license
    });
    this.__writingCopy(["README.md"], {
      name: this.props.name,
      fullName: this.props.fullName,
      description: this.props.description,
      author: this.props.author,
      year: new Date().getFullYear()
    });
    this.__writingCopy([
      "build",
      "public",
      "src",
      ".browserslistrc",
      ".editorconfig",
      ".env.development",
      ".env.production",
      ".env.test",
      ".eslintrc.js",
      ".eslintrc.json",
      ".gitignore",
      "babel.config.js",
      "ci.yml",
      "stylelint.config.js",
      "tsconfig.json",
      "vue.config.js"
    ]);
  }

  __writingCopy(filePath, params) {
    this.log(this, "haha");
    filePath.forEach(item => {
      this.fs.copyTpl(
        this.templatePath(item),
        this.destinationPath(item),
        params
      );
    });
  }

  // 安装依赖阶段
  install() {
    this.log("install...");
    // 5.0.0以上版本废弃,默认关闭
    this.npmInstall();
  }
};

5.0.0版本后不自动install依赖

yeoman-generator5.0.0版本后不会自动install依赖,需要手动触发,触发方式有以下几种:

image.png

配置完模板文件和配置文件,脚手架已经完成,接下来我们进行脚手架测试。

如何测试脚手架

npm link模块链接到全局

通过npm link将模块链接到全局node_modules中,可在全局中使用该模块。

调用yo xxx生成脚手架项目

本地开发的generator-XXX未经过发布,需要在package.json所在目录开启命令行,输入npm link将其安装到本地的全局环境,然后通过yo XXXyo XXX:YYY的方式来调用,结果如下图:

image.png

按提示输入项目的scope、名称、描述等信息,会自动生成项目。进入生成的项目后,如没有install依赖,需要手动install依赖后再运行。

可通过npm link,yo xxx的方式来测试是否可以顺利生成脚手架项目。

如出现# npm ERR! notarget No matching version found for @babel/helper-validator-identifier@7.9.5的错误,可通过npm cache clean --force尝试解决。

如何发布脚手架

将本地开发的generator-xxx脚手架做为npm包发布。 发布步骤为:

  1. 登录npm:npm login
  2. 发布npm:npm publish

发布成功,如下图: image.png

npm上查看刚才发布的包generator-vue-3

image.png

如何使用脚手架

全局安装脚手架,比如generator-vue-3

npm install -g generator-vue-3

使用yo调用脚手架生成项目

yo vue-3

生成成功后,进入目录,启动项目。

参考文章

  1. juejin.cn/post/684490…
  2. zhuanlan.zhihu.com/p/66190308
  3. zhuanlan.zhihu.com/p/51704240
  4. yeoman.github.io/generator/i…
  5. juejin.cn/post/684490…