专栏首页进击的全栈搭建自己的脚手架
原创

搭建自己的脚手架

工欲善其事,必先利其器

最近接手了一个内部配置运营平台,大概了解了代码结构之后,第一波优化就是搭建了一套脚手架。

对于多人协作开发的 SPA 项目,一个方便的脚手架能够带来很大的便利:

  1. 脚手架能够规范化项目结构,对于项目整体优化和维护有着重要的意义;
  2. 通过脚手架生成代码能够避免开发者相互拷贝,导致缺陷在代码中的扩散;
  3. 减少重复劳动,降低开发门槛。

不难发现,社区优秀的开源框架往往都会提供一套脚手架供开发者快速上手,比如create-react-appvue-cli等。接下来我们就尝试使用yeoman来快速搭建一套自己的脚手架。

安装脚手架脚手架

yeoman,是一套脚手架生成工具。首先我们全局安装一下 yeoman 的 cli。

npm install -g yo

接下来,我们通过“脚手架的脚手架”来快速搭建我们自己的脚手架。

# Install:
npm install -g generator-generator
# Run:
yo generator

按照提示输入一堆问题之后,我们会得到如下的一个目录结构:

|- __tests__ # 测试代码
    |- app.js # app测试代码
|- generators # 脚手架目录
    |- app # 默认脚手架
        |- index.js # 入口
        |- templates # 模板文件夹
            |- ... # 各种模板文件
|- package.json

脚手架的生命周期

generators/app/index.js中,我们需要编写脚手架的主要逻辑,大概的代码结构是这样的:

"use strict";
const Generator = require("yeoman-generator");
const chalk = require("chalk");
const mkdirp = require("mkdirp");
const yosay = require("yosay");
const _ = require("lodash");

module.exports = class extends Generator {
  constructor(args, opts) {
    super(args, opts);
    this.displayName = "NewPage";
    this.entry = "newPage";
  }

  initializing() {}

  async prompting() {
    const done = this.async();

    const prompts = [
      {
        type: "input",
        name: "displayName",
        message: "请输入页面名称",
        default: this.displayName
      }
    ];

    this.log(
      yosay(`Welcome to the neat ${chalk.red("generator-***")} generator!`)
    );
    const answers = await this.prompt(prompts);

    this.displayName = _.upperFirst(answers.displayName);
    this.entry = _.camelCase(answers.displayName);

    done();
  }

  writing() {
    const pagePath = "./src/app/" + this.entry + "/";
    const done = this.async();

    mkdirp(this.destinationPath(pagePath), () => {
      ["index.js"].forEach(filename => {
        this.fs.copyTpl(
          this.templatePath(filename),
          this.destinationPath(pagePath + filename),
          {
            displayName: this.displayName,
            entry: this.entry
          }
        );
      });

      this.log(chalk.green(this.entry + "已经生成!"));
      done();
    });
  }

  end() {
    this.log(chalk.green("done"));
  }
};

其中initializingpromptingwritingend就属于生命周期函数,各个生命周期函数和使用场景如下:

1. initializing

初始化方法,一些初始化操作,或者检查脚手架状态等

2. prompting

与用户交互,让用户输入配置,一般在这里运行this.prompt()

3. configuring

为项目创建配置文件

4. default

默认分组,其他自定义方法会在这一步骤依次运行

5. writing

生成代码,一般会从templates进行读取文件

6. conflicts

处理冲突

7. install

安装阶段,比如让项目执行npm install命令

8. end

结束阶段,清理并返回生成结果


脚手架中的方法会按照这个顺序执行,如果你有一些私有方法不希望被自动执行,需要采用一定的技巧,比如:

在方法前增加下划线,标识为私有方法;

