相信大家在前端开发中都使用过很多前端脚手架,如vue-cli,create-vite,create-vue等;本篇文章将会为大家详细介绍这些前端脚手架是如何实现的,并且从零实现一个create-kitty脚手架发布到npm上。

pnpm搭建Monorepo环境

使用pnpm管理对项目进行管理是非常方便的,极大的解决了多个包在本地调试繁琐的问题。

什么是Monorepo?

就是指在一个大的项目仓库中,管理多个模块/包(package),这种类型的项目大都在项目根目录下有一个packages文件夹,分多个项目管理。大概结构如下:

-- packages
-- pkg1
--package.json
-- pkg2
--package.json
--package.json

简单来说就是单仓库 多项目

目前很多我们熟知的项目都是采用这种模式,如Vant,ElementUI,Vue3等。打造一个Monorepo环境的工具有很多,如:lerna、pnpm、yarn等,这里我们将使用pnpm来开发我们的UI组件库。

使用pnpm

安装

首先新建文件夹kitty-ui;然后执行

npm install pnpm -g

初始化package.json

pnpm init

新建配置文件 .npmrc

shamefully-hoist = true

这里简单说下为什么要配置shamefully-hoist

如果某些工具仅在根目录的node_modules时才有效,可以将其设置为true来提升那些不在根目录的node_modules,就是将你安装的依赖包的依赖包的依赖包的...都放到同一级别(扁平化)。说白了就是不设置为true有些包就有可能会出问题。

monorepo的实现

接下就是pnpm如何实现monorepo的了。

为了我们各个项目之间能够互相引用我们要在根目录下新建一个pnpm-workspace.yaml文件将我们的包关联起来

packages:
- 'packages/**'

这样就能将我们项目下的packages目录下的所有包关联起来了,当然如果你想关联更多目录你只需要往里面添加即可。根据上面的目录结构很显然你在根目录下新packages文件夹,packages文件夹新建create-kitty以及测试用的cli-demo文件夹,并分别执行pnpm init

脚手架简单实现

create-kittypackage.json中新增bin属性然后指向index.js。这里去掉了main属性,因为我们开发的不是工具包。

{
"name": "create-kitty",
"version": "1.0.0",
"description": "",
"bin": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

当然,bin也支持对象格式,让脚手架可以使用多个命令

"bin": {
"create-kitty": "index.js",
"create-app": "index.js"
},

新建create-kitty/index.js作为脚手架入口

#! /usr/bin/env node

console.log("hello kitty")

注意cli入口文件需要加#! /usr/bin/env node指定脚本的解释程序为node,否则会报错

本地测试脚手架

来到测试包cli-demo文件夹下直安装create-kitty

pnpm add create-kitty

然后就会发现cli-demo下的package出现依赖,同时加上scripts

{
"name": "cli-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"create-kitty": "create-kitty"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"create-kitty": "workspace:^1.0.0"
}
}

然后执行pnpm run create-kitty

说明

当我们上线的时候可以直接执行npm create kitty或者npm init kitty,它们其实就是执行npx create-kitty

解析用户输入参数

一般脚手架都会根据用户输入不同命令来做出不同操作,比如用户输入kitty-ui -v提示版本号,kitty-ui -h显示帮助信息等。而实现这些基本原理就是process.argv

create-kitty下的入口文件index.js修改为

#! /usr/bin/env node

console.log(process.argv);

然后执行这个文件并带几个参数

然后会发现用户传的参数在process.argv数组的第二位以后,这样我们就可以根据用户输入不同参数做不同操作了。但是这样很不方便,所以我们可以使用框架来解决。

使用第三方库解析参数

首先安装command-line-args

pnpm add command-line-args -S

接下来看它是如何获取用户输入参数的

#! /usr/bin/env node
import commandLineArgs from 'command-line-args'; //配置命令参数
const optionDefinitions = [
{ name: 'version', alias: 'v', type: Boolean },
{ name: 'arg1', type: String },
{ name: 'arg2', type: Number },
];
const options = commandLineArgs(optionDefinitions);
console.log(options);

