1. 脚手架介绍
1. 脚手架的本质作用
创建项目基础结构、提供项目规范和约定
约定:
- 相同的组织结构
- 相同的开发范式
- 相同的模块依赖
- 相同的工具配置
- 相同的基础代码
eg: 举个例子
IDE 创建项目的过程就是一个脚手架的工作流程
例如iOS项目在使用XCode的新建项目时,选择好选项点击创建,最后生成的项目文件及目录就是一个脚手架的执行结果
前端脚手架
- 脚手架的作用
- 常用的脚手架工具
- 通用脚手架工具剖析
- 开发一款脚手架
常用的脚手架工具
- React项目 -> create-reace-app
- Vue项目 -> vue-cli
- Angular项目 -> angular-cli
都是根据信息创建对应的项目基础结构
还有通用型
- Yeoman 根据一套模板生成一个对应的项目结构
- Plop 项目开发过程中用于生成特定类型的文件
- 例如创建一个组件 / 模块所需要的文件
2. 脚手架的工作原理
2. Yeoman、Generator
Yeoman
The web's scaffolding tool for modern webapps
1. Yeoman的基本使用
- 在全局范围安装yo
npm install yo --global
# 或者
yarn global add yo
- 安装对应的generator
github.com/yeoman/generator-node
npm install generator-node --global
# 或者
yarn global add generator-node
- 通过
yo
运行generator
cd path/to/project-dir
mkdir my-module
yo node
2. Yeoman (Sub Generator)
子集生成器
yo node:cli
# 为了执行该模块命令可以如下操作
npm link
[module_name] --help # 查看一下
3. 常规使用步骤
- 明确你的需求;
- 找到合适的
Generator
; - 全局范围安装找到的
Generator
; - 通过
Yo
运行对应的Generator
; - 通过命令行交互填写选项;
- 生成你所需要的项目结构;
4. 自定义 Generator
基于Yeoman搭建自己的脚手架
4.1 创建Generator模块
Generator 本质上就是一个NPM模块
- Generator 基本结构
模块名称必须是 generator-<name>
# 1. 创建
mkdir generator-sample
# 2. 进入
cd generator-sample
# 3. 创建身份证
npm init
# 4. 安装 yeoman-generator (生成器的基类)
npm i yeoman-generator
// 目录结构
// generators/app/index.js
// 在index.js当中
module.exports = class extends Generator {
writing() {
// Yeoman 自动在生成文件阶段调用此方法
// 我们这里尝试往项目目录中写入文件
this.fs.write(
this.destinationPath('temp.txt'),
Math.random().toString()
)
}
}
- 将该模块link到全局
npm link
- 进入到新目录(脚手架创建的新项目) 执行
yo sample
4.2 根据模板创建文件
模板文件夹 templates
// 创建模板文件夹 templates
writing() {
// Yeoman 自动在生成文件阶段调用此方法
// 通过模板方式写入文件到目标目录
// 1. 模板文件路径
const tmpl = this.templatePath('foo.txt')
// 2. 输出目标路径
const output = this.destinationPath('foo.txt')
// 3. 模板数据上下文
const context = { title: 'Hello zce~', success: false }
this.fs.copyTpl(tmpl, output, context)
}
- 相对于手动创建每一个文件,模板的方式大大提高了效率
4.3 接收用户输入
prompting() {
// Yeoman 在询问用户环节会自动调用此方法
// 在此方法中可以调用父类的 prompt() 方法发出对用户的命令询问
return this.prompt([
{
type: 'input', // 用户输入的方式
name: 'name', // 得到结果的键
message: 'Your project name', // 给用户的提示
default: this.appname // appname 为项目生成目录的文件夹的名称
}
]).then(answers => {
// answers = { name: 'user input value' }
this.answers = answers
})
}
4.4 Vue Generator案例
- 示例代码
prompting() {
return this.prompt([
{
type: 'input',
name: 'projectName',
message: 'Your project name',
default: this.appname
}
]).then(answers => {
this.answers = answers
})
}
writing() {
const templates = [
'.browserslistrc',
'.editorconfig',
'.eslintrc.js',
'.gitignore',
'babel.config.js',
'package.json',
'README.md',
'tsconfig.json',
'public/favicon.ico',
'public/index.html',
'src/assets/logo.png',
'src/components/HelloWorld.vue',
'src/store/index.ts',
'src/App.vue',
'src/main.ts',
'src/shims-tsx.d.ts',
'src/shims-vue.d.ts',
'tests/unit/example.spec.ts'
]
templates.forEach(item => {
this.fs.copyTpl(
this.templatePath(item),
this.destinationPath(item),
this.answers
)
})
}
4.5 发布 Generator
3. Plop
Plop
一个小而美的脚手架工具;
创建项目中特定类型文件的小工具;
类似于Yeoman的Sub Generator
- 举例:比如说react项目中 每次创建一个页面时需要有3个文件
- Header.css
- Header.js
- Header.test.js
- 可以将这个封装成一个模板
1. Plop 的具体使用
- 在根目录下创建 plopfile.js 文件,需要导出一个函数,接收一个 plop 对象
- 创建模板文件夹
plop-templates/xxxxx.hbs
```javascript
// 模板文件举例
import React from 'react'
export default () => {
<div className="{{name}}">
<h1>{{name}}</h1>
</div>
}
module.exports = plop => {
// arg1 生成器的名称
// arg2 配置选项
plop.setGenerator('component', {
description: 'create a compoent', // 描述
// 命令交互
prompts: [
{
type: 'input',
name: 'name', // 键
message: 'component name', // 问题描述
default: 'MyComponent', // 默认值
}
],
actions: [
{
type: 'add', // 代表添加文件
path: 'src/components/{{name}}/{{name}}.js',
templateFile: 'plop-templates/components.hbs'
}
]
})
}
- 执行
yarn plop component # (component是生成器的名称)
总结:
使用plop的步骤
- 将
plop
模块作为项目开发依赖安装 - 在项目根目录下创建一个
plopfile.js
文件 - 在
plopfile.js
文件中定义脚手架任务 - 编写用于生成特定类型文件的模板
- 通过
Plop
提供的CLI
运行脚手架任务
4. 脚手架的工作原理
使用nodejs创建一个脚手架
npm init
在
pagkage.json
中添加一个"bin": "cli.js"
在
cli.js
内
#!/usr/bin/env node
// Node CLI 应用入口文件必须 要有这样的文件头
// 如果是 Linux 或者 macOS 系统下还需要修改此文件的读写权限为 755
console.log('cli working!')
// scaffolding-cli
// 脚手架的工作过程:
// 1. 通过命令行交互询问用户问题
// 2. 根据用户回答的结果生成文件
// 使用 inquirer
const fs = require('fs')
const path = require('path')
const inquirer = require('inquirer')
const ejs = require('ejs')
inquirer.prompt([
{
type: 'input',
name: 'projectName',
message: 'Project name',
default: 'scaffolding-cli'
}
]).then(answers => {
console.log(answers)
// 模板目录
const tempDir = path.join(__dirname, 'templates')
// 目标目录
const destDir = process.cwd()
// 将模板下的文件全部输出到目标目录
fs.readdir(tempDir, (err, files) => {
if (err) throw err;
files.forEach(file => {
// console.log(file)
// 通过模板引擎渲染文件
ejs.renderFile(
path.join(tempDir, file),
answers,
(err, result) => {
if (err) throw err;
// 将结果写入目标目录
fs.writeFileSync(path.join(destDir, file), result)
}
)
})
})
})