日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Vue3 Teleport 組件的實踐及原理

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

使用場景

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

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

 
 
 
 
  1.  
  2.  
  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.  
  22.  
  23.  
  24.  

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

 
 
 
 
  1.  
  2.  
  3. .container { 
  4.   height: 80vh; 
  5.   margin: 50px; 
  6.   overflow: hidden; 
  7.  
  8.  
  9.  
  10.  

Modal

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

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

 
 
 
 
  1.  
  2. .container { 
  3.   height: 80vh; 
  4.   margin: 50px; 
  5.   overflow: hidden; 
  6.   transform: translateZ(0); 
  7.  

Modal

這個時候,使用 Teleport 組件就能解決這個問題了。

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

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

 
 
 
 
  1.  

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

使用 Teleport 的 Modal

源碼解析

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

 
 
 
 
  1. Vue.createApp({ 
  2.   template: ` 
  3.      
  4.       
     teleport to body 
       
  5.      
  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 進行創(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 的第一個參數(shù)為 Teleport,最后得到的 vnode 中會有一個 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é)點,會依據(jù) type 和 shapeFlag 走不同的邏輯。

 
 
 
 
  1. // packages/runtime-core/src/renderer.ts 
  2. const render = (vnode, container) => { 
  3.   if (vnode == null) { 
  4.     // 當前組件為空,則將組件銷毀 
  5.     if (container._vnode) { 
  6.       unmount(container._vnode, null, null, true) 
  7.     } 
  8.   } else { 
  9.     // 新建或者更新組件 
  10.     // container._vnode 是之前已創(chuàng)建組件的緩存 
  11.     patch(container._vnode || null, vnode, container) 
  12.   } 
  13.   container._vnode = vnode 
  14.  
  15. // patch 是表示補丁,用于 vnode 的創(chuàng)建、更新、銷毀 
  16. const patch = (n1, n2, container) => { 
  17.   // 如果新舊節(jié)點的類型不一致,則將舊節(jié)點銷毀 
  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 時,最后會調(diào)用 Teleport.process 方法,Vue3 中很多地方都是通過 process 的方式來處理 vnode 相關(guā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é)點,掛載到屬性 `to` 對應的節(jié)點上 
  21.         mount(target) 
  22.       } 
  23.     } 
  24.     else { 
  25.       // n1不存在,更新節(jié)點即可 
  26.     } 
  27.   } 

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

總結(jié)

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

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


文章標題:Vue3 Teleport 組件的實踐及原理
標題鏈接:http://www.5511xx.com/article/dheihis.html