手记

[Vue CLI 3] 源码系列之useTaobaoRegistry

通过下列方式可以安装最新版本的 Vue CLI(注释:sudo 自行选择)

sudo npm install -g @vue/cli

然后通过下列命令创建项目:

vue create demo

这时候,会询问你是否使用 taobao 的 registry

Your connection to the default npm registry seems to be slow.

Use https://registry.npm.taobao.org for faster installation?

然后选择 Yes 后,发现在用户的根目录中出现了一个 .vuerc文件,内容如下:

{  "useTaobaoRegistry": true}

本文从源码设计角度看一下背后的实现:

在新版本 Vue CLI 中目录结构变动了,我们找到了如下几个文件:

@vue/cli/lib/util/shouldUseTaobao.js

这个文件的函数只会执行一次:设置了变量 checkedresult

let checkedlet result

在函数内部一上来就会判断

if (checked) return result

第一步:需要在命令行以询问方式:

一般多会采用 inquirer 这个工具包,先加载:

const inquirer = require('inquirer')

然后调用 prompt 方法,注意这里设置了 type confirm 的方式

然后用 chalk 这个工具包来在命令行改变字颜色

const chalk = require('chalk')

最核心的代码片段如下:

定义了 name、type 和 message 字段:

const { useTaobaoRegistry } = await inquirer.prompt([

    {      name: 'useTaobaoRegistry',      type: 'confirm',      message: chalk.yellow(        ` Your connection to the default npm registry seems to be slow.\n` +          `   Use ${chalk.cyan(registries.taobao)} for faster installation?`

      )

    }

  ])

第二步:判断 register 的速度

定义一个变量 faster

let faster

这里使用了 Promise.race 函数(返回一个 promise,一旦迭代器中的某个promise 解决或拒绝,返回的 promise就会解决或拒绝。)

try {

    faster = await Promise.race([

      ping(defaultRegistry),

      ping(registries.taobao)

    ])

  } catch (e) {}

这里的变量就是:

const registries = require('./registries')

如上,来自一个同级的 registries.js 文件

const defaultRegistry = registries.npm

registries 在 @vue/cli/lib/util/registries.js

源码内容如下:维护了 3 个映射关系,里面就有官方 registrytaobao

const registries = {

  npm: 'https://registry.npmjs.org',

  yarn: 'https://registry.yarnpkg.com',

taobao: 'https://registry.npm.taobao.org'}module.exports = registries

我们看一下最核心的 ping 函数:

使用了 @vue/cli-shared-utilsrequest 方法

async function ping (registry) {  await request.get(`${registry}/vue-cli-version-marker/latest`)  return registry

}

@vue/cli-shared-utils/lib/request.js 看一下源码:

对外暴露了 get 方法,内部依赖 request-promise-native 工具包(uses native ES6 promises),传入了一个对象:

  • method 方法为 'GET'

  • resolveWithFullResponse

  • json

  • uri  请求地址

核心代码如下:

exports.request = {

  get (uri) {    // lazy require

    const request = require('request-promise-native')    const reqOpts = {

      method: 'GET',

      resolveWithFullResponse: true,

      json: true,

      uri

    }    return request(reqOpts)

  }

}

第三步:写入一个 .vuerc 文件

定义了 save 函数,代码实现如下:

const save = val => {

    result = val

    saveOptions({ useTaobaoRegistry: val })    return val

  }

saveOptions 在  @vue/cli/lib/options.js 中定义:

exports.saveOptions = toSave => {  // 实现在下面}

在里面定义了一个 defaults 的对象,里面默认设置了 useTaobaoRegistry 为  undefined:

exports.defaults = {

  useTaobaoRegistry: undefined

}

核心是采用了 fs.writeFileSync 往指定目录写文件:

注释:关于写入路径可以看一下 rcPath.js 文件提供的 getRcPath

const rcPath = exports.rcPath = getRcPath('.vuerc')

注意:下面的 JSON.stringify 的第三个参数,也是通过 try catch 的方式:

fs.writeFileSync(rcPath, JSON.stringify(options, null, 2))

那如果用户本地已经设置了呢,先获取本地的设置:

核心是使用了 execa 这个工具包:

const execa = require('execa')

定义了一个参数 userCurrent ,传入了命令和参数:

(await execa(`npm`, ['config', 'get', 'registry'])).stdout

比较两个路径:

if (removeSlash(userCurrent) !== removeSlash(defaultRegistry)) {    // user has configured custom regsitry, respect that

    return save(false)

}

removeSlash 的实现如下:

function removeSlash (url) {  return url.replace(/\/$/, '')

}

第三个问题:用户第一次设置之后,后面的创建项目操作是如何处理的呢?

在 @vue/cli/lib/util/shouldUseTaobao.js 内部,会调用 loadOptions 函数(下面会提到)

const saved = loadOptions().useTaobaoRegistry

@vue/cli/lib/options.js

会定义一个变量:

let cachedOptions

对外暴露了 loadOptions 函数:

exports.loadOptions = () => {

}

在 loadOptions 函数内部:

第一步:会先看 cachedOptions 是否有值:

if (cachedOptions) {    return cachedOptions

}

然后会读取配置文件内容:通过 fs.readFileSync 方法,然后用 JSON.parse 转成对象

// 判断配置文件是否存在if (fs.existsSync(rcPath)) {}

内部使用 try catch,给 cacheOptions 赋值

JSON.parse(fs.readFileSync(rcPath, 'utf-8'));

所以第二次这里因为 .vuerc 文件已经写入了内容,所以第一步就返回了



作者:dailyvuejs
链接:https://www.jianshu.com/p/b422bb6244b8


0人推荐
随时随地看视频
慕课网APP