美文网首页Vue.js前端开发笔记vue-cli
[Vue CLI 3] 源码系列之init

[Vue CLI 3] 源码系列之init

作者: dailyvuejs | 来源:发表于2018-08-29 15:39 被阅读0次

    用惯老版本 Vue CLI 的同学一般多会选择使用如下命令来创建模板项目:

    vue init webpack demo

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

    因为使用了同样一个 vue 命令,所以之前的会被覆盖,如果还需要使用,需要自行安装:

    npm install -g @vue/cli-init

    我们先看一下,如果本地已经安装了最新版本的 Vue CLI,执行之前的 vue init 会出现什么?

    命令行会提示你如下内容:

    Command vue init requires a global addon to be installed.

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

    那它背后的设计是如何的呢?

    1、首先还是 vue 的一个扩展命令:

    我们还是找到 @vue/cli/bin/vue.js,我们发现如下代码:

    之前我们也提到过,命令行最核心的基础包是: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')

    })

    然后调用了 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' ]

    ------ 分割线 -----

    本文来自微信公众号:[前端新视野]的原创文章

    相关文章

      网友评论

        本文标题:[Vue CLI 3] 源码系列之init

        本文链接:https://www.haomeiwen.com/subject/dgckwftx.html