自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

給我五分鐘,保證教會你在vue3中動態(tài)加載遠程組件

開發(fā) 前端
這篇文章講了在vue3中如何從服務(wù)端加載遠程組件,首先我們需要使用defineAsyncComponent方法定義一個異步組件,這個異步組件是可以直接在template中像普通組件一樣使用。

前言

在一些特殊的場景中(比如低代碼、減少小程序包體積、類似于APP的熱更新),我們需要從服務(wù)端動態(tài)加載.vue文件,然后將動態(tài)加載的遠程vue組件渲染到我們的項目中。今天這篇文章我將帶你學(xué)會,在vue3中如何去動態(tài)加載遠程組件。

defineAsyncComponent異步組件

想必聰明的你第一時間就想到了defineAsyncComponent方法。我們先來看看官方對defineAsyncComponent方法的解釋:

定義一個異步組件,它在運行時是懶加載的。參數(shù)可以是一個異步加載函數(shù),或是對加載行為進行更具體定制的一個選項對象。

defineAsyncComponent方法的返回值是一個異步組件,我們可以像普通組件一樣直接在template中使用。和普通組件的區(qū)別是,只有當(dāng)渲染到異步組件時才會調(diào)用加載內(nèi)部實際組件的函數(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ù)端獲取遠程組件

有了defineAsyncComponent方法后事情從表面上看著就很簡單了,我們只需要寫個方法從服務(wù)端拿到vue文件的code代碼字符串,然后在defineAsyncComponent方法中使用resolve拿到的vue組件。

第一步就是本地起一個服務(wù)器,使用服務(wù)器返回我們的vue組件。這里我使用的是http-server,安裝也很簡單:

npm install http-server -g

使用上面的命令就可以全局安裝一個http服務(wù)器了。

接著我在項目的public目錄下新建一個名為remote-component.vue的文件,這個vue文件就是我們想從服務(wù)端加載的遠程組件。remote-component.vue文件中的代碼如下:

<template>
  <p>我是遠程組件</p>
  <p>
    當(dāng)前遠程組件count值為:<span class="count">{{ count }}</span>
  </p>
  <button @click="count++">點擊增加遠程組件count</button>
</template>

<script setup>
import { ref } from "vue";
const count = ref(0);
</script>

<style>
.count {
  color: red;
}
</style>

從上面的代碼可以看到遠程vue組件和我們平時寫的vue代碼沒什么區(qū)別,有template、ref響應(yīng)式變量、style樣式。

接著就是在終端執(zhí)行http-server ./public --cors命令啟動一個本地服務(wù)器,服務(wù)器默認端口為8080。但是由于我們本地起的vite項目默認端口為5173,所以為了避免跨域這里需要加--cors。 ./public的意思是指定當(dāng)前目錄的public文件夾。

啟動了一個本地服務(wù)器后,我們就可以使用 http://localhost:8080/remote-component.vue鏈接從服務(wù)端訪問遠程組件啦,如下圖:

圖片圖片

從上圖中可以看到在瀏覽器中訪問這個鏈接時觸發(fā)了下載遠程vue組件的操作。

defineAsyncComponent加載遠程組件

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ù)端拿到遠程組件的code代碼字符串應(yīng)該就行啦,代碼如下:

同時使用console.log("code", code)打個日志看一下從服務(wù)端過來的vue代碼。

上面的代碼看著已經(jīng)完美實現(xiàn)動態(tài)加載遠程組件了,結(jié)果不出意外在瀏覽器中運行時報錯了。如下圖:

圖片圖片

在上圖中可以看到從服務(wù)端拿到的遠程組件的代碼和我們的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++">點擊增加本地組件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ù)端拿到遠程vue組件源碼后,我們需要一個工具將拿到的vue組件源碼編譯成vue組件對象。幸運的是優(yōu)秀的vue不光暴露出一些常見的API,而且還將一些底層API給暴露了出來。比如在@vue/compiler-sfc包中就暴露出來了compileTemplate、compileScript、compileStyleAsync等方法。

