赞
踩
目录
Teleport 是 Vue3 新特性,可以把组件渲染到指定位置,类似于 React 的 Portal
假设定义了一个弹框组件,他的外容器有 position:absolute 样式,那么这个样式极其容易被父元素影响;
也就是说:如果弹窗在不同的父元素里,可能会被干扰定位,展现不同的效果;
使用 Teleport 可以把弹框组件挪动到 body 上,避免挂载到其他父节点上,同时还能使用特定页面的数据;
综上所述:在 A页面 中引入弹窗组件,弹窗组件使用 A页面 的数据,使用 Teleport 后,弹框组件 最终节点不挂载到 A页面 的容器上,而是挂载到 body 上
to 指的是 teleport 内包裹的内容,会被挂载到哪个元素(支持 id、class)的 同级位置
disabled 如果设为 true,则 to 属性失效
- <teleport :disabled="false" to='body'>
- <A></A>
- </teleport>
源码位置:core-main\packages\runtime-core\src\renderer.ts
通过 patch 函数,判断是不是 teleport 组件,如果是,则执行 process 方法
- const patch: PatchFn = (
- n1,
- n2,
- container,
- anchor = null,
- parentComponent = null,
- parentSuspense = null,
- isSVG = false,
- slotScopeIds = null,
- optimized = __DEV__ && isHmrUpdating ? false : !!n2.dynamicChildren
- ) => {
- if (n1 === n2) {
- return
- }
-
- ...
-
- const { type, ref, shapeFlag } = n2
- switch (type) {
- case Text:
- ...
- case Comment:
- ...
- case Static:
- ...
- case Fragment:
- ...
- default:
- if (shapeFlag & ShapeFlags.ELEMENT) {
- ...
- } else if (shapeFlag & ShapeFlags.COMPONENT) {
- ...
- // 如果是 teleport 组件,则会调用 process 方法
- } else if (shapeFlag & ShapeFlags.TELEPORT) {
- ;(type as typeof TeleportImpl).process(
- n1 as TeleportVNode,
- n2 as TeleportVNode,
- container,
- anchor,
- parentComponent,
- parentSuspense,
- isSVG,
- slotScopeIds,
- optimized,
- internals
- )
- } else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
- ...
- }
-
- // set ref
- if (ref != null && parentComponent) {
- setRef(ref, n1 && n1.ref, parentSuspense, n2 || n1, !n2)
- }
- }
下面是 创建、更新、删除逻辑:
- export const TeleportImpl = {
- __isTeleport: true,
- // 创建、更新
- process(
- n1: TeleportVNode | null,
- n2: TeleportVNode,
- container: RendererElement,
- anchor: RendererNode | null,
- parentComponent: ComponentInternalInstance | null,
- parentSuspense: SuspenseBoundary | null,
- isSVG: boolean,
- slotScopeIds: string[] | null,
- optimized: boolean,
- internals: RendererInternals
- ) {
- const {
- mc: mountChildren,
- pc: patchChildren,
- pbc: patchBlockChildren,
- o: { insert, querySelector, createText, createComment }
- } = internals
-
- const disabled = isTeleportDisabled(n2.props)
- let { shapeFlag, children, dynamicChildren } = n2
-
- // #3302
- // HMR updated, force full diff
- if (__DEV__ && isHmrUpdating) {
- optimized = false
- dynamicChildren = null
- }
-
- if (n1 == null) {
- // 在主视图插入空白节点或空白文本
- const placeholder = (n2.el = __DEV__
- ? createComment('teleport start')
- : createText(''))
- const mainAnchor = (n2.anchor = __DEV__
- ? createComment('teleport end')
- : createText(''))
- insert(placeholder, container, anchor)
- insert(mainAnchor, container, anchor)
- // 关键代码,获取目标元素的 DOM 节点(在这里调用方法 resolveTarget 获取 to 属性值,并通过 querySelector 取到那个元素)
- const target = (n2.target = resolveTarget(n2.props, querySelector))
- const targetAnchor = (n2.targetAnchor = createText(''))
- if (target) {
- insert(targetAnchor, target)
- // #2652 we could be teleporting from a non-SVG tree into an SVG tree
- isSVG = isSVG || isTargetSVG(target)
- } else if (__DEV__ && !disabled) {
- warn('Invalid Teleport target on mount:', target, `(${typeof target})`)
- }
-
- // 向目标元素挂载节点
- const mount = (container: RendererElement, anchor: RendererNode) => {
- // Teleport *always* has Array children. This is enforced in both the
- // compiler and vnode children normalization.
- // 挂载子节点
- if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
- mountChildren(
- children as VNodeArrayChildren,
- container,
- anchor,
- parentComponent,
- parentSuspense,
- isSVG,
- slotScopeIds,
- optimized
- )
- }
- }
-
- if (disabled) {
- // 若 disabled 为 true,则挂载到原来的位置,不变化
- mount(container, mainAnchor)
- } else if (target) {
- // 若 disabled 为 false,则挂载到新的目标节点(to 属性指定的位置)
- mount(target, targetAnchor)
- }
- } else {
- // update content
- // 更新逻辑
- n2.el = n1.el
- const mainAnchor = (n2.anchor = n1.anchor)!
- const target = (n2.target = n1.target)!
- const targetAnchor = (n2.targetAnchor = n1.targetAnchor)!
- const wasDisabled = isTeleportDisabled(n1.props)
- const currentContainer = wasDisabled ? container : target
- const currentAnchor = wasDisabled ? mainAnchor : targetAnchor
- isSVG = isSVG || isTargetSVG(target)
-
- // 更新子节点处理 disabled 变化的逻辑
- if (dynamicChildren) {
- // fast path when the teleport happens to be a block root
- patchBlockChildren(
- n1.dynamicChildren!,
- dynamicChildren,
- currentContainer,
- parentComponent,
- parentSuspense,
- isSVG,
- slotScopeIds
- )
- // even in block tree mode we need to make sure all root-level nodes
- // in the teleport inherit previous DOM references so that they can
- // be moved in future patches.
- traverseStaticChildren(n1, n2, true)
- } else if (!optimized) {
- patchChildren(
- n1,
- n2,
- currentContainer,
- currentAnchor,
- parentComponent,
- parentSuspense,
- isSVG,
- slotScopeIds,
- false
- )
- }
-
- if (disabled) {
- if (!wasDisabled) {
- // enabled -> disabled
- // move into main container
- moveTeleport(
- n2,
- container,
- mainAnchor,
- internals,
- TeleportMoveTypes.TOGGLE
- )
- }
- } else {
- // target changed
- if ((n2.props && n2.props.to) !== (n1.props && n1.props.to)) {
- const nextTarget = (n2.target = resolveTarget(
- n2.props,
- querySelector
- ))
- if (nextTarget) {
- moveTeleport(
- n2,
- nextTarget,
- null,
- internals,
- TeleportMoveTypes.TARGET_CHANGE
- )
- } else if (__DEV__) {
- warn(
- 'Invalid Teleport target on update:',
- target,
- `(${typeof target})`
- )
- }
- } else if (wasDisabled) {
- // disabled -> enabled
- // move into teleport target
- moveTeleport(
- n2,
- target,
- targetAnchor,
- internals,
- TeleportMoveTypes.TOGGLE
- )
- }
- }
- }
- },
-
- // 移除
- remove(
- vnode: VNode,
- parentComponent: ComponentInternalInstance | null,
- parentSuspense: SuspenseBoundary | null,
- optimized: boolean,
- { um: unmount, o: { remove: hostRemove } }: RendererInternals,
- doRemove: Boolean
- ) {
- const { shapeFlag, children, anchor, targetAnchor, target, props } = vnode
-
- if (target) {
- hostRemove(targetAnchor!)
- }
-
- // an unmounted teleport should always remove its children if not disabled
- if (doRemove || !isTeleportDisabled(props)) {
- hostRemove(anchor!)
- if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
- for (let i = 0; i < (children as VNode[]).length; i++) {
- const child = (children as VNode[])[i]
- unmount(
- child,
- parentComponent,
- parentSuspense,
- true,
- !!child.dynamicChildren
- )
- }
- }
- }
- },
-
- move: moveTeleport,
- hydrate: hydrateTeleport
- }
参考文章:是个萌妹大佬,快关注他
不希望组件被重新渲染,影响使用体验;希望提升性能
举个栗子:两个表单切换时,默认情况下,会清空填写内容;若使用了 keep-alive,则会保留原来填写的内容
keep-alive 存在时,组件只会执行一次 onMounted 函数;不会执行 onUnMounted 函数;会反复执行 onActivated、onDeactivated 函数;具体过程如下:
注意事项:
- <!-- 基本用法 -->
- <keep-alive>
- <component :is="test"></component>
- </keep-alive>
-
- <!-- 多个条件判断的子组件 -->
- <keep-alive>
- <compA v-if="isTrue"></compA>
- <compB v-else></compB>
- </keep-alive>
-
- <!-- 和 `<transition>` 一起使用 -->
- <transition>
- <keep-alive>
- <A></A>
- </keep-alive>
- </transition>
二者可以接收三种类型的参数:用逗号分隔的字符串、正则表达式或一个数组
举个栗子:
- // 只缓存 A B 组件
- <keep-alive :include="[A, B]">
- <A></A>
- <B></B>
- <C></C>
- </keep-alive>
-
- // 缓存 除了 C 组件外的组件
- <keep-alive :exclude="[C]">
- <A></A>
- <B></B>
- <C></C>
- </keep-alive>
最多缓存的组件数,会使用一种算法,缓存最活跃的组件们
- // 最多缓存10个组件
- <keep-alive :max="10">
- <A></A>
- ...
- </keep-alive>
源码位置:runtime-core/src/components/KeepAlive.ts
强烈推荐直接听小满的讲解视频:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。