注意这里使用了es6的语法,所以需要将package.json中的type指定为module

接下来实战一些,检测用户输入--help指令,然后我们输出一个帮助的配置。

首先安装command-line-usage

pnpm add command-line-usage -S

首先引入commandLineUsage,然后写个配置

import commandLineUsage from 'command-line-usage';
//帮助内容
const helpSections = [
{
header: 'create-kitty',
content: '一个快速生成组件库搭建环境的脚手架',
},
{
header: 'Options',
optionList: [
{
name: 'version',
typeLabel: '{underline boolean}',
description: '版本号',
},
{
name: 'arg1',
typeLabel: '{underline string}',
description: '参数1',
},
{
name: 'arg2',
typeLabel: '{underline number}',
description: '参数2',
},
],
},
];

command-line-args中配置个help命令,当获取到help的时候直接打印

const optionDefinitions = [
{ name: 'help', alias: 'h', type: Boolean },
{ name: 'version', alias: 'v', type: Boolean },
{ name: 'arg1', type: String },
{ name: 'arg2', type: Number },
];
const options = commandLineArgs(optionDefinitions);
if (options.help) console.log(commandLineUsage(helpSections))

但是这样好像不太好看。这里可以借助chalk工具给打印加个颜色

pnpm add chalk -S

我比较喜欢绿色,所以让控制台输出绿色的字体

import chalk from 'chalk';

...

if (options.help) console.log(chalk.green(commandLineUsage(helpSections)))

当然它还有很多用法,感兴趣可以自己去研究。到这里关于用户参数解析部分差不多就结束了,下面介绍如何实现交互式命令

交互式命令

当我们使用脚手架时,有些会提示我们输入项目名称和选择模板等,比如执行npm create vite的时候

如果只是简单交互可以通过node自带的process.stdin或者readline模块实现。比如用readline实现一个简单的交互式命令

import readline from 'readline'
... const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question('你是谁?', function (anwser) {
console.log(`我是${anwser}`);
//添加close事件,不然不会结束
rl.close();
});

但是这里并不打算用原生的交互,而是使用prompts工具实现。首先安装

pnpm add prompts -S

然后模拟一个交互式表单,其中又输入文字,密码,单选,多选

import prompts from 'prompts'
const promptsOptions = [
{
type: 'text',
name: 'user',
message: '用户'
},
{
type: 'password',
name: 'password',
message: '密码'
},
{
type: 'select',//单选
name: 'gender',
message: '性别',
choices: [
{ title: '男', value: 0 },
{ title: '女', value: 1 }
]
},
{
type: 'multiselect', //多选
name: 'study',
message: '选择学习框架',
choices: [
{ title: 'Vue', value: 0 },
{ title: 'React', value: 1 },
{ title: 'Angular', value: 2 }
]
},
] const getInputInfo = async () => {
const res = await prompts(promptsOptions)
console.log(res)
}
getInputInfo()

然后执行看下效果

我们拿到用户输入的表单即可执行相应的操作。

拉取模板

当我们获取到用户输入或选择的信息的时候,我们就可以输出对应模板给用户。这里有两种选择,一种是直接将模板放在包里,另一种是远程拉取git仓库。本篇文章暂时先介绍第二种方式

首先安装download-git-repo,使用它的clone方法

pnpm add download-git-repo -S

然后安装一个loading插件ora

pnpm add ora -S

写一个clone函数(这里便于文章讲解全都写在一个文件里,后续会进行模块的规划)

const gitClone = (remote, name, option) => {
const downSpinner = ora('正在下载模板...').start();
return new Promise((resolve, reject) => {
download(remote, name, option, err => {
if (err) {
downSpinner.fail();
console.log("err", chalk.red(err));
reject(err);
return;
};
downSpinner.succeed(chalk.green('模板下载成功!'));
resolve();
});
});
};

定义远程仓库模板列表以及分支并且对项目名称进行一些校验,然后在获取到用户选择信息后执行gitclone

