相同类型的项目往往会有许多相似之处,脚手架工具就是为了帮助我们因为这些相似之处所带来的 “重复劳动”,使我们能够快速得到一个项目的基础结构。
相似之处的体现
以 vue 项目代码为例
Yeoman 是一种通用项目的前端脚手架工具,不像是Vue-cli仅服务于Vue项目。 Yeoman的灵活性更高,通过不同的Generator可以组成不同的项目(webApp,node项目)脚手架,我们自己也可以定义属于自己的脚手架工具。
Yeoman = yo + 变量 generator ,就可以生成不同的应用的脚手架工具
yarn global add yo
yarn global add generator-node
PS:generator- 前缀拼接上对应的模块名如node
3. 通过 yo命令来运行所安装的 generator-node 模块
根据generator-node的一系列的配置(指下图中询问的问题),就能够得到生成的node项目的基础结构了
子生成器模块是为了在通过Generator生成的项目结构的基础上再来生成一些配置文件如eslint,editorconfig等等,这并不是每一个Generator都会有Sub Generator,这需要Generator能够提供Sub Generator。
生成editorconfig例子:
before:
首先我们清空已经有的editorconfig的配置
after:
// 通过 yo + generator模块名 + : + Sub Generator名字(去对应的Generator的官方哪里找)
yo node:editorconfig
会提示我们是否要重写这个文件,选择y。
此时我们再次查看editorconfig这个文件,就已经变化了。
有时候我们对这些配置文件,要是自己手动来写的,常常会写错,我们根本记不住这么多。这时候我们就可以查查看对应的generator有没有提供 sub generator来生成这些配置文件
自定义Generator本质就是 npm 模块
创建一个文件,并且当前package名字需要以 generator-xxx 命名,运行 yarn init, 并安装 yeoman-genenrator
在入口index.js文件引入 yeoman-generator 作为基类来生成自己的类,并将templates文件作为输出模板,想起解释请看下面代码。
// 此文件作为 Generator 的核心入口
// 1. 需要引如 yeoman-generator 的一个基类
const Generator = require('yeoman-generator')
// 2. 创建我们自己的genenrator类,然后继承基类,最后导出我们所创建的类型。
// 当我们通过 yo 生成器名子 的时候会自动执行这个入口文件,并调用我们所创建并导出这个类的生命周期来做某些事情,如writing中的生成项目的基础结构
module.exports = class extends Generator {
// 3. 如果需要以命令行交互的形式询问用户一些问题关于如何生成项目的基础结构 可以使用prompting方法
prompting() {
// 4. prompt 方法返回promise对象
return this.prompt([
{
type: 'input', // 交互类型
name: 'name', // 用来存储当前输入值的变量名称
message: 'Your project name ?', // 问题名称
default: this.appname // 默认值, this.appname 是通过yo运行我们自定义generator 时所在的文件名
}
])
.then(answers => {
// 5. 将我们拿到的问题的答案通过this.answer 变量存储起来,以便我们再接下来生成项目基础结构的时候使用得到。
this.answers = answers
})
}
// 6. writing方法是用来生成项目文件的方法
// 7. yeoman-generator 可以允许我们以模板的形式生成项目基础结构, 我们需要再 generators/app/ 下面创建一个 template 文件夹,将我们需要生成的项目文件都放在这个文件夹里面。
// 8. 因为是通过模板引擎生成项目基础结构,所以可以通过模板引擎的模板语法进行 "挖坑" 如 <%= name %>,如果想要原封不动的输出 <%= name %>,可以通过再添加一个%来进行转译如 <%%=name%>
writing() {
// 9. 把templates文件下的每一个文件通过模板转换到目标路径
// 将每一个文件的相对路径定义成数组,通过遍历的形式通过模板生成目标路径
const templates = [
'.gitignore',
'babel.config.js',
'package.json',
'package-lock.json',
'README.md',
'src/assets/logo.png',
'src/components/HelloWorld.vue',
'src/components/Calendar/index.vue',
'src/components/Calendar/utils.js',
'src/App.vue',
'src/main.js',
'public/favicon.ico',
'public/index.html'
]
templates.forEach(item => {
// item 为每个文件路径
// 10. 通过yeoman-generator这个基类提供的fs.copyType 将 template 下的文件生成 我们自己项目的文件
// fs.copyType(模板路径,自己项目输出目标路径,模板参数)的参数
//
this.fs.copyTpl(
this.templatePath(item),
this.destinationPath(item),
this.answers
)
})
}
}
如果遇到以下问题,需要将 yeoman-generator 的版本降至 4.0
运行之后的结果
此时借着自定义 generator 就生成了属于自己的脚手架
发布 自定义的Generator 实际上就是发布 npm 模块
直接运行 yarn publish 进行发布
发布的时候需要注意当前 npm yarn 源不能够是淘宝镜像的源,需要切换本身原有的源地址。
发布成功之后就可以直接通过 npm yarn 来安装
一个小而美的小型脚手架工具
主要的作用用于据模板代码生成一些重复的特定类型的文件代码
如下面的例子:
https://blog.csdn.net/u012733501/article/details/106858603/
yarn add plop -D
// 当我们运行plop命令的时候,会以这个文件作为入口文件,这个文件需要导出一个函数。
// 1. 导出一个函数用于创建生成器任务,此函数会接受一个plop对象,用于创建生成器任务
module.exports = plop => {
// 2. 设置生成器,此函数接受两个参数,第一个参数为 Generator 的名称(也是plop运行命令的名称), 第二个参数是 config对象。
plop.setGenerator('component', {
description: '创建一个组件',
// 3. prompts 与用户的命令行交互
prompts: [
{
type: 'input', // 交互类型
name: 'name', // 变量名,存储用户输入的信息
message: 'component name', // 问题
validate(val) {
// val 为输入的值
// 如果验证不通过,需要返回字符串,字符串将会被展示在控制台
// 验证通过,则返回true
return true;
},
default: 'reborn'
}
],
// 4. 完成命令行交互之后需要执行的动作
// 5. 需要在项目的根目录下 plop-templates 用于存放 plop 模板,plop对于模板代码的处理选择了 handlebars 作为模板
actions: [
{
// 添加一个全新的文件
type: 'add',
// 添加的文件路径, 可以使用插值表达式,值来源于与用户进行命令行交互的时候获取的输入,
path: 'src/components/{
{name}}/{
{name}}.js',
// 模板文件
templateFile: 'plop-templates/component.hbs'
},
{
type: 'add',
path: 'src/components/{
{name}}/{
{name}}.css',
templateFile: 'plop-templates/component.css.hbs'
},
{
type: 'add',
path: 'src/components/{
{name}}/{
{name}}.vue',
templateFile: 'plop-templates/components.vue.hbs'
},
]
})
}
此时就根据模板创建了三个文件
handlebars 模板
输出结果
#!/usr/bin/env node
// Node CLI 应用的入口文件必须要有这样的文件头
// 如果是 Linux 或者 macOS 系统下还需要修改此文件的读写权限为 755
// 具体就是通过 chmod 755 cli.js 实现修改
// 脚手架的工作过程:
// 1. 通过命令行交互询问用户问题
// 2. 根据用户回答的问题结果生成文件
const inquirer = require('inquirer')
const path = require('path')
const fs = require('fs')
const ejs = require('ejs')
// 1. 通过命令行交互询问用户问题
inquirer.prompt([
// input 类型
{
type: 'input',
name: 'name',
message: 'project name?',
validate(val) {
console.log(val)
if (!val) {
return '请输入'
} else {
return true
}
}
},
]).then(answer => {
// 2. 根据用户回答的问题结果 => 通过 template下面的模板文件 与 inquirer 结果通过 ejs 模板引擎结合,得到组合后的模板,然后再将其再命令工具运行的目录进行写入。
// 模板目录
const tmplDir = path.join(__dirname, 'templates')
// 输出目录
const destDir = process.cwd()
// 将模板下的文件全部转换到目标目录
// readdir 方法扫描目标路径下的所有文件,得到一个文件名的数组
fs.readdir(tmplDir, (err, files) => {
if (err) throw err
files.forEach(file => {
// 通过模板引擎渲染路径所对应的文件
ejs.renderFile(path.join(tmplDir, file), answer, (err, result) => {
if (err) throw err
// 将所得到的模板结果进行写入操作
fs.writeFileSync(path.join(destDir, file), result)
})
})
})
})
脚手架的功能就是帮助我们快速得到一个项目的基础结构或是生成特定的文件,脚手架的职能就是解决 “重复劳动” 。在生成特定的重复性的文件(代码)往往不能够很 “死板” , 这就需要借着一款命令行交互工具使我们知道用户ta想要啥样的文件(代码),我们在将用户想要的内容通过模板引擎工具来组合并输出,这样就得到了我们想要的结果了。