手写一个自己的前端脚手架
简述
很多人一直很纠结什么是脚手架?其实核心功能就是创建项目初始文件,那问题又来了,市面上的脚手架不够用?为什么还要自己写?
一提到脚手架,你就会想到:vite,vue-cli,create-react-app,dva-cli…他们的特点就是专一!但是在实际开发场景中,你会发现有以下一系列的问题:
- 业务类型多
- 多次造轮子,无法统一
- 公司代码规范,无法统一
很多时候我们开发时需要新建项目,把已有项目代码复制一遍,保留基础能力。(但是这个过程非常琐碎而又耗时)。那我们可以自己定制化模板,实现一个属于自己的脚手架。
一.常用模块
- typescript:将TS代码转义成js代码
- commander:参数解析–help其实就借助了他
- inquirer:交互式命令行工具,有他就可以实现命令行的选择功能
- chalk:粉笔帮我们在控制台中画出各种各样的颜色
- ini:配置文件ini格式解析器和序列化器
- ora:实现命令行loading
- nodemon:监控文件变化执行编译命令
二.开发环境搭建
npm init
npm install commander inquirer chalk ini ora
npm install @type/node typescript nodemon --save-dev
用当前系统的node来执行文件
#! usr/bin/env node
输入命令后,会有在一个新建的shell中执行指定的脚本,在执行这个脚本的时候,我们需要来指定这个脚本的解释程序是node。/usr/bin/env
就是告诉系统可以在PATH目录中查找。 所以配置#!/usr/bin/env node
, 就是解决了不同的用户node路径不同的问题,可以让系统动态的去查找node来执行你的脚本文件。
链接全局包
npm link
临时的链接到全局下,将全局的包链接到本地(临时的),可以方便我调试我们写的包
在package.json
文件里加入字段
"bin":{
"项目启动命令名":"项目位置"
},
ts配置
为了编写ESModule风格的node.js程序,对ts进行以下配置
{
"compilerOptions": {
"target": "es6", //编译成es6代码
"module": "nodenext", //模块选择es6
"outDir": "bin", //输出到bin目录下
"moduleResolution": "nodenext", //模块解析方式
"esModuleInterop": true, //模块转换
"resolveJsonModule": true, //允许解析json
"rootDir": "src", //根目录src,就是将src目录下的内容输出到bin下
"baseUrl": "./src"
},
"include": ["src"], //src为原代码目录
"exclude": ["node_modules"]
}
利用nodemon来进行监控文件
新建nodemon.json文件
{
"watch":["src"],
"ext":"ts",
"exec":"npm run build”
}
表示监控src目录下的所有ts文件,去帮我们执行npm run build命令
或者可以在package.json文件中添加
"scripts": {
"build":"tsc"
},
然后使用tsc
可以直接执行文件,上面nodemon可以改为
{
"watch":["src"],
"ext":"ts",
"exec":"tsc"
}
至此一个基础的开发环境已经做好了
三.解析命令行参数
下面开始开始进行解析命令行传递的参数
使用commander来进行解析
直接解析用户参数
program.parse(process.argv);
但是这个时候我们发现了问题:我们没有自定义解析用户参数
比如我们想要获取到version;
slivue --version //这里以slivue为项目的启动命令
我们是获取不到项目的版本号的,所以我们需要自定义一个;
首先,你需要获取到package.json文件的路径,这里我们使用ES6来实现
import { createRequire } from "module";
import { dirname, join } from "path";
import { fileURLToPath } from "url";
//以当前的文件路径做到一个require
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const require = createRequire(import.meta.url);
const pkg = require(join(__dirname, "../package.json"));
自定义指令
然后就需要开始用 commander来自定义指令了
这里就举个简单的例子:
//1.通过脚手架来创建一个项目 create(拉取仓库中的模板)
//2.配置拉取的信息,配置系统文件 config
program
.command("create <project-name>")
.description("create a project")
.option("-f, --force", "overwrite taget diretory")
.action((name, option) => {
console.log(name, option);
//内容自己实现
});
还比如基础的config:
//slivue config --set a 1
program
.command("config [value]")
.description("inspect config")
.option("-g, --get <path>", "get value")
.option("-s, --set <path> <value>", "set value")
.option("-d, --delete<path>", "delete value")
.action(async (value, option) => {
(await import("./commands/config.js")).default(value, option);
});
四.拉取项目模板
接下来就到了核心部分了:
先新建一个仓库,在新建仓库里搭建一个你需要的项目模板
然后使用第三方库download-git-repo 下载模板
当然这里默认是使用github,gitee的话目前不支持直接应用,需要去git官方里查询
npm i download-git-repo
然后使用download函数
const downloadUrl = "https://github.com:仓库地址#分支名";
download(downloadUrl, name, { clone: true }, (error) => {
if (error) {
console.log(`创建项目失败:${name}`);
console.log("失败原因:", error);
} else {
console.log(`成功创建项目:${name}`);
}
});
需要注意的是这里downloadUrl的格式,可以理解为仓库地址 + #分支名,但需要将仓库地址中https://github.com后面的 '/ '换成 ‘:’ 否则你就会发现一直报128的错误~(我就被这坑过) 128错误的发生还出现在在相同目录下创建相同名字的项目
现在我们就可以切到自己期望的目录下去之星我们创建项目的命令,
至此我们的基本功能就已经实现。
后记:
后续又添加了config
模块的具体实现,主要就是获取用户本地的配置文件然后去合并覆盖。