用惯老版本 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
,咱们发现以下代码:微信
以前咱们也提到过,命令行最核心的基础包是:commander
app
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' ]
------ 分割线 -----
本文来自微信公众号:[前端新视野]的原创文章