如果你看過我寫的 vue3編譯原理揭秘 開源電子書(點擊閱讀原文跳轉(zhuǎn)到電子書),你應(yīng)該對這幾個方法覺得很熟悉。

  • 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ù)為遠程組件的URL,第二個參數(shù)為options。在options中有個getFile方法,獲取遠程組件的code代碼字符串就是在這里去實現(xiàn)的。

我們在終端來看看經(jīng)過loadModule函數(shù)處理后拿到的vue組件對象是什么樣的,如下圖:

圖片圖片

從上圖中可以看到經(jīng)過loadModule函數(shù)的處理后就拿到來vue組件對象啦,并且這個組件對象上面也有熟悉的render函數(shù)和setup函數(shù)。其中render函數(shù)是由遠程組件的template模塊編譯而來的,setup函數(shù)是由遠程組件的script模塊編譯而來的。

看到這里你可能有疑問,遠程組件的style模塊怎么沒有在生成的vue組件對象上面有提現(xiàn)呢?

答案是style模塊編譯成的css不會塞到vue組件對象上面去,而是單獨通過options上面的addStyle方法傳回給我們了。addStyle方法接收的參數(shù)textContent的值就是style模塊編譯而來css字符串,在addStyle方法中我們是創(chuàng)建了一個style標簽,然后將得到的css字符串插入到頁面中。

完整父組件代碼如下:

<template>
  <LocalChild />
  <div class="divider" />
  <button @click="showRemoteChild = true">加載遠程組件</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)點擊“加載遠程組件”按鈕后再去渲染遠程組件RemoteChild。我們來看看執(zhí)行效果,如下圖:

圖片圖片

從上面的gif圖中可以看到,當(dāng)我們點擊“加載遠程組件”按鈕后,在network中才去加載了遠程組件remote-component.vue。并且將遠程組件渲染到了頁面上后,通過按鈕的點擊事件可以看到遠程組件的響應(yīng)式依然有效。

vue3-sfc-loader同時也支持在遠程組件中去引用子組件,你只需在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方法
}

其實vue3-sfc-loader包的核心代碼就300行左右,主要就是調(diào)用vue暴露出來的一些底層API。如下圖:

圖片圖片

總結(jié)

這篇文章講了在vue3中如何從服務(wù)端加載遠程組件,首先我們需要使用defineAsyncComponent方法定義一個異步組件,這個異步組件是可以直接在template中像普通組件一樣使用。

但是由于defineAsyncComponent接收的組件必須是編譯后的vue組件對象,而我們從服務(wù)端拿到的遠程組件就是一個普通的vue文件,所以這時我們引入了vue3-sfc-loader包。vue3-sfc-loader包的作用就是在運行時將一個vue文件編譯成vue組件對象,這樣我們就可以實現(xiàn)從服務(wù)端加載遠程組件了。

責(zé)任編輯:武曉燕 來源: 前端歐陽
相關(guān)推薦

2020-11-06 08:54:43

Vue 3.0函數(shù)代碼

2009-11-25 09:02:12

2022-03-18 15:41:29

原子化服務(wù)HarmonyOS鴻蒙

2021-04-23 09:50:41

topLinux命令

2022-07-20 11:13:05

前端JSONVue3

2017-04-25 12:07:51

AndroidWebViewjs

2023-08-15 15:50:42

2011-06-12 18:30:07

投影機技巧

2022-03-08 08:39:22

gRPC協(xié)議云原生

2009-11-05 10:56:31

WCF通訊

2023-12-06 08:48:36

Kubernetes組件

2021-03-24 10:58:08

Vuesrc前端

2024-03-13 08:37:18

Vue3Suspense異步組件

2023-08-23 07:21:44

JsonSchema測試

2015-12-03 14:10:26

systemd容器Linux

2009-11-16 10:53:30

Oracle Hint

2024-12-11 07:00:00

面向?qū)ο?/a>代碼

2025-03-13 06:22:59

2020-06-16 08:47:53

磁盤

2023-04-26 15:17:33

Vue 3開發(fā)前端
點贊
收藏

51CTO技術(shù)棧公眾號