EP43 - 傳送

更新於 發佈於 閱讀時間約 12 分鐘
Teleport 看標題真的不知道是什麼?
原來我對網路的知識還是蠻缺乏的~
繼續看下去www

<Teleport> 是一個內建組件,允許我們將組件模板的一部分「傳送」到存在於該組件 DOM 階層之外的 DOM 節點中。

基本用法 - Basic Usage

有時我們可能會遇到以下情況:組件模板的一部分在邏輯上屬於該組件,但從視覺的角度來看,應該顯示在 DOM 中的其他位置,超出 Vue 應用程序的範疇。

最常見的例子是構建全屏模式對話框。理想情況下,我們希望模式對話框的按鈕和模式本身在同一組件內,因為它們都與模式的開/關狀態相關。但這意味著模式將與按鈕一起渲染,並深深嵌套在應用程序的 DOM 層次中。這在通過 CSS 定位模式時可能會產生一些棘手的問題。

考慮以下 HTML 結構。

<div class="outer">
<h3>Vue Teleport Example</h3>
<div>
<MyModal />
</div>
</div>

以下是 <MyModal> 的實現:

<script setup>
import { ref } from 'vue'

const open = ref(false)
</script>

<template>
<button @click="open = true">Open Modal</button>

<div v-if="open" class="modal">
<p>Hello from the modal!</p>
<button @click="open = false">Close</button>
</div>
</template>

<style scoped>
.modal {
position: fixed;
z-index: 999;
top: 20%;
left: 50%;
width: 300px;
margin-left: -150px;
}
</style>

該組件包含一個按鈕來觸發模式的開啟,以及一個 class 為 .modal 的 div,該 div 將包含模式的內容和一個用於自我關閉的按鈕。

在初始 HTML 結構中使用這個組件時,有許多潛在問題:

  • position: fixed 只會將元素相對於視口放置,當沒有祖先元素設置了 transform、perspective 或 filter 屬性時。如果我們打算用 CSS 變換動畫祖先 <div class="outer">,將會破壞模式的佈局!
  • 模式的 z-index 受其包含元素的限制。如果有其他元素與 <div class="outer"> 重疊並且具有更高的 z-index,則會覆蓋我們的模式。

<Teleport> 提供了一種乾淨的解決方法,通過允許我們脫離嵌套的 DOM 結構來解決這些問題。讓我們修改 <MyModal> 使用 <Teleport>

<button @click="open = true">Open Modal</button>

<Teleport to="body">
<div v-if="open" class="modal">
<p>Hello from the modal!</p>
<button @click="open = false">Close</button>
</div>
</Teleport>

<Teleport> 的 to 目標預期是一個 CSS 選擇器字符串或實際的 DOM 節點。在這裡,我們基本上是在告訴 Vue「將這個模板片段傳送到 body 標籤」。

您可以點擊下面的按鈕並通過瀏覽器的開發者工具檢查 <body> 標籤:

您可以將 <Teleport><Transition> 結合使用以創建動畫模式 - 參見此範例

提示

傳送的目標必須在 <Teleport> 組件掛載時已經存在於 DOM 中。理想情況下,這應該是整個 Vue 應用程序之外的元素。如果目標是由 Vue 渲染的其他元素,您需要確保該元素在 <Teleport> 之前已經掛載。

與組件一起使用 - Using with Components​

<Teleport> 僅改變渲染的 DOM 結構,不會影響組件的邏輯層次。也就是說,如果 <Teleport> 包含一個組件,該組件將仍然是包含 <Teleport> 的父組件的邏輯子組件。屬性傳遞和事件發射將繼續以相同的方式運作。

這也意味著來自父組件的注入將按預期工作,並且子組件將在 Vue Devtools 中嵌套在父組件之下,而不是被放置在實際內容移動到的地方。

禁用 Teleport - Disabling Teleport​

在某些情況下,我們可能希望有條件地禁用 <Teleport>。例如,我們可能希望在桌面上渲染一個組件作為覆蓋層,但在移動設備上以內嵌的方式呈現。<Teleport> 支援 disabled 屬性,可以動態切換:

<template>
<Teleport :disabled="isMobile">
...
</Teleport>
</template>

其中 isMobile 狀態可以通過檢測媒體查詢的變化來動態更新。

多個 Teleport 指向相同的目標 - Multiple Teleports on the Same Target​

一個常見的用例是可重用的 <Modal> 組件,可能同時有多個實例活躍。在這種情況下,多個 <Teleport> 組件可以將它們的內容掛載到相同的目標元素上。這樣的順序將是簡單的附加 - 後面掛載的內容將位於目標元素內的前面內容之後。

