【第9期】create-vue —— 极速创建 vue 项目

263 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情

摘要

今天学习的源码内容是create-vue,一个基于 vite 创建 vue 项目的脚手架。就好比之前常用的 Vue CLI 是基于 webpack 创建 vue 项目的。

create-vue 初体验

首先我们要执行一下,npm init vue@3 。然后有若干配置项,配置选择完毕后,项目创建就成功啦,非常的快。

image.png

然后一次执行后三条命令,启动项目,打开本地地址,就可以看到项目的首页。

image.png

这里我们研究下这个命令是怎么回事——npm init vue@3 npm init 的官网介绍

实际上执行 npm init vue@3 就是执行了 npm exec create-vue@3

PS:在这里我使用 node@16.16.0 版本执行命令 会报如下错误,还没弄清是从哪块出现的问题:

image.png

源码分析

接下来,就是对create-vue 的源码进行分析。

开启调试

我这里使用的是 VSCode,首先选择需要调试的文件,这里就是 index.js , 然后如图点击运行与调试

image.png

就进入了调试模式啦!

image.png

介绍引入的包

minimist 是一个用于处理命令行调用node指令时,处理node之后的一系列参数的模块。

prompts 轻量级、美观、用户友好的交互式提示。

kolorist 给控制台输入内容上色。

import minimist from 'minimist'
import prompts from 'prompts'
import { red, green, bold } from 'kolorist'

后续调试中会遇到 __dirname is not defined, 问题这里添加如下内容来解决

import { fileURLToPath } from 'url'
const __dirname = path.dirname(fileURLToPath(import.meta.url))

关键函数 init

获取路径、参数

让我们看看关键的异步函数 async function init(),这个函数就是我们执行 时会调用的函数,可以携带如下命令行参数:

  • possible options:
  • --default
  • --typescript / --ts
  • --jsx
  • --router / --vue-router
  • --vuex
  • --with-tests / --tests / --cypress
  • --force (for force overwriting)

首先获取当前脚本工作目录 cwd (pathname of the current working directory) ,以及执行命令时携带的参数。

  const cwd = process.cwd()
  const argv = minimist(process.argv.slice(2), {
    alias: {
      typescript: ['ts'],
      'with-tests': ['tests', 'cypress'],
      router: ['vue-router']
    },
    // all arguments are treated as booleans
    boolean: true
  })
交互式配置选项

然后是一些交互式的选项配置内容,例如下面的就是让用户输入项目名称,默认是 vue-project

{
  name: 'projectName',
  type: targetDir ? null : 'text',
  message: 'Project name:',
  initial: defaultProjectName,
  onState: (state) => (targetDir = String(state.value).trim() || defaultProjectName)
},

此外还包括如图的一些配置项:

image.png

根据配置复制模板

当获得了用户的配置之后,脚本接下来的工作内容就是将如下的模板文件,复制到用户创建项目的位置,基本的文件都在base 里,其它文件夹下的文件内容大家可以自行查看,这里就不列举啦。

image.png

以下就是将base的代码复制过去,其中的关键函数是renderTemplate, 位于 utils\renderTemplate.js,它的功能就是递归地将文件复制。

  const render = function render(templateName) {
    const templateDir = path.resolve(templateRoot, templateName)
    renderTemplate(templateDir, root)
  }

  // Render base template
  render('base')

我觉得这里实际上做的就是复制粘贴的工作,只不过它是定制化的。而我个人的工作经验遇到的情况都是,有人做了一个面向项目组的或者小组的这么一个模板,放到 git 上,然后有新项目了就拉下来删删改改的。目前还没遇到做一个这样的定制化脚手架,能够提高项目创建效率的用武之地。

配置了 ts

如果配置了 ts,那么就要将所有 js 结尾的文件重命名为 ts结尾的文件。同时也要修改入口文件里的引入文件名称。

  if (needsTypeScript) {
    // rename all `.js` files to `.ts`
    // rename jsconfig.json to tsconfig.json
    preOrderDirectoryTraverse(
      root,
      () => {},
      (filepath) => {
        if (filepath.endsWith('.js')) {
          fs.renameSync(filepath, filepath.replace(/\.js$/, '.ts'))
        } else if (path.basename(filepath) === 'jsconfig.json') {
          fs.renameSync(filepath, filepath.replace(/jsconfig\.json$/, 'tsconfig.json'))
        }
      }
    )
    // Rename entry in `index.html`
    const indexHtmlPath = path.resolve(root, 'index.html')
    const indexHtmlContent = fs.readFileSync(indexHtmlPath, 'utf8')
    fs.writeFileSync(indexHtmlPath, indexHtmlContent.replace('src/main.js', 'src/main.ts'))
  }
不需要测试

项目创建的时候默认配置是带测试的,如果不需要测试,那么会删除相应的文件

  if (!needsTests) {
    // All templates assumes the need of tests.
    // If the user doesn't need it:
    // rm -rf cypress **/__tests__/
    preOrderDirectoryTraverse(
      root,
      (dirpath) => {
        const dirname = path.basename(dirpath)

        if (dirname === 'cypress' || dirname === '__tests__') {
          emptyDir(dirpath)
          fs.rmdirSync(dirpath)
        }
      },
      () => {}
    )
  }
生成 readme

根据创建项目时使用的包管理器,来生成相应的版本的 readme 文件

  const packageManager = /pnpm/.test(process.env.npm_execpath)
    ? 'pnpm'
    : /yarn/.test(process.env.npm_execpath)
    ? 'yarn'
    : 'npm'

  // README generation
  fs.writeFileSync(
    path.resolve(root, 'README.md'),
    generateReadme({
      projectName: result.projectName || defaultProjectName,
      packageManager,
      needsTypeScript,
      needsTests
    })
  )

总结与收获

本次源码阅读了解了 create-vue 脚手架是如何创建项目的,本项目的源码比较简单,易于理解。收获如下:

  1. 了解了手脚架的基本功能;
  2. 了解了minimist prompts kolorist三个包的功能;
  3. 学会了在 VSCode 中直接调试文件的方法;