乐趣区

vuecli脚手架源码解析一

前言:前段时间撸过 react 脚手架,现在新公司用 vue,最近花时间撸一下 vue-cli,撸完之后,再亲自写一个简化版的 cli。

看脚手架的思路,就是顺着 package.json 文件看下去即可,看 bin 字段到底执行的是什么文件。

正式开始

react,vue 脚手架都用的 lerna 分包,所以一进来直接去看 packages 目录, 进入 @vue 目录, 最后来到 cli 目录下。

1. 按照惯例,这种脚手架都可以分为两部分

  • 生成项目基本文件,组织文件结构,说白了就是创建样板项目,如 vue create hello-word
  • 开发命令如 vue-cli-service serve | build | lint。

vue create hello-word

就从这个命令出发,看看这个命令到底做了哪些操作。

可以看到 vue 其实是执行了 bin 目录下的 vue.js 文件。

2. vue.js,了解依赖的库和文件
这个文件我讲细致一点点,直接从上而下过代码。

const chalk = require('chalk')  // 相等于 console 的时候加上了颜色,const semver = require('semver') // 规范版本号
const requiredVersion = require('../package.json').engines.node // 获取依赖的 node 最低版本
const didYouMean = require('didyoumean') // 有点类似于纠正输出错误,用户输出错误命令,给出联想提示。
checkNodeVersion(requiredVersion, 'vue-cli') // 这个方法,判定当前 node 版本是否低于最低要求版本 requiredVersion 
const fs = require('fs')    // 文件模块
const path = require('path') // 路径模块
const slash = require('slash') // 用于转换 Windows 反斜杠路径转换为正斜杠路径 \ => /
const minimist = require('minimist') // 解析命令行参数 process.argv, 有的项目用 arg。
const program = require('commander') // 这个就是命令库,定义命令,说白点,就是定义一个 create 命令,当你输入 vue create 的时候就会执行对应的方法。const loadCommand = require('../lib/util/loadCommand') // 这个是自定义方法,根据不同的参数加载不同的 js 文件,比如 test 就加载 test.js, 里面有异常流处理,比如文件不存在怎么办,这里就不多复述。

3. create 命令
他这里定义非常非常多的命令,但是我们一般就用一个 create,这里只详细讲解这一个。
前置知识点,commander库,这个库是用来定义命令的,有一些基本知识点,可以先去了解一下。

program
  .command('create <app-name>')
  .description('create a new project powered by vue-cli-service')
  .option('-p, --preset <presetName>', 'Skip prompts and use saved or remote preset')
  .option('-d, --default', 'Skip prompts and use default preset')
  .option('-i, --inlinePreset <json>', 'Skip prompts and use inline JSON string as preset')
  .option('-m, --packageManager <command>', 'Use specified npm client when installing dependencies')
  .option('-r, --registry <url>', 'Use specified npm registry when installing dependencies (only for npm)')
  .option('-g, --git [message]', 'Force git initialization with initial commit message')
  .option('-n, --no-git', 'Skip git initialization')
  .option('-f, --force', 'Overwrite target directory if it exists')
  .option('-c, --clone', 'Use git clone when fetching remote preset')
  .option('-x, --proxy', 'Use specified proxy when creating project')
  .option('-b, --bare', 'Scaffold project without beginner instructions')
  .option('--skipGetStarted', 'Skip displaying"Get started"instructions')
  .action((name, cmd) => {const options = cleanArgs(cmd) // 对 options 参数做一些格式化

    if (minimist(process.argv.slice(3))._.length > 1) {// 输入的参数太多,不合法,走默认值}
    // --git makes commander to default git to true
    if (process.argv.includes('-g') || process.argv.includes('--git')) {options.forceGit = true}
    require('../lib/create')(name, options)
  })

看源码说话,create 代表命令 <app-name> 表示项目名,后面带 - 表示参数,一个完整的命令就是 create hello -p。一旦 process.argvs 中带了 create 就会执行这个命令,并且进入到 action 中去,执行对应的操作。
require(‘../lib/create’)(name, options) 到这里算是走完了最简单的一步,解析出参数,然后执行真正的 create 文件,并将参数传过去。至于 create 文件里面的故事,下一章再讲。

退出移动版