vite 學(xué)習(xí)筆記

參考文章

背景

Vite是一個構(gòu)建工具,旨在為現(xiàn)代web項(xiàng)目提供更快辽旋、更精簡的開發(fā)體驗(yàn)珠增。

vite主要分兩個模塊:

  • 通過native ES module實(shí)現(xiàn)的本地開發(fā)服務(wù),可以提供極快的熱更新服務(wù)。
  • 通過rollup對生產(chǎn)環(huán)境打包剂跟,可以極大地優(yōu)化打包生產(chǎn)環(huán)境的代碼术吝。

vite 和 webpack 開發(fā)環(huán)境最大的區(qū)別就是vite 在開發(fā)環(huán)境拋棄了打包這一個理念阿逃,直接在開發(fā)環(huán)境使用Javascript module,減少打包帶來的時間損耗,極大地方便了本地開發(fā)。

對于vite的學(xué)習(xí)窗悯,我主要總結(jié)了以下四個模塊進(jìn)行總結(jié)。

  • 模塊路徑解析
  • 不同格式文件處理
  • 熱更新
  • 預(yù)打包

模塊路徑解析

對于一個native es module服務(wù)系統(tǒng)而言偷拔,不同模塊的路徑解析非常重要蒋院,這里面有以下幾個問題:

  • node_modules等特殊路徑內(nèi)容如何處理
  • 相對路徑不便于記錄唯一文件路徑

vite的解決方式:

  • 將裸模塊(node_modules)做轉(zhuǎn)換 "vue.js" --> "/@modules/vue.js"
  • 將相對路徑轉(zhuǎn)為絕對路徑,便于vite統(tǒng)一文件路徑識別莲绰。 import '../../a.js' --> '/src/a.js'

不同格式文件處理

通過不同格式的處理欺旧,我們可以理解類似于webpack loader對于不同文件是如何處理的, 了解vite工作機(jī)制。

  • vue
  • css
  • json
  • html

對于不同格式的文件蛤签,vite統(tǒng)一都處理成javascript格式辞友,在返回的response 中添加
Content-Type: application/javascript; charset=utf-8

vue:

vue 的組件是一個單文件組件的機(jī)制。一個vue組件的定義基本分三個部分:

<template></template>
<script></script>
<style></style>

編譯器會將一個vue組件的三部分分別處理。在vite中称龙,請求一個組件的資源:

截屏2021-03-18 下午7.53.28.png

Helloworld.vue script 邏輯部分編譯:

// 此文件可以理解為一個組件的script邏輯部分
import string from '/src/string.js'
const __script = {
    name: 'HelloWorld',
    props: {
        msg: String
    },
    data() {
        return {
                age: 123
        }
    }
}
// 這里引入組件的template部分
import "/src/components/HelloWorld.vue?type=style&index=0"
// 這里引入組件的style部分
import {render as __render} from "/src/components/HelloWorld.vue?type=template"
__script.render = __render
__script.__hmrId = "/src/components/HelloWorld.vue"
__script.__file = "/Users/lizhuang/gitcode/vite-test/src/components/HelloWorld.vue"
export default __script

HelloWorld.vue?type=style 樣式部分編譯:

import { updateStyle } from "/vite/client"
const css = "\nh1 {\n  background: red;\n}\n"
updateStyle("62a9ebed-0", css)
export default css

HelloWorld.vue?type=template 結(jié)構(gòu)部分編譯:

import {
  toDisplayString as _toDisplayString,
  createVNode as _createVNode,
  createTextVNode as _createTextVNode,
  Fragment as _Fragment,
  openBlock as _openBlock,
  createBlock as _createBlock
} from "/@modules/vue.js"

const _hoisted_1 = /*#__PURE__*/
_createVNode("p", null, "string1", -1 /* HOISTED */
)
const _hoisted_2 = /*#__PURE__*/
_createVNode("p", null, [/*#__PURE__*/
_createTextVNode("Edit "), /*#__PURE__*/
_createVNode("code", null, "components/HelloWorld.vue"), /*#__PURE__*/
_createTextVNode(" to test hot module replacement.")], -1 /* HOISTED */
)

export function render(_ctx, _cache, $props, $setup, $data, $options) {
    return (_openBlock(),
    _createBlock(_Fragment, null, [_createVNode("h1", null, _toDisplayString($props.msg), 1 /* TEXT */
    ), _createVNode("button", {
        onClick: _cache[1] || (_cache[1] = $event=>($data.count++))
    }, "count is: " + _toDisplayString($data.count), 1 /* TEXT */
    ), _hoisted_1, _createVNode("p", null, _toDisplayString($data.string), 1 /* TEXT */
    ), _hoisted_2], 64 /* STABLE_FRAGMENT */
    ))
}

JSON 文件格式處理

通過 rollup-pluginutils 的dataToEsm方法

{
  custom: 'data',
  to: ['treeshake']
}

轉(zhuǎn)變?yōu)椋?/p>

export const custom = 'data';
export const to = ['treeshake'];
export default { custom, to };

CSS 文件格式處理

其實(shí)在vue的樣式部分已經(jīng)有所涉及留拾。

import { updateStyle } from "/vite/client"
const css = "\nh1 {\n  background: red;\n}\n"
updateStyle("62a9ebed-0", css)
export default css

其中 updateStyle 是更新樣式的關(guān)鍵函數(shù),我們進(jìn)行分析:


