vue-cli脚手架源码解析(二)

前言:紧接上篇,这篇文章着重讲解create.js,这是建立项目的主文件。css

从上而下,逐行解析。vue


  1. 先看依赖文件
const fs = require('fs-extra')  // 这个是操做文件的库,node自带的库须要写不少的hack
const path = require('path')    // 路径
const chalk = require('chalk')  // console上色
const inquirer = require('inquirer') // 这个很重要,是命令行交互用的,好比咱们建立项目的时候,他会让你选择是否采用vue-router,是否使用vuex。
const Creator = require('./Creator') // 这个后面细说
const { clearConsole } = require('./util/clearConsole') // 这个就是console一些信息,不重要
const { getPromptModules } = require('./util/createTools') // 这个后面说
const validateProjectName = require('validate-npm-package-name') // 校验包名字,不重要
  1. 重点来看create方法,我删除一些不重要的内容
async function create (projectName, options) {
  // projectName就是咱们建立的项目名字,好比hello-word
  // options就是咱们输入的参数了,例如 -p -f -d

  const cwd = options.cwd || process.cwd()  //运行脚手架的目录
  const inCurrent = projectName === '.'  // 项目名是不是个.(也能够理解为目录,若是是.就表明当前目录,不须要建立了)
  const name = inCurrent ? path.relative('../', cwd) : projectName
  const targetDir = path.resolve(cwd, projectName || '.') // 会建立目录

  const result = validateProjectName(name)  // 校验名字是否合理,例如是否有空格
  // 校验错误流被我删除了,无外乎提示错误。

  if (fs.existsSync(targetDir)) {   // 目录是否存在
    if (options.force) {       //参数是否有-f,若是有就强制删除目录
      await fs.remove(targetDir)
    } else {    // 不然就让用户本身选择,是覆盖啊,合并啊,仍是啥的
      await clearConsole()  // 输出一些提示吧,这个不重要
      if (inCurrent) {  // 若是是当前目录建立项目,给个confirm提示
        const { ok } = await inquirer.prompt([
          {
            name: 'ok',
            type: 'confirm',
            message: `Generate project in current directory?`
          }
        ])
        if (!ok) {
          return
        }
      } else {  /
        const { action } = await inquirer.prompt([
          {
            name: 'action',
            type: 'list',
            message: `Target directory ${chalk.cyan(targetDir)} already exists. Pick an action:`,
            choices: [
              { name: 'Overwrite', value: 'overwrite' },
              { name: 'Merge', value: 'merge' },
              { name: 'Cancel', value: false }
            ]
          }
        ])
        if (!action) {
          return
        } else if (action === 'overwrite') {
          console.log(`\nRemoving ${chalk.cyan(targetDir)}...`)
          await fs.remove(targetDir)
        }
      }
    }
  }
  // 总结上面的一堆代码,就是建立一个项目目录,来放代码,若是目录存在是强制删除仍是覆盖合并。
  const creator = new Creator(name, targetDir, getPromptModules()) // 真正建立的代码在这里面去了。
  
  // name就是项目名
  // targetDir就是 path.resolve(cwd, projectName || '.')就是目录地址
  // getPromptModules这个是引入第三方库     
    
  await creator.create(options)  
}

看下getPromptModules(),他引入了好多的库,这些库,在用户选择不一样的初始化项目的时候用获得。node

return [
    'babel',
    'typescript',
    'pwa',
    'router',
    'vuex',
    'cssPreprocessors',
    'linter',
    'unit',
    'e2e'
  ].map(file => require(`../promptModules/${file}`))

真正的建立看来仍是要等到下一篇文章再说了,再重点介绍下inquirer这个库。vue-router

inquirer命令行交互的库,好比咱们常见的vuex

clipboard.png

一个是让你手动输入,y或者n,一个是选择。typescript

clipboard.png

当全部的交互完成会,会返回一个对象,就是用户选择或者输入的值,后续的输出逻辑就依赖这个值。npm

const inquirer = require('inquirer');
inquirer.prompt([
    {
        type: 'input',
        message: '请选择是否建立(y/n)',
        name: 'xory',
        default: 'y',
    },
    {
        type: 'list',
        message: '你喜欢什么水果?',
        name: 'xig',
        default: '0',
        choices:['西瓜','苹果','香蕉']
    },
    {
    type: 'input',
    message: '请输入手机号:',
    name: 'phone',
    validate: function(val) {
        if(val.match(/\d{11}/g)) { // 校验位数
           return true;
        }
        return "请输入11位数字";
    }
    }
]).then(answer => {
    console.log(answer); // answer就是用户的输入
})

重点的方法就prompt,prompt接收一个数组,数组里面是一个配置对象,重点是type,不一样的type须要配置不一样的数据,好比当type是list的时候,须要传一个choices,用于给用户选择。数组