脚手架

262 阅读3分钟
脚手架工具概要

前端工程化的发起者

创建项目基础结构、提供项目规范和约定

  • 相同的组织结构
  • 相同的开发范式
  • 相同的模块依赖
  • 相同的工具配置
  • 相同的基础代码
常用的脚手架工具

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')
})