这里写自定义目录标题
- 一、为什么需要脚手架?
- 二、前置-第三方工具的使用
- 1. 创建demo并运行-4步
- 新建文件夹 zyfcli,并初始化npm init -y
- 配置入口文件
- 2.commander-命令行指令
- 3. chalk-命令行美化工具
- 4. inquirer-命令行交互工具
- 5. figlet-艺术字
- 6. ora-loading工具
- 7. npm link 本地调试npm包的神器
- 8. 小demo的完整代码
- 三、正式版走起
- 1. 处理bin的index文件
- 2.处理create.js
- 3. 添加utils工具函数
- 4. 查看效果
- 四、发布到npm
- 五、参考文章
- 六、源码仓库地址
- 七、踩过的坑
- 其他
好多前端童鞋工作多年依然不会搭建脚手架,本文就介绍下如何从零开始搭建一个属于你自己的前端脚手架,提高自己的工程化实力,同时也提高团队的开发效率。
先看下github的dipper-cli仓库和npm上的成果:
欢迎大家到github上点赞,项目已开源,欢迎大家加入。大家可以加我微信哈 zyfts1
,一块讨论前端发展。
好了,下面开始开发您的第一个前端脚手架吧
一、为什么需要脚手架?
- 减少重复性的工作,不再从零创建一个项目,或者复制粘贴另一个项目的代码 。
- 根据动态交互生成项目结构和配置文件,具备更高的灵活性和人性化定制的能力 。
- 有利于多人开发协作,避免了人工传递文件的繁琐。
- 可以集成多套开发模板,根据项目需要选择合适的模板。
二、前置-第三方工具的使用
实现一个脚手架,通常需要以下工具
- commander: 命令行工具
- chalk: chalk是一个颜色的插件。可以通过chalk.green(‘success’)来改变颜色。修改控制台输出内容样式
- inquirer: 用于命令行交互问询等
- download-git-repo: 来通过git下载项目模板的插件
- figlet: 生成好看的艺术字,增加终端美观度
- ora: 用于实现node命令环境的loading效果,并显示各种状态的图标,显示 loading 动画
- npm link: 本地调试npm包的神器。
**注意:**插件的版本。
为了演示先创建一个小项目
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
注意:版本过高会报错,已踩坑…
配置入口文件
在根目录下新建bin/index.js【整个脚手架的入口文件】
#! /usr/bin/env nodeconsole.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 nodeconst 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')figlet.textSync("dipper-cli", { font: "3D-ASCII", horizontalLayout: "default", verticalLayout: "default", whitespaceBreak: true, })
- 效果
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. npm link 本地调试npm包的神器
npm link:可以在本地调试我们正在开发的脚手架或组件库的神器,不用费事的发布到npm后再调试。
详细的用法教程很多,就不赘述了,没用过的小伙伴可以参考这篇文章,下面介绍一下常用方法 :
- 创建: npm link,软后就可在本地调试了
- 查看所有的软连接: npm ls -g
- 取消软连:npm unlink
- 卸载npm包:npm uninstall -g 简写
npm un -g <name>
还有个实用的命令:
npm ls -g
: 可以查看全局安装的npm包,
例如:如上图所指,即为我们本地包,其他的是npm包,如果要从全局删除需要用npm un -g <name>
指令。
8. 小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'));// loadingconst 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 nodeconst 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
额,名字太简单了改下名字,就叫北斗cli吧 dipper-cli
修改下package继续发包,注意package的name和bin中的名称。
小技巧,大家可以先到npm.com中输入名字看下有没有相似的名字。
发布成功
五、参考文章
- 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
六、源码仓库地址
- dipper-cli: https://github.com/zyf118725/dipper-cli
- react模版-还在丰富中: https://github.com/zyf118725/reactTs
- 未来其他的模版也一并放在这个仓库。
七、踩过的坑
下班码字不易,如果喜欢请点赞关注,谢谢
其他
2024.4.16 号,进入热榜21,记录一下😄
感谢CSDN官方的推荐,粉丝量一下涨了好几十 😄,未来将持续产出高质量的文章,将枯燥的知识写的有趣生动。再远一点试试能否在退休之前写本书 🤠。