【源碼】Vue-cli2 源碼閱讀與改造

前言

最近組里讓我寫個(gè)腳手架赋铝,用腳手架來生成模板項(xiàng)目。比如每個(gè)公司都會(huì)有很多后臺(tái)管理系統(tǒng)泳挥,用于給產(chǎn)品然痊、客服等內(nèi)部人員使用。對(duì)于一個(gè)新的管理后臺(tái)屉符,如果每次都從頭開始寫剧浸,必然浪費(fèi)太多時(shí)間∷粝悖或者copy以前的項(xiàng)目進(jìn)行改造又不太優(yōu)雅。如果寫一個(gè)像vue-cli這樣的腳手架進(jìn)行命令行創(chuàng)建那再好不過了真仲。

一開始我感覺會(huì)有點(diǎn)難初澎,像vue-cli這樣的項(xiàng)目,經(jīng)歷了很多次的提交碑宴,代碼量不少。我之前的想象是延柠,vue-cli中進(jìn)行許多的目錄、文件的讀寫與創(chuàng)建贞间,用戶的交互,各種npm包的下載增热,腳本的運(yùn)行,最終才生成了項(xiàng)目峻仇。想想就有點(diǎn)亂。

但看過后發(fā)現(xiàn)變簡(jiǎn)單多了》惭粒總的來說,vue-cli分兩部分朝蜘,一部分為vue-cli,vue-cli只是一個(gè)拉取倉庫的作用芹务,一個(gè)下載的作用蝉绷。另一部分就是真正的模板了。這個(gè)模板也是github上的項(xiàng)目枣抱,按照一定的規(guī)范進(jìn)行編寫熔吗。

正文

這次看的是 vue-cli2,vue-cli3進(jìn)行了很多封裝佳晶,既然只需要其拉取倉庫的作用桅狠,那么vue-cli2就夠了。其目錄結(jié)構(gòu)如圖:

image.png

網(wǎng)上也有許多vue-cli2的源碼解析轿秧,這里只是簡(jiǎn)單說明中跌。
首先從package.json開始,查看一些命令菇篡。也許用了vue-cli3漩符,很多人已經(jīng)忘記vue-cli2的命令了。在package.json中有:

  "bin": {
    "vue": "bin/vue",
    "vue-init": "bin/vue-init",
    "vue-list": "bin/vue-list"
  }

"bin" 字段的作用是能讓我們?cè)诿畲翱谌州斎朊顖?zhí)行驱还。

下面將以 vue init webpack my-project 為例嗜暴。說明輸入命令后發(fā)生了什么。

輸入命令后议蟆,此時(shí)將運(yùn)行bin文件夾下的vue-init腳本:


image.png

vue-init主要看這幾個(gè)部分:

第一部分

首先第一句是必須的闷沥,這樣才能在全局下運(yùn)行命令。

#!/usr/bin/env node
第二部分

首先要知道的是咐容,模板可以從各種地方下載舆逃,而不僅僅是官方提供的那幾個(gè)(雖然許多時(shí)候用不到,但其的確提供了)戳粒。這一部分主要是獲取命令中的參數(shù)路狮,從而得出模板、項(xiàng)目名等信息蔚约。進(jìn)而確定如何去獲取模板奄妨。

let template = program.args[0]  // 獲取模板名稱: webpack
const hasSlash = template.indexOf('/') > -1   // 模板名稱是否有斜杠 (這里為false)
const rawName = program.args[1]  // 項(xiàng)目名稱: my-project
const inPlace = !rawName || rawName === '.'
const name = inPlace ? path.relative('../', process.cwd()) : rawName
const to = path.resolve(rawName || '.')
const clone = program.clone || false // 是否采用 clone 模式,默認(rèn) http 方式下載模版

