灌南县建设局网站福州 建站 软件
这里写自定义目录标题
- 为什么需要脚手架?
 - 前置-第三方工具的使用
 - 1. 创建demo并运行-4步
 - 新建文件夹 zyfcli,并初始化npm init -y
 - 配置入口文件
 
- 2.commander-命令行指令
 - 3. chalk-命令行美化工具
 - 4. inquirer-命令行交互工具
 - 5. figlet-艺术字
 - 6. ora-loading工具
 - 7. 小demo的完整代码
 
- 正式版走起
 - 1. 处理bin的index文件
 - 2.处理create.js
 - 3. 添加utils工具函数
 - 4. 查看效果
 
- 发布到npm
 - 参考
 - 代码仓库
 - npm 地址
 

好多前端工作多年依然不会搭建脚手架,本文就介绍下如何从零开始搭建一个前端脚手架。
为什么需要脚手架?
- 减少重复性的工作,不再从零创建一个项目,或者复制粘贴另一个项目的代码 。
 - 根据动态交互生成项目结构和配置文件,具备更高的灵活性和人性化定制的能力 。
 - 有利于多人开发协作,避免了人工传递文件的繁琐。
 - 可以集成多套开发模板,根据项目需要选择合适的模板。
 
前置-第三方工具的使用
实现一个脚手架,通常需要以下工具
- commander: 命令行工具
 - download-git-repo: 来通过git下载项目模板的插件
 - inquirer: 用于命令行交互问询等
 - ora: 用于实现node命令环境的loading效果,并显示各种状态的图标,显示 loading 动画
 - chalk: chalk是一个颜色的插件。可以通过chalk.green(‘success’)来改变颜色。修改控制台输出内容样式
 - log-symbols: 用于在打印输出的内容中加入icon更友好(显示出 √ 或 × 等的图标)
 
**注意:**插件的版本。
为了演示先创建一个小项目
1. 创建demo并运行-4步
新建文件夹 zyfcli,并初始化npm init -y
装包-注意版本
pnpm i commander@9.5.0 chalk@4.0.0 inquirer@8.2.1 ora@4.0.0 figlet download-git-repo  ora log-symbols
 
注意:版本过高会报错,已踩坑…
 
配置入口文件
在根目录下新建bin/index.js【整个脚手架的入口文件】
#! /usr/bin/env node
console.log('hello world')
 
验证结果:在命令行中输入node ./bin/index.js,如果能打印出hello即成功
将入口文件配置到package.json 的bin字段
{"name": "zyfcli","bin": "bin/index.js",  
}
// 写法2,注意bin里key,需要和nage保持一致
{"name": "zyfcli","bin": {"zyfcli": "bin/www"}
}
 
npm link将命令挂载到全局
 执行 npm link将命令挂载到全局,然后再输入 zyfcli 就可以到达刚才node ./bin/index.js 的效果了
2.commander-命令行指令
引入commander
const program = require("commander");program.name('zyfcli').usage(`<command>[option]`).version(`1.0.0`)
// 解析用户执行命令传入参数
program.parse(process.argv);
 
在命令行输入commander --help,即可看到简单的效果
 
3. chalk-命令行美化工具
#! /usr/bin/env node
const program = require("commander");
const chalk = require('chalk')program.name('zyfcli').usage(`<command>[option]`).version(`1.0.0`)// 解析用户执行命令传入参数
program.parse(process.argv);// 演示美化工具
console.log(`${chalk.green("hello")} zyf`);
 
输入 zyfcli,看hello显示颜色
 
 其他用法
console.log(`${chalk.green  --颜色.bold   --加粗.underline  --下划线("hello")} zyf`);
 
4. inquirer-命令行交互工具
const Inquirer = require('inquirer');// 命令行交互
new Inquirer.prompt([{name: 'zyfcli',type: "checkbox",message: "Check the features needed for your project",choices: [{name: 'Babel',checked: 'true',},{name: 'TypeScript',}]}
]).then((data) => {console.log(data);
})
 

5. figlet-艺术字
- 安装 npm i figlet
 - 使用
 
const figlet = require('figlet')console.log(figlet.textSync('Hello Word'));
 
- 效果 

 
6. ora-loading工具
注意版本
- 使用
 
const ora = require('ora');
const spinner = ora('Loading unicorns').start();setTimeout(() => {// spinner.color = 'yellow';spinner.text = 'Loading rainbows';spinner.succeed()// spinner.stop()
}, 1000);
 
效果
 
7. 小demo的完整代码
#! /usr/bin/env node
// 演示工具的使用
const program = require("commander");
const chalk = require('chalk');
const Inquirer = require('inquirer');
const figlet = require('figlet')
const ora = require('ora');program.name('zyfcli').usage(`<command>[option]`).version(`1.0.0`)// 演示美化工具
console.log(`${chalk.green.bold.underline("hello")} zyf`);// 命令行交互
// new Inquirer.prompt([
//   {
//     name: 'zyfcli',
//     type: "checkbox",
//     message: "Check the features needed for your project",
//     choices: [
//       {
//         name: 'Babel',
//         checked: 'true',
//       },
//       {
//         name: 'TypeScript',
//       }
//     ]
//   }
// ]).then((data) => {
//   console.log(data);
// })// 艺术字
console.log(figlet.textSync('Hello Word'));// loading
const spinner = ora('Loading unicorns').start();setTimeout(() => {// spinner.color = 'yellow';spinner.text = 'Loading rainbows';// spinner.succeed()// spinner.stop()
}, 1000);// 解析用户执行命令传入参数
program.parse(process.argv); 
正式版走起
先看下目录结构
 
