欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

尤雨溪開發(fā)vue?dev?server理解vite原理

 更新時(shí)間:2022年07月15日 17:26:00   作者:若川  
這篇文章主要為大家介紹了尤雨溪開發(fā)的玩具vite,vue-dev-server來理解vite原理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

1.引言

vuejs組織 下,找到了尤雨溪幾年前寫的“玩具 vite” vue-dev-server,發(fā)現(xiàn)100來行代碼,很值得學(xué)習(xí)。于是有了這篇文章。

閱讀本文,你將學(xué)到:

1. 學(xué)會(huì) vite 簡單原理

2. 學(xué)會(huì)使用 VSCode 調(diào)試源碼

3. 學(xué)會(huì)如何編譯 Vue 單文件組件

4. 學(xué)會(huì)如何使用 recast 生成 ast 轉(zhuǎn)換文件

5. 如何加載包文件

2. vue-dev-server 它的原理是什么

vue-dev-server#how-it-works README 文檔上有四句英文介紹。

發(fā)現(xiàn)谷歌翻譯的還比較準(zhǔn)確,我就原封不動(dòng)的搬運(yùn)過來。

  • 瀏覽器請(qǐng)求導(dǎo)入作為原生 ES 模塊導(dǎo)入 - 沒有捆綁。
  • 服務(wù)器攔截對(duì) *.vue 文件的請(qǐng)求,即時(shí)編譯它們,然后將它們作為 JavaScript 發(fā)回。
  • 對(duì)于提供在瀏覽器中工作的 ES 模塊構(gòu)建的庫,只需直接從 CDN 導(dǎo)入它們。
  • 導(dǎo)入到 .js 文件中的 npm 包(僅包名稱)會(huì)即時(shí)重寫以指向本地安裝的文件。 目前,僅支持 vue 作為特例。 其他包可能需要進(jìn)行轉(zhuǎn)換才能作為本地瀏覽器目標(biāo) ES 模塊公開。

也可以看看vitejs 文檔,了解下原理,文檔中圖畫得非常好。

看完本文后,我相信你會(huì)有一個(gè)比較深刻的理解。

3. 準(zhǔn)備工作

3.1 克隆項(xiàng)目

本文倉庫 vue-dev-server-analysis

# 推薦克隆我的倉庫
git clone https://github.com/lxchuan12/vue-dev-server-analysis.git
cd vue-dev-server-analysis/vue-dev-server
# npm i -g yarn
# 安裝依賴
yarn
# 或者克隆官方倉庫
git clone https://github.com/vuejs/vue-dev-server.git
cd vue-dev-server
# npm i -g yarn
# 安裝依賴
yarn

一般來說,我們看源碼先從package.json文件開始:

// vue-dev-server/package.json
{
  "name": "@vue/dev-server",
  "version": "0.1.1",
  "description": "Instant dev server for Vue single file components",
  "main": "middleware.js",
  // 指定可執(zhí)行的命令
  "bin": {
    "vue-dev-server": "./bin/vue-dev-server.js"
  },
  "scripts": {
    // 先跳轉(zhuǎn)到 test 文件夾,再用 Node 執(zhí)行 vue-dev-server 文件
    "test": "cd test && node ../bin/vue-dev-server.js"
  }
}

根據(jù) scripts test 命令。我們來看 test 文件夾。

3.2 test 文件夾

vue-dev-server/test 文件夾下有三個(gè)文件,代碼不長。

  • index.html
  • main.js
  • text.vue

如圖下圖所示。

接著我們找到 vue-dev-server/bin/vue-dev-server.js 文件,代碼也不長。

3.3 vue-dev-server.js

// vue-dev-server/bin/vue-dev-server.js
#!/usr/bin/env node
const express = require('express')
const { vueMiddleware } = require('../middleware')
const app = express()
const root = process.cwd();
app.use(vueMiddleware())
app.use(express.static(root))
app.listen(3000, () => {
  console.log('server running at http://localhost:3000')
})

原來就是express啟動(dòng)了端口3000的服務(wù)。重點(diǎn)在 vueMiddleware 中間件。接著我們來調(diào)試這個(gè)中間件。

鑒于估計(jì)很多小伙伴沒有用過VSCode調(diào)試,這里詳細(xì)敘述下如何調(diào)試源碼。學(xué)會(huì)調(diào)試源碼后,源碼并沒有想象中的那么難。

3.4 用 VSCode 調(diào)試項(xiàng)目

vue-dev-server/bin/vue-dev-server.js 文件中這行 app.use(vueMiddleware()) 打上斷點(diǎn)。

