使用 Yeoman 自定义 Generator,包含新建 Vue 模板案例
Posted GoldenaArcher
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用 Yeoman 自定义 Generator,包含新建 Vue 模板案例相关的知识,希望对你有一定的参考价值。
使用 Yeoman 自定义 Generator,包含新建 Vue 模板案例
本章学习内容如下:
- 学习 Yeoman 即 Generator 的使用
- 使用 Yeoman 自定义一个 Generator
- 案例学习:使用 Yeoman 自定义一个 Vue 项目
- 案例学习:使用 inquirer 和 ejs 实现一个小型的脚手架工具
什么是 Yeoman
Yeoman 是一个通用型的网页应用的脚手架工具,和 angular-cli, create-react-app, vue-cli 比起来,它是一个更加通用的脚手架工具,但是也因此它的灵活性更高。
Yeoman 支持的项目结构包含但不限于:
Yeoman 提供了 generator 的平台,而使用 generator 就能够通过 generator 中已经写好的配置构建项目模块。并且,不同的 generators 可以组合使用,yeoman 也支持用户自行上传 generator。
Yeoman 的安装,及使用 generator
先确定 npm 或是 yarn 有没有安装,随后通过 npm/yarn 全局安装 yeoman:
# 全局安装 yeoman
$ yarn global add yo
windows 环境下,如果是通过 yarn 安装的,需要将 %LOCALAPPDATA%/Yarn/config/global
加到系统变量 PATH 之中。
再安装对应 generator,也是全局安装。
这里安装的是 generator-node,使用该 generator 可以生成一个 node 模块项目。
$ npm install generator-node --global
some output here
这里 npm 和 yarn 串用只是表示两个都能够成功的安装 yeoman 和 generator-node,工具二选一,选自己用的最顺手的即可。
最后可以通过命令 yo
去生成一个新的 node 模块项目:
# 可以通过 yo --generators 去查看所有已经安装的 generatos
$ yo --generators
Available Generators:
node
boilerplate
cli
editorconfig
eslint
git
readme
jasmine
mocha
webapp
# 通过yo运行generator
$ yo node
界面上会出现一系列的提示,根据提示就可以填写完成模块的信息,并且实现模块的安装:
通过 vscode 打开当前根目录,能发现 yeoman 已经生成了一个 node 项目模块,并且已经完成了基础的项目配置。
windows 系统上安装 yeoman 的报错记录,之前我也单独开了一篇文章,如果出现因为 visual studio 报错,或是缺少 cairo.h 文件报错,可以看下这篇:完整记录一次 Yeomen 安装记录,报错到三压飙升,说不定能节省很多时间。
生成 sub generator
之前使用 yo generator
会生成一个单独的项目,但是很多情况下的需求并不是生成一个新的项目,而是在原有的项目中添加新的功能。
这时候就可以通过 yeoman 提供的 sub generator 去实现,具体的语法为:
yo generator:sub-generator
以在上一个步骤生成的 node 项目为例,这里想要加入一个 cli 功能,并加入对应的文件和模块,就可以根据上面的语法去实行:
# 语法为 yo generator:sub-generator
# 这里的sub-generator 为 cli
$ yo node:cli
# 随后会提示是否重写原有的 package.json,选yes即可
package.json 中新增的 cli 部分:
{
"bin": "lib/cli.js",
"dependencies": {
"meow": "^3.7.0"
}
}
# 将项目关联到全局范围内,这样就可以使用 这个模块名称 作为 cli
yeoman> yarn link
yarn link v1.22.10
success Registered "test-modules".
info You can now run `yarn link "test-modules"` in the projects where you want to use this package and it will be used instead.
Done in 0.14s.
# yarn 进行安装新的依赖包
yeoman > yarn
# 省略掉大量的安装信息
# cli 应用就正常工作了
yeoman> test-modules --help
test whether yo works or not
Usage
$ test-modules [input]
Options
--foo Lorem ipsum. [Default: false]
Examples
$ test-modules
unicorns
$ test-modules rainbows
unicorns & rainbows
注:并不是所有的 generator 都提供子集的生成器,所以还是需要通过官方文档去查看一下具体的配置信息。
如何正确使用 Yeoman
首先,需要确定以下的需求:
- 明确需求
- 找到合适的 Generator
- 全局范围安装找到对应的 Generator
- 通过 Yo 运行对应的 Generator
- 通过命令行交互填写选项
- 生成所需的项目结构
以生成一个通用的网页应用为例,比较合适的 generator 是一个名为 webapp 的 generator。
这里就以安装一个 webapp 为例,再使用 yeoman 去建立一个新的项目
# 假设没有安装 generator-webapp,需要先安装
my-web-app> yarn global add generator-webapp
# 下面会挑出一些提示,例如说是使用sass还是bootstrap,BDD还是TDD等,按需修改即可
命令行跳出来的提示:
最后获得 web 应用的基础结构:
随后通过 yarn start
命令去启动这个 web-app:
一个基础的网页应用就已经搭建成功了。
大多数情况下,Yeoman 中已有的 generator 可以满足大部分的需求。对于特别具体的需求,Yeoman 尚且还没有 generator 去提供实现的,可以通过自定义一个 generator 的方法去实现。
自定义 Generator
这里会一步一步过一遍,使用 Yeoman 搭建自己的脚手架的步骤。
在找不到合适的 generator 的前提下,自己写一个脚手架的意义还是很大的,官方的脚手架毕竟更加的通用,在我使用 CRA 的时候会遇到以下问题:
-
没有 react-redux,
我的项目用的都是 redux 做状态管理,而 react 也支持 mobx 或是其它的工具,所以并不会强制规定一定要用 redux。
这也就意味着在使用 CRA 之后还需要手动配置 redux 以及相关的 store、reducer 和 actions
-
react-router
react-router 好像也是社区支持,没有并在官方项目中的依赖包,所以也有同样的问题。
-
eslint
同样的问题,同样的配置需要复制黏贴很多遍
-
…
创建 Generator 模块
Generator 本质上就是一个有着特定结构的 npm 模块:
Generator 的目录结构:
|- generators # 生成器目录
| |- app # 默认声称其目录
| | |- index.js # 默认生成器实现
|- package.json # 模块包配置文件
如果需要在项目中插入其他的生成器,结构则如下:
|- generators # 生成器目录
| |- app # 默认声称其目录
| | |- index.js # 默认生成器实现
+| |- components # 其他生成器目录
+| | |- index.js # 其他生成器实现
|- package.json # 模块包配置文件
新加的 components 中的内容,就是使用其他生成器添加的新模块。
要注意的是,Yeoman 中的 Generator 命名规范必须遵从generator-<name>
的规范,否则会无法被识别。
下面就一步步的完成一个 Generator 的实现。
案例-创建 generator 模块
-
新建一个新的目录,这里的目录名为 generator-sample
-
cd 到 generator-sample 中
-
初始化一个 node 项目
generator-sample> yarn init yarn init v1.22.10 # 省略若干初始化配置
-
安装 yeoman-generator 依赖包
generator-sample> yarn add yeoman-generator
初始化完成后,一个基础的 node 项目就被构建完成了:
-
添加 generator 结构目录
就是上文标注的目录,
-
实现 index.js
这里 index.js 的作用非常简单,就是在跟目录下生成一个 temp.txt,随后写入一个随机数
// Generator 的核心入口 // 道出一个继承自 Yeoman Generator 的类型 // 可以通过调用父类,Yeoman Generator,提供的一些方法去进行功能的实现 const Generator = require('yeoman-generator'); module.exports = class extends Generator { writing() { // Yeomen 自动在生成文件阶段调用此方法 // 这里尝试往根目录中写入文件 this.fs.write( this.destinationPath('temp.txt'), Math.random().toString() ); } };
-
全局连接,使得当前模块可以被 yarn 找到
generator-sample> yarn link
-
创建一个新的文件夹去使用 generator-sample
generator-sample> cd .. yeoman> mkdir test-proj yeoman> ls Mode LastWriteTime Length Name ---- ------------- ------ ---- d----- 6/16/2021 4:18 AM generator-sample d----- 6/16/2021 4:30 AM test-proj yeoman> cd .\\test-proj\\ test-proj>
-
使用 generator-sample
test-proj> yo sample create temp.txt
-
通过自定义 Generator 实现的项目就完成了
如下:
temp.txt 中的内容就是一串随机数
根据模板创建文件
大多数情况下的代码都是比较固定化的,有一定的模板,如 React 中的类的格式就是:
import React, { Component } from 'react';
export class Name extends Component {
render() {
return <div></div>;
}
}
export default Name;
所以能够使用模板创建文件,也是一个可以极大地缩减脚手架开发时间的功能。
在 Generator 中,模板的结构如下:
|- generators # 生成器目录
| |- app # 默认声称其目录
| | |- templates
| | | |- template-here
| | |- index.js # 默认生成器实现
|- package.json # 模块包配置文件
接下来就按照步骤,实现一个从模板中生成文件的案例
-
创建模板文件
// 这是一个 EJS 模板文件 import React, { Component } from 'react'; export class <%= compName %> extends Component { render() { return <div></div>; } } export default <%= compName %>;
这里生成的 templage 文件名为 foo.txt,它使用的是 EJS 的语法。亲测来说,使用
.txt
和.html
都没什么问题,使用.ejs
好想要将输入地址修改为一个目录,这点之后也可以玩着试试看。 -
修改 index.js
这里就不需要使用
this.fs.write()
去实现这个功能了,因为 Generator 自带了导入模板引擎的函数:copyTpl()
。copyTpl()
接受 3 个参数:输入文件,输出文件,以及内容,用法如下:const Generator = require('yeoman-generator'); module.exports = class extends Generator { writing() { const templatePath = this.templatePath('foo.txt'); const output = this.destinationPath('foo.js'); const context = { compName: 'Home' }; this.fs.copyTpl(templatePath, output, context); } };
-
运行 generator
# 这里已经使用 yarn link 过了,新的项目需要关联到全局能够让 yeoman 找得到 test-proj> yo sample create foo.js
-
查看输出结果
这里就生成了一个标准的 React class-based component 了:
// 这是一个 EJS 模板文件 import React, { Component } from 'react'; export class Home extends Component { render() { return <div></div>; } } export default Home;
在数据结构重复并且有一定程度的重复性时,使用模板引擎可以有效的减少代码重复量,并且有效的提升性能。
接受用户输入
在很多 cli 工具中,命令行会提示用户输入对应的信息,例如说在新建一个 node 项目时,命令行会提示需要项目名称、版本号、用户名,等。
Yeoman 也内部实现了这个功能,使用 prompting() {}
就可以获取用户输入的信息。
下面是具体的流程:
-
修改模板引擎
这里稍微修改一下流程,使用
.html
作为模板引擎。<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title><%= title %></title> </head> <body> <%= title %> </body> </html>
-
修改 index.js
这里主要就是在 prompting 中提示用户输入信息,以及在 writing 中获取用户输入的信息
const Generator = require('yeoman-generator'); module.exports = class extends Generator { prompting() { // Yeoman在向用户询问输入时会调用这个函数 // prompting 会返回一个回调函数 return this.prompt([ { type: 'input', name: 'title', message: 'Your project name', default: this.appname, // 当前目录下文件夹的名字 }, ]).then((answers) => { // 将获取的所有输入保存起来,让 writing 可以接受到 this.answers = answers; }); } writing() { const templatePath = this.templatePath('project-name.html'); const output = this.destinationPath('project-name.html'); const context = this.answers; // {title: 'user-input-value' } this.fs.copyTpl(templatePath, output, context); } };
-
运行 generator
依旧是老一套了
test-proj> yo sample ? Your project name project-name create project-name.html
-
查看输出结果
这是输出的 HTML 文档,其中原本是 ejs 模板引擎的地方,就被替换成了之前输入的
project-name
。<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>project-name</title> </head> <body> project-name </body> </html>
Vue Generator 案例
按步骤实现一个 vue generator
-
新建一个文件夹
这里将文件夹命名为 generator-vue-sample
-
cd 到 generator-vue-sample,初始化一个新的 node 项目
generator-vue-sample> yarn init question name (generator-vue-sample): question version (1.0.0): question description: question entry point (index.js): question repository url: question author: question license (MIT): question private: success Saved package.json
-
添加 yeoman-generator 的依赖
generator-vue-sample> yarn add yeoman-generator # 省略若干安装输出
一直到这一步,都和上面新建一个 generator 的步骤是完全一致的。
但是,从下面开始实现 templates 和 index.js 这里开始,就不太一样了
-
新建 templates 文件夹,并且将一个 vue 的项目复制进去
-
被复制进去的 vue 项目结构:
src 文件目录:
基本上说大部分的项目所需要的配置,包括 router,eslint,production,development 之类的都内容结构都已经新建好了。
-
复制完成后 generator 的结构:
-
-
将原本的项目名称改为 EJS 模板引擎
这里包括以下的文件名称:
- README.md
- package.json
- src/index.html
这里只需要将原本实现好的名称,如 my-project,改为 EJS 模板引擎
<%= name %>
即可。另外,如果用的是 Vue 自动生成的项目,需要将 index.html 中的
<%= BASE_URL %>
改为<%%= BASE_URL %>favicon.ico
,将<%= htmlWebpackPlugin.options.title %>
改为<%%= htmlWebpackPlugin.options.title %>
。<%% %>
指的是保留当前变量,使其不被转义。在复制 templates 中的内容过程中,
copyTpl()
会解析对应的模板引擎内容,并且替换成prompting()
中获得的用户输入。这与 接受用户输入 中的内容是相似的,只不过这里要替换更多的内容而易。
-
按照 generator 的目录结构,新建并实现 index.js
这里其实变动还挺多的,因为之前的案例中只是输出单一文件,但是这里要输出多个文件,所以内容会有相应的改变:
const Generator = require('yeoman-generator'); module.exports = class extends Generator { prompting() { // 这里可以输出多个 input // 当前案例中值获取了项目名称 return this.prompt([ { type: 'input', name: 'name', message: 'Your project name', default: this.appname, }, ]).then((answers) => { this.answers = answers; }); } writing() { // 文件名组成的数组 const templates = [ 'babel.config.js', 'package.json', 'public/favicon.ico', 'public/index.html', 'README.md', '.browserslistrc', '.editorconfig', 'src/App.vue', 'src/assets/logo.png', 'src/components/HelloWorld.vue', 'src/main.js', 'src/router.js', 'yarn.lock', 'src/store/index.js', 'src/utils/index.js', 'src/views/index.js', 'src/App.vue', 'src/main.js', 'src/router.js', ]; // 循环输出,而不是只输出单独的一个文件 templates.forEach((item) => { this.fs.copyTpl( this.templatePath(item), this.destinationPath(item), this.answers ); }); } };
-
连接当前 generator
依旧是用
yarn link
将当前的 generator 连到全局使用 -
尝试生成新的 vue 项目
创建一个新的文件夹,并且使用当前 Generator
使用 vscode 打开项目结构,其结构如下:
是一个有着 store,路由等功能的项目了。
少了一些配置文件是因为
templates
的文件名没有写全,这个最好是写个函数去动态获取 templates 下的所有文件和文件夹,不要写死。这里是写死值是处于案例方便展示的效果。
这样,一个自定义的 vue 项目模板就完成了。
发布 Generator
Generator 的发布还是简单的,毕竟本质上来说,Generator 就是一个 npm 的模块。
发布的步骤就是:
-
新建一个 github 的仓库用来托管写好的代码
千万别忘了新建一个
.gitignore
文件,然后将node_modules
写进.gitignore
-
使用 yarn 或是 npm 将发布插件
如果发布失败就查看一下是不是使用淘宝镜像了,淘宝镜像是一个只读镜像,不接受任何的改变,所以在发布前需要将镜像改回官方(npm 或是 yarn)的镜像。
发布完成的模块就可以直接在 npm 的官网上找到,要安装就可以使用以下命令:
npm install 发布的Generator
脚手架的工作原理
脚手架的工作原理说起来还挺简单的:
- 命令行交互获取信息
- 通过模板引擎将信息写入文档中
所以这里就学习去创建一个 cli 应用。
依旧是一步一步的来,按操作尝试生成一个脚手架 cli。
-
新建一个目录,并初始化一个 package.json
front> mkdir sample-cli front> cd .\\sample-cli\\ yarn init v1.22.10nit --yes warning The yes flag has been set. This will automatically answer yes to all questions, which may have security implications. success Saved package.json Done in 0.10s.
-
修改 package.json
设置 cli 的入口文件
{ "name": "sample-cli", "version": "1.0.0", "main": "index.js", "bin": "cli.js", "license": "MIT" }
-
添加 cli.js 这个文件
#!/usr/bin/env node // 设置环境变量的 文件头 console.log('cli start');
-
依旧是使用
yarn link
将这个 node modules 关联到全局完成这一步就可以完成一个最基本的交互了:
success Registered "sample-cli". info You can now run `yarn link "sample-cli"` in the projects where you want to use this package and it will be used instead. Done in 0.12s. sample-cli> sample-cli cli start
-
具体业务 1:发起命令行交互
交互使用的是 inquirer 这个依赖包,先安装依赖包:
sample-cli> yarn add inquirer
inquirer 提供了一个 prompt 的函数去进行命令行交互,其实现如下:
#!/usr/bin/env node // 设置环境变量的 文件头 const inquirer = require("inquirer"); inquirer .prompt([ { type: "input", name: "name", message: "Project name", }, ]) .then((answers) => { console.log(answers); });
和之前使用 Yeoman 的过程非常的相似,交互过程如下:
sample-cli> sample-cli ? Project name project-name { name: 'project-name' }
-
生成模板用的文件
这一步应该也很熟了。
现在根目录下创建 templates 文件夹,里面先设置一个模板案例
templates/index.html,它所包含的代码为:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title><%= name %></title> </head> <body> <h1><%= name %></h1> </body> </html>
后面又新建了一个 index.css,不过里面没什么内容,只是表示可以读取多个文件而已
-
读取模板用的文件
就是自行封装一个函数去读取 templates 文件夹下的文件,实现的代码为:
// 新增的引用 const path = require('path'); const fs = require('fs'); // 上面的函数没有变化,就不放进来了 then((answers) => { // 根据用户提供的内容生成文件 const tmplDir = path.join(__dirname, 'templates'); // 目标输出目录 const destDir = process.cwd(); // 读取模板目录下所有文件 fs.readdir(tmplDir, (err,
以上是关于使用 Yeoman 自定义 Generator,包含新建 Vue 模板案例的主要内容,如果未能解决你的问题,请参考以下文章