1. 处理bin的index文件
#! /usr/bin/env node
const program = require("commander");
const chalk = require("chalk");
const figlet = require("figlet");program.name("zyfcli").usage(`zyfcli <command> [option]`).version(`zyfcli ${require("../package.json").version}`);program.command("create <project-name>") // 增加创建指令.description("create a new project") // 添加描述信息.option("-f, --force", "overwrite target directory if it exists") // 强制覆盖.action((projectName, cmd) => {// 处理用户输入create 指令附加的参数require("../lib/create")(projectName, cmd);});program.command("config [value]").description("inspect and modify the config").option("-g, --get <key>", "get value by key").option("-s, --set <key> <value>", "set option[key] is value").option("-d, --delete <key>", "delete option by key").action((value, keys) => {console.log(value, keys);});program.on("--help", function () {console.log("\r\n" +figlet.textSync("zyf-cli", {font: "3D-ASCII",horizontalLayout: "default",verticalLayout: "default",width: 80,whitespaceBreak: true,}));// 前后两个空行调整格式,更舒适console.log();console.log(`Run ${chalk.cyan("zyfcli <command> --help")} for detailed usage of given command.`);console.log();
});program.parse(process.argv);
 
create.js先不写东西
看下效果
 
2.处理create.js
路径:根目录/lib/create.js
const path = require("path");
const fs = require("fs-extra");
const Inquirer = require("inquirer");
const downloadGitRepo = require("download-git-repo");
const chalk = require("chalk");
const util = require("util");
const { loading } = require("./util");module.exports = async function (projectName, options) {// 获取当前工作目录const cwd = process.cwd();const targetDirectory = path.join(cwd, projectName);// 处理文件夹await handleFolder(projectName, options, targetDirectory);// 1.选择模版const { template } = await new Inquirer.prompt([{name: "template",type: "list",message: "Please choose a template to create project",choices: [{ name: 'react', value: 'zyf118725/reactTs' },{ name: 'vue', value: 'https://vue仓库' }, // 演示{ name: 'angular', value: 'https://angular仓库' },],},]);// 2.下载await download(template, targetDirectory);// 3.模板使用提示console.log(`\r\nSuccessfully created project ${chalk.cyan(projectName)}`);console.log(`\r\n  cd ${chalk.cyan(projectName)}`);console.log("  npm install");// console.log("  npm run serve\r\n");
};// 处理文件夹创建重名问题
async function handleFolder(projectName, options, targetDirectory) {if (fs.existsSync(targetDirectory)) {if (options.force) {// 删除重名目录await fs.remove(targetDirectory);} else {let { isOverwrite } = await new Inquirer.prompt([{name: "isOverwrite", // 与返回值对应type: "list", // list 类型message: "Target directory exists, Please choose an action",choices: [{ name: "Overwrite", value: true },{ name: "Cancel", value: false },],},]);if (!isOverwrite) {console.log("Cancel");return;} else {await loading(`Removing ${projectName}, please wait a minute`,fs.remove,targetDirectory);}}}
}// 下载git仓库
async function download(templateUrl, targetDirectory) {const downloadGitRepoPromise = util.promisify(downloadGitRepo);await loading("downloading template, please wait",downloadGitRepoPromise,templateUrl,targetDirectory // 项目创建位置);
}
 
3. 添加utils工具函数
封装axios等函数。
const ora = require("ora");/*** 睡觉函数* @param {Number} n 睡眠时间*/
function sleep(n) {return new Promise((resolve, reject) => {setTimeout(() => {resolve();}, n);});
}/*** loading加载效果* @param {String} message 加载信息* @param {Function} fn 加载函数* @param {List} args fn 函数执行的参数* @returns 异步调用返回值*/
async function loading(message, fn, ...args) {const spinner = ora(message);spinner.start(); // 开启加载try {let executeRes = await fn(...args);spinner.succeed();return executeRes;} catch (error) {spinner.fail("request fail, reTrying");await sleep(1000);return loading(message, fn, ...args);}
}module.exports = { loading };
 
4. 查看效果
创建一个democli项目

 
发布到npm
npm包的发布比较简单,就不在赘述了,没整过的小伙伴可以查下教程
- npm login
 - npm publish
 

 额,名字太简单了改下名字
 修改下package继续发包,注意package的name和bin中的名称。
 
发布成功
 
参考
- 掘金-从0到1搭建React脚手架 https://www.yuque.com/yafei/dqso8a/pzb1yp89dc4uppno
 - https://blog.csdn.net/gao_xu_520/article/details/120505635
 - 掘金-工具详解:https://juejin.cn/post/7077717940941881358
 - commander中文文档(github):https://github.com/tj/commander.js/blob/master/Readme_zh-CN.md
 
代码仓库
- zyfcli: https://github.com/zyf118725/zyfcli
 - react: https://github.com/zyf118725/reactTs
 
npm 地址
https://www.npmjs.com/package/yfli
未完待续。。。
 
下班码字不易,如果有帮到你请给个打赏谢谢
 