const remoteList = {
1: "https://gitee.com/geeksdidi/kittyui.git",
2: 'xxx'
}
const branch = 'master'
const getInputInfo = async () => { const res = await prompts(promptsOptions)
if (!res.name || !res.template) return
gitClone(`direct:${remoteList[res.template]}#${branch}`, res.name, { clone: true })
}
getInputInfo()

然后增加一些个性化校验

const promptsOptions = [
{
type: 'text',//单选
name: 'name',
message: 'project-name',
validate(val) {
if (!val) return '模板名称不能为空!';
if (fs.existsSync(res.name)) return '项目名已存在'
if (val.match(/[^A-Za-z0-9\u4e00-\u9fa5_-]/g)) return '模板名称包含非法字符,请重新输入';
return true;
}
}, {
type: 'select',//单选
name: 'template',
message: 'select a framework',
choices: [
{ title: 'kitty-ui', value: 1 },
{ title: 'kitty-demo', value: 2 }
]
}
]

最后看下执行效果

clone成功后加上提示告知用户如何使用,然后进行发布

const gitClone = (remote, name, option) => {
const loadingOra = ora('正在下载模板...').start();
return new Promise((resolve, reject) => {
download(remote, name, option, err => {
if (err) {
loadingOra.fail();
console.log("err", chalk.red(err));
reject(err);
return;
};
loadingOra.succeed(chalk.green('success'));
console.log(`Done. Now run:\r\n`);
console.log(chalk.green(`cd ${name}`));
console.log(chalk.blue("npm install"));
console.log("npm run dev\r\n");
resolve();
});
});
};

发布

一般来说发布前要进行打包生成commonjs以支持低版本node,这里暂时不进行打包操作,直接执行pnpm publish即可完成发布(注意,pnpm发布时需要先将代码提交到远程仓库)。

使用

随便新建文件夹然后执行npm init kitty或者npm create kitty

同时文件中出现了我们的模板文件

总结

本篇文章介绍了如何使用pnpm搭建Monorepo环境以及本地开发包的测试。同时讲解了如何使用command-line-args,download-git-repo,prompts等工具的使用,最终实现了一个交互式的命令行的脚手架create-kitty

后面会对create-kitty进行完善,让它成为一个可以快速搭建vue3组件开发环境的脚手架,如果你对组件库开发感兴趣的话,可以关注我或者我的公众号 web前端进阶,组件库的所有实现细节都在这个里。

如果这篇文章对你有所帮助的话,点个赞吧!

源码地址

create-kitty

从0搭建vue3组件库: 如何完整搭建一个前端脚手架?的更多相关文章

  1. 从0搭建Vue3组件库:button组件

    button组件几乎是每个组件库都有的:其实实现一个button组件是很简单的.本篇文章将带你一步一步的实现一个button组件.如果你想了解完整的组件库搭建,你可以先看使用Vite和TypeScri ...

  2. 从0搭建vue3组件库:Shake抖动组件

    先看下效果 其实就是个抖动效果组件,实现起来也非常简单.之所以做这样一个组件是为了后面写Form表单的时候会用到它做一个规则校验,比如下面一个简单的登录页面,当点击登录会提示用户哪个信息没输入,当然这 ...

  3. 从0搭建vue3组件库:自动化发布、管理版本号、生成 changelog、tag

    今天看到一篇文章中提到了一个好用的工具release-it.刚好可以用在我正在开发的vue3组件库.纸上得来终觉浅,绝知此事要躬行,说干就干,下面就介绍如何将release-it应用到实际项目中,让组 ...

  4. 从0搭建vue3组件库: Input组件

    本篇文章将为我们的组件库添加一个新成员:Input组件.其中Input组件要实现的功能有: 基础用法 禁用状态 尺寸大小 输入长度 可清空 密码框 带Icon的输入框 文本域 自适应文本高度的文本域 ...

  5. Vite+TS带你搭建一个属于自己的Vue3组件库

    theme: nico 前言 随着前端技术的发展,业界涌现出了许多的UI组件库.例如我们熟知的ElementUI,Vant,AntDesign等等.但是作为一个前端开发者,你知道一个UI组件库是如何被 ...

  6. 开箱即用 yyg-cli(脚手架工具):快速创建 vue3 组件库和vue3 全家桶项目

    1 yyg-cli 是什么 yyg-cli 是优雅哥开发的快速创建 vue3 项目的脚手架.在 npm 上发布了两个月,11月1日进行了大升级,发布 1.1.0 版本:支持创建 vue3 全家桶项目和 ...

  7. 使用web-component搭建企业级组件库

    组件库的现状 前端目前比较主流的框架有react,vuejs,angular等. 我们通常去搭建组件库的时候都是基于某一种框架去搭建,比如ant-design是基于react搭建的UI组件库,而ele ...

  8. Vue3 企业级优雅实战 - 组件库框架 - 1 搭建 pnpm monorepo

    前两篇文章分享了基于 vite3 vue3 的组件库基础工程 vue3-component-library-archetype 和用于快速创建该工程的工具 yyg-cli,但在中大型的企业级项目中,通 ...

  9. Vue3 企业级优雅实战 - 组件库框架 - 3 搭建组件库开发环境

    前文已经初始化了 workspace-root,从本文开始就需要依次搭建组件库.example.文档.cli.本文内容是搭建 组件库的开发环境. 1 packages 目录 前面在项目根目录下创建了 ...

