[Vue CLI 3] 原始碼系列之useTaobaoRegistry

amadan發表於2021-09-09

透過下列方式可以安裝最新版本的 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  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: '',

  yarn: '',

taobao: ''}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
連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2310/viewspace-2815571/,如需轉載,請註明出處,否則將追究法律責任。

相關文章