[Vue CLI 3] 源碼系列之init

用慣老版本 Vue CLI 的同窗通常多會選擇使用以下命令來建立模板項目:前端

vue init webpack demo

可是在新版中,推薦使用 vue create,官方也提到了:vue

由於使用了一樣一個 vue 命令,因此以前的會被覆蓋,若是還須要使用,須要自行安裝:node

npm install -g @vue/cli-init

咱們先看一下,若是本地已經安裝了最新版本的 Vue CLI,執行以前的 vue init 會出現什麼?webpack

命令行會提示你以下內容:git

Command vue init requires a global addon to be installed.

Please run npm install -g @vue/cli-init and try again.web

那它背後的設計是如何的呢?vue-cli

一、首先仍是 vue 的一個擴展命令:npm

咱們仍是找到 @vue/cli/bin/vue.js,咱們發現以下代碼:微信

以前咱們也提到過,命令行最核心的基礎包是:commanderapp

const program = require('commander')

這裏配置了 command、description、option 和 action

program

.command('init <template> <app-name>')

.description('generate a project from a remote template (legacy API, requires @vue/cli-init)')

.option('-c, --clone', 'Use git clone when fetching remote template')

.option('--offline', 'Use cached template')

.action(() => {

loadCommand('init', '@vue/cli-init')

})

而後調用了 loadCommand,傳入了 2 個參數,咱們看一下 @vue/cli/lib/util/loadCommand

文件對外暴露一個 loadCommand 函數,接受 2 個參數

module.exports = function loadCommand (commandName, moduleName) {

// ...

}

內部會經過 try catch 加載對應的第二個參數 moduleName,這裏爲 @vue/cli-init

try {
return require(moduleName);
}

這裏由於咱們本地沒有,會報錯進入 catch:

Error: Cannot find module '@vue/cli-init'

咱們看一下 catch 的處理:

由於有 2 個地方會判斷 err 因此複用了一個函數: isNotFoundError

const isNotFoundError = err => {
return err.message.match(/Cannot find module/)
}

注意這裏有一個策略,在判斷沒有以後,會再次 try catch 到全局裏面看

catch (err) {
if (isNotFoundError(err)) {
//...
} else {
throw err
}
}

代碼實現以下,用來一個工具包:import-global

try {
return require('import-global')(moduleName)
}

若是再失敗,就只能出提示了,就是上面一開始咱們看到的:

這裏用了工具包 chalk 來給文字作樣式,同時還有一個有用的:

會判斷你是否安裝了yarn,若是有,就推薦你用它來全局安裝 @vue/cli-init

判斷函數來自咱們以前 config 一直打交道的 @vue/cli-shared-utils

函數名 hasYarn

const { hasYarn } = require('@vue/cli-shared-utils')

咱們看一下具體實現:源碼在 @vue/cli-shared-utils/lib/env.js

外層有一個變量: _hasYarn

let _hasYarn

函數結構:

exports.hasYarn = () => {

}

會作幾層判斷:

先看這個 env 變量

if (process.env.VUE_CLI_TEST) {
return true
}

而後再看那個變量,有值就直接返回

if (_hasYarn != null) {
return _hasYarn
}

最核心的來了:

使用核心模塊 child_process

const { execSync } = require('child_process')

執行 yarnpkg 命令

execSync('yarnpkg --version', { stdio: 'ignore' })

而後分別給變量賦值,有就是 true,不然是 false

------------------------------- 分割線 ----

咱們參照建議,全局安裝以後,咱們查看 @vue/cli-init/index.js

代碼一共這麼多行:做者在 readme 也提示的很清晰

This is simply an alias to the old vue-cli@2.x.

核心是使用 execa 工具包,執行老版本的 vue-cli/bin/vue-init,傳入了命令行上面的參數(這裏沒有用工具包)

const execa = require('execa')
const binPath = require.resolve('vue-cli/bin/vue-init')
execa(
binPath,
process.argv.slice(process.argv.indexOf('init') + 1),
{ stdio: 'inherit' }
)

咱們看一下在命令行輸入:vue init webpack demo 以後,process.argv 是什麼?

[ '/usr/local/bin/node','/usr/local/bin/vue',
'init',
'webpack',
'demo' ]

process.argv.indexOf('init') + 1 返回的是:

3

process.argv.slice(3) 返回的是:

[ 'webpack', 'demo' ]

------ 分割線 -----

本文來自微信公衆號:[前端新視野]的原創文章
相關文章
相關標籤/搜索