工欲善其事,必先利其器
最近接手了一个内部配置运营平台,大概了解了代码结构之后,第一波优化就是搭建了一套脚手架。
对于多人协作开发的 SPA 项目,一个方便的脚手架能够带来很大的便利:
不难发现,社区优秀的开源框架往往都会提供一套脚手架供开发者快速上手,比如create-react-app
、vue-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"));
}
};
其中initializing
、prompting
、writing
、end
就属于生命周期函数,各个生命周期函数和使用场景如下:
初始化方法,一些初始化操作,或者检查脚手架状态等
与用户交互,让用户输入配置,一般在这里运行this.prompt()
为项目创建配置文件
默认分组,其他自定义方法会在这一步骤依次运行
生成代码,一般会从templates
进行读取文件
处理冲突
安装阶段,比如让项目执行npm install
命令
结束阶段,清理并返回生成结果
脚手架中的方法会按照这个顺序执行,如果你有一些私有方法不希望被自动执行,需要采用一定的技巧,比如:
在方法前增加下划线,标识为私有方法;
_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.file
、assert.fileContent
、assert.noFile
和assert.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 工具的帮忙,能够极大地提高开发效率和体验。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。