前端脚手架基础

18 阅读4分钟

脚手架: 一个用来快速搭建前端项目的工具,并基础的实现代码规范和 文件目录格式的统一。

目前常见的脚手架有 vue-clicreate-react-app等,他们都是通过简单的命令,拉取对应的项目模板,完成内容的快速构建。

今天先研究一下脚手架搭建的基础知识。

创建脚手架文件夹

mkdir test-cli

初始化文件夹

npm init -y

创建所需文件

  1. README.md
  2. .gitignore
  3. bin文件夹(存放脚手架入口文件)
  4. lib文件夹(存放脚手架不同命令的不同操作函数)

修改package.json

// 添加bin字段,值为脚手架的入口文件路径
"bin": "./bin/cli.js"

开启本地调试

npm link

npm link是一种把包链接到包文件夹的方式,即:可以在不发布npm模块的情况下,调试该模块,并且修改模块后会实时生效,不需要通过npm install进行安装

编写脚手架启动命令

安装命令行工具
npm install commander --save
自定义脚手架命令

./bin/cli.js

const commander = require('commander');
​
// 实例化一个命令行
const program = new commander.Command();
​
// create指令
program
    .command('create <app-name>')
    .description('create a new project')
    .option('-f, --force', 'overwrite target directory if it exist')
    .action((name, options) => {
        require('../lib/create')(name, options);
        console.log('name: ', name, '\r\n', 'options: ', options)
    })
​
// 基础提示
program
    .version(`v${reuqire('../package.json').version}`)
    .usage('<command> [option]')
​
program.parse(process.argv);
​
console.log('test cli working ~~')
定义create命令执行函数
npm install fs-extra log-symbols --save

./lib/create.js

const path = require('path');
const fs = require('fs-extra');
// 给用户提示
const symbols = require('log-symbols');
​
module.exports = async function(name, options) {
    // 获取当前路径
    const curPath = process.cwd();
    // 拼接用户传入项目名
    const dirPath = path.join(curPath, name);
    // 判断是否已存在同名文件夹
    const dirExist = fs.existsSync(dirPath);
    
    if (dirExist) {
        // 如果已存在
        // 判断用户是否有传入强制执行option、
        if (options.force) {
            // 如果传入
            // 删除已存在文件
            await fs.removeSync(dirPath);
            // 创建新文件夹
            await fs.mkdirSync(dirPath);
        } else {
            // 没有传入,提醒用户已存在相同文件
            console.log('当前路径下,已存在与项目同名文件夹,请检查后重试')
        }
    } else {
        // 创建新文件夹
        await fs.mkdirSync(dirPath);
    }
}
命令行提示样式美化
// chalk 修改命令行提示文字颜色
// figlet 艺术字效果
npm install chalk@4.0.0 figlet --save
命令行交互
npm install inquirer --save

./lib/inquirerPrompt.js

const inquirer = require('inquirer');
​
module.exports = async function inquirerPrompt(argv) {
    return new Promise((resolve, reject) => {
        inquirer.prompt([
            {
                type: 'input',
                name: 'name',
                message: 'project name',
                default: name,
            },
            {
                type: 'input',
                name: 'version',
                message: 'project version',
                default: '1.0.0'
            },
            {
                type: 'input',
                name: 'author',
                message: 'project author',
                default: ''
            },
            {
                type: 'list',
                name: 'frame',
                message: 'choose frame type',
                choices: ['vue', 'react', 'express', 'nest']
            }
        ]).then(aws => {
            const { frame } = aws;
            if (frame === 'vue') {
                inquirer.prompt([
                    {
                        type: 'list',
                        name: 'ui-lib',
                        message: 'choose ui lib',
                        choices: ['Element']
                    },
                ]).then(aws2 => {
                    resolve(aws2);
                }).catch(err => {
                    reject(err);
                })
            } else if (frame === 'react') {
                inquirer.prompt([
                    {
                        type: 'list',
                        name: 'ui-lib',
                        message: 'choose ui lib',
                        choices: ['Antd']
                    }
                ]).then(aws2 => {
                    resolve(aws2);
                }).catch(err => {
                    reject(err);
                })
            }
        })
    })
}

将方法引入create.js

// ...
const inquirerPrompt = require('./inquirerPrompt');
​
module.exports = async function(name, options) {
    // ...
    
    if (dirExist) {
        // 如果已存在
        // 判断用户是否有传入强制执行option、
        if (options.force) {
            // 如果传入
            // 删除已存在文件
            await fs.removeSync(dirPath);
            // 创建新文件夹
            await fs.mkdirSync(dirPath);
            // new start --------------
            inquirerPrompt({
                name,
                ...options
            })
            // new end ----------------
        } else {
            // 没有传入,提醒用户已存在相同文件
            console.log('当前路径下,已存在与项目同名文件夹,请检查后重试')
        }
    } else {
        // 创建新文件夹
        await fs.mkdirSync(dirPath);
        // new start --------------
        inquirerPrompt({
            name,
            ...options
        })
         // new end ---------------
    }
}
使用交互数据复制测试文件
npm install ejs --save

在根目录下创建template文件夹,在里面创建index.htmlindex.css文件

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>
    <!-- ejs 语法 -->
    <%= name %>
  </title>
</head>
<body>
  <h1><%= name %></h1>
</body></html>

index.css

body {
    padding: 20px
}

修改create.js代码

// ...
const inquirerPrompt = require('./inquirerPrompt');
​
module.exports = async function(name, options) {
    // ...
    
    if (dirExist) {
        // 如果已存在
        // 判断用户是否有传入强制执行option、
        if (options.force) {
            // 如果传入
            // 删除已存在文件
            await fs.removeSync(dirPath);
            // 创建新文件夹
            await fs.mkdirSync(dirPath);
            // new start --------------
            const aws = inquirerPrompt({
                name,
                ...options
            })
            // 获取样板文件路径
            const tempPath = path.join(__dirname, '../template');
            // 获取当前路径
            const curPath = process.cwd();
            // 读取template文件夹
            fs.readdir(tempPath, (err, files) => {
                // 使用ejs渲染替换变量
                files.forEach(file => {
                    ejs.renderFile(path.join(tempPath, file), aws).then(data) => {
                        // 写入当前目录
                        fs.writefileSync(path.join(curPath, file), data, err => {
                            throw err;
                        })
                    })
                })
            })
            // new end ----------------
        } else {
            // 没有传入,提醒用户已存在相同文件
            console.log('当前路径下,已存在与项目同名文件夹,请检查后重试')
        }
    } else {
        // 创建新文件夹
        await fs.mkdirSync(dirPath);
        // new start --------------
        const aws = inquirerPrompt({
            name,
            ...options
        })
         // new end ---------------
    }
}

npm包总结

  1. commander 自定义命令行工具
  2. chalk 命令行提示文字样式修改 需要使用chalk@4.0.0,当前默认下载5.0.0,缺少commonjs规范
  3. figlet 艺术字效果
  4. fs-extra 文件操作
  5. log-symbols 命令行log
  6. inquirer 命令行交互 需要使用inquirer@8.0.0,当前默认下载inquirer包,缺少commonjs规范
  7. ejs 操作修改html文件