在当下框架繁多,工具链也繁多的情况下,每一条工具链都存在着各自创建项目的方式,并且每个工具链的能提供的能力不胜枚举,如果作为一个独立的工具它们都是优秀的,而如果我们平时要用到多种技术栈,对于"创建一个项目"这个具体的动作,每一个工具都有自己的想法,且它们又一直在迭代和改变打开方式,无形中给我们增加了一些记忆的负担。
缘何?
盘点一下目前主流的框架/技术栈创建一个项目的方式:
umi
创建一个简单项目,部分:
- pnpm dlx create-umi@latest 4版本
- bunx create-umi 4版本
- npx create-umi@latest 4版本
- yarn create umi 4版本
- yarn create @umijs/umi-app 3版本
- 其它...
vite
创建一个项目,部分:
- npm create vite@latest my-vue-app --template vue
- npm create vite@latest my-vue-app -- --template vue-ts
- npm create vite@latest my-react-ts-app -- --template react-ts
- npx degit user/project my-project
- 其它...
好复杂,我懒得记!
企业内部一般有更好的脚手架,可以忽略这种场景。
归纳
其实大部分工具链提供的项目模板主要分为3类:
- 简易的一个框架的项目
- 附加某个UI框架的项目
- 社区提供的项目
对于包管理工具而言:
每个工具链对npm
、yarn
、bun
、npx
的支持程度也不一样。
盘点需求
使用者的视角:
我想"统一 "它们的打开方式,我不管你什么用
npm
、yarn
、bun
还是什么,也不管你预制了一堆什么,我既不想记你提供基础命令,也嫌你提供的命令太长!
那么就瞄准这个"痛点",通过简单的方式开启我们"快速创建一个项目"这一需求。
数据结构
平台分类
ts
type TPlatform = 'umi' | 'vite' // ... 省略其它
项目类型
ts
type TCmdInfo = {
cmd: string;
desc: string;
};
type TProjectType = {
simple: TCmdInfo[];
template?: TCmdInfo[];
other?: TCmdInfo[];
};
整合后
typescript
type TInitMethod = {
[key in TPlatform]: TProjectType;
};
最终形态
typescript
const umi: TProjectType = {
simple: [
{
cmd: 'pnpm dlx create-umi@latest',
desc: '用pnpm创建一个umi4的项目',
},
// 省略部分...
],
template: [
{
cmd: 'pnpm create umi --template electron',
desc: '用pnpm创建一个umi-electron的项目',
},
// 省略部分...
],
};
const vite: TProjectType = {
simple: [
{
cmd: 'npm create vite@latest',
desc: '创建一个简单的vite项目',
},
// 省略部分...
],
template: [
{
cmd: 'npm create vite@latest my-vue-app --template vue',
desc: '创建一个vue项目,适用于npm@6',
},
// 省略部分...
],
};
const methodsOptions: TInitMethod = {
umi: umi,
vite: vite,
};
工具开发
认识ink
此次我们通过一个有趣的CLI
工具(ink
)实现这个个性化需求,它属于是更"上层"的实现,以React
组件的形式完成CLI
工具的开发,体验很不错。
下面简单介绍一下ink
的用法:更多有趣的打开方式见 传送👉 ink
此次要用到的主要是Select
组件,如以下形态及交互方式:
以上界面及交互对应的代码如下:来源
jsx
import React from 'react';
import {render} from 'ink';
import SelectInput from 'ink-select-input';
const Demo = () => {
const handleSelect = item => {
// 一些逻辑
};
const items = [
{
label: 'First',
value: 'first'
},
{
label: 'Second',
value: 'second'
},
{
label: 'Third',
value: 'third'
}
];
return <SelectInput items={items} onSelect={handleSelect} />;
};
render(<Demo />);
主要逻辑
根据上面梳理的需求及数据结构,为了最大化的简化操作,将整体的操作分为三步,即:选择平台 -> 选择项目类型 -> 选择具体项目
三步都是选择, 完全的减少了使用负担么不是(hahaha)
因此基于上述三步均可采用Select
组件,对其进行简单的封装后代码如下:
jsx
interface ISelect {
// 由外部传入
options: TSelectOptions;
onChange: (value: string) => void;
visible: boolean;
}
const Select = (props: ISelect) => {
const {options, onChange, visible} = props;
const handleSelect = (item: any) => {
typeof onChange === 'function' && onChange(item.value);
};
return (
<SelectInput
isFocused={visible}
items={options}
onSelect={handleSelect}
itemComponent={item => {
return <Text color={'cyan'}>{item.label}</Text>;
}}
/>
);
};
而最终的目的其实是想要得到具体的的命令,并快速的创建一个项目,在得到一个命令后采用clipboardy
将其复制到剪切板就可以啦。
jsx
// 省略前置逻辑
const handleCopyScript = (script: string) => {
clipboard.write(script).then(() => {
console.log('已复制到剪切板');
// 此时就可以愉快次Ctrl + V 去创建你想要的项目啦
});
};
到此基本结束!
最后
我承认结束地有点突然!
起初想一个做强大的工具发布到npm
公开使用,但做的过程中发觉其实当下前端生态已经极其庞大了,将所有工具的都整合显然不符合思考的初衷,同时众口难调难以满足绝大多数人的需求,因此考虑再三还是只分享一个思路吧。
目前实现了基础的CLI
框架结构,还不是很复杂,如果做成一个完整的CLI
工具应该有的样子就不利于二次改造,所以分享的是针对这一个问题(痛点)的思考及对应的解决方式,感兴趣的朋友可以CV对应的代码做一个自己心目中最简单的工具。
此需求不是人人都有,可能只有像我这种"懒人"才会考虑,所以此文更多的成分是分享一种基于开发体验的思考,希望带来一定的共鸣。
如果你跟我一样"懒"且有同样的需求,欢迎戳👇文末的【工具源码】获取代码进行二次开发。