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

Vue3 Teleport 組件的實(shí)踐及原理

開發(fā) 前端
Vue3 的組合式 API 以及基于 Proxy 響應(yīng)式原理已經(jīng)有很多文章介紹過了,除了這些比較亮眼的更新,Vue3 還新增了一個(gè)內(nèi)置組件:Teleport。這個(gè)組件的作用主要用來將模板內(nèi)的 DOM 元素移動(dòng)到其他位置。

[[354904]]

Vue3 的組合式 API 以及基于 Proxy 響應(yīng)式原理已經(jīng)有很多文章介紹過了,除了這些比較亮眼的更新,Vue3 還新增了一個(gè)內(nèi)置組件:Teleport。這個(gè)組件的作用主要用來將模板內(nèi)的 DOM 元素移動(dòng)到其他位置。

使用場景

業(yè)務(wù)開發(fā)的過程中,我們經(jīng)常會(huì)封裝一些常用的組件,例如 Modal 組件。相信大家在使用 Modal 組件的過程中,經(jīng)常會(huì)遇到一個(gè)問題,那就是 Modal 的定位問題。

話不多說,我們先寫一個(gè)簡單的 Modal 組件。

  1. <!-- Modal.vue --> 
  2. <style lang="scss"
  3. .modal { 
  4.   &__mask { 
  5.     position: fixed; 
  6.     top: 0; 
  7.     left: 0; 
  8.     width: 100vw; 
  9.     height: 100vh; 
  10.     background: rgba(0, 0, 0, 0.5); 
  11.   } 
  12.   &__main { 
  13.     margin: 0 auto; 
  14.     margin-bottom: 5%; 
  15.     margin-top: 20%; 
  16.     width: 500px; 
  17.     background: #fff; 
  18.     border-radius: 8px; 
  19.   } 
  20.   /* 省略部分樣式 */ 
  21. </style> 
  22. <template> 
  23.   <div class="modal__mask"
  24.     <div class="modal__main"
  25.       <div class="modal__header"
  26.         <h3 class="modal__title">彈窗標(biāo)題</h3> 
  27.         <span class="modal__close">x</span> 
  28.       </div> 
  29.       <div class="modal__content"
  30.         彈窗文本內(nèi)容 
  31.       </div> 
  32.       <div class="modal__footer"
  33.         <button>取消</button> 
  34.         <button>確認(rèn)</button> 
  35.       </div> 
  36.     </div> 
  37.   </div> 
  38. </template> 
  39.  
  40. <script> 
  41. export default { 
  42.   setup() { 
  43.     return {}; 
  44.   }, 
  45. }; 
  46. </script> 

然后我們在頁面中引入 Modal 組件。

  1. <!-- App.vue --> 
  2. <style lang="scss"
  3. .container { 
  4.   height: 80vh; 
  5.   margin: 50px; 
  6.   overflow: hidden; 
  7. </style> 
  8. <template> 
  9.   <div class="container"
  10.     <Modal /> 
  11.   </div> 
  12. </template> 
  13.  
  14. <script> 
  15. export default { 
  16.   components: { 
  17.     Modal, 
  18.   }, 
  19.   setup() { 
  20.     return {}; 
  21.   } 
  22. }; 
  23. </script> 

Modal

如上圖所示, div.container 下彈窗組件正常展示。使用 fixed 進(jìn)行布局的元素,在一般情況下會(huì)相對(duì)于屏幕視窗來進(jìn)行定位,但是如果父元素的 transform, perspective 或 filter 屬性不為 none 時(shí),fixed 元素就會(huì)相對(duì)于父元素來進(jìn)行定位。

我們只需要把 .container 類的 transform 稍作修改,彈窗組件的定位就會(huì)錯(cuò)亂。

  1. <style lang="scss"
  2. .container { 
  3.   height: 80vh; 
  4.   margin: 50px; 
  5.   overflow: hidden; 
  6.   transform: translateZ(0); 
  7. </style> 

Modal

這個(gè)時(shí)候,使用 Teleport 組件就能解決這個(gè)問題了。

“Teleport 提供了一種干凈的方法,允許我們控制在 DOM 中哪個(gè)父節(jié)點(diǎn)下呈現(xiàn) HTML,而不必求助于全局狀態(tài)或?qū)⑵洳鸱譃閮蓚€(gè)組件。-- Vue 官方文檔

我們只需要將彈窗內(nèi)容放入 Teleport 內(nèi),并設(shè)置 to 屬性為 body,表示彈窗組件每次渲染都會(huì)做為 body 的子級(jí),這樣之前的問題就能得到解決。

  1. <template> 
  2.   <teleport to="body"
  3.     <div class="modal__mask"
  4.       <div class="modal__main"
  5.         ... 
  6.       </div> 
  7.     </div> 
  8.   </teleport> 
  9. </template> 

可以在 https://codesandbox.io/embed/vue-modal-h5g8y 查看代碼。

使用 Teleport 的 Modal

源碼解析

我們可以先寫一個(gè)簡單的模板,然后看看 Teleport 組件經(jīng)過模板編譯后,生成的代碼。

  1. Vue.createApp({ 
  2.   template: ` 
  3.     <Teleport to="body"
  4.       <div> teleport to body </div>   
  5.     </Teleport> 
  6.   ` 
  7. }) 

模板編譯后的代碼

簡化后代碼:

  1. function render(_ctx, _cache) { 
  2.   with (_ctx) { 
  3.     const { createVNode, openBlock, createBlock, Teleport } = Vue 
  4.     return (openBlock(), createBlock(Teleport, { to"body" }, [ 
  5.       createVNode("div"null" teleport to body ", -1 /* HOISTED */) 
  6.     ])) 
  7.   } 

可以看到 Teleport 組件通過 createBlock 進(jìn)行創(chuàng)建。

  1. // packages/runtime-core/src/renderer.ts 
  2. export function createBlock( 
  3.  type, props, children, patchFlag 
  4. ) { 
  5.   const vnode = createVNode( 
  6.     type, 
  7.     props, 
  8.     children, 
  9.     patchFlag 
  10.   ) 
  11.   // ... 省略部分邏輯 
  12.   return vnode 
  13.  
  14. export function createVNode( 
  15.   type, props, children, patchFlag 
  16. ) { 
  17.   // class & style normalization. 
  18.   if (props) { 
  19.     // ... 
  20.   } 
  21.  
  22.   // encode the vnode type information into a bitmap 
  23.   const shapeFlag = isString(type) 
  24.     ? ShapeFlags.ELEMENT 
  25.     : __FEATURE_SUSPENSE__ && isSuspense(type) 
  26.       ? ShapeFlags.SUSPENSE 
  27.       : isTeleport(type) 
  28.         ? ShapeFlags.TELEPORT 
  29.         : isObject(type) 
  30.           ? ShapeFlags.STATEFUL_COMPONENT 
  31.           : isFunction(type) 
  32.             ? ShapeFlags.FUNCTIONAL_COMPONENT 
  33.             : 0 
  34.  
  35.   const vnode: VNode = { 
  36.     type, 
  37.     props, 
  38.     shapeFlag, 
  39.     patchFlag, 
  40.     key: props && normalizeKey(props), 
  41.     ref: props && normalizeRef(props), 
  42.   } 
  43.  
  44.   return vnode 
  45.  
  46. // packages/runtime-core/src/components/Teleport.ts 
  47. export const isTeleport = type => type.__isTeleport 
  48. export const Teleport = { 
  49.   __isTeleport: true
  50.   process() {} 

傳入 createBlock 的第一個(gè)參數(shù)為 Teleport,最后得到的 vnode 中會(huì)有一個(gè) shapeFlag 屬性,該屬性用來表示 vnode 的類型。isTeleport(type) 得到的結(jié)果為 true,所以 shapeFlag 屬性最后的值為 ShapeFlags.TELEPORT(1 << 6)。

  1. // packages/shared/src/shapeFlags.ts 
  2. export const enum ShapeFlags { 
  3.   ELEMENT = 1, 
  4.   FUNCTIONAL_COMPONENT = 1 << 1, 
  5.   STATEFUL_COMPONENT = 1 << 2, 
  6.   TEXT_CHILDREN = 1 << 3, 
  7.   ARRAY_CHILDREN = 1 << 4, 
  8.   SLOTS_CHILDREN = 1 << 5, 
  9.   TELEPORT = 1 << 6, 
  10.   SUSPENSE = 1 << 7, 
  11.   COMPONENT_SHOULD_KEEP_ALIVE = 1 << 8, 
  12.   COMPONENT_KEPT_ALIVE = 1 << 9 

在組件的 render 節(jié)點(diǎn),會(huì)依據(jù) type 和 shapeFlag 走不同的邏輯。

  1. // packages/runtime-core/src/renderer.ts 
  2. const render = (vnode, container) => { 
  3.   if (vnode == null) { 
  4.     // 當(dāng)前組件為空,則將組件銷毀 
  5.     if (container._vnode) { 
  6.       unmount(container._vnode, nullnulltrue
  7.     } 
  8.   } else { 
  9.     // 新建或者更新組件 
  10.     // container._vnode 是之前已創(chuàng)建組件的緩存 
  11.     patch(container._vnode || null, vnode, container) 
  12.   } 
  13.   container._vnode = vnode 
  14.  
  15. // patch 是表示補(bǔ)丁,用于 vnode 的創(chuàng)建、更新、銷毀 
  16. const patch = (n1, n2, container) => { 
  17.   // 如果新舊節(jié)點(diǎn)的類型不一致,則將舊節(jié)點(diǎn)銷毀 
  18.   if (n1 && !isSameVNodeType(n1, n2)) { 
  19.     unmount(n1) 
  20.   } 
  21.   const { type, ref, shapeFlag } = n2 
  22.   switch (type) { 
  23.     case Text: 
  24.       // 處理文本 
  25.       break 
  26.     case Comment: 
  27.       // 處理注釋 
  28.       break 
  29.     // case ... 
  30.     default
  31.       if (shapeFlag & ShapeFlags.ELEMENT) { 
  32.         // 處理 DOM 元素 
  33.       } else if (shapeFlag & ShapeFlags.COMPONENT) { 
  34.         // 處理自定義組件 
  35.       } else if (shapeFlag & ShapeFlags.TELEPORT) { 
  36.         // 處理 Teleport 組件 
  37.         // 調(diào)用 Teleport.process 方法 
  38.         type.process(n1, n2, container...); 
  39.       } // else if ... 
  40.   } 

可以看到,在處理 Teleport 時(shí),最后會(huì)調(diào)用 Teleport.process 方法,Vue3 中很多地方都是通過 process 的方式來處理 vnode 相關(guān)邏輯的,下面我們重點(diǎn)看看 Teleport.process 方法做了些什么。

  1. // packages/runtime-core/src/components/Teleport.ts 
  2. const isTeleportDisabled = props => props.disabled 
  3. export const Teleport = { 
  4.   __isTeleport: true
  5.   process(n1, n2, container) { 
  6.     const disabled = isTeleportDisabled(n2.props) 
  7.     const { shapeFlag, children } = n2 
  8.     if (n1 == null) { 
  9.       const target = (n2.target = querySelector(n2.prop.to))       
  10.       const mount = (container) => { 
  11.         // compiler and vnode children normalization. 
  12.         if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { 
  13.           mountChildren(children, container) 
  14.         } 
  15.       } 
  16.       if (disabled) { 
  17.         // 開關(guān)關(guān)閉,掛載到原來的位置 
  18.         mount(container) 
  19.       } else if (target) { 
  20.         // 將子節(jié)點(diǎn),掛載到屬性 `to` 對(duì)應(yīng)的節(jié)點(diǎn)上 
  21.         mount(target) 
  22.       } 
  23.     } 
  24.     else { 
  25.       // n1不存在,更新節(jié)點(diǎn)即可 
  26.     } 
  27.   } 

其實(shí)原理很簡單,就是將 Teleport 的 children 掛載到屬性 to 對(duì)應(yīng)的 DOM 元素中。為了方便理解,這里只是展示了源碼的九牛一毛,省略了很多其他的操作。

總結(jié)

希望在閱讀文章的過程中,大家能夠掌握 Teleport 組件的用法,并使用到業(yè)務(wù)場景中。盡管原理十分簡單,但是我們有了 Teleport 組件,就能輕松解決彈窗元素定位不準(zhǔn)確的問題。

本文轉(zhuǎn)載自微信公眾號(hào)「更了不起的前端」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系更了不起的前端公眾號(hào)。

 

 

 

責(zé)任編輯:武曉燕 來源: 更了不起的前端
相關(guān)推薦

2021-05-12 10:25:29

開發(fā)技能代碼

2021-03-31 08:01:50

Vue3 Vue2 Vue3 Telepo

2021-10-29 07:47:35

Vue 3teleport傳送門組件

2023-11-28 09:03:59

Vue.jsJavaScript

2024-10-24 09:18:45

2023-11-29 09:05:59

Vue 3場景

2024-04-08 07:28:27

PiniaVue3狀態(tài)管理庫

2022-09-20 11:00:14

Vue3滾動(dòng)組件

2021-12-01 08:11:44

Vue3 插件Vue應(yīng)用

2023-04-02 10:06:24

組件vue3sign2.

2021-05-18 07:51:37

Suspense組件Vue3

2024-08-13 09:26:07

2020-06-09 11:35:30

Vue 3響應(yīng)式前端

2021-11-30 08:19:43

Vue3 插件Vue應(yīng)用

2023-04-27 11:07:24

Setup語法糖Vue3

2022-07-29 11:03:47

VueUni-app

2021-11-19 09:29:25

項(xiàng)目技術(shù)開發(fā)

2021-10-21 06:52:17

Vue3組件 API

2024-04-16 07:46:15

Vue3STOMP協(xié)議WebSocket

2021-09-27 06:29:47

Vue3 響應(yīng)式原理Vue應(yīng)用
點(diǎn)贊
收藏

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