經過下列方式能夠安裝最新版本的 Vue CLI(註釋:sudo 自行選擇)javascript
sudo npm install -g @vue/cli
而後經過下列命令建立項目:前端
vue create demo
這時候,會詢問你是否使用 taobao 的 registryvue
Your connection to the default npm registry seems to be slow. Use https://registry.npm.taobao.org for faster installation?
而後選擇 Yes 後,發如今用戶的根目錄中出現了一個 .vuerc
文件,內容以下:java
{ "useTaobaoRegistry": true }
本文從源碼設計角度看一下背後的實現:vue-cli
在新版本 Vue CLI 中目錄結構變更了,咱們找到了以下幾個文件:npm
@vue/cli/lib/util/shouldUseTaobao.js
json
這個文件的函數只會執行一次
:設置了變量 checked
、result
promise
let checked let result
在函數內部一上來就會判斷微信
if (checked) return result
第一步:須要在命令行以詢問方式:async
通常多會採用 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 個映射關係,裏面就有官方 registry
和 taobao
的
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-utils
的 request
方法
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),傳入了一個對象:
核心代碼以下:
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
文件已經寫入了內容,因此第一步就返回了
本文原創來自微信公衆號:前端新視野
擴展連接: