用慣老版本 Vue CLI 的同學(xué)一般多會(huì)選擇使用如下命令來創(chuàng)建模板
項(xiàng)目:
vue init webpack demo
但是在新版中,推薦使用 vue create
赃阀,官方也提到了:
因?yàn)槭褂昧送瑯右粋€(gè) vue
命令晒屎,所以之前的會(huì)被覆蓋佑附,如果還需要使用搞乏,需要自行安裝:
npm install -g @vue/cli-init
我們先看一下邻眷,如果本地已經(jīng)安裝了最新版本的 Vue CLI眠屎,執(zhí)行之前的 vue init 會(huì)出現(xiàn)什么?
命令行會(huì)提示你如下內(nèi)容:
Command vue init requires a global addon to be installed.
Please run npm install -g @vue/cli-init and try again.
那它背后的設(shè)計(jì)是如何的呢耗溜?
1组力、首先還是 vue 的一個(gè)擴(kuò)展命令:
我們還是找到 @vue/cli/bin/vue.js
,我們發(fā)現(xiàn)如下代碼:
之前我們也提到過抖拴,命令行最核心的基礎(chǔ)包是:commander
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')
})
然后調(diào)用了 loadCommand
阿宅,傳入了 2 個(gè)參數(shù),我們看一下 @vue/cli/lib/util/loadCommand
文件對(duì)外暴露一個(gè) loadCommand 函數(shù)笼蛛,接受 2 個(gè)參數(shù)
module.exports = function loadCommand (commandName, moduleName) {
// ...
}
內(nèi)部會(huì)通過 try catch 加載對(duì)應(yīng)的第二個(gè)參數(shù) moduleName洒放,這里為 @vue/cli-init
try {
return require(moduleName);
}
這里因?yàn)槲覀儽镜貨]有,會(huì)報(bào)錯(cuò)進(jìn)入 catch:
Error: Cannot find module '@vue/cli-init'
我們看一下 catch 的處理:
因?yàn)橛?2 個(gè)地方會(huì)判斷 err 所以復(fù)用了一個(gè)函數(shù): isNotFoundError
const isNotFoundError = err => {
return err.message.match(/Cannot find module/)
}
注意這里有一個(gè)策略滨砍,在判斷沒有之后往湿,會(huì)再次 try catch 到全局里面看
catch (err) {
if (isNotFoundError(err)) {
//...
} else {
throw err
}
}
代碼實(shí)現(xiàn)如下妖异,用來一個(gè)工具包:import-global
try {
return require('import-global')(moduleName)
}
如果再失敗,就只能出提示了领追,就是上面一開始我們看到的:
這里用了工具包 chalk
來給文字做樣式他膳,同時(shí)還有一個(gè)有用的:
會(huì)判斷你是否安裝了yarn
,如果有绒窑,就推薦你用它來全局安裝 @vue/cli-init
判斷函數(shù)來自我們之前 config 一直打交道的 @vue/cli-shared-utils
函數(shù)名 hasYarn
:
const { hasYarn } = require('@vue/cli-shared-utils')
我們看一下具體實(shí)現(xiàn):源碼在 @vue/cli-shared-utils/lib/env.js
外層有一個(gè)變量: _hasYarn
let _hasYarn
函數(shù)結(jié)構(gòu):
exports.hasYarn = () => {
}
會(huì)做幾層判斷:
先看這個(gè) env 變量
if (process.env.VUE_CLI_TEST) {
return true
}
然后再看那個(gè)變量棕孙,有值就直接返回
if (_hasYarn != null) {
return _hasYarn
}
最核心的來了:
使用核心模塊 child_process
const { execSync } = require('child_process')
執(zhí)行 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 工具包订雾,執(zhí)行老版本的 vue-cli/bin/vue-init肢预,傳入了命令行上面的參數(shù)(這里沒有用工具包)
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' ]
------ 分割線 -----
本文來自微信公眾號(hào):[前端新視野]的原創(chuàng)文章