以下是用法示例:

<template>
<Teleport to="#modals">
<div>A</div>
</Teleport>
<Teleport to="#modals">
<div>B</div>
</Teleport>
</template>

渲染結果將為:

<div id="modals">
<div>A</div>
<div>B</div>
</div>

延遲 Teleport - Deferred Teleport ​

在 Vue 3.5 及以上版本中,我們可以使用 defer 屬性來延遲 Teleport 的目標解析,直到應用程序的其他部分已經掛載。這樣可以使 Teleport 目標指向由 Vue 渲染的容器元素,但位於組件樹的後面部分:

<template>
<Teleport defer to="#late-div">...</Teleport>

<!-- 在模板的稍後部分 -->
<div id="late-div"></div>
</template>

請注意,目標元素必須在與 Teleport 相同的掛載/更新循環中渲染 - 也就是說,如果 <div> 只在一秒後才掛載,Teleport 仍然會報告錯誤。defer 的工作方式類似於掛載生命週期鉤子。

相關文件

Q: 還是看不懂Teleport耶!?

<Teleport> 是 Vue.js 提供的一個內建組件,主要用於將組件的某部分渲染到 DOM 中的不同位置。這意味著即使某個元素邏輯上屬於某個組件,它也可以在視覺上被放置在 DOM 的其他位置。

主要用途:

  1. 模態框(Modal)和覆蓋層(Overlay)
    • 當你需要在整個應用程序的頂層顯示一個模態框時,使用 <Teleport> 可以避免層疊上下文(stacking context)造成的問題。例如,模態框通常需要有一個較高的 z-index 值來覆蓋其他內容,如果將模態框放在原本的組件中,可能會因為 z-index 被限制而無法正確顯示。使用 <Teleport> 可以將其渲染到 <body> 中,解決這個問題。
  2. 簡化 CSS 定位
    • 由於 CSS 的 position: fixedposition: absolute 定位可能會受到其父元素樣式的影響,使用 <Teleport> 可以使這些元素獨立於其邏輯結構,直接放到頁面的合適位置,避免層疊和定位的問題。

示例:

假設你有一個按鈕來打開模態框,你希望這個模態框在頁面最上層顯示,而不是在原本的按鈕旁邊。你可以這樣寫:

<template>
<button @click="open = true">打開模態框</button>

<Teleport to="body">
<div v-if="open" class="modal">
<p>這是模態框內容</p>
<button @click="open = false">關閉</button>
</div>
</Teleport>
</template>

在這個例子中,模態框的內容會被「瞬移」到 <body> 中,這樣它就不會受到任何父元素的影響,可以自由地用 CSS 定位並且不會被其他元素遮擋。

Q: DOM不同位置是什麼意思?

對於 DOM 的不同位置,指的是在 HTML 結構中元素的層級和順序。在 Web 開發中,DOM(文檔物件模型)是用來表示網頁結構的樹狀結構,所有的 HTML 元素都是這個樹的節點。每個節點都有其父節點和子節點,這決定了它們的層級關係。

什麼是不同位置?

  1. 層級結構
    • 每個 DOM 元素都有一個明確的位置,根據其在 HTML 樹中的位置來決定。例如,嵌套在 <div> 內的 <p> 標籤,其位置就被限制在這個 <div> 中。
  2. 渲染位置
    • 一個元素的渲染位置是它在視覺上顯示的位置。即使一個元素在邏輯結構中是某個組件的子元素,它也可以被移動到 DOM 中的其他位置進行渲染。這就是 <Teleport> 的用途。

例如:

假設你有以下 HTML 結構:

<div id="app">
<h1>我的網站</h1>
<button id="openModal">打開模態框</button>
</div>
  • 在這個結構中,<h1><button> 都是 <div id="app"> 的子元素。
  • 如果使用 <Teleport>,你可以將模態框的內容渲染到 <body> 中,而不是在 <div id="app"> 內部。這樣模態框就不會受到父元素的樣式影響,並且可以正確顯示在頁面最上層。
<Teleport to="body">
<div class="modal">
<p>這是模態框內容</p>
<button @click="closeModal">關閉</button>
</div>
</Teleport>

在這個例子中,模態框將會被渲染到 <body> 標籤下,而不是在原本的 <div id="app"> 結構內。這樣做可以解決層疊上下文和 z-index 的問題,使模態框能夠正確顯示。

