Во многих статьях был представлен комбинированный API Vue3 и принцип реагирования на основе прокси.В дополнение к этим более привлекательным обновлениям Vue3 также добавляет встроенный компонент:Teleport. Роль этого компонента в основном используется для перемещения элементов DOM в шаблоне на другие позиции.
сцены, которые будут использоваться
<!-- 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">弹窗标题</h3>
<span class="modal__close">x</span>
</div>
<div class="modal__content">
弹窗文本内容
</div>
<div class="modal__footer">
<button>取消</button>
<button>确认</button>
</div>
</div>
</div>
</template>
<script>
export default {
setup() {
return {};
},
};
</script>
<!-- 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
transform
, perspective
илиfilter
имущество неnone
час,fixed
Элементы относительно родительского элемента будут расположены.
Нам просто нужно.container
Категорияtransform
С небольшой модификацией позиционирование всплывающего компонента будет запутанным.
<style lang="scss">
.container {
height: 80vh;
margin: 50px;
overflow: hidden;
transform: translateZ(0);
}
</style>
В это время используйтеTeleport
Компоненты могут решить эту проблему.
Teleport предоставляет простой способ, позволяющий нам контролировать, под каким родительским узлом HTML отображается в DOM, без необходимости прибегать к глобальному состоянию или разбивать его на два компонента. -- Официальная документация Vue
Нам просто нужно поместить содержимое всплывающего окна вTeleport
Внутри и установитьto
собственностьbody
, указывая, что всплывающий компонент будет отображаться какbody
, чтобы можно было решить предыдущую задачу.
<template>
<teleport to="body">
<div class="modal__mask">
<div class="modal__main">
...
</div>
</div>
</teleport>
</template>
допустимыйКод Sandbox.io/embed/v UE - ничего ...Проверьте код.
Анализ исходного кода
Мы могли бы начать с простого шаблона, а затем посмотретьTeleport
После сборки шаблона сгенерированного кода.
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
создавать.
// 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
Первый параметрTeleport
, будет один в финальном vnodeshapeFlag
Атрибут, который используется для представления типа vnode.isTeleport(type)
Результатtrue
,такshapeFlag
Последнее значение свойства равноShapeFlags.TELEPORT
(1 << 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
}
В узле рендеринга компонента он будет основан наtype
а такжеshapeFlag
Возьмите другую логику.
// packages/runtime-core/src/renderer.ts
const render = (vnode, container) => {
if (vnode == null) {
// 当前组件为空,则将组件销毁
if (container._vnode) {
unmount(container._vnode, null, null, true)
}
} else {
// 新建或者更新组件
// container._vnode 是之前已创建组件的缓存
patch(container._vnode || null, vnode, container)
}
container._vnode = vnode
}
// patch 是表示补丁,用于 vnode 的创建、更新、销毁
const patch = (n1, n2, container) => {
// 如果新旧节点的类型不一致,则将旧节点销毁
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 组件
// 调用 Teleport.process 方法
type.process(n1, n2, container...);
} // else if ...
}
}
Видно, что обработкаTeleport
Teleport.process
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) {
// 开关关闭,挂载到原来的位置
mount(container)
} else if (target) {
// 将子节点,挂载到属性 `to` 对应的节点上
mount(target)
}
}
else {
// n1不存在,更新节点即可
}
}
}
На самом деле принцип очень прост,Teleport
изchildren
to
Соответствующий элемент DOM. Чтобы облегчить понимание, здесь он показывает только долю исходного кода, пропуская много других операций.
Суммировать
Надеюсь, что в процессе прочтения статьи вы усвоитеTeleport
Использование компонентов и использование в бизнес-сценариях. Хотя принцип очень прост, у нас естьTeleport
компонента можно легко решить проблему неточного позиционирования всплывающих элементов.