/**
* content: css文件內(nèi)容
*/
function updateStyle(id, content) {
  ...
        if (!style) {
      style = new CSSStyleSheet()
      style.replaceSync(content)
      document.adoptedStyleSheets = [...document.adoptedStyleSheets, style]
    } else {
      style.replaceSync(content)
    }
  ...
}    
        

vite利用** CSSStyleSheet **代表一個樣式表茵瀑,利用javascript的接口編輯或者添加相關(guān)的樣式间驮。

當(dāng)然還有一個特殊的情況就是css文件中有@import 等操作, 這種特殊的情況躬厌,vite直接使用style標(biāo)簽進(jìn)行樣式插入马昨。

cosnt style = document.createElement('style')
style.setAttribute('type', 'text/css')
style.innerHTML = content
document.head.appendChild(style)

熱更新

vite1 代碼較少,這可以讓我們低成本的學(xué)習(xí)一個開發(fā)環(huán)境熱更新的具體細(xì)節(jié)

image.png

其中第四部處理不同文件的方式扛施,列在了下方:

async function handleMessage(payload: HMRPayload) {
  const { path, changeSrcPath, timestamp } = payload as UpdatePayload
  switch (payload.type) {
    case 'connected':
      console.log(`[vite] connected.`)
      break
    case 'vue-reload':
      queueUpdate(
        import(`${path}?t=${timestamp}`)
          .catch((err) => warnFailedFetch(err, path))
          .then((m) => () => {
            __VUE_HMR_RUNTIME__.reload(path, m.default)
            console.log(`[vite] ${path} reloaded.`)
          })
      )
      break
    case 'vue-rerender':
      const templatePath = `${path}?type=template`
      import(`${templatePath}&t=${timestamp}`).then((m) => {
        __VUE_HMR_RUNTIME__.rerender(path, m.render)
        console.log(`[vite] ${path} template updated.`)
      })
      break
    case 'style-update':
      // check if this is referenced in html via <link>
      const el = document.querySelector(`link[href*='${path}']`)
      if (el) {
        el.setAttribute(
          'href',
          `${path}${path.includes('?') ? '&' : '?'}t=${timestamp}`
        )
        break
      }
      // imported CSS
      const importQuery = path.includes('?') ? '&import' : '?import'
      await import(`${path}${importQuery}&t=${timestamp}`)
      break
      .... 還有很多鸿捧,就不一一列舉了
  }
}

預(yù)打包

vite的預(yù)打包優(yōu)化手段其實(shí)和小程序頁面預(yù)加載技術(shù),以及網(wǎng)頁的prefetch疙渣,preload等的原理是基本一致的匙奴,當(dāng)我們盡量少的打包過后,那么預(yù)打包那些沒有處理的文件就是優(yōu)化的手段之一妄荔。

vite 會去分析package.json 當(dāng)中的依賴項(xiàng)泼菌,會將依賴進(jìn)行打包并緩存:

截屏2021-03-21 下午3.40.26.png

其中l(wèi)odash較為特殊,因?yàn)槠湮募姸嗬沧猓绻贿M(jìn)行預(yù)打包的話哗伯,開發(fā)項(xiàng)目將會請求很多相關(guān)文件,造成網(wǎng)頁reload時性能衰減篷角。所以vite預(yù)打包的另外一個重要的功能就是通過rollup或者esbuild(vite 不同版本實(shí)現(xiàn)不同)焊刹,將過于零散的文件打包,減少網(wǎng)絡(luò)請求恳蹲,提高頁面reload性能虐块。

截屏2021-03-21 下午3.42.57.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市嘉蕾,隨后出現(xiàn)的幾起案子贺奠,更是在濱河造成了極大的恐慌,老刑警劉巖错忱,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件敞嗡,死亡現(xiàn)場離奇詭異,居然都是意外死亡航背,警方通過查閱死者的電腦和手機(jī)喉悴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來玖媚,“玉大人箕肃,你說我怎么就攤上這事〗衲В” “怎么了勺像?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵障贸,是天一觀的道長。 經(jīng)常有香客問我吟宦,道長篮洁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任殃姓,我火速辦了婚禮袁波,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蜗侈。我一直安慰自己篷牌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布踏幻。 她就那樣靜靜地躺著枷颊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪该面。 梳的紋絲不亂的頭發(fā)上夭苗,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機(jī)與錄音隔缀,去河邊找鬼题造。 笑死,一個胖子當(dāng)著我的面吹牛蚕泽,可吹牛的內(nèi)容都是我干的晌梨。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼须妻,長吁一口氣:“原來是場噩夢啊……” “哼仔蝌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起荒吏,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤敛惊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后绰更,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瞧挤,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年儡湾,在試婚紗的時候發(fā)現(xiàn)自己被綠了特恬。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡徐钠,死狀恐怖癌刽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤显拜,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布衡奥,位于F島的核電站,受9級特大地震影響远荠,放射性物質(zhì)發(fā)生泄漏矮固。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一譬淳、第九天 我趴在偏房一處隱蔽的房頂上張望档址。 院中可真熱鬧,春花似錦瘦赫、人聲如沸辰晕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至替裆,卻和暖如春校辩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背辆童。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工宜咒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人把鉴。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓故黑,卻偏偏與公主長得像,于是被迫代替她去往敵國和親庭砍。 傳聞我的和親對象是個殘疾皇子场晶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

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