前端脚手架工具

1,033 阅读5分钟

1. 脚手架介绍

1. 脚手架的本质作用

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

约定:

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

eg: 举个例子

IDE 创建项目的过程就是一个脚手架的工作流程

例如iOS项目在使用XCode的新建项目时,选择好选项点击创建,最后生成的项目文件及目录就是一个脚手架的执行结果

前端脚手架

  1. 脚手架的作用
  2. 常用的脚手架工具
  3. 通用脚手架工具剖析
  4. 开发一款脚手架
常用的脚手架工具
  1. React项目 -> create-reace-app
  2. Vue项目 -> vue-cli
  3. Angular项目 -> angular-cli

都是根据信息创建对应的项目基础结构

还有通用型

  1. Yeoman 根据一套模板生成一个对应的项目结构
  2. Plop 项目开发过程中用于生成特定类型的文件
    • 例如创建一个组件 / 模块所需要的文件

2. 脚手架的工作原理

2. Yeoman、Generator

Yeoman

The web's scaffolding tool for modern webapps

6LYsyt.png

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. 常规使用步骤

  1. 明确你的需求;
  2. 找到合适的Generator;
  3. 全局范围安装找到的Generator;
  4. 通过Yo运行对应的Generator;
  5. 通过命令行交互填写选项;
  6. 生成你所需要的项目结构;

4. 自定义 Generator

基于Yeoman搭建自己的脚手架

4.1 创建Generator模块

Generator 本质上就是一个NPM模块

  • Generator 基本结构

模块名称必须是 generator-

6OSaM6.png

# 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的步骤

  1. plop 模块作为项目开发依赖安装
  2. 在项目根目录下创建一个 plopfile.js 文件
  3. plopfile.js 文件中定义脚手架任务
  4. 编写用于生成特定类型文件的模板
  5. 通过 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)
        }
      )
    })
  })

})