_privateMethod () {
    this.log('This is a private method.);
}

在构造函数中实现方法;

constructor(args, opts) {
    super(args, opts);
    this.privateMethod = function () {
        this.log('This is a private method.);
    }
}

测试和发布

创建脚手架时,我们可以选择生成测试代码,这里默认会使用jest来进行测试。

简单的测试代码如下:

"use strict";
const path = require("path");
const assert = require("yeoman-assert");
const helpers = require("yeoman-test");

describe("generator-***:app", () => {
  beforeAll(() => {
    return helpers.run(path.join(__dirname, "../generators/app")).withPrompts({
      displayName: "TestPage"
    });
  });

  it("creates files", () => {
    assert.file(["src/app/testPage/index.js"]);
  });
});

yeoman为我们提供了测试工具,helpers.run()就可以在沙箱中运行脚手架,并且可以通过withPrompts()方法来指定prompting阶段的各个参数。

在测试中,常用的检查方法有assert.fileassert.fileContentassert.noFileassert.noFileContent等。它们分别代表“文件应当存在”、“文件应当存在内容***”、“文件不应该存在”、“文件不应该存在内容***”。

开发好的脚手架可以上传npm,这样就可以供其他人使用,或者你也可以在目录下运行npm link命令,将本地脚手架添加到本地npm链接中。

之后就可以使用命令行来运行脚手架了:

yo ***
# ***是脚手架的名字,比如generator-abc, 就可以通过 yo abc 来运行

一些小技巧

在运行脚手架时,我们希望始终使用的是最新版本的脚手架,如果你的脚手架存放在npm,可以通过以下方法进行验证:

const pkg = require("../../package.json");

const remoteVersion = execSync("npm view generator-*** version")
  .toString()
  .trim();
const localVersion = pkg.version;

可以使用npm命令来进行版本升级,首先将代码的改动commit到git仓库,之后根据此次更新的程度,来使用不同的命令:

npm version patch # bug修复
npm version minor # 增加了新的feature,并且是一个兼容性更新
npm version major # 增加了新的feature,并且存在不兼容问题的更新

如果希望每次修改脚手架时都能自动进行测试和发布 npm,这里就需要 CI 工具的帮忙,能够极大地提高开发效率和体验。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • useMemo & useCallback 指北

    很多时候我们在学习新东西之后总是会很兴奋地去做各种尝试。在React hooks正式面世之后,团队也在很多业务中开始尝试使用这种新语法。除却提及最为广泛的use...

    暂七师黑管手
  • 表单联动解决方案探讨

    表单联动是前端经常面临的问题,联动实际上是一组表单项和表单项之间的依赖关系的集合。比如经典的“省-市-区”三级联动,就包含了“省”、“市”、“区”三个表单项,以...

    暂七师黑管手
  • 小程序 — 实现左滑删除效果②

    (1)在上一章中,我们给movable-view绑定了一个bindchange事件,事件名为onChange,这个事件是干吗的呢?

    Ewall
  • react新手demo——TodoList

    今天我们就使用 react 来实现一个简易版的 todolist ,我们可以使用这个 demo 进行 list 的增删改差,实际效果如上图所示。大家可以clon...

    庞小明
  • 劳斯莱斯宣布与英特尔合作,为自动货船开发全球系统

    劳斯莱斯今天宣布,它将使用英特尔芯片,为在公海上运载货物的自动船舶开发一个全球系统。劳斯莱斯以英国为基础,并且正在芬兰和挪威的研发中心开发运输技术。

    AiTechYun
  • 实战 | 一不小心创造了新的编程语言!

    鱼皮平时会写一些有趣的小项目练练手。谁知道,前段时间,一不小心创造了一门新的编程语言!

    程序员鱼皮
  • 一切都是为了蓄力5G!

    运营商想要进行网络转型,大步迈向5G时代,没有强劲的数据处理能力可不行。中国移动深知这一点,正在努力把握信息通信发展大势,深入实施“大连接”战略,并借助英特尔超...

    SDNLAB
  • 【漏洞赏析】安全业务那些洞

    aerfa
  • 英文面试问题之-“你有这个行业的工作经验吗?”

    问题: Do you have any work experience in this field ?

    公众号php_pachong
  • 托福被取消?这个英语测试AI出题,2天出分,全球900+大学承认

    始终未见“拐点”的疫情给准备申请出国的学生带来了极大的困扰,其中之一就是语言考试的取消。以托福为例,1月27日,托福宣布2月考试取消;一个月后,3月份的考试也宣...

    大数据文摘

扫码关注云+社区

领取腾讯云代金券