当前位置: 首页 > article >正文

Teleport 传送

Teleport

之前写 vue3 的时候发现 vue3 有个非常好用的内置标签Teleport
将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去。

最近在写 vue2 发现需要用到这个功能,但 vue2 有没有这样的组件,于是自己写了这个组件

<template>
  <div :class="className">
    <slot />
  </div>
</template>

<script>
export default {
  name: 'teleport',
  props: {
    /**
     * 目标元素,可以是字符串或DOM元素
     */
    to: {
      type: [String, HTMLElement],
      required: true
    },
    /**
     * 传送位置,可选值:before、after
     */
    where: {
      type: String,
      default: 'after'
    },
    /**
     * 是否禁用传送功能
     */
    disabled: Boolean
  },
  data() {
    return {
      nodes: [],
      waiting: false,
      observer: null,
      parent: null
    }
  },
  watch: {
    to: 'maybeMove',
    where: 'maybeMove',
    disabled(value) {
      if (value) {
        this.disable()
        this.teardownObserver()
      } else {
        this.bootObserver()
        this.move()
      }
    }
  },
  mounted() {
    // Store a reference to the nodes
    this.nodes = Array.from(this.$el.childNodes)

    if (!this.disabled) {
      this.bootObserver()
    }

    // Move slot content to target
    this.maybeMove()
  },
  beforeDestroy() {
    // Move back
    this.disable()

    // Stop observing
    this.teardownObserver()
  },
  computed: {
    className() {
      if (this.disabled) {
        return ['teleportClass']
      }
      return ['teleportClass', 'hidden']
    }
  },
  methods: {
    maybeMove() {
      if (!this.disabled) {
        this.move()
      }
    },
    move() {
      this.waiting = false

      // to允许传递一个具体的DOM元素引用,供高级操作使用
      if (typeof this.to === 'string') {
        this.parent = document.querySelector(this.to)
      } else if (this.to) {
        // 如果他不是字符串,就认为他是个DOM元素引用,直接赋值
        this.parent = this.to
      } else {
        // 无法被识别的目标,不做处理
      }

      if (!this.parent) {
        this.disable()

        this.waiting = true

        return
      }

      if (this.where === 'before') {
        this.parent.prepend(this.getFragment())
      } else {
        this.parent.appendChild(this.getFragment())
      }
    },
    disable() {
      this.$el.appendChild(this.getFragment())
      this.parent = null
    },
    // Using a fragment is faster because it'll trigger only a single reflow
    // See https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment
    getFragment() {
      const fragment = document.createDocumentFragment()

      this.nodes.forEach((node) => fragment.appendChild(node))

      return fragment
    },
    onMutations(mutations) {
      // Makes sure the move operation is only done once
      let shouldMove = false

      for (let i = 0; i < mutations.length; i++) {
        const mutation = mutations[i]
        const filteredAddedNodes = Array.from(mutation.addedNodes).filter(
          (node) => !this.nodes.includes(node)
        )

        if (Array.from(mutation.removedNodes).includes(this.parent)) {
          this.disable()
          this.waiting = !this.disabled
        } else if (this.waiting && filteredAddedNodes.length > 0) {
          shouldMove = true
        } else {
          // 都不满足的情况下,不做任何事
        }
      }

      if (shouldMove) {
        this.move()
      }
    },
    bootObserver() {
      if (this.observer) {
        return
      }

      this.observer = new MutationObserver((mutations) =>
        this.onMutations(mutations)
      )

      this.observer.observe(document.body, {
        childList: true,
        subtree: true,
        attributes: false,
        characterData: false
      })
    },
    teardownObserver() {
      if (this.observer) {
        this.observer.disconnect()
        this.observer = null
      }
    }
  }
}
</script>

<style scoped>
.hidden {
  display: none;
  visibility: hidden;
}
</style>


http://www.kler.cn/a/469955.html

相关文章:

  • visual studio 自动调整代码格式的问题:
  • 深度解析与实践:HTTP 协议
  • future和CompletableFuture
  • Go Ebiten随机迷宫生成示例
  • SpringMVC的消息转换器
  • Scala_【5】函数式编程
  • 前端开发中页面优化的方法
  • LLM之RAG实战(五十一)| 使用python和Cypher解析PDF数据,并加载到Neo4j数据库
  • unity3d——3D动画学习day01 状态机相关参数
  • HackMyVM-Always靶机的测试报告
  • Linux文件系统权限
  • 2024 高级爬虫笔记(五)mysql、MongoDB、reids
  • 【Java数据结构】二叉树
  • 内网穿透的应用-自托管文件分享系统PicoShare搭建流程与远程共享实战教程
  • 大数据-240 离线数仓 - 广告业务 测试 ADS层数据加载 DataX数据导出到 MySQL
  • LInux单机安装Redis
  • 现代前端框架
  • html+css+js网页设计 体育 中满体育4个页面
  • html 元素中的data-v-xxxxxx 是什么?为什么有的元素有?有的没有?
  • 并行通信和串行通信
  • JVM调优,参数在哪里设置的?
  • STM32F4适配WINUSB2.0
  • Tableau数据可视化与仪表盘搭建-数据可视化原理
  • 从单点 Redis 到 1 主 2 从 3 哨兵的架构演进之路
  • Spring AMQP ----消息转换器
  • C#编程中dynamic类型