欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Vue3 Teleport 的實(shí)踐及原理

 更新時間:2020年12月02日 10:46:35   作者:Shenfq  
這篇文章主要介紹了Vue3 Teleport 組件的實(shí)踐及原理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

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

使用場景

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

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

<!-- Modal.vue -->
<style lang="scss">
.modal {
 &__mask {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background: rgba(0, 0, 0, 0.5);
 }
 &__main {
  margin: 0 auto;
  margin-bottom: 5%;
  margin-top: 20%;
  width: 500px;
  background: #fff;
  border-radius: 8px;
 }
 /* 省略部分樣式 */
}
</style>
<template>
 <div class="modal__mask">
  <div class="modal__main">
   <div class="modal__header">
    <h3 class="modal__title">彈窗標(biāo)題</h3>
    <span class="modal__close">x</span>
   </div>
   <div class="modal__content">
    彈窗文本內(nèi)容
   </div>
   <div class="modal__footer">
    <button>取消</button>
    <button>確認(rèn)</button>
   </div>
  </div>
 </div>
</template>

<script>
export default {
 setup() {
  return {};
 },
};
</script>

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

<!-- App.vue -->
<style lang="scss">
.container {
 height: 80vh;
 margin: 50px;
 overflow: hidden;
}
</style>
<template>
 <div class="container">
  <Modal />
 </div>
</template>

<script>
export default {
 components: {
  Modal,
 },
 setup() {
  return {};
 }
};
</script>

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

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

<style lang="scss">
.container {
 height: 80vh;
 margin: 50px;
 overflow: hidden;
 transform: translateZ(0);
}
</style>

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

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

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

<template>
 <teleport to="body">
  <div class="modal__mask">
   <div class="modal__main">
    ...
   </div>
  </div>
 </teleport>
</template>

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

源碼解析

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

Vue.createApp({
 template: `
  <Teleport to="body">
   <div> teleport to body </div> 
  </Teleport>
 `
})

簡化后代碼:

function render(_ctx, _cache) {
 with (_ctx) {
  const { createVNode, openBlock, createBlock, Teleport } = Vue
  return (openBlock(), createBlock(Teleport, { to: "body" }, [
   createVNode("div", null, " teleport to body ", -1 /* HOISTED */)
  ]))
 }
}

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

// packages/runtime-core/src/renderer.ts
export function createBlock(
  type, props, children, patchFlag
) {
 const vnode = createVNode(
  type,
  props,
  children,
  patchFlag
 )
 // ... 省略部分邏輯
 return vnode
}

export function createVNode(
 type, props, children, patchFlag
) {
 // class & style normalization.
 if (props) {
  // ...
 }

 // encode the vnode type information into a bitmap
 const shapeFlag = isString(type)
  ? ShapeFlags.ELEMENT
  : __FEATURE_SUSPENSE__ && isSuspense(type)
   ? ShapeFlags.SUSPENSE
   : isTeleport(type)
    ? ShapeFlags.TELEPORT
    : isObject(type)
     ? ShapeFlags.STATEFUL_COMPONENT
     : isFunction(type)
      ? ShapeFlags.FUNCTIONAL_COMPONENT
      : 0

 const vnode: VNode = {
  type,
  props,
  shapeFlag,
  patchFlag,
  key: props && normalizeKey(props),
  ref: props && normalizeRef(props),
 }

 return vnode
}

// packages/runtime-core/src/components/Teleport.ts
export const isTeleport = type => type.__isTeleport
export const Teleport = {
 __isTeleport: true,
 process() {}
}

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

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

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

// packages/runtime-core/src/renderer.ts
const render = (vnode, container) => {
 if (vnode == null) {
  // 當(dāng)前組件為空,則將組件銷毀
  if (container._vnode) {
   unmount(container._vnode, null, null, true)
  }
 } else {
  // 新建或者更新組件
  // container._vnode 是之前已創(chuàng)建組件的緩存
  patch(container._vnode || null, vnode, container)
 }
 container._vnode = vnode
}

// patch 是表示補(bǔ)丁,用于 vnode 的創(chuàng)建、更新、銷毀
const patch = (n1, n2, container) => {
 // 如果新舊節(jié)點(diǎn)的類型不一致,則將舊節(jié)點(diǎn)銷毀
 if (n1 && !isSameVNodeType(n1, n2)) {
  unmount(n1)
 }
 const { type, ref, shapeFlag } = n2
 switch (type) {
  case Text:
   // 處理文本
   break
  case Comment:
   // 處理注釋
   break
  // case ...
  default:
   if (shapeFlag & ShapeFlags.ELEMENT) {
    // 處理 DOM 元素
   } else if (shapeFlag & ShapeFlags.COMPONENT) {
    // 處理自定義組件
   } else if (shapeFlag & ShapeFlags.TELEPORT) {
    // 處理 Teleport 組件
    // 調(diào)用 Teleport.process 方法
    type.process(n1, n2, container...);
   } // else if ...
 }
}

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

// packages/runtime-core/src/components/Teleport.ts
const isTeleportDisabled = props => props.disabled
export const Teleport = {
 __isTeleport: true,
 process(n1, n2, container) {
  const disabled = isTeleportDisabled(n2.props)
  const { shapeFlag, children } = n2
  if (n1 == null) {
   const target = (n2.target = querySelector(n2.prop.to))   
   const mount = (container) => {
    // compiler and vnode children normalization.
    if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
     mountChildren(children, container)
    }
   }
   if (disabled) {
    // 開關(guān)關(guān)閉,掛載到原來的位置
    mount(container)
   } else if (target) {
    // 將子節(jié)點(diǎn),掛載到屬性 `to` 對應(yīng)的節(jié)點(diǎn)上
    mount(target)
   }
  }
  else {
   // n1不存在,更新節(jié)點(diǎn)即可
  }
 }
}

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

總結(jié)

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

到此這篇關(guān)于詳解Vue3 Teleport 的實(shí)踐及原理的文章就介紹到這了,更多相關(guān)Vue3 Teleport組件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論