Yeoman - 搭建自己的脚手架
引言
随着前端工程化的理念不断深入,越来越多的人选择使用脚手架来从零到一搭建自己的项目。其中大家最熟悉的就是create-react-app
和vue-cli
,它们可以帮助我们初始化配置、生成项目结构、自动安装依赖,最后我们一行指令即可运行项目开始开发,或者进行项目构建(build)。
这些脚手架提供的都是普遍意义上的最佳实践,但是我在开发中发现,随着业务的不断发展,必然会出现需要针对业务开发的实际情况来进行调整。例如:
通过调整插件与配置实现 Webpack 打包性能优化后
删除脚手架构建出来的部分功能
项目架构调整
融合公司开发工具
……
总而言之,随着业务发展,我们往往会沉淀出一套更“个性化”的业务方案。这时候我们最直接的做法就是开发出一个该方案的脚手架来,以便今后能复用这些最佳实践与方案。
脚手架怎么工作?
功能丰富程度不同的脚手架,复杂程度自然也不太一样。但是总体来说,脚手架的工作大体都会包含几个步骤:
初始化,一般在这个时候会进行环境的初始化,做一些前置的检查
用户输入,例如用 vue-cli 的时候,它会“问”你很多配置选项
生成配置文件
生成项目结构,这是候可能会使用一个项目模版
安装依赖
清理、校验等收尾工作
生命周期
Yeoman 内置的“生命周期”方法执行顺序如下:
initializing - 初始化一些状态之类的,通常是和用户输入的
options
或者arguments
打交道,这个后面说。prompting - 和用户交互的时候(命令行问答之类的)调用。
configuring - 保存配置文件(如
.babelrc
等)。default - 其他方法都会在这里按顺序统一调用。
writing - 在这里写一些模板文件。
conflicts - 处理文件冲突,比如当前目录下已经有了同名文件。
install - 开始安装依赖。
end - 擦屁股的部分... Say Goodbye maybe...
其中 default 阶段会执行你自定义地各种方法。
开发一个自己的脚手架
准备一个项目模版
脚手架是帮助我们快速生成一套既定的项目架构、文件、配置,而最常见的做法的就是先写好一套项目框架模版,等到脚手架要生成项目时,则将这套模版拷贝到目标目录下。这里其实会有两个小点需要关注。
第一个是模版内变量的填充。
在模版中的某些文件内容可能会需要生成时动态替换,例如根据用户在终端中输入的内容,动态填充package.json
中的name
值。而 Yeoman 内置了 ejs 作为模版引擎,可以直接使用。
第二个就是模版的放置位置。
一种是直接放在本地,也就是直接放到 generator 中,跟随 generator 一起下载,每次安装都是本地拷贝,速度很快,但是项目模版自身的更新升级比较困难,需要提示用户升级 generator。
另一种则是将模版文件放到某个服务器上,每次使用脚手架初始化时通过某个地址动态下载,想要更新升级模版会很方便,通常会选择托管在 github 上。
关于第二个模版放置究竟是选择在本地好,还是远端好,其实还是依据你个人的业务场景而定,在不同的场景的限制的需求不同,我之前既写过模版放在本地的脚手架(即和脚手架一起通过 npm 安装),也写过托管在 git 仓库上的这种方式。
创建 generator
创建 Yeoman 的 generator 需要遵循它的规则。
首先是 generator 命名规则。需要以generator
打头,横线连接。
执行命令
1 npm i -g yo
2 npm i generator-generator -g
3 yo generator
# 若3一直处于卡顿状态可以使用4命令,执行generator
# 4 yo
例如你想创建一个名为 webpack-kickoff 的 generator,包名需要取成 generator-webpack-kickoff,需要在执行3时输入包名
。
执行3命令后,主要生成如下文件:
├── .yo-rc.json ├── package.json ├── generators │ ├── app │ ├── templates │ ├── dummyfile.txt │ ├── index.js
.yo-rc.json
用于存储项目配置,一般不会用到,无需关注package.json
npm 项目信息文件,主要关注 author、version 域即可generators
目录即项目模板代码generators/templates
用于存放项目模板文件generators/app/index.js
定义项目手脚架的代码
在index.js
中写入你的脚手架工作流程。
此外,你创建的 generator 类需要继承 yeoman-generator。所以我们会在generators/app/index.js
中写如下代码:
const Generator = require('yeoman-generator');
class WebpackKickoffGenerator extends Generator {constructor(params, opts) {super(params, opts);}
}
module.exports = WebpackKickoffGenerator;
还记得之前提到的“生命周期”方法么?包括 initializing、prompting、default、writing、conflicts、install 和 end。除了default
,其他都代表了 Generator 中的一个同名方法,你需要的就是在子类中重写后所需的对应方法。default
阶段则会执行用户定义的类方法。
例如,你想在初始化时打印下版本信息,可以这么做:
const Generator = require('yeoman-generator');
class WebpackKickoffGenerator extends Generator {constructor(params, opts) {super(params, opts);}initializing() {const version = require('../../package.json').version;this.log(version);}
}
module.exports = WebpackKickoffGenerator;
可见,剩下的工作就是在 WebpackKickoffGenerator 类中填充各种方法的实现细节了。
处理用户交互
脚手架工作中一般都会有一些用户自定义的内容,例如创建的项目目录名,或者是否启用某个配置等。这些交互一般都是通过交互式的终端来实现的,例如下面这个功能。
可以使用 Inquirer.js 来实现。而 Yeoman 已经帮我们集成好了,直接在 generator 里调用this.prompt
即可。
在用户交互部分的需求也比较简单,只需要询问用户所需创建的项目目录名即可,随后也会作为项目名。按照 Yeoman 的流程规范,我们将该部分代码写在 prompting
方法中:
prompting() {const done = this.async();const opts = [{type: 'input',name: 'dirName',message: 'Please enter the directory name for your project:',default: 'webpack-app',validate: dirName => {if (dirName.length < 1) {return '⚠️ directory name must not be null!';}return true;}}];return this.prompt(opts).then(({dirName}) => {this.dirName = dirName;done();});}
注意,由于用户交互是一个“异步”的行为,为了让后续生命周期方法在“异步”完成后再继续执行,需要调用this.async()
方法来通知方法为异步方法,避免顺序执行完同步代码后直接调用下一阶段的生命周期方法。调用后会返回一个函数,执行函数表明该阶段完成。
下载模版
我们选择将模版托管在 github 上,因此在生成具体项目代码前,需要将相应的文件下载下来。可以使用 download-git-repo 来快速实现。
class WebpackKickoffGenerator extends Generator {// ……_downloadTemplate() {return new Promise((resolve, reject) => {const dirPath = this.destinationPath(this.dirName, '.tmp');download('alienzhou/webpack-kickoff-template', dirPath, err => {if (err) {reject(err);return;}resolve();});});}// ……
}
这里我们使用了this.destinationPath()
方法,该方法主要用于获取路径。不传参时返回当前命令行运行的目录;如果收到多个参数,则会进行路径的拼接。
此外,如果你细心的话,会发现_downloadTemplate()
方法带了一个下划线前缀。这是 Yeoman 中的一个约定:Yeoman 执行顺序中有个default
阶段,该阶段包含了所有用户自定义的类方法。但是,如果某些方法你不希望被 Yeoman 的脚手架流程直接调用,而是作为工具方法提供给其他类方法,则可以添加一个下划线前缀。对于这种命名的方法,则会在default
阶段被忽略。
模版文件拷贝
项目模版下载完毕后,下面就可以将相关的目录、文件拷贝到目标文件夹中。这些都可以在writing
阶段操作。此时需要遍历模版中的所有目录,将所有文件进行模版填充与拷贝。遍历方式如下:
class WebpackKickoffGenerator extends Generator {// ……_walk(filePath, templateRoot) {if (fs.statSync(filePath).isDirectory()) {fs.readdirSync(filePath).forEach(name => {this._walk(path.resolve(filePath, name), templateRoot);});return;}const relativePath = path.relative(templateRoot, filePath);const destination = this.destinationPath(this.dirName, relativePath);this.fs.copyTpl(filePath, destination, {dirName: this.dirName});}// ……
}
这里使用了this.fs.copyTpl()
方法,它支持文件拷贝,同时还可以指定相应的模版参数,此外,如果出现重名覆盖情况会在控制台自动输出相应信息。
最后,把下载与拷贝整合起来即可完成writing
阶段。
根据用户的选择,判断哪些文件需要copy
class WebpackKickoffGenerator extends Generator {// ……writing() {const done = this.async();this._downloadTemplate().then(() => {const templateRoot = this.destinationPath(this.dirName, '.tmp');this._walk(templateRoot, templateRoot);fs.removeSync(templateRoot);done();}).catch(err => {this.env.error(err);});}// ……
}
yo 提供mem-fs-editor实例接口,包含一系列fs工具:
this.fs.read
- 读取文件this.fs.readJSON
- 以JSON方式读取文件this.fs.write
- 写文件this.fs.writeJson
- 以JSON 方式写文件this.fs.append
- 将内容已追加方式写入文件this.fs.extendJSON
- 扩展JSON文件内容this.fs.delete
- 删除文件此外,还有一系列路径及模板接口:
this.fs.copyTpl
- 复制模板文件,并按参数解析模板内容,写入目标文件中this.templatePath
- 返回模板文件路径,即上述generator/app/templates
中的文件路径this.destinationPath
- 返回目标文件路径,即执行 yo 生成模板文件的路径this.registerTransformStream
- 生命钩子接口,用于转化文件内容,兼容gulp
插件
依赖安装
到目前,脚手架已经可以帮我们把项目开发所需的配置、目录结构、依赖清单都准备好了。这时候可以进一步帮开发人员将依赖安装完毕,这样脚手架创建项目完成后,开发人员就可以直接开发了。
Yeoman 也提供了this.npmInstall()
来方法来实现 npm 包的安装:
class WebpackKickoffGenerator extends Generator {// ……install() {this.npmInstall('', {}, {cwd: this.destinationPath(this.dirName)});}// ……
}
到这里,脚手架的核心功能就完成了。已经可以使用咱们的这个 generator 来快速创建项目了。很简单吧~
完整的代码可以参考 generator-webpack-kickoff。
Sub-Generator
在generator目录下添加其他目录,并添加templates目录和index.js文件
命令 yo xxx 默认执行 app/index.js
命令 yo xxx:component 默认执行 component/index.js
可以实现在项目中添加component模块
使用脚手架
Yeoman - 搭建自己的脚手架相关推荐
- 使用yeoman搭建脚手架并发布到npm
前言 最近主要在写前端的模板类项目,由于其中的webpack配置和引进数据注入ejs模板的方法大同小异,所以萌发出把该模板框架提取出来作为一个常用的脚手架,也方便以后同事可以进行复用. 之前在看< ...
- springboot项目结构_从零搭建Spring Boot脚手架(1):开篇以及技术选型
1. 前言 目前Spring Boot已经成为主流的Java Web开发框架,熟练掌握Spring Boot并能够根据业务来定制Spring Boot成为一个Java开发者的必备技巧,但是总是零零碎碎 ...
- 从零搭建Spring Boot脚手架(2):增加通用的功能
1. 前言 上一篇说了我要一步步地搭建Spring Boot脚手架,首先会集成Spring MVC并进行定制化以满足日常开发的需要,我们先做一些刚性的需求定制,后续再补充细节.如果你看了本文有什么问题 ...
- 从零搭建Spring Boot脚手架(4):手写Mybatis通用Mapper
1. 前言 今天继续搭建我们的kono Spring Boot脚手架,上一文把国内最流行的ORM框架Mybatis也集成了进去.但是很多时候我们希望有一些开箱即用的通用Mapper来简化我们的开发.我 ...
- 从零搭建Spring Boot脚手架(2):增加通用的功能(转载)
前言 上一篇说了我要一步步地搭建Spring Boot脚手架,首先会集成Spring MVC并进行定制化以满足日常开发的需要,我们先做一些刚性的需求定制,后续再补充细节.如果你看了本文有什么问题可以留 ...
- 从零搭建Spring Boot脚手架:开篇以及技术选型1
1. 前言 目前Spring Boot已经成为主流的Java Web开发框架,熟练掌握Spring Boot并能够根据业务来定制Spring Boot成为一个Java开发者的必备技巧,但是总是零零碎碎 ...
- 使用vue3 +vite + typeScript + elementPlus搭建一个项目脚手架
使用vue3 + vite + elementPlus搭建一个项目脚手架 这篇文章就教大家如何使用vue3+vite+ts+element-plus搭建一个项目,步骤详细,献给不爱看文档的诸位,希望这 ...
- 搭建vue项目脚手架_逆战班出品
搭建vue项目脚手架 1. 安装 执行以下命令(npm) npm install @vue/cli -g//全局安装 创建目录 vue create 目录文件名 进入目录 cd 目录文件名 启动项目 ...
- 【webpack系列】从零搭建 webpack4+react 脚手架(四)
经过三个章节的学习,你已经学会搭建了一个基于webpack4的react脚手架.如果要更改配置,比如,你希望把编译后的js文件和css文件等单独放dist下的static目录下,你想想,是不是有点麻烦 ...
最新文章
- vue 生命周期 笔记
- 用python编写一个高效搜索代码工具
- microsoftonenote_Microsoft
- 2017/9/26Codeforces E题
- UVA 125 Numbering Paths
- docker 安全性_使用最新的安全性增强来调整Docker
- 多个类共同继承一个父类
- Taro小程序生成二维码保存本地
- php视频教程打包下载 - 网络上最好的php视频教程
- iOS 百度地图与高德地图经纬度转换(坐标转换)
- ps|grep命令详解
- bugku misc-旋转跳跃
- python画线段代码_python画线代码
- bootstrap collapse切换改变图标
- cad无法安装_CAD软件下载好了,为什么安装不上?有两个原因,解决方法在这
- 随机森林oob_score及oob判断特征重要性
- java语音实现_用JAVA实现语音交互的功能(即语音聊天室的功能)
- RSA算法计算d的两种方法。
- 二叉树(Binary Tree)详解
- 产品质量异常的正确处理流程
热门文章
前言 最近主要在写前端的模板类项目,由于其中的webpack配置和引进数据注入ejs模板的方法大同小异,所以萌发出把该模板框架提取出来作为一个常用的脚手架,也方便以后同事可以进行复用. 之前在看< ...
1. 前言 目前Spring Boot已经成为主流的Java Web开发框架,熟练掌握Spring Boot并能够根据业务来定制Spring Boot成为一个Java开发者的必备技巧,但是总是零零碎碎 ...
1. 前言 上一篇说了我要一步步地搭建Spring Boot脚手架,首先会集成Spring MVC并进行定制化以满足日常开发的需要,我们先做一些刚性的需求定制,后续再补充细节.如果你看了本文有什么问题 ...
1. 前言 今天继续搭建我们的kono Spring Boot脚手架,上一文把国内最流行的ORM框架Mybatis也集成了进去.但是很多时候我们希望有一些开箱即用的通用Mapper来简化我们的开发.我 ...
前言 上一篇说了我要一步步地搭建Spring Boot脚手架,首先会集成Spring MVC并进行定制化以满足日常开发的需要,我们先做一些刚性的需求定制,后续再补充细节.如果你看了本文有什么问题可以留 ...
1. 前言 目前Spring Boot已经成为主流的Java Web开发框架,熟练掌握Spring Boot并能够根据业务来定制Spring Boot成为一个Java开发者的必备技巧,但是总是零零碎碎 ...
使用vue3 + vite + elementPlus搭建一个项目脚手架 这篇文章就教大家如何使用vue3+vite+ts+element-plus搭建一个项目,步骤详细,献给不爱看文档的诸位,希望这 ...
搭建vue项目脚手架 1. 安装 执行以下命令(npm) npm install @vue/cli -g//全局安装 创建目录 vue create 目录文件名 进入目录 cd 目录文件名 启动项目 ...
经过三个章节的学习,你已经学会搭建了一个基于webpack4的react脚手架.如果要更改配置,比如,你希望把编译后的js文件和css文件等单独放dist下的static目录下,你想想,是不是有点麻烦 ...