likes
comments
collection
share

能不能不记命令也能愉快的创建一个项目?

作者站长头像
站长
· 阅读数 10

在当下框架繁多,工具链也繁多的情况下,每一条工具链都存在着各自创建项目的方式,并且每个工具链的能提供的能力不胜枚举,如果作为一个独立的工具它们都是优秀的,而如果我们平时要用到多种技术栈,对于“创建一个项目”这个具体的动作,每一个工具都有自己的想法,且它们又一直在迭代和改变打开方式,无形中给我们增加了一些记忆的负担。

缘何?

盘点一下目前主流的框架/技术栈创建一个项目的方式:

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框架的项目
  • 社区提供的项目

对于包管理工具而言:

每个工具链对npmyarnbunnpx的支持程度也不一样。

盘点需求

使用者的视角:

我想“统一”它们的打开方式,我不管你什么用npmyarnbun还是什么,也不管你预制了一堆什么,我既不想记你提供基础命令,也嫌你提供的命令太长!

那么就瞄准这个“痛点”,通过简单的方式开启我们“快速创建一个项目”这一需求。

数据结构

平台分类

type TPlatform = 'umi' | 'vite' // ... 省略其它

项目类型

type TCmdInfo = {
  cmd: string;
  desc: string;
};

type TProjectType = {
  simple: TCmdInfo[];
  template?: TCmdInfo[];
  other?: TCmdInfo[];
};

整合后

type TInitMethod = {
	[key in TPlatform]: TProjectType;
};

最终形态

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组件,如以下形态及交互方式:

能不能不记命令也能愉快的创建一个项目?

以上界面及交互对应的代码如下:来源

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组件,对其进行简单的封装后代码如下:

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将其复制到剪切板就可以啦。

// 省略前置逻辑
const handleCopyScript = (script: string) => {
  clipboard.write(script).then(() => {
    console.log('已复制到剪切板');
    // 此时就可以愉快次Ctrl + V 去创建你想要的项目啦
  });
};

到此基本结束!

最后

我承认结束地有点突然!

起初想一个做强大的工具发布到npm公开使用,但做的过程中发觉其实当下前端生态已经极其庞大了,将所有工具的都整合显然不符合思考的初衷,同时众口难调难以满足绝大多数人的需求,因此考虑再三还是只分享一个思路吧。

目前实现了基础的CLI框架结构,还不是很复杂,如果做成一个完整的CLI工具应该有的样子就不利于二次改造,所以分享的是针对这一个问题(痛点)的思考及对应的解决方式,感兴趣的朋友可以CV对应的代码做一个自己心目中最简单的工具。

此需求不是人人都有,可能只有像我这种“懒人”才会考虑,所以此文更多的成分是分享一种基于开发体验的思考,希望带来一定的共鸣。

如果你跟我一样"懒"且有同样的需求,欢迎戳👇文末的【工具源码】获取代码进行二次开发。

资料