create-vite
的源码很简单,只有一个文件,代码总行数400
左右,但是实际需要阅读的代码大约只有200
行左右,废话不多说,直接开始吧。
代码结构
create-vite
的代码结构非常简单,直接将index.ts
拉到最底下,发现只执行了一个函数init()
:
init().catch((e) => {console.error(e)
})
我们的故事将从这里开始。
init()
init()
函数的代码有点长,但是实际上也不复杂,我们先来看看它最开头的两行代码:
async function init() {const argTargetDir = formatTargetDir(argv._[0])const argTemplate = argv.template || argv.t
}
首先可以看到init
函数是一个异步函数,最开始的两行代码分别获取了argv._[0]
和argv.template
或者argv.t
;
这个
argv
是怎么来的,当然是通过一个解析包来解析的,在顶部有这样的一段代码:const argv = minimist(process.argv.slice(2), { string: ['_'] })
就是这个
minimist
包,它的作用就是解析命令行参数,感兴趣的可以自行了解,据说这个包也是百来行代码。
继续往下,这两个参数就是我们在执行create-vite
命令时传入的参数,比如:
create-vite my-vite-app
那么argv._[0]
就是my-vite-app
;
如果我们执行的是:
create-vite my-vite-app --template vue
那么argv.template
就是vue
。
argv.t
就是argv.template
的简写,相当于:
create-vite my-vite-app --t vue
# 等价于
create-vite my-vite-app --template vue
![](https://i-blog.csdnimg.cn/blog_migrate/26640376607f39f052c502e165d159f1.png)
通过打断点的方式,可以看到结果和我们预想的一样。
formatTargetDir(argv._[0])
就是格式化我们传入的目录,它会去掉目录前后的空格和最后的/
,比如:
formatTargetDir(' my-vite-app ') // my-vite-app
formatTargetDir(' my-vite-app/') // my-vite-app
这个代码很简单,就不贴出来了,继续往下:
let targetDir = argTargetDir || defaultTargetDir
targetDir
是我们最终要创建的目录,defaultTargetDir
的值是vite-project
,如果我们没有传将会用这个值来兜底。
紧接着后面跟着一个getProjectName
的函数,通常来讲这种代码可以跳过先不看,但是这里的getProjectName
函数有点特殊;
const getProjectName = () =>targetDir === '.' ? path.basename(path.resolve()) : targetDir
它会根据targetDir
的值来判断我们的项目是不是在当前目录下创建的,如果是的话,就会返回当前目录的名字,比如:
create-vite .
![](https://i-blog.csdnimg.cn/blog_migrate/145ca86f571183033a2b39147af58b49.png)
可以看到如果项目名称传的是.
,那么getProjectName
函数就会返回当前目录的名字,也就是create-vite
(根据自己的情况而定);
不看源码还真不知道这里还可以这么用,继续往下,就是定义了一个问题数组:
result = await prompts([])
这个prompts
函数是一个交互式命令行工具,它会根据我们传入的问题数组来进行交互,就比如源码中,一共列出了6个问题:
projectName
:项目名称overwrite
:是否覆盖已存在的目录overwriteChecker
:检测覆盖的目录是否为空packageName
:包名framework
:框架variant
:语言
当执行create-vite
命令时,后面不跟着任何参数,而且我们一切操作都是合规的,那么只会经历三个问题:
projectName
:项目名称framework
:框架variant
:语言
![](https://i-blog.csdnimg.cn/blog_migrate/4d6bd855555f631c709e28a81e213207.png)
projectName
:项目名称
配置项如下:
var projectName = {type: argTargetDir ? null : 'text',name: 'projectName',message: reset('Project name:'),initial: defaultTargetDir,onState: (state) => {targetDir = formatTargetDir(state.value) || defaultTargetDir}
}
先来简单介绍一个每一个配置项的含义:
type
:问题的类型,这里的null
表示不