脚手架工具概要
前端工程化的发起者
创建项目基础结构、提供项目规范和约定
- 相同的组织结构
- 相同的开发范式
- 相同的模块依赖
- 相同的工具配置
- 相同的基础代码
常用的脚手架工具
yeoman
Yeoman使用
yarn global add yo
yarn global add generator-node
yo node:cli
yarn link
my-module --help
自定义Generator
Yeoman的generator必须是generator-开头
mkdir generator-sample
yarn init
yarn add yeoman-generator
// generators/app/index.js入口文件
// Generator的核心入口
// 需要导出一个继承自Yeoman-Generator的类型
const Generator = require('yeoman-generator')
module.exports = class extends Generator {
// 对命令行询问
prompting () {
return this.prompt([
{
type: 'input',
name: 'name',
message: 'your project name',
default: this.appname
}
])
.then(answers => {
this.answers = answers
})
}
writing() {
// this.fs.write(
// this.destinationPath('test.txt'),
// Math.random().toString()
// )
// 模板路径
const tmpl = this.templatePath('foo.txt')
// 输出目标路径
const output = this.destinationPath('foo.txt')
// 模板数据上下文
const context = this.answers
this.fs.copyTpl(tmpl, output, context)
}
}
yarn link到全局
在新文件夹
yo sample
自定义vue-generator
yarn init
yarn add yeoman-generator
// generators/app/index.js入口文件
const Generator = require('yeoman-generator')
module.exports = class extends Generator {
prompting () {
return this.prompt([
{
type: 'input',
name: 'name',
message: 'your',
default: this.appname
}
])
.then(answers => {
this.answers = answers
})
}
writing () {
// 文件相对路径
const templates = [
'.browserslistrc',
'babel.config.js',
'package-lock.json',
'package.json',
'README.md',
'public/favicon.ico',
'public/index.html',
'src/assets/logo.png',
'src/components/HelloWorld.vue',
'src/router/index.ts',
'src/store/index.ts',
'src/views/About.vue',
'src/App.vue',
]
templates.forEach(item => {
this.fs.copyTpl(
this.templatePath(item),
this.destinationPath(item),
this.answers
)
})
}
}
app/templates/public/html
<link rel="icon" href="<%%= BASE_URL %>">
<title><%= name %></title>
// readme.md
# <%= name %>
// package.json
"name": "<%= name %>",
yarn link
mkdir test-zxt-vue
yo zxt-vue
发布Generator
放到远端仓库
yarn publish
yarn publish --registry=https://registry.yarnpkg.com
// 安装使用
- 明确需求
- 找到合适的Generator
- 全局范围安装Generator
- 通过Yo命令运行对应的Generator
- 通过命令行交互填写选项
- 生成所需要的项目结构
脚手架工作原理
mkdir sample-scaffolding
npm init
// package.json添加
{
"name": "sample-scaffolding",
"version": "1.0.3",
"description": "小型node脚手架",
"bin": "cli.js", //
"repository": "",
"author": "zxt",
"license": "MIT",
"dependencies": {
"ejs": "^3.1.3",
"inquirer": "^7.2.0"
},
"main": "index.js"
}
// cli.js
// yarn add inquirer
// yarn add ejs
// #!/usr/bin/env node cli入口文件必须要有特定文件头
// Node CLI 应用入口文件必须要有这样的文件夹
// 如果是Linux 或者macOS系统下还需要修改此文件的读写权限为755
// 具体就是通过chmod 755 cli.js实现修改
// 原理
// 1.通过命令行交互询问用户问题
// 2.根据用户回答的结果生成文件
const path = require('path');
const fs = require('fs')
const inquirer = require('inquirer')
const ejs = require('ejs')
inquirer.prompt([
{
type: 'input',
name: 'name',
message: 'Project name?'
}
])
.then(answers => {
// 模板目录
const tmplDir = path.join(__dirname, 'templates')
// 目标目录
const destDir = process.cwd()
// 将模板下的文件全部转换到目标目录
fs.readdir(tmplDir, (err, files) => {
if (err) throw err
files.forEach(file => {
// 通过模板引擎渲染文件
ejs.renderFile(path.join(tmplDir, file),answers, (err, result) => {
if (err) throw err
fs.writeFileSync(path.join(destDir, file), result)
})
});
})
})
Plop
小型脚手架工具,重复创建相同文件
// plop入口文件
module.exports = plop => {
plop.setGenerator('component', {
description: 'create a component',
prompts: [
{
type: 'input',
name: 'name',
message: 'component name',
default: 'MyComponent'
}
],
actions: [
{
type: 'add',
path: 'src/components/{{name}}/{{name}}js',
templateFile: 'plop-templates/component.hbs'
},
{
type: 'add',
path: 'src/components/{{name}}/{{name}}js',
templateFile: 'plop-templates/component.css.hbs'
},
{
type: 'add',
path: 'src/components/{{name}}/{{name}}.test.js',
templateFile: 'plop-templates/component.test.hbs'
}
]
})
}
Gulp
组合任务
yarn init -yes
yarn add gulp --dev
// yarn gulp foo
exports.foo = done => {
console.log('foo')
done()
}
// yarn gulp default
exports.default = done => {
console.log('default')
done()
}
// yarn gulp bar
const gulp = require('gulp')
gulp.task('bar', done => {
console.log('bar')
done()
})
const { series, parallel } = require('gulp')
const task1 = done => {
setTimeout(() => {
console.log('task1')
done()
}, 1000)
}
const task2 = done => {
setTimeout(() => {
console.log('task2')
done()
}, 1000)
}
const task3 = done => {
setTimeout(() => {
console.log('task3')
done()
}, 1000)
}
exports.foo = series(task1, task2, task3)
exports.bar = parallel(task1, task2, task3)
异步任务
exports.callback = done => {
console.log('callback')
done()
}
exports.callback_error = done => {
console.log('callback')
done(new Error('task failed'))
}
exports.promise = done => {
console.log('promise')
return Promise.resolve()
}
exports.promise_error = done => {
console.log('promise')
return Promise.reject(new Error('task failed'))
}
const timeout = time => {
return new Promise(resolve => {
setTimeout(resolve, time)
})
}
exports.async = async () => {
await timeout(1000)
console.log('async task')
}
const fs = require('fs')
exports.stream = () => {
const readStream = fs.createReadStream('package.json')
const writeStream = fs.createWriteStream('temp.txt')
readStream.pipe(writeStream)
return readStream
}
Gulp构建过程核心工作原理
const fs = require('fs')
const { Transform } = require('stream')
exports.default = () => {
// 文件读取流
const read = fs.createReadStream('normalize.css')
// 文件写入流
const write = fs.createWriteStream('normalize.min.css')
// 核心转换过程实现
// chunk => 读取流中读取到的内容(Bufffer)
const transform = new Transform({
transform: (chunk, encoding, callback) => {
const input = chunk.toString()
const outpit = input.replace(/\s+/g, '').replace('//*.+?*//g', '')
callback(null, output)
}
})
// 把读取出来的文件流导入写入文件流 /* */
read.pipe(write)
return read
}
封装自动化构建工作流
新建zxt-pages
将gulpfile.js拷贝到lib/index.js
将gulpfile.js的package.json内的dependencies拷贝到zxt-pages/package.json
yarn
yarn link
到zxt-gulp-demo安装yarn link "zxt-pages"
// zxt-gulp-demo/gulpfile.js
module.exports = require("zxt-pages")
FIS
fis.match('*.{js,css,png}', {
release: '/assets/$0'
})
fis.match('**/*.scss', {
rExt: '.css',
parser: fis.plugin('node-sass')
})
fis.match('**/*.js', {
parser: fis.plugin('babel-6.x')
optimizer: fis.plugin('uglify-js')
})