從Vue轉(zhuǎn)換看Webpack與Vite 代碼轉(zhuǎn)換機制差異詳解
配置方式
我們知道,Webpack 是使用 loader 轉(zhuǎn)換代碼的,而 Vite/Rollup 則是使用插件轉(zhuǎn)換代碼,那這兩種機制有什么差異呢?我們用 Vue 的轉(zhuǎn)換來說明一下。
Vite 使用插件轉(zhuǎn)換代碼,直接在 plugins 使用 @vitejs/plugin-vue 即可
// vite.config.js
import vue from '@vitejs/plugin-vue'
export default {
plugins: [vue(), /* 其他插件 */ ]
}
Webpack 使用 loader 轉(zhuǎn)換代碼,有時候需要同時配合 Plugin 才能完成代碼轉(zhuǎn)換(例如 Vue)
// webpack.config.js
const { VueLoaderPlugin } = require('vue-loader')
module.exports = {
mode: 'development',
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
// 它會應(yīng)用到普通的 `.js` 文件
// 以及 `.vue` 文件中的 `<script>` 塊
{
test: /\.js$/,
loader: 'babel-loader'
},
// 它會應(yīng)用到普通的 `.css` 文件
// 以及 `.vue` 文件中的 `<style>` 塊
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
}
]
},
plugins: [
new VueLoaderPlugin()
]
}
為什么 webpack 使用 loader 還不夠,還需要 Vue plugin?
這個問題我們留在后面說明
Vue 文件編譯的流程
下面是一個簡單的 Vue SFC (單文件組件):
<script setup>
import { ref } from 'vue'
const msg = ref('Hello World!')
</script>
<template>
<h2>{{ msg }}</h2>
</template>
<style>
h2{
font-size: 50px
}
</style>
Vue SFC 分為 3 個部分:
- script,可以是 JS、TS 等語法
- template(會被轉(zhuǎn)換成 render 函數(shù))
- style,可以是 CSS、Less 等語法
由于 Vue 文件包含三個部分,而一個模塊經(jīng)過轉(zhuǎn)換后仍然是一個模塊(例如經(jīng)過 loader 轉(zhuǎn)換后,仍然是一份代碼,不能變成三個部分)
但我們可以用一個巧妙的辦法去解決這個問題:使用一個臨時模塊,去分別引入 script、template、style,并將其組合,偽代碼如下:
// 引入 main script,獲取到的是組件的配置對象
import script from './Main.vue?vue&type=script'
// 引入 template
import { render } from './Main.vue?vue&type=template&id=xxxxxx'
// 引入 css
import './Main.vue?vue&type=style&index=0&id=xxxxxx'
// 給組件對象設(shè)置 render 函數(shù)
script.render = render
// 設(shè)置一些元信息,在開發(fā)環(huán)境有用
script.__file = 'example.vue'
// style 的 scope id,用于組件樣式隔離
script.__scopeId = 'xxxxxx'
export default script
一個 Vue 的會有大致如下的處理流程:
- 將 Vue SFC 轉(zhuǎn)換成臨時模塊,分別引入 script、template、style
- vue-loader/插件會保存 script、template、style 的內(nèi)容
- 打包工具遇到 import 語句,會分別處理:
- script:從 vue-loader/插件中,取出之前緩存的 script,然后交給其他 JS loader/插件處理(如 babel)
- template:從 vue-loader/插件中,取出之前緩存的 template,然后交給其他 JS loader/插件處理(因為 template 轉(zhuǎn)換成 render 函數(shù),這部分也是 JS 類型)
- style:從 vue-loader/插件中,取出之前緩存的 style,然后交給其他 Style loader/插件處理(如 Less)

Vue 的轉(zhuǎn)換,在 webpack 和 vite 都是類似的思路,只不過由于 webpack 和 Vite 的機制不同,在 Vue 的轉(zhuǎn)換插件上的的使用和實現(xiàn)上,也會有所差異。
Vite 的 Vue 轉(zhuǎn)換流程
Vite/Rollup 使用插件轉(zhuǎn)換模塊,由于沒有顯式地聲明模塊跟插件的匹配規(guī)則(例如 webpack 顯式聲明了 Vue 文件用 vue-loader 處理),因此每個模塊的轉(zhuǎn)換都需要經(jīng)過所有的插件
插件只能處理它能處理的模塊(例如:Vue 插件不能后處理 less 模塊),Vite/Rollup 插件必須要在插件內(nèi)部對模塊類型進(jìn)行判斷,然后后決定是否進(jìn)行處理。
export default function vuePlugin() {
return {
name: 'transform-vue',
transform(source, id) {
// source 文件的內(nèi)容或上一個插件轉(zhuǎn)換過的內(nèi)容
// id 一般為文件的真實路徑,需要在插件內(nèi)判斷文件是否為 vue 后綴
if (isVueFile(id)) {
// 對 Vue 模塊進(jìn)行轉(zhuǎn)換
return // 返回轉(zhuǎn)換后的內(nèi)容
}
// 其他類型模塊不作處理
}
}
}
上面的插件,就只對 Vue 模塊進(jìn)行處理,其他的模塊,則直接交給下一個插件處理。
Vite Vue 插件的大致處理流程如下:
./Main.vue在 load 階段,會依次經(jīng)過所有插件,如果沒有被處理,則默認(rèn)是讀取文件的內(nèi)容。(一般情況下也不需要處理)./Main.vue在 transform 階段,會依次經(jīng)過所有插件,經(jīng)過 Vue 處理后(分離 template、script、style),會轉(zhuǎn)換成臨時模塊,然后再經(jīng)過其他插件處理(例如 babel)- 打包工具解析轉(zhuǎn)換后的代碼,遇到
./Main.vue?vue&type=script ./Main.vue?vue&type=script在 load 階段,會依次經(jīng)過所有插件,經(jīng)過 Vue 插件,從之前的緩存中,取出 script 部分(如果插件執(zhí)行 load 階段時有返回值,則立即結(jié)束 load 階段)./Main.vue?vue&type=script在 transform 階段,會依次經(jīng)過所有插件,最終得到轉(zhuǎn)換后的代碼

