• 11 【Teleport CSS功能】


    21.内置组件 Teleport

    什么是Teleport?—— 是一个内置组件,它是一种能够将我们的模板渲染至指定DOM节点,不受父级style、v-show等属性影响,但data、prop数据依旧能够共用的技术;类似于 React 的 Portal。

    主要解决的问题 因为Teleport节点挂载在其他指定的DOM节点下,完全不受父级style样式影响

    1. teleport 是内置组件,可以直接在模板中使用,无需注册。
    2. 可以被打包工具 tree-shake。所以它们只会在被使用的时候被引入。
    3. 需要直接主动访问(获取)它们的场景,也可以将它们显性导入。

    21.1 基本用法

    有时我们可能会遇到这样的场景:一个组件模板的一部分在逻辑上从属于该组件,但从整个应用视图的角度来看,它在 DOM 中应该被渲染在整个 Vue 应用外部的其他地方。

    这类场景最常见的例子就是全屏的模态框。理想情况下,我们希望触发模态框的按钮和模态框本身是在同一个组件中,因为它们都与组件的开关状态有关。但这意味着该模态框将与按钮一起渲染在应用 DOM 结构里很深的地方。这会导致该模态框的 CSS 布局代码很难写。

    父组件App.vue

    
    
    
    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    子组件Child.vue

    
    
    
    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    现在来看没有问题

    image-20220809195810727

    可是如果父元素加一个定位:

      position: relative;
    
    • 1

    image-20220809195844153

    遮罩层就会变成这样

    以后嵌套组件变多了,遮罩组件的父级组件存在定位语句是很正常的。如果能把这个组件渲染到body标签的子标签就好了。这时就可以使用Teleport组件。

    • to - string :必传
      挂载的目标,只能是父级标签。兄弟、子级都会报错。
      挂载目标必须是有效的查询选择器或 HTMLElement

    • disabled - boolean :非必传

      • 是否禁用。true则挂载到目标节点下,false为当前位置。

      • 动态变化 disabled 值时,只是位置会变动,内容并不会销毁重新渲染!

    接收一个 to prop 来指定传送的目标。to 的值可以是一个 CSS 选择器字符串,也可以是一个 DOM 元素对象。这段代码的作用就是告诉 Vue “把以下模板片段传送到 body 标签下”。

    父组件App.vue中使用组件的位置改成这样。

        
          
        
    
    • 1
    • 2
    • 3

    image-20220809200202370

    可以发现这个组件确实被渲染到body标签下面了。

    提示

    挂载时,传送的 to 目标必须已经存在于 DOM 中。理想情况下,这应该是整个 Vue 应用 DOM 树外部的一个元素。如果目标元素也是由 Vue 渲染的,你需要确保在挂载 之前先挂载该元素。

    以下teleport例子将会报错!

    teleport目标元素
    demo
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    以下官方代码列举写法。但是前面说过 “ 挂载目标必须是有效的查询选择器或 HTMLElement ”

    实际填h1 h2等原生标签、以及自定义的标签都是没问题的。以下应该是官方文档的一个纰漏。

    
    
    
    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    关于样式: teleport

    teleport挂载的目标即使不在当前组件,scoped 内的样式在 teleport 中是生效的。

    原因:scoped 生成的唯一属性会作用于 teleport 内的各元素

    21.2 官方案例

    试想下面这样的 HTML 结构:

    <div class="outer">
      <h3>Tooltips with Vue 3 Teleporth3>
      <div>
        <MyModal />
      div>
    div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    接下来我们来看看 modal-button 的实现。

    <script setup>
    import { ref } from 'vue'
    
    const open = ref(false)
    </script>
    
    <template>
      <button @click="open = true">Open Modal</button>
    
      <div v-if="open" class="modal">
        <p>Hello from the modal!</p>
        <button @click="open = false">Close</button>
      </div>
    </template>
    
    <style scoped>
    .modal {
      position: fixed;
      z-index: 999;
      top: 20%;
      left: 50%;
      width: 300px;
      margin-left: -150px;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    这个组件中有一个

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    接收一个 to prop 来指定传送的目标。to 的值可以是一个 CSS 选择器字符串,也可以是一个 DOM 元素对象。这段代码的作用就是告诉 Vue “把以下模板片段传送到 body 标签下”。

    我们也可以将 `` 结合使用来创建一个带动画的模态框。你可以看看这个示例

    21.3 禁用 Teleport

    在某些场景下可能需要视情况禁用 。举例来说,我们想要在桌面端将一个组件当做浮层来渲染,但在移动端则当作行内组件。我们可以通过对 动态地传入一个 disabled prop 来处理这两种不同情况。

    
      ...
    
    
    • 1
    • 2
    • 3

    这里的 isMobile 状态可以根据 CSS media query 的不同结果动态地更新。

    21.4 多个 Teleport 共享目标

    一个可重用的模态框组件可能同时存在多个实例。对于此类场景,多个 组件可以将其内容挂载在同一个目标元素上,而顺序就是简单的顺次追加,后挂载的将排在目标元素下更后面的位置上。

    比如下面这样的用例:

    
      
    A
    B
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    渲染的结果为:

    A
    B
    • 1
    • 2
    • 3
    • 4

    参考

    22.CSS功能

    22.1 CSS Modules#

    一个

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    得出的 class 将被哈希化以避免冲突,实现了同样的将 CSS 仅作用于当前组件的效果。

    参考 CSS Modules spec 以查看更多详情,例如 global exceptionscomposition

    22.1.1 自定义注入名称#

    你可以通过给 module attribute 一个值来自定义注入 class 对象的属性名:

    自定义注入名称(多个可以用数组)

    
     
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    与组合式 API 一同使用#

    可以通过 useCssModule API 在 setup()

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    22.2 CSS 中的 v-bind()#

    单文件组件的

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这个语法同样也适用于 ``,且支持 JavaScript 表达式 (需要用引号包裹起来):

    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    实际的值会被编译成哈希化的 CSS 自定义属性,因此 CSS 本身仍然是静态的。自定义属性会通过内联样式的方式应用到组件的根元素上,并且在源值变更的时候响应式地更新。

    22.3 组件作用域 CSS

    22.3.1
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    转换为:

    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    子组件的根元素#

    使用 scoped 后,父组件的样式将不会渗透到子组件中。不过,子组件的根节点会同时被父组件的作用域样式和子组件的作用域样式影响。这样设计是为了让父组件可以从布局的角度出发,调整其子组件根元素的样式。

    22.3.1 深度选择器

    处于 scoped 样式中的选择器如果想要做更“深度”的选择,也即:影响到子组件,可以使用 :deep() 这个伪类

    
     
    // 编译后
    .a[data-v-f3f3eg9] .b {
      /* ... */
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 通过 v-html 创建的 DOM 内容不会被作用域样式影响,但你仍然可以使用深度选择器来设置其样式。
    • 并且我们有时会有需要修改引入的外部组件库的样式的需求,此时就需要使用深度选择器,或者直接

    22.3.3 插槽选择器

    默认情况下,作用域样式不会影响到 渲染出来的内容,因为它们被认为是父组件所持有并传递进来的。使用 :slotted 伪类以明确地将插槽内容作为选择器的目标:

    A.vue

    在A组件修改class a 的颜色

    
     
    
     
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    App.vue

    
     
    
     
     
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    image-20220810215440054

    默认情况下,作用域样式不会影响到 渲染出来的内容,因为它们被认为是父组件所持有并传递进来的。

    解决方案 slotted

    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    image-20220810215539547

    22.3.4 全局选择器

    在之前我们想加入全局 样式 通常都是新建一个style 标签 不加scoped 现在有更优雅的解决方案

    
     
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如果想让其中一个样式规则应用到全局,比起另外创建一个 一定要写在scoped里面

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    效果等同于上面

    22.3.5 混合使用局部与全局样式

    你也可以在同一个组件中同时包含作用域样式和非作用域样式:

    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    22.3.6 注意

    • Scoped 样式不能代替 class。由于浏览器渲染各种各样 CSS 选择器的方式,p { color: red } 结合作用域样式使用时 (即当与 attribute 选择器组合的时候) 会慢很多倍。如果你使用 class 或者 id 来替代,例如 .example { color: red },那你几乎就可以避免性能的损失。

    • 在递归组件中小心使用后代选择器! 对于一个使用了 .a .b 选择器的样式规则来说,如果匹配到 .a 的元素包含了一个递归的子组件,那么所有的在那个子组件中的 .b 都会匹配到这条样式规则。

    22.4 组件修改子组件样式

    首先抛出一个最暴力的方案,但这存在影响其他同名样式的风险。当你意识不到哪出问题的时候,就会让人抓狂。(不推荐的方案!)

    涉及到异步组件加载了对应全局样式,又访问了其他页面,选择器相同时则会出现样式不以你预想的情况出现!

    // 全局样式
    <style>
    .customClassName{
        color:red;
    }
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    vue2方案:

    // 或者

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    vue3 推荐方案:

    vue2的写法在vue3 仍然可以使用,但vue3 文档只写了 :deep() 这一个API案例,为了统一风格和提高规范,建议按照v3文档 使用·:deep()

    
     
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    无论>>> /deep/还是 :deep() 都是写在 .el-dialog .el-dialog__body前。此处进行原因分析、解释:

    1. 首先 scoped 特性会使组件自身的所有元素增加唯一标识属性(例如: data-v-7d766012),并且编译后CSS选择器也会有这段唯一标识。注意!是组件自身的元素。不包括子组件、父组件!
    2. 特例:子组件的根节点会同时被子组件和父组件的样式影响!这是为了方便父组件对子组件进行布局!但是仅限于根组件,如果子组件没有根节点,如VUE3的片段特性(多根节点),则无效。
    3. 重点来了:在deep前的选择器,是会带上唯一标识的,deep后面的选择器是不会带上这唯一标识!如果带上了父组件的唯一标识,自然匹配不上子组件对应的元素属性,就会不生效。

    代码案例解释:

    
     
    // 编译结果如下
    .a[data-v-f3f3eg9] .b .c{
      /* ... */
    }
     
    .a是当前组件的类名,data-v-f3f3eg9 是当前组件的唯一标识, .b .c是你想影响的子组件的对应类名的样式!
    这时候当前组件的.a自然匹配的上, .b .c 由于不存在父组件的唯一标识,自然也就匹配的上子组件对应的.b .c
     
     
    !!如果是 .a[data-v-f3f3eg9] .b[data-v-f3f3eg9] .c
    由于是唯一标识, .b自然不可能是 data-v-f3f3eg9
     
    此时这就匹配不到子元素对应的class='b',自然也无法将样式作用于 class='c' !!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • 相关阅读:
    java毕业设计大学生过程培养信息系统Mybatis+系统+数据库+调试部署
    LabVIEW中编程更改进程的优先级
    【软件设计师21天-考点整理】7)计算机系统构成及硬件基础知识
    蓝桥杯---列名
    火爆全网,22个web自动化测试疑难解答总结,一路狂飙...
    Google Play 索引表
    Llama3 中文通用 Agent 微调模型来啦!(附手把手微调实战教程)
    【Unity入门计划】Collision2D类&Collider2D类
    ES6 Map数据结构
    Spring MVC 框架学习(八)---- SSM 框架整合
  • 原文地址:https://blog.csdn.net/DSelegent/article/details/126255617