17. 实现 Teleport
17. 实现 Teleport
Teleport
组件介绍
1.render(h(Teleport, { to: '#root' }, [123, 456]), app);
Vue3 新增组件,该组件可以将制定内容渲染到制定容器中。默认内容都是渲染到元素app
内,我们可以将其渲染到任意节点 (传送门)
const shapeFlag = isString(type)
? ShapeFlags.ELEMENT
: isTeleport(type) // 如果是穿梭框
? ShapeFlags.TELEPORT
: isObject(type)
? ShapeFlags.STATEFUL_COMPONENT
: isFunction(type)
? ShapeFlags.FUNCTIONAL_COMPONENT
: 0; // 函数式组件
创建虚拟节点的时候标识组件类型。
Teleport
挂载
2.if (shapeFlag & ShapeFlags.TELEPORT) {
type.process(n1, n2, container, anchor, {
mountChildren, // 挂载孩子
patchChildren, // 更新孩子
move(vnode, container, anchor) {
// 移动元素
hostInsert(
vnode.component ? vnode.component.subTree.el : vnode.el,
container,
anchor
);
}
});
}
export const TeleportImpl = {
__isTeleport: true,
remove(vnode, unmount) {
const { shapeFlag, children } = vnode;
if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
for (let i = 0; i < children.length; i++) {
const child = children[i];
unmount(child);
}
}
},
process(n1, n2, container, anchor, internals) {
let { mountChildren, patchChildren, move } = internals;
if (!n1) {
// 创建一个目标
const target = (n2.target = document.querySelector(n2.props.to));
if (target) {
mountChildren(n2.children, target, anchor);
}
} else {
patchChildren(n1, n2, container); // 比对儿子
if (n2.props.to !== n1.props.to) {
// 更新并且移动位置
// 获取下一个元素
const nextTarget = document.querySelector(n2.props.to);
n2.children.forEach((child) => move(child, nextTarget, anchor));
}
}
}
};
export const isTeleport = (type) => type.__isTeleport;
Teleport
卸载
3.const unmount = (vnode) => {
const { shapeFlag } = vnode;
if (vnode.type === Fragment) {
unmountChildren(vnode.children);
} else if (shapeFlag & ShapeFlags.COMPONENT) {
unmount(vnode.component.subTree);
} else if (shapeFlag & ShapeFlags.TELEPORT) {
vnode.type.remove(vnode, unmount);
} else {
hostRemove(vnode.el);
}
};