找到 vue-dev-server/package.jsonscripts,把鼠標(biāo)移動(dòng)到 test 命令上,會(huì)出現(xiàn)運(yùn)行腳本調(diào)試腳本命令。如下圖所示,選擇調(diào)試腳本。

點(diǎn)擊進(jìn)入函數(shù)(F11)按鈕可以進(jìn)入 vueMiddleware 函數(shù)。如果發(fā)現(xiàn)斷點(diǎn)走到不是本項(xiàng)目的文件中,不想看,看不懂的情況,可以退出或者重新來過??梢杂脼g覽器無痕(隱私)模式(快捷鍵Ctrl + Shift + N,防止插件干擾)打開 http://localhost:3000,可以繼續(xù)調(diào)試 vueMiddleware 函數(shù)返回的函數(shù)。

如果你的VSCode不是中文(不習(xí)慣英文),可以安裝簡體中文插件。
如果 VSCode 沒有這個(gè)調(diào)試功能。建議更新到最新版的 VSCode(目前最新版本 v1.61.2)。

接著我們來跟著調(diào)試學(xué)習(xí) vueMiddleware 源碼??梢韵瓤粗骶€,在你覺得重要的地方繼續(xù)斷點(diǎn)調(diào)試。

4. vueMiddleware 源碼

4.1 有無 vueMiddleware 中間件對(duì)比

不在調(diào)試情況狀態(tài)下,我們可以在 vue-dev-server/bin/vue-dev-server.js 文件中注釋 app.use(vueMiddleware()),執(zhí)行 npm run test 打開 http://localhost:3000。

再啟用中間件后,如下圖。

看圖我們大概知道了有哪些區(qū)別。

4.2 vueMiddleware 中間件概覽

我們可以找到vue-dev-server/middleware.js,查看這個(gè)中間件函數(shù)的概覽。

// vue-dev-server/middleware.js
const vueMiddleware = (options = defaultOptions) => {
  // 省略
  return async (req, res, next) => {
    // 省略
    // 對(duì) .vue 結(jié)尾的文件進(jìn)行處理
    if (req.path.endsWith('.vue')) {
    // 對(duì) .js 結(jié)尾的文件進(jìn)行處理
    } else if (req.path.endsWith('.js')) {
    // 對(duì) /__modules/ 開頭的文件進(jìn)行處理
    } else if (req.path.startsWith('/__modules/')) {
    } else {
      next()
    }
  }
}
exports.vueMiddleware = vueMiddleware

vueMiddleware 最終返回一個(gè)函數(shù)。這個(gè)函數(shù)里主要做了四件事:

  • 對(duì) .vue 結(jié)尾的文件進(jìn)行處理
  • 對(duì) .js 結(jié)尾的文件進(jìn)行處理
  • 對(duì) /__modules/ 開頭的文件進(jìn)行處理
  • 如果不是以上三種情況,執(zhí)行 next 方法,把控制權(quán)交給下一個(gè)中間件

接著我們來看下具體是怎么處理的。

我們也可以斷點(diǎn)這些重要的地方來查看實(shí)現(xiàn)。比如:

4.3 對(duì) .vue 結(jié)尾的文件進(jìn)行處理

if (req.path.endsWith('.vue')) {
  const key = parseUrl(req).pathname
  let out = await tryCache(key)
  if (!out) {
    // Bundle Single-File Component
    const result = await bundleSFC(req)
    out = result
    cacheData(key, out, result.updateTime)
  }
  send(res, out.code, 'application/javascript')
}

4.3.1 bundleSFC 編譯單文件組件

這個(gè)函數(shù),根據(jù) @vue/component-compiler 轉(zhuǎn)換單文件組件,最終返回瀏覽器能夠識(shí)別的文件。

const vueCompiler = require('@vue/component-compiler')
async function bundleSFC (req) {
  const { filepath, source, updateTime } = await readSource(req)
  const descriptorResult = compiler.compileToDescriptor(filepath, source)
  const assembledResult = vueCompiler.assemble(compiler, filepath, {
    ...descriptorResult,
    script: injectSourceMapToScript(descriptorResult.script),
    styles: injectSourceMapsToStyles(descriptorResult.styles)
  })
  return { ...assembledResult, updateTime }
}

接著我們來看 readSource 函數(shù)實(shí)現(xiàn)。

4.3.2 readSource 讀取文件資源

這個(gè)函數(shù)主要作用:根據(jù)請(qǐng)求獲取文件資源。返回文件路徑 filepath、資源 source、和更新時(shí)間 updateTime