const tmp = path.join(home, '.vue-templates', template.replace(/[\/:]/g, '-')) // 將模板下載到該路徑炊琉,并非直接下載到創(chuàng)建項(xiàng)目的路徑(緩存)
if (program.offline) { // 是否使用離線模版(使用上一行代碼中緩存下的)
  console.log(`> Use cached template at ${chalk.yellow(tildify(tmp))}`)
  template = tmp
}
第三部分
function run () {
  // check if template is local
  if (isLocalPath(template)) { // 是否使用本地模版 - 判斷模版路徑是否是本地
    const templatePath = getTemplatePath(template)
    if (exists(templatePath)) {
      generate(name, templatePath, to, err => {
        if (err) logger.fatal(err)
        console.log()
        logger.success('Generated "%s".', name)
      })
    } else {
      logger.fatal('Local template "%s" not found.', template)
    }
  } else {
    checkVersion(() => {
      if (!hasSlash) {
        // use official templates
        const officialTemplate = 'vuejs-templates/' + template
        if (template.indexOf('#') !== -1) {
          downloadAndGenerate(officialTemplate)
        } else {
          if (template.indexOf('-2.0') !== -1) {
            warnings.v2SuffixTemplatesDeprecated(template, inPlace ? '' : name)
            return
          }

          // warnings.v2BranchIsNowDefault(template, inPlace ? '' : name)
          downloadAndGenerate(officialTemplate)
        }
      } else {
        downloadAndGenerate(template)
      }
    })
  }
}

確定好命令中各種參數(shù)后展蒂,然后就根據(jù)參數(shù)進(jìn)行下載了又活。首先isLocalPath(template) 判斷參數(shù)是否是一個(gè)路徑,如 ../../webpack锰悼,而我們輸入是參數(shù)為 webpack柳骄。則走到 else 邏輯。else 中首先進(jìn)行版本檢查: checkVersion箕般,這就是如果有新版本的vue-cli了,將在命令行中詢問我們是否下載曲初。

由于沒有斜杠(hasSlash),且不帶版本號(hào)臼婆,最終走到了 downloadAndGenerate(officialTemplate) 去下載官方模板颁褂,其中經(jīng)過拼接后officialTemplate的值為: vuejs-templates/webpack傀广。至于 downloadAndGenerate(template)是自定義模板伪冰,后面再講。

第四部分
function downloadAndGenerate (template) {
  const spinner = ora('downloading template')
  spinner.start()
  // Remove if local template exists
  if (exists(tmp)) rm(tmp)
  download(template, tmp, { clone }, err => {
    spinner.stop()
    if (err) logger.fatal('Failed to download repo ' + template + ': ' + err.message.trim())
    generate(name, tmp, to, err => {
      if (err) logger.fatal(err)
      console.log()
      logger.success('Generated "%s".', name)
    })
  })
}

downloadAndGenerate 方法沒什么內(nèi)容靠柑,主要看其中的 download 方法病往。download 是通過一個(gè)npm包 download-git-repo
引入的 :const download = require('download-git-repo')骄瓣。download 方法也沒有什么內(nèi)容榕栏,主要是根據(jù)url去下載(http)或者 clone項(xiàng)目蕾各。

那么如何根據(jù)傳入的 officialTemplate: vuejs-templates/webpack 就能得出URL呢,主要在于 normalize:

image.png

最終得出的URL為:https://github.com/vuejs-templates/webpack妨托。就這樣兰伤,我們終于找到官方模板的位置了,總的來說vue-cli只起了一個(gè)下載的作用均澳,而真正的模板隱藏于此找前!這也就是命令 vue init webpack my-projectwebpack 參數(shù)的作用判族。在vuejs-templates 這個(gè)用戶下,我們還可以看到其他幾個(gè)模板颗品,如當(dāng)運(yùn)行 vue init pwa my-project 時(shí)將下載pwa模板躯枢。

image.png

還記得第三部分中說到的 downloadAndGenerate(template)邏輯嗎锄蹂?那是有斜桿/ 時(shí)將去下載自定義模板得糜。
也就是說晰洒,如果運(yùn)行命令 vue init my-templates/webpack my-project,它將到 my-templates 這個(gè)用戶下尋找自定義的模板進(jìn)行下載治宣!

這個(gè)自定義模板編寫是具有一定規(guī)范侮邀,具體可以看 https://github.com/vuejs/vue-cli/tree/v2#custom-templates

第五部分

在執(zhí)行完 download 后贝润,這是將模板下載完成而已打掘。這個(gè)模板不是直接就用的鹏秋,還記得我們創(chuàng)建項(xiàng)目時(shí)在命令窗口進(jìn)行的詢問與選擇嗎:

