前端从零到一搭建脚手架并发布到npm

张三疯ts 2024-07-02 09:33:04 阅读 63

这里写自定义目录标题

一、为什么需要脚手架?二、前置-第三方工具的使用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.js3. 添加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 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')

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

// 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 loginnpm publish

在这里插入图片描述

额,名字太简单了改下名字,就叫北斗cli吧 dipper-cli

修改下package继续发包,注意package的name和bin中的名称。

小技巧,大家可以先到npm.com中输入名字看下有没有相似的名字。

在这里插入图片描述

发布成功

在这里插入图片描述

五、参考文章

https://blog.csdn.net/gao_xu_520/article/details/120505635掘金-工具详解:https://juejin.cn/post/7077717940941881358commander中文文档(github):https://github.com/tj/commander.js/blob/master/Readme_zh-CN.md

六、源码仓库地址

dipper-cli: https://github.com/zyf118725/dipper-clireact模版-还在丰富中: https://github.com/zyf118725/reactTs未来其他的模版也一并放在这个仓库。

七、踩过的坑

在这里插入图片描述

1.注意npm包的版本。

万年大坑,好多同学跟着博客一路敲代码,结果一启动就报错,简直怀疑人生。

注意,这可能就是依赖包版本的问题。比如commander 最新版用的是es模块化方式,而大多数博客上用的的v9.0版本,博主也没注意提醒版本问题,结果就是一启动就报错。

2.npm包名导致的npm发包失败问题

如果npm包的名称有重复或者类似,注意名字类似也会提交失败。

一个小妙招,大家起名字前可以先到 https://www.npmjs.com/ 网站上搜索下有没有类似的名称,省的后续再改名。

在这里插入图片描述

下班码字不易,如果喜欢请点赞关注,谢谢

在这里插入图片描述


其他

2024.4.16 号,进入热榜21,记录一下😄

在这里插入图片描述

感谢CSDN官方的推荐,粉丝量一下涨了好几十 😄,未来将持续产出高质量的文章,将枯燥的知识写的有趣生动。再远一点试试能否在退休之前写本书 🤠。

在这里插入图片描述



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。