前言
搭建脚手架 基本已成为前端必备技能,无论是为公司基础建设 还是团队内部统一 都是一个很好的方向
脚手架的功能
脚手架的意义
快速创建项目,统一项目结构,基础建设
脚手架工作流程
- 全局安装cli 命令
- 选择配置项
- 生成代码
脚手架的搭建
下面我们来分步介绍如何搭建一个脚手架
1. 如何能全局安装自己的cli命令
全局安装命令 需要我们将代码发布成npm包 publish到仓库 就像vue create-react-app一样 通过npm intalll -g xx 来安装。然后vue/create-react-app 命令就已经在本地了,即可通过vue xxx 初始化工程 当然,也可以用npx create-react-app xxx 来直接初始化
搭建项目结构
pnpm init初始化package.json, 并修改对应的字段
{
"name": "arch-cli",
"version": "0.0.1",
"description": "arch-cli",
"keywords": [
"arch",
"material",
"init"
],
"bin": {
"arch-cli": "lib/bin.js" // 将arch-cli命令 指向lib/bin.js文件
},
"scripts": {
"dev": "tsc -w",
"build": "tsc",
"clean": "rm -rf lib",
"prepublishOnly": "npm run build"
},
"dependencies": {
"chalk": "^4.0.0",
"commander": "^9.4.1",
"fs-extra": "^11.1.0",
"inquirer": "^9.1.4"
},
"files": [
"lib"
],
"license": "MIT"
}
tsc --init初始化配置typescript
{
"compilerOptions": {
"declaration": true,
"skipLibCheck": true,
"esModuleInterop": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"module": "CommonJS",
"target": "es6",
"moduleResolution": "node",
"jsx": "preserve",
"lib": ["ES2020", "DOM", "ES2015"]
},
"exclude": ["node_modules"]
}
mkdir src && cd src && touch bin.ts新建src/bin.ts
#!/usr/bin/env node
console.log('hello arch-cli');
pnpm run dev && pnpm link --global将命令link到全局 方便本地调试 (注意:link的时候是根据package.json中的name字段来的)- 在命令行输入
arch-cli基本工作完成
目录结构
.
├── LICENSE
├── README.md
├── node_modules
│ ├── axios -> .pnpm/axios@1.2.1/node_modules/axios
│ ├── chalk -> .pnpm/chalk@4.1.2/node_modules/chalk
│ ├── commander -> .pnpm/commander@9.4.1/node_modules/commander
│ ├── fs-extra -> .pnpm/fs-extra@11.1.0/node_modules/fs-extra
│ └── inquirer -> .pnpm/inquirer@9.1.4/node_modules/inquirer
├── package.json
├── pnpm-lock.yaml
├── src
│ └── bin.ts
└── tsconfig.json
Q&A
- 执行
pnpm link --global时,怎么确定可以在本地使用命令了?
答: 可以用which arch-cli看看本地是否找得到命令
至于typescript 和 package.json 相关的配置暂不细说,根据上述代码直接配置即可,我们遇到问题后再慢慢展开
2. 交互设计
在第一步完成的基础上, 需要用户输入一些命令参数来帮助完成。这时候就需要用到一些第三方的package了,大致列举了一下常用的几个包名
| 包名 | 功能介绍 | |
|---|---|---|
| commander | 自定义命令行 | |
| inquirer | 命令行交互 | |
| chalk | 控制台输出美化 | |
| download-git-repo | 下载远程代码 | |
| ora | 控制台打印loading |
每个包的使用 这里就不一一列举了, 核心代码会贴在下面
src/bin.ts
#!/usr/bin/env node
import { program } from 'commander';
import chalk from 'chalk';
import { createApp } from './createApp';
const SUBCOMMANDS = ['create', 'init', 'version'];
program
.name('arch-cli')
.usage('[commands] [options]')
.arguments('<cmd>')
.action((cmd) => {
if (SUBCOMMANDS.indexOf(cmd) === -1) {
chalk.red('Invalid command...');
program.help();
}
});
program
.command('create <projectName> [destination]')
.description('create a new project')
.action((name) => {
console.log('name', name);
createApp(name);
});
program.parse(process.argv);
src/createApp.ts
import ora from 'ora';
import inquirer from 'inquirer';
import download from 'download-git-repo';
const createApp = async (projectName: string) => {
const question = [
{
type: 'list',
name: 'appType',
message: '你想创建什么类型的项目?',
choices: [
{ name: 'PC', value: 'PC' },
{ name: 'H5(未完成)', value: 'H5' },
],
},
];
const answer = await inquirer.prompt(question);
if (answer?.appType === 'PC') {
const spinner = ora({
spinner: 'soccerHeader',
prefixText: `loading ${answer?.appType} template`,
});
spinner.start('正在下载模板...');
download(
'harry118/typescript-react-template',
`${process.cwd()}/${projectName}`,
function (err: any) {
if (!err) {
spinner.succeed('下载成功,😁');
} else {
return spinner.fail(
'下载失败😭,确保你的网络连接正常,能访问github.com'
);
}
}
);
}
};
export { createApp };
注意事项
inquirer和chalk包都是安装的指定版本, 如果想升级最新包, 需要改成ESM的方式,新包不支持CommonJS导入 参考文章 可借机了解node commonjs esm typescript 结合开发时的来龙去脉- typescript几乎是项目必备了,开发前需要提前
pnpm run dev即时编译 - 该项目只是简单的走完基本流程,细节还有很多没有做。本来想贴一个github链接 想了下,自己亲自动手不是更好吗