項(xiàng)目的作者拼岳?項(xiàng)目的描述惜纸?是否選擇eslint绝骚?eslint的版本?等等粪牲。那么如何根據(jù)模板來生成自己最終的項(xiàng)目呢腺阳?

可以看到后面再調(diào)用 generate 進(jìn)行項(xiàng)目的生成:

generate(name, tmp, to, err => {
      if (err) logger.fatal(err)
      console.log()
      logger.success('Generated "%s".', name)
    })

generate中主要使用了 Metalsmith 這個(gè)靜態(tài)網(wǎng)站生成器的插件亭引,這個(gè)插件配合https://github.com/vuejs/vue-cli/tree/v2#custom-templates 規(guī)范進(jìn)行生成項(xiàng)目皮获。具體怎么使用看其文檔即可了。

限于篇幅购公,vue-cli的源碼閱讀就到這了宏浩,網(wǎng)上也有很多分析靠瞎。下面說明如何進(jìn)行簡(jiǎn)單的改造就可以使vue-cli為自己所用较坛。也就是如何從公司gitlab進(jìn)行拉取模板扒最。

vue-cli已經(jīng)提供了不同平臺(tái)下載模板的方式:github、gitlab法竞、bitbucket。默認(rèn)是從github下載模板薛躬,如從gitlab下載則運(yùn)行:

vue init gitlab:username/repo my-project

而我們是要從公司的gitlab上拉取型宝,公司gitlab的域名是這樣組成的gitlab.xxxx.com絮爷。需要我們進(jìn)行改造。在第三部分的run方法中岖寞,將 officialTemplate 的拼接改為:

const officialTemplate = `gitlab:gitlab.xxxx.com:username/${template}`

那么仗谆,當(dāng)你運(yùn)行vue init webpack my-project時(shí)淑履,將默認(rèn)從你公司的gitlab上拉取模板秘噪。那接下來就是如何編寫模板的事了,按照https://github.com/vuejs/vue-cli/tree/v2#custom-templates 去做吧捷绒。

結(jié)束語

9月30日:文章是之前寫的暖侨,簡(jiǎn)書之前不給發(fā)崇渗。祝大家國慶快樂吧!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末葫掉,一起剝皮案震驚了整個(gè)濱河市俭厚,隨后出現(xiàn)的幾起案子挪挤,更是在濱河造成了極大的恐慌叼丑,老刑警劉巖鸠信,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件星立,死亡現(xiàn)場(chǎng)離奇詭異葬凳,居然都是意外死亡沮明,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門酱畅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來江场,“玉大人址否,你說我怎么就攤上這事》担” “怎么了音同?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵权均,是天一觀的道長。 經(jīng)常有香客問我恋沃,道長必指,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任研侣,我火速辦了婚禮炮捧,結(jié)果婚禮上咆课,老公的妹妹穿的比我還像新娘扯俱。我一直安慰自己,他們只是感情好殊校,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布为流。 她就那樣靜靜地躺著让簿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪莲祸。 梳的紋絲不亂的頭發(fā)上锐帜,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天抹估,我揣著相機(jī)與錄音,去河邊找鬼药蜻。 笑死语泽,一個(gè)胖子當(dāng)著我的面吹牛视卢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播惋砂,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼西饵,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了眷柔?” 一聲冷哼從身側(cè)響起驯嘱,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎茂蚓,沒想到半個(gè)月后聋涨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锥忿,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡敬鬓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年础芍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了数尿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡诊杆,死狀恐怖晨汹,靈堂內(nèi)的尸體忽然破棺而出贷盲,到底是詐尸還是另有隱情,我是刑警寧澤铝穷,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布晦炊,位于F島的核電站筹陵,受9級(jí)特大地震影響镊尺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜语稠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一仙畦、第九天 我趴在偏房一處隱蔽的房頂上張望慨畸。 院中可真熱鬧衣式,春花似錦、人聲如沸碴卧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叹阔。三九已至,卻和暖如春条获,著一層夾襖步出監(jiān)牢的瞬間宴杀,已是汗流浹背碧绞。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國打工讥邻, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人系宜。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓盹牧,卻偏偏與公主長得像汰寓,于是被迫代替她去往敵國和親苹粟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子嵌削,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容