随机推荐

  1. 一文详解 implementation api embed

    最近使用 Android Studio 从事项目开发时,发现对 implementation.api.embed 的用法了解的不是很清楚,这里准备一篇文章对其使用场景或者说是使用方式进行一个总结. d ...

  2. which 和 that 在定语从句中作介词宾语的用法

    关系代词在定语从句中作介词的宾语,且介词在关系代词之前时,关系代词应该用 which:介词在定语从句句末,关系代词可以用 that 或 which. (一)He teaches in a school ...

  3. 记一次有意思的 SQL 实现 → 分组后取每组的第一条记录

    开心一刻 今天,朋友气冲冲的走到我面前 朋友:我不是谈了个女朋友,谈了三个月嘛,昨天我偷看她手机,你猜她给我备注什么 我:备注什么? 朋友:舔狗 2 号! 我一听,气就上来了,说道:走,找她去,这婆娘 ...

  4. P5384[Cnoi2019]雪松果树 (长链剖分)

    题面 一棵以 1 1 1 为根的 N N N 个节点的有根树, Q Q Q 次询问,每次问一个点 u u u 的 k k k 级兄弟有多少个(第 k k k 代祖先的第 k k k 代孩子),如果没有 ...

  5. Dapr中国社区活动之 分布式运行时开发者日 (2022.09.03)

    自2019年10月首次发布以来,Dapr(Distributed Application Runtime,分布式应用运行时)因其"更稳定"."更可靠".&quo ...

  6. 如何用 refcursor 返回结果集

    可以通过返回 Refcursor 类型的函数,或者out 类型的函数或 procedure 返回结果集. 一.返回refcursor 类型的函数 create or replace function ...

  7. KingbaseES 支持pivot and unpivot 功能

    KingbaseES 通过扩展插件支持了pivot 和unpivot 功能.以下以例子的方式介绍. 一.功能介绍 创建扩展: create extension kdb_utils_function; ...

  8. Windows客户端DNS工作原理

    通常大家对Windows客户端DNS的配置存在多个误区. 误区一,配置1个内网DNS,1个外网DNS.解析内网的时候用内网DNS,解析外网的时候用外网. 电脑怎么知道哪个是内网?哪个是外网?我们内部的 ...

  9. 《Win10——如何进入高级启动选项》

    Win10--如何进入高级启动选项       第一种方法: 管理员命令提示符输入如下代码,自动重启并进入高级启动选项. shutdown /r /o /f /t 00     第二种方法: 1. 管 ...

  10. Kubernetes 配置管理

    ConfigMap(可变配置管理) 对于应用的可变配置在 Kubernetes 中是通过一个 ConfigMap 资源对象来实现的,我们知道许多应用经常会有从配置文件.命令行参数或者环境变量中读取一些 ...