const path = require('path')
const fs = require('fs')
const readFile = require('util').promisify(fs.readFile)
const stat = require('util').promisify(fs.stat)
const parseUrl = require('parseurl')
const root = process.cwd()
async function readSource(req) {
  const { pathname } = parseUrl(req)
  const filepath = path.resolve(root, pathname.replace(/^\//, ''))
  return {
    filepath,
    source: await readFile(filepath, 'utf-8'),
    updateTime: (await stat(filepath)).mtime.getTime()
  }
}
exports.readSource = readSource

接著我們來看對(duì) .js 文件的處理

4.4 對(duì) .js 結(jié)尾的文件進(jìn)行處理

if (req.path.endsWith('.js')) {
  const key = parseUrl(req).pathname
  let out = await tryCache(key)
  if (!out) {
    // transform import statements
    // 轉(zhuǎn)換 import 語句 
    // import Vue from 'vue'
    // => import Vue from "/__modules/vue"
    const result = await readSource(req)
    out = transformModuleImports(result.source)
    cacheData(key, out, result.updateTime)
  }
  send(res, out, 'application/javascript')
}

針對(duì) vue-dev-server/test/main.js 轉(zhuǎn)換

import Vue from 'vue'
import App from './test.vue'
new Vue({
  render: h => h(App)
}).$mount('#app')
import Vue from "/__modules/vue"
import App from './test.vue'
new Vue({
  render: h => h(App)
}).$mount('#app')

4.4.1 transformModuleImports 轉(zhuǎn)換 import 引入

recast

validate-npm-package-name

也就是針對(duì) npm 包轉(zhuǎn)換。 這里就是 "/__modules/vue"

import Vue from 'vue' => import Vue from "/__modules/vue"

4.5 對(duì) /__modules/ 開頭的文件進(jìn)行處理

import Vue from "/__modules/vue"

這段代碼最終返回的是讀取路徑 vue-dev-server/node_modules/vue/dist/vue.esm.browser.js 下的文件。

if (req.path.startsWith('/__modules/')) {
  // 
  const key = parseUrl(req).pathname
  const pkg = req.path.replace(/^\/__modules\//, '')
  let out = await tryCache(key, false) // Do not outdate modules
  if (!out) {
    out = (await loadPkg(pkg)).toString()
    cacheData(key, out, false) // Do not outdate modules
  }
  send(res, out, 'application/javascript')
}

4.5.1 loadPkg 加載包(這里只支持Vue文件)

目前只支持 Vue 文件,也就是讀取路徑 vue-dev-server/node_modules/vue/dist/vue.esm.browser.js 下的文件返回。

// vue-dev-server/loadPkg.js
const fs = require('fs')
const path = require('path')
const readFile = require('util').promisify(fs.readFile)
async function loadPkg(pkg) {
  if (pkg === 'vue') {
    // 路徑
    // vue-dev-server/node_modules/vue/dist
    const dir = path.dirname(require.resolve('vue'))
    const filepath = path.join(dir, 'vue.esm.browser.js')
    return readFile(filepath)
  }
  else {
    // TODO
    // check if the package has a browser es module that can be used
    // otherwise bundle it with rollup on the fly?
    throw new Error('npm imports support are not ready yet.')
  }
}
exports.loadPkg = loadPkg

至此,我們就基本分析完畢了主文件和一些引入的文件。對(duì)主流程有個(gè)了解。

5. 總結(jié)

最后我們來看上文中有無 vueMiddleware 中間件的兩張圖總結(jié)一下:

啟用中間件后,如下圖。

瀏覽器支持原生 type=module 模塊請(qǐng)求加載。vue-dev-server 對(duì)其攔截處理,返回瀏覽器支持內(nèi)容,因?yàn)闊o需打包構(gòu)建,所以速度很快。

<script type="module">
    import './main.js'
</script>

5.1 import Vue from 'vue' 轉(zhuǎn)換

// vue-dev-server/test/main.js
import Vue from 'vue'
import App from './test.vue'
new Vue({
  render: h => h(App)
}).$mount('#app')

main.js 中的 import 語句 import Vue from 'vue' 通過 recast 生成 ast 轉(zhuǎn)換成 import Vue from "/__modules/vue" 而最終返回給瀏覽器的是 vue-dev-server/node_modules/vue/dist/vue.esm.browser.js

5.2 import App from './test.vue' 轉(zhuǎn)換

main.js 中的引入 .vue 的文件,import App from './test.vue' 則用 @vue/component-compiler 轉(zhuǎn)換成瀏覽器支持的文件。

5.3 后續(xù)還能做什么?

鑒于文章篇幅有限,緩存 tryCache 部分目前沒有分析。簡單說就是使用了 node-lru-cache 最近最少使用 來做緩存的(這個(gè)算法常考)。后續(xù)應(yīng)該會(huì)分析這個(gè)倉庫的源碼,歡迎持續(xù)關(guān)注我@若川。

非常建議讀者朋友按照文中方法使用VSCode調(diào)試 vue-dev-server 源碼。源碼中還有很多細(xì)節(jié)文中由于篇幅有限,未全面展開講述。

值得一提的是這個(gè)倉庫的 master 分支,是尤雨溪兩年前寫的,相對(duì)本文會(huì)比較復(fù)雜,有余力的讀者可以學(xué)習(xí)。

也可以直接去看 vite 源碼。

看完本文,也許你就能發(fā)現(xiàn)其實(shí)前端能做的事情越來越多,不由感慨:前端水深不可測,唯有持續(xù)學(xué)習(xí),更多關(guān)于vue dev server理解vite原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Vue條件循環(huán)判斷+計(jì)算屬性+綁定樣式v-bind的實(shí)例

    Vue條件循環(huán)判斷+計(jì)算屬性+綁定樣式v-bind的實(shí)例

    今天小編就為大家分享一篇Vue條件循環(huán)判斷+計(jì)算屬性+綁定樣式v-bind的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-09-09
  • vue模板語法-插值詳解

    vue模板語法-插值詳解

    這篇文章主要介紹了vue模板語法-插值,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下
    2017-03-03
  • 結(jié)合el-upload組件實(shí)現(xiàn)大文件分片上傳功能

    結(jié)合el-upload組件實(shí)現(xiàn)大文件分片上傳功能

    Element UI的el-upload上傳組件相信各位小伙伴都已經(jīng)非常熟悉,最近接了一個(gè)新需求,要求在el-upload組件基礎(chǔ)上實(shí)現(xiàn)分片上傳功能,即小于等于5M文件正常上傳,大于5M文件切成5M每片上傳,那么這個(gè)功能怎么實(shí)現(xiàn)呢?一起看看吧
    2022-09-09
  • Vue報(bào)錯(cuò)Component?name"Home"should?always?be?multi問題

    Vue報(bào)錯(cuò)Component?name"Home"should?always?be?mult

    這篇文章主要介紹了Vue報(bào)錯(cuò)Component?name"Home"should?always?be?multi問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • vue swipeCell滑動(dòng)單元格(仿微信)的實(shí)現(xiàn)示例

    vue swipeCell滑動(dòng)單元格(仿微信)的實(shí)現(xiàn)示例

    這篇文章主要介紹了vue swipeCell滑動(dòng)單元格(仿微信)的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • 解決antd日期選擇組件,添加value就無法點(diǎn)擊下一年和下一月問題

    解決antd日期選擇組件,添加value就無法點(diǎn)擊下一年和下一月問題

    這篇文章主要介紹了解決antd日期選擇組件,添加value就無法點(diǎn)擊下一年和下一月問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • Vue實(shí)現(xiàn)購物車詳情頁面的方法

    Vue實(shí)現(xiàn)購物車詳情頁面的方法

    這篇文章主要介紹了Vue實(shí)戰(zhàn)之購物車詳情頁面的實(shí)現(xiàn),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-08-08
  • Vue3封裝localStorage基本使用示例詳解

    Vue3封裝localStorage基本使用示例詳解

    localStorage 和 sessionStorage 屬性允許在瀏覽器中存儲(chǔ) key/value 對(duì)的數(shù)據(jù),localStorage 用于長久保存整個(gè)網(wǎng)站的數(shù)據(jù),保存的數(shù)據(jù)沒有過期時(shí)間,直到手動(dòng)去刪除,本文給大家介紹Vue3封裝localStorage-基本使用,感興趣的朋友一起看看吧
    2023-12-12
  • vue新玩法VueUse工具庫具體用法@vueuse/core詳解

    vue新玩法VueUse工具庫具體用法@vueuse/core詳解

    這篇文章主要介紹了vue新玩法VueUse-工具庫@vueuse/core,VueUse不是Vue.use,它是一個(gè)基于?Composition?API?的實(shí)用函數(shù)集合,下面是具體的一些用法,需要的朋友可以參考下
    2022-08-08
  • vant4 封裝日期段選擇器的實(shí)現(xiàn)

    vant4 封裝日期段選擇器的實(shí)現(xiàn)

    本文主要介紹了vant4 封裝日期段選擇器的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03

最新評(píng)論