avatar-img
2會員
71內容數
分享生活趣事~
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
卡關的人生 的其他內容
學會了使用 <KeepAlive>,這是一個內建組件,允許我們在動態切換多個組件時有條件地緩存組件實例,避免狀態丟失並提高速度。使用 include 和 exclude 屬性來自定義緩存行為,並透過 max 屬性限制緩存的實例數量。
<TransitionGroup> 是一個內建組件,專為對列表中的元素或組件進行插入、移除和順序變更的動畫處理而設計。與 <Transition> 的主要區別在於,<TransitionGroup> 預設不會渲染包裹元素,且不支持轉場模式。
<Transition> 用於元素或組件進入和離開 DOM 時的動畫,而 <TransitionGroup> 則應用於 v-for 列表的插入、移除或移動。這些組件透過 CSS 類別來控制動畫,例如進入/離開的狀態類別。可以透過 name 屬性自訂過渡效果的命名。
Vue 插件是自包含的代碼,通過 app.use() 方法安裝,用於擴展應用功能。插件可定義為包含 install() 方法的物件,或簡單的函數。插件用途包括註冊全局組件、自定義指令、資源注入和添加全局屬性。
Vue 自定義指令用於擴充功能,需要直接操作 DOM 時使用。常見例子如 v-focus 指令,使元素在插入 DOM 時自動獲得焦點。指令定義物件可提供多個生命週期鉤子函數,例如 mounted、updated 等。指令可以簡寫為函數,當行為在 mounted 和 updated 時相同時特別方便。
在學習 Vue 的可重用性(Reusability)時,Composables 是重要概念,專指可以重複使用的函式。這些函式利用 Vue 的 Composition API 封裝和重用有狀態的邏輯,幫助開發者在應用程式中進行常見任務的邏輯重用,例如格式化日期或追蹤鼠標位置。
學會了使用 <KeepAlive>,這是一個內建組件,允許我們在動態切換多個組件時有條件地緩存組件實例,避免狀態丟失並提高速度。使用 include 和 exclude 屬性來自定義緩存行為,並透過 max 屬性限制緩存的實例數量。
<TransitionGroup> 是一個內建組件,專為對列表中的元素或組件進行插入、移除和順序變更的動畫處理而設計。與 <Transition> 的主要區別在於,<TransitionGroup> 預設不會渲染包裹元素,且不支持轉場模式。
<Transition> 用於元素或組件進入和離開 DOM 時的動畫,而 <TransitionGroup> 則應用於 v-for 列表的插入、移除或移動。這些組件透過 CSS 類別來控制動畫,例如進入/離開的狀態類別。可以透過 name 屬性自訂過渡效果的命名。
Vue 插件是自包含的代碼,通過 app.use() 方法安裝,用於擴展應用功能。插件可定義為包含 install() 方法的物件,或簡單的函數。插件用途包括註冊全局組件、自定義指令、資源注入和添加全局屬性。
Vue 自定義指令用於擴充功能,需要直接操作 DOM 時使用。常見例子如 v-focus 指令,使元素在插入 DOM 時自動獲得焦點。指令定義物件可提供多個生命週期鉤子函數,例如 mounted、updated 等。指令可以簡寫為函數,當行為在 mounted 和 updated 時相同時特別方便。
在學習 Vue 的可重用性(Reusability)時,Composables 是重要概念,專指可以重複使用的函式。這些函式利用 Vue 的 Composition API 封裝和重用有狀態的邏輯,幫助開發者在應用程式中進行常見任務的邏輯重用,例如格式化日期或追蹤鼠標位置。
你可能也想看
Google News 追蹤
Thumbnail
大家好,我是woody,是一名料理創作者,非常努力地在嘗試將複雜的料理簡單化,讓大家也可以體驗到料理的樂趣而我也非常享受料理的過程,今天想跟大家聊聊,除了料理本身,料理創作背後的成本。
Thumbnail
哈囉~很久沒跟各位自我介紹一下了~ 大家好~我是爺恩 我是一名圖文插畫家,有追蹤我一段時間的應該有發現爺恩這個品牌經營了好像.....快五年了(汗)時間過得真快!隨著時間過去,創作這件事好像變得更忙碌了,也很開心跟很多厲害的創作者以及廠商互相合作幫忙,還有最重要的是大家的支持與陪伴🥹。  
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
如何在 Vite 專案中安裝和設置 TypeScript 及路徑別名的步驟,包括安裝必要的依賴、配置 vite.config.js、tsconfig.json 的設置,及如何創建類型聲明文件來正確識別 .vue 文件。
Thumbnail
各位使用 Vue.js 開發的小夥伴們,你們都怎麼實作父子層組件資料的雙向綁定呢?如果你還在寫 prop + emit 的話,不妨進來看看吧。
Thumbnail
Quasar Dialog 的 Invoking custom component 很好用,但是有些困擾的地方,一起來看看有甚麼辦法吧。
Thumbnail
切換頁面卡卡有很多種原因,這裡舉的例子只針對元件太大的情境。 除了想辦法拆分外,還有一個方法就是利用 Vue 的 Async Component。
Thumbnail
一開始你先把你的專案push上去後,修改vite.config.ts ,要在裡面新增  base: "/Cart/" (/放自己的專案名稱/) build: {outDir: "docs"}, 接下來你要去你的github setting 裡面 -> Page ->選Deploy fro
Thumbnail
你好,在下最近在學習開發web,學了html css js,也得出一些心得,由於網路上已有許多教學,所以我會著重在如何開發出to do List,以及解釋我寫的程式碼。相關的教學我會直接貼網址。如果我有什麼地方出錯,或者是可以寫得更好,歡迎在下方留言,討論。 首先先介紹我的開發環境: 我用了vs
「嵌入 Embedding」這個概念, 是指將「資料 Data」轉換為「向量 Vector」格式的過程。 資料可以是影片的樣本, 可以是音訊的樣本, 可以是圖片的樣本, 可以是文字的樣本。 不同類型的樣本, 可以透過相對應的預訓練神經網路模型, 將資料
Thumbnail
平常我們在 html 上常看到的例如 v-for、v-model 等等... 也是VUE已經幫我們定義好的指令,而這次我們可以依這自己的需求來建立。 此功能屬於較進階的功能,因此實戰中會比較少見,市面上還是有不少完善的套件能達到同樣效果,建議可以先往這方面察找
Thumbnail
大家好,我是woody,是一名料理創作者,非常努力地在嘗試將複雜的料理簡單化,讓大家也可以體驗到料理的樂趣而我也非常享受料理的過程,今天想跟大家聊聊,除了料理本身,料理創作背後的成本。
Thumbnail
哈囉~很久沒跟各位自我介紹一下了~ 大家好~我是爺恩 我是一名圖文插畫家,有追蹤我一段時間的應該有發現爺恩這個品牌經營了好像.....快五年了(汗)時間過得真快!隨著時間過去,創作這件事好像變得更忙碌了,也很開心跟很多厲害的創作者以及廠商互相合作幫忙,還有最重要的是大家的支持與陪伴🥹。  
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
如何在 Vite 專案中安裝和設置 TypeScript 及路徑別名的步驟,包括安裝必要的依賴、配置 vite.config.js、tsconfig.json 的設置,及如何創建類型聲明文件來正確識別 .vue 文件。
Thumbnail
各位使用 Vue.js 開發的小夥伴們,你們都怎麼實作父子層組件資料的雙向綁定呢?如果你還在寫 prop + emit 的話,不妨進來看看吧。
Thumbnail
Quasar Dialog 的 Invoking custom component 很好用,但是有些困擾的地方,一起來看看有甚麼辦法吧。
Thumbnail
切換頁面卡卡有很多種原因,這裡舉的例子只針對元件太大的情境。 除了想辦法拆分外,還有一個方法就是利用 Vue 的 Async Component。
Thumbnail
一開始你先把你的專案push上去後,修改vite.config.ts ,要在裡面新增  base: "/Cart/" (/放自己的專案名稱/) build: {outDir: "docs"}, 接下來你要去你的github setting 裡面 -> Page ->選Deploy fro
Thumbnail
你好,在下最近在學習開發web,學了html css js,也得出一些心得,由於網路上已有許多教學,所以我會著重在如何開發出to do List,以及解釋我寫的程式碼。相關的教學我會直接貼網址。如果我有什麼地方出錯,或者是可以寫得更好,歡迎在下方留言,討論。 首先先介紹我的開發環境: 我用了vs
「嵌入 Embedding」這個概念, 是指將「資料 Data」轉換為「向量 Vector」格式的過程。 資料可以是影片的樣本, 可以是音訊的樣本, 可以是圖片的樣本, 可以是文字的樣本。 不同類型的樣本, 可以透過相對應的預訓練神經網路模型, 將資料
Thumbnail
平常我們在 html 上常看到的例如 v-for、v-model 等等... 也是VUE已經幫我們定義好的指令,而這次我們可以依這自己的需求來建立。 此功能屬於較進階的功能,因此實戰中會比較少見,市面上還是有不少完善的套件能達到同樣效果,建議可以先往這方面察找