解析create-vite,理解其工作流程

176 阅读2分钟

准备

  1. 将大佬准备好的代码拉下来

    git clone https://github.com/lxchuan12/vite-analysis.git
    
  2. 安装包管理工具和依赖

    cd vite-analysis/vite2
    # 体验一下pnpm
    npm i -g pnpm
    pnpm i
    
  3. 用vs code打开clone的代码,ctrl + shift + P, 输入Auto Attach 打开自动附加智能模式,这样调试一遍代码就知道 create-vite 是如何工作的了。

调试代码

我理解的npm create vite@lastest 干了啥:将你选定的模板复制到你命名的项目目录下,并告诉你这个项目怎么跑起来。

# 跑一下create-vite的入口文件,vs Code就自动进入调试模式了,按F10(单步跳过)查看代码如何执行的。
node vite2/packages/create-vite/index.js

下面逐行解释一下init 方法!

命令中获取项目存放位置目录和模板

let targetDir = formatTargetDir(argv._[0])
let template = argv.template || argv.t
const defaultTargetDir = 'vite-project' // 项目存放位置也是项目默认Project name
const getProjectName = () =>
    targetDir === '.' ? path.basename(path.resolve()) : targetDir

argv._[0] 获取项目要存放的位置,例:执行node vite2/packages/create-vite/index.js ./my-vite, 则argv._ 的值为['.\my-vite\'] 。调试项目时并未指定位置,也未指定模板,所以targetDir和template的值均为undefinedgetProjectName方法,如果是指定当前目录,则返回当前目录名称,否则返回targetDir

获取用户输入的Project name和选择的模板名称等

let result = {}
​
 try {
     result = await prompts(
         [
             // ......
         ]
     )
 } catch (cancelled) {
     console.log(cancelled.message)
     return
 }
​
// user choice associated with prompts
const { framework, overwrite, packageName, variant } = result

prompts方法会在终端和用户进行交互,最终拿到用户输入的值,下方是我选择的值:

image-20230111172412950.png

最终result的值: image-20230111172617947.png

由于我选的默认值vite-project也是一个空目录或者说是一个合法目录,不需要清空vite-project目录下的文件,所以result中没有overwrite属性。

image-20230111172905321.png

确定用户项目存放路径和用户选择的模板在vite中存放的位置

const root = path.join(cwd, targetDir)
// root: 'F:\vite-analysis\vite2\vite-project'// 创建root目录,就是用户创建项目的那个目录
if (overwrite) {
    emptyDir(root)
} else if (!fs.existsSync(root)) {
    fs.mkdirSync(root, { recursive: true })
}
​
// determine template
template = variant || framework || template
​
console.log(`\nScaffolding project in ${root}...`)
​
// import.meta.url返回当前模块的 URL 路径。
// 最终拿到vite中模板存放目录
// templateDir: F:\vite-analysis\vite2\packages\create-vite\template-react-ts
const templateDir = path.resolve(
    fileURLToPath(import.meta.url),
    '..',
    `template-${template}`
)

将vite中的模板复制到用户创建的目录

// copy 文件
const write = (file, content) => {
    const targetPath = renameFiles[file]
    ? path.join(root, renameFiles[file])
    : path.join(root, file)
    if (content) {
        fs.writeFileSync(targetPath, content)
    } else {
        copy(path.join(templateDir, file), targetPath)
    }
}
​
const files = fs.readdirSync(templateDir)
// files: (8) ['index.html', 'package.json', 'public', 'src', 'tsconfig.json', 'tsconfig.node.json', 'vite.config.ts', '_gitignore']
// 这里复制了除了package.json的其他所有文件,因为要修改package.json的内容,所以没有直接复制过去
for (const file of files.filter((f) => f !== 'package.json')) {
    write(file)
}

复制package.json文件

// 读取package.json
const pkg = JSON.parse(
    fs.readFileSync(path.join(templateDir, `package.json`), 'utf-8')
)
// 修改name
pkg.name = packageName || getProjectName()
// 复制package.json
write('package.json', JSON.stringify(pkg, null, 2))

确定用户用的哪个包管理工具

const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent)
const pkgManager = pkgInfo ? pkgInfo.name : 'npm'console.log(`\nDone. Now run:\n`)
if (root !== cwd) {
    console.log(`  cd ${path.relative(cwd, root)}`)
}
switch (pkgManager) {
    case 'yarn':
        console.log('  yarn')
        console.log('  yarn dev')
        break
    default:
        console.log(`  ${pkgManager} install`)
        console.log(`  ${pkgManager} run dev`)
        break
}

image-20230112090558701.png

到此,init函数走完,项目创建完成。