template 和 style 部分類似就不重復(fù)寫了。
需要注意的是,這跟 @vite/plugin-vue 實際的處理方式不完全一致,主要的區(qū)別是:我們這里在臨時模塊,引入了 template、script、style 三個部分,實際上,可以直接將 template、script 內(nèi)聯(lián)到臨時模塊,這樣就只需要 import style 部分即可。
Webpack 的 Vue 轉(zhuǎn)換流程
在 webpack 的配置文件中,需要顯式聲明 rule,為對應(yīng)的模塊配置對應(yīng)的 loader。
// webpack.config.js
{
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
// 它會應(yīng)用到普通的 `.js` 文件
// 以及 `.vue` 文件中的 `<script>` 塊
{
test: /\.js$/,
loader: 'babel-loader'
},
// 它會應(yīng)用到普通的 `.css` 文件
// 以及 `.vue` 文件中的 `<style>` 塊
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
}
]
},
}
配置 Vue 時,我們做了如下配置:
- Vue 文件會交給 vue-loader 處理
- js 文件給 babel-loader 處理
- CSS 文件給 css-loader 和 style-loader 處理

我們再來回顧一下這個流程:
Main.vue匹配中 vue-loader,被處理成臨時模塊./Main.vue?vue&type=script匹配中 vue-loader(webpack 會去掉 query 部分,因此/\.vue$/可以匹配),從緩存中取出 Vue SFC script 的內(nèi)容。
到了這一步,我們會發(fā)現(xiàn),匹配不到其他 loader 了,因為 babel-loader 匹配的規(guī)則是 /\.js$/,這樣轉(zhuǎn)換就沒辦法再進(jìn)行下去了,這就是 webpack loader 機制的局限性。
因此僅僅使用 loader,是沒有辦法將 JS、CSS 傳遞給對應(yīng) loader 處理的,這也是 webpack loader 機制的局限性
為了解決這個問題,借助 webpack plugin:
// webpack.config.js
const { VueLoaderPlugin } = require('vue-loader')
module.exports = {
module: {
rules: [
// 省略...
]
},
plugins: [
new VueLoaderPlugin()
]
}
VueLoaderPlugin 做了什么?
VueLoaderPlugin 的內(nèi)容比較復(fù)雜,本文不會詳細(xì)的說明。這里直說最終的轉(zhuǎn)換結(jié)果:
Webpack 提供一種內(nèi)聯(lián) loader 的能力:
import script from "-!babel-loader!vue-loader??ref--0!./App.vue?vue&type=script&setup=true&lang=js"
這種內(nèi)聯(lián) loader 的能力,在 import 的路徑中顯式的指定了該模塊會經(jīng)過的 loader:
- 從后往前看,最后的是處理的文件
- loader 的執(zhí)行順序為從右到左(loader 用 ! 分割)
VueLoaderPlugin 會為 script、template、style,根據(jù)不同給的類型,生成不同的內(nèi)聯(lián) loader import 語句,使它們能夠正確地被其他的 loader 處理。
對比和總結(jié)
webpack 顯式指定了模塊對應(yīng)的 loader,正是這個機制,導(dǎo)致 vue SFC 的 script、template、style,沒辦法被其他 loader 處理,需要插件做一些復(fù)雜的操作,最終用 Inline loader import 強制指定 loader,整個過程比較復(fù)雜。
Vite/Rollup 的模塊會經(jīng)過所有的插件,在插件中過濾出需要處理的模塊,其他的交給下一個插件處理。這樣的機制使 Vue 文件的各個部分,能經(jīng)過所有插件的處理,從而避免了 webpack 遇到的問題,這也使 Vue 在 Vite/Rollup 中的轉(zhuǎn)換實現(xiàn)更為清晰和簡單。
最后,我們通過這樣的對比,目的不能說明 Webpack/Vite/Rollup 誰好誰壞,而是在學(xué)習(xí)過程中,橫向?qū)Ρ龋由顚λ鼈兊牧私狻?/p>
以上就是從Vue轉(zhuǎn)換看Webpack與Vite 代碼轉(zhuǎn)換機制差異詳解的詳細(xì)內(nèi)容,更多關(guān)于Vue Webpack Vite代碼轉(zhuǎn)換機制差異的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue將秒數(shù)轉(zhuǎn)成"時分秒"格式實例代碼
在項目中,請求后臺接口返回的值是秒,這篇文章主要給大家介紹了關(guān)于vue將秒數(shù)轉(zhuǎn)成"時分秒"格式的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-06-06
在Vue3中創(chuàng)建和使用自定義指令的實現(xiàn)方式
在現(xiàn)代前端開發(fā)中,Vue.js 是一個非常流行的框架,它提供了許多強大的功能來幫助開發(fā)者構(gòu)建高效的用戶界面,自定義指令是 Vue.js 的一個重要特性,它允許開發(fā)者擴展 HTML 元素的功能,本文將詳細(xì)介紹如何在 Vue3 中創(chuàng)建和使用自定義指令,并提供示例代碼來幫助理解2024-12-12
基于vue3與supabase系統(tǒng)認(rèn)證機制詳解
這篇文章主要介紹了基于vue3與supabase系統(tǒng)認(rèn)證機制,,系統(tǒng)使用基于 JWT (JSON Web Token) 的認(rèn)證方式,提供了安全可靠的用戶身份管理機制,需要的朋友可以參考下2025-04-04

