在vue3中動態(tài)加載遠(yuǎn)程組件的流程步驟
前言
在一些特殊的場景中(比如低代碼、減少小程序包體積、類似于APP的熱更新),我們需要從服務(wù)端動態(tài)加載.vue
文件,然后將動態(tài)加載的遠(yuǎn)程vue組件渲染到我們的項(xiàng)目中。今天這篇文章我將帶你學(xué)會,在vue3中如何去動態(tài)加載遠(yuǎn)程組件。
defineAsyncComponent異步組件
想必聰明的你第一時間就想到了defineAsyncComponent
方法。我們先來看看官方對defineAsyncComponent
方法的解釋:
定義一個異步組件,它在運(yùn)行時是懶加載的。參數(shù)可以是一個異步加載函數(shù),或是對加載行為進(jìn)行更具體定制的一個選項(xiàng)對象。
defineAsyncComponent
方法的返回值是一個異步組件,我們可以像普通組件一樣直接在template中使用。和普通組件的區(qū)別是,只有當(dāng)渲染到異步組件時才會調(diào)用加載內(nèi)部實(shí)際組件的函數(shù)。
我們先來簡單看看使用defineAsyncComponent
方法的例子,代碼如下:
import { defineAsyncComponent } from 'vue' const AsyncComp = defineAsyncComponent(() => { return new Promise((resolve, reject) => { // ...從服務(wù)器獲取組件 resolve(/* 獲取到的組件 */) }) }) // ... 像使用其他一般組件一樣使用 `AsyncComp`
defineAsyncComponent
方法接收一個返回 Promise 的回調(diào)函數(shù),在Promise中我們可以從服務(wù)端獲取vue組件的code代碼字符串。然后使用resolve(/* 獲取到的組件 */)
將拿到的組件傳給defineAsyncComponent
方法內(nèi)部處理,最后和普通組件一樣在template中使用AsyncComp
組件。
從服務(wù)端獲取遠(yuǎn)程組件
有了defineAsyncComponent
方法后事情從表面上看著就很簡單了我們只需要寫個方法從服務(wù)端拿到vue文件的code代碼字符串,然后在defineAsyncComponent
方法中使用resolve
拿到的vue組件。
第一步就是本地起一個服務(wù)器,使用服務(wù)器返回我們的vue組件。這里我使用的是http-server
,安裝也很簡單:
npm install http-server -g
使用上面的命令就可以全局安裝一個http服務(wù)器了。
接著我在項(xiàng)目的public目錄下新建一個名為remote-component.vue
的文件,這個vue文件就是我們想從服務(wù)端加載的遠(yuǎn)程組件。remote-component.vue
文件中的代碼如下:
<template> <p>我是遠(yuǎn)程組件</p> <p> 當(dāng)前遠(yuǎn)程組件count值為:<span class="count">{{ count }}</span> </p> <button @click="count++">點(diǎn)擊增加遠(yuǎn)程組件count</button> </template> <script setup> import { ref } from "vue"; const count = ref(0); </script> <style> .count { color: red; } </style>
從上面的代碼可以看到遠(yuǎn)程vue組件和我們平時寫的vue代碼沒什么區(qū)別,有template
、ref
響應(yīng)式變量、style
樣式。
接著就是在終端執(zhí)行http-server ./public --cors
命令啟動一個本地服務(wù)器,服務(wù)器默認(rèn)端口為8080
。但是由于我們本地起的vite項(xiàng)目默認(rèn)端口為5173
,所以為了避免跨域這里需要加--cors
。 ./public
的意思是指定當(dāng)前目錄的public
文件夾。
啟動了一個本地服務(wù)器后,我們就可以使用 http://localhost:8080/remote-component.vue鏈接從服務(wù)端訪問遠(yuǎn)程組件啦,如下圖:
從上圖中可以看到在瀏覽器中訪問這個鏈接時觸發(fā)了下載遠(yuǎn)程vue組件的操作。
defineAsyncComponent加載遠(yuǎn)程組件
const RemoteChild = defineAsyncComponent(async () => { return new Promise(async (resolve) => { const res = await fetch("http://localhost:8080/remote-component.vue"); const code = await res.text(); console.log("code", code); resolve(code); }); });
接下來我們就是在defineAsyncComponent
方法接收的 Promise 的回調(diào)函數(shù)中使用fetch從服務(wù)端拿到遠(yuǎn)程組件的code代碼字符串應(yīng)該就行啦,代碼如下:
同時使用console.log("code", code)
打個日志看一下從服務(wù)端過來的vue代碼。
上面的代碼看著已經(jīng)完美實(shí)現(xiàn)動態(tài)加載遠(yuǎn)程組件了,結(jié)果不出意外在瀏覽器中運(yùn)行時報錯了。如下圖:
在上圖中可以看到從服務(wù)端拿到的遠(yuǎn)程組件的代碼和我們的remote-component.vue
的源代碼是一樣的,但是為什么會報錯呢?
這里的報錯信息顯示加載異步組件報錯,還記得我們前面說過的defineAsyncComponent
方法是在回調(diào)中resolve(/* 獲取到的組件 */)
。而我們這里拿到的code
是一個組件嗎?
我們這里拿到的code
只是組件的源代碼,也就是常見的單文件組件SFC。而defineAsyncComponent
中需要的是由源代碼編譯后拿的的vue組件對象,我們將組件源代碼丟給defineAsyncComponent
當(dāng)然會報錯了。
看到這里有的小伙伴有疑問了,我們平時在父組件中import子組件不是也一樣在template就直接使用了嗎?
子組件local-child.vue
代碼:
<template> <p>我是本地組件</p> <p> 當(dāng)前本地組件count值為:<span class="count">{{ count }}</span> </p> <button @click="count++">點(diǎn)擊增加本地組件count</button> </template> <script setup> import { ref } from "vue"; const count = ref(0); </script> <style> .count { color: red; } </style>
父組件代碼:
<template> <LocalChild /> </template> <script setup lang="ts"> import LocalChild from "./local-child.vue"; console.log("LocalChild", LocalChild); </script>
上面的import導(dǎo)入子組件的代碼寫了這么多年你不覺得怪怪的嗎?
按照常理來說要import導(dǎo)入子組件,那么在子組件里面肯定要寫export才可以,但是在子組件local-child.vue
中我們沒有寫任何關(guān)于export的代碼。
答案是在父組件import導(dǎo)入子組件觸發(fā)了vue-loader或者@vitejs/plugin-vue插件的鉤子函數(shù),在鉤子函數(shù)中會將我們的源代碼單文件組件SFC編譯成一個普通的js文件,在js文件中export default
導(dǎo)出編譯后的vue組件對象。
這里使用console.log("LocalChild", LocalChild)
來看看經(jīng)過編譯后的vue組件對象是什么樣的,如下圖:
從上圖可以看到經(jīng)過編譯后的vue組件是一個對象,對象中有render
、setup
等方法。defineAsyncComponent方法接收的組件就是這樣的vue組件對象,但是我們前面卻是將vue組件源碼丟給他,當(dāng)然會報錯了。
最終解決方案vue3-sfc-loader
從服務(wù)端拿到遠(yuǎn)程vue組件源碼后,我們需要一個工具將拿到的vue組件源碼編譯成vue組件對象。幸運(yùn)的是優(yōu)秀的vue不光暴露出一些常見的API,而且還將一些底層API給暴露了出來。比如在@vue/compiler-sfc
包中就暴露出來了compileTemplate
、compileScript
、compileStyleAsync
等方法。
compileTemplate
方法:用于處理單文件組件SFC中的template模塊。compileScript
方法:用于處理單文件組件SFC中的script模塊。compileStyleAsync
方法:用于處理單文件組件SFC中的style模塊。
而vue3-sfc-loader
包的核心代碼就是調(diào)用@vue/compiler-sfc
包的這些方法,將我們的vue組件源碼編譯為想要的vue組件對象。 下面這個是改為使用vue3-sfc-loader
包后的代碼,如下:
import * as Vue from "vue"; import { loadModule } from "vue3-sfc-loader"; const options = { moduleCache: { vue: Vue, }, async getFile(url) { const res = await fetch(url); const code = await res.text(); return code; }, addStyle(textContent) { const style = Object.assign(document.createElement("style"), { textContent, }); const ref = document.head.getElementsByTagName("style")[0] || null; document.head.insertBefore(style, ref); }, }; const RemoteChild = defineAsyncComponent(async () => { const res = await loadModule( "http://localhost:8080/remote-component.vue", options ); console.log("res", res); return res; });
loadModule
函數(shù)接收的第一個參數(shù)為遠(yuǎn)程組件的URL,第二個參數(shù)為options
。在options
中有個getFile
方法,獲取遠(yuǎn)程組件的code代碼字符串就是在這里去實(shí)現(xiàn)的。
我們在終端來看看經(jīng)過loadModule
函數(shù)處理后拿到的vue組件對象是什么樣的,如下圖:
從上圖中可以看到經(jīng)過loadModule
函數(shù)的處理后就拿到來vue組件對象啦,并且這個組件對象上面也有熟悉的render
函數(shù)和setup
函數(shù)。其中render
函數(shù)是由遠(yuǎn)程組件的template模塊編譯而來的,setup
函數(shù)是由遠(yuǎn)程組件的script模塊編譯而來的。
看到這里你可能有疑問,遠(yuǎn)程組件的style模塊怎么沒有在生成的vue組件對象上面有提現(xiàn)呢?
答案是style模塊編譯成的css不會塞到vue組件對象上面去,而是單獨(dú)通過options
上面的addStyle
方法傳回給我們了。addStyle
方法接收的參數(shù)textContent
的值就是style模塊編譯而來css字符串,在addStyle
方法中我們是創(chuàng)建了一個style標(biāo)簽,然后將得到的css字符串插入到頁面中。
完整父組件代碼如下:
<template> <LocalChild /> <div class="divider" /> <button @click="showRemoteChild = true">加載遠(yuǎn)程組件</button> <RemoteChild v-if="showRemoteChild" /> </template> <script setup lang="ts"> import { defineAsyncComponent, ref, onMounted } from "vue"; import * as Vue from "vue"; import { loadModule } from "vue3-sfc-loader"; import LocalChild from "./local-child.vue"; const showRemoteChild = ref(false); const options = { moduleCache: { vue: Vue, }, async getFile(url) { const res = await fetch(url); const code = await res.text(); return code; }, addStyle(textContent) { const style = Object.assign(document.createElement("style"), { textContent, }); const ref = document.head.getElementsByTagName("style")[0] || null; document.head.insertBefore(style, ref); }, }; const RemoteChild = defineAsyncComponent(async () => { const res = await loadModule( "http://localhost:8080/remote-component.vue", options ); console.log("res", res); return res; }); </script> <style scoped> .divider { background-color: red; width: 100vw; height: 1px; margin: 20px 0; } </style>
在上面的完整例子中,首先渲染了本地組件LocalChild
。然后當(dāng)點(diǎn)擊“加載遠(yuǎn)程組件”按鈕后再去渲染遠(yuǎn)程組件RemoteChild
。我們來看看執(zhí)行效果,如下圖:
從上面的gif圖中可以看到,當(dāng)我們點(diǎn)擊“加載遠(yuǎn)程組件”按鈕后,在network中才去加載了遠(yuǎn)程組件remote-component.vue
。并且將遠(yuǎn)程組件渲染到了頁面上后,通過按鈕的點(diǎn)擊事件可以看到遠(yuǎn)程組件的響應(yīng)式依然有效。
vue3-sfc-loader
同時也支持在遠(yuǎn)程組件中去引用子組件,你只需在options
額外配置一個pathResolve
就行啦。pathResolve
方法配置如下
const options = { pathResolve({ refPath, relPath }, options) { if (relPath === ".") // self return refPath; // relPath is a module name ? if (relPath[0] !== "." && relPath[0] !== "/") return relPath; return String( new URL(relPath, refPath === undefined ? window.location : refPath) ); }, // getFile方法 // addStyle方法 }
其實(shí)vue3-sfc-loader
包的核心代碼就300行左右,主要就是調(diào)用vue暴露出來的一些底層API。如下圖:
總結(jié)
這篇文章講了在vue3中如何從服務(wù)端加載遠(yuǎn)程組件,首先我們需要使用defineAsyncComponent
方法定義一個異步組件,這個異步組件是可以直接在template中像普通組件一樣使用。
但是由于defineAsyncComponent
接收的組件必須是編譯后的vue組件對象,而我們從服務(wù)端拿到的遠(yuǎn)程組件就是一個普通的vue文件,所以這時我們引入了vue3-sfc-loader
包。vue3-sfc-loader
包的作用就是在運(yùn)行時將一個vue文件編譯成vue組件對象,這樣我們就可以實(shí)現(xiàn)從服務(wù)端加載遠(yuǎn)程組件了。
以上就是在vue3中動態(tài)加載遠(yuǎn)程組件的流程步驟的詳細(xì)內(nèi)容,更多關(guān)于vue3加載遠(yuǎn)程組件的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue使用axios?post發(fā)送json數(shù)據(jù)跨域請求403的解決方案
這篇文章主要介紹了vue使用axios?post發(fā)送json數(shù)據(jù)跨域請求403的解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12vue導(dǎo)出excel多層表頭的實(shí)現(xiàn)方案詳解
這篇文章主要為大家詳細(xì)介紹了vue導(dǎo)出excel多層表頭的實(shí)現(xiàn)方案,文中的示例代碼簡潔易懂,具有一定的借鑒價值,有需要的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-04-04Vee-validate 父組件獲取子組件表單校驗(yàn)結(jié)果的實(shí)例代碼
vee-validate 是為 Vue.js 量身打造的表單校驗(yàn)框架,允許您校驗(yàn)輸入的內(nèi)容并顯示對應(yīng)的錯誤提示信息。這篇文章主要介紹了Vee-validate 父組件獲取子組件表單校驗(yàn)結(jié)果 ,需要的朋友可以參考下2019-05-05在Vue3和TypeScript中大文件分片上傳的實(shí)現(xiàn)與優(yōu)化
本文介紹在 Vue 3 和 TypeScript 環(huán)境下大文件分片上傳的實(shí)現(xiàn)與優(yōu)化,包括項(xiàng)目前后端技術(shù)棧,前端的文件切片、并發(fā)上傳、計算 Hash、斷點(diǎn)續(xù)傳和用戶體驗(yàn)優(yōu)化,后端的文件接收存儲、切片合并、異常處理與日志記錄,還提及遇到的問題及解決方案,總結(jié)了此方式的優(yōu)勢和重要性2025-01-01VUE+elementui組件在table-cell單元格中繪制微型echarts圖
這篇文章主要介紹了VUE+elementui組件在table-cell單元格中繪制微型echarts圖,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04vue項(xiàng)目打包部署_nginx代理訪問方法詳解
今天小編就為大家分享一篇vue項(xiàng)目打包部署_nginx代理訪問方法詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-09-09