• 十、组件(8)


    本章概要

    • 递归组件
    • 异步更新队列
    • Teleport

    10.11.2 递归组件

    组件可以在自己的模板中递归调用自身,但这需要使用 name 选项为组件指定一个内部调用的名称。
    当调用 Vue.createApp({}).component({})全局注册组件时,这个全局的 ID 会自动设置为该组件的name选项。
    递归组件和程序语言中的递归函数调用一样,都需要有一个条件结束递归,否则就会导致无限循环。
    例如,可以通过 v-if 指令(表达式计算为假时)结束递归。
    以下是一个分类树状显示例子:

    DOCTYPE html>
    <html>
    
    <head>
    	<meta charset="UTF-8">
    head>
    
    <body>
    	<div id="app">
    		<category-component :list="categories">category-component>
    	div>
    
    	<script src="https://unpkg.com/vue@next">script>
    	<script>
    		const CategoryComponent = {
    			name: 'catComp',
    			props: {
    				list: {
    					type: Array
    				}
    			},
    
    			template: `
              			
    `
    } const app = Vue.createApp({ data() { return { categories: [ { name: '程序设计', children: [ { name: 'Java', children: [ { name: 'Java SE' }, { name: 'Java EE' } ] }, { name: 'C++' } ] }, { name: '前端框架', children: [ { name: 'Vue.js' }, { name: 'React' } ] }] } }, components: { CategoryComponent } }).mount('#app');
    script> body> html>
    • 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
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    渲染结果如下:
    在这里插入图片描述

    10.11.3 异步更新队列

    代码如下:

    DOCTYPE html>
    <html>
    
    <head>
    	<meta charset="UTF-8">
    head>
    
    <body>
    	<div id="app">
    		<my-component>my-component>
    	div>
    
    	<script src="https://unpkg.com/vue@next">script>
    	<script>
    		const app = Vue.createApp({});
    		app.component('my-component', {
    			data() {
    				return {
    					message: '无限恐怖'
    				}
    			},
    			methods: {
    				change() {
    					this.message = '无限曙光';
    					console.log(this.$refs.msg.textContent);
    				}
    			},
    			template: `
                  	

    {{ message }}

    `
    }) app.mount('#app');
    script> body> html>
    • 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

    代码很简单,当点击“修改内容”按钮时,修改组件 message 数据属性的值,然后在 Console 窗口中输出组件模板中 p 元素的文本内容。

    按理说,p 元素的内容就是 message 属性的值,修改了 message 属性的值,在 change() 方法中理应输出修改后的值,但实际上输出的是“无限恐怖”。

    这是因为 Vue 在数据变化需要更新 DOM 时并不是同步执行,而是异步执行的。每当侦听到数据更改时,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。

    如果同一个观察者被多次触发,只会将其放入队列中一次。Vue在缓冲时会去除重复数据,这样可以避免不必要的计算 和 DOM 操作。

    然后,在下一个时间循环tick中,Vue 刷新队列并执行实际的工作。Vue 在内部对异步队列尝试使用原生的 Promise.then、MutationObserver 和 setImmediate ,如果执行环境不支持,则会采用 setTimeout(fn,0)代替。

    对于本例,当在change() 方法中修改 message 属性值的时候,该组件不会立即重新渲染。当队列刷新时,组件会在下一个 tick 中更新。多数情况下,不需要关心这个过程,但是如果想在数据更改后立即访问更新后的 DOM ,这时就需要用到 Vue.nextTick(callback) 方法,传递给 Vue.nextTick() 方法的回调函数会在 DOM 更新后完成被调用。

    修改上述代码,如下:

    change() {
      this.message = '无限曙光';
      Vue.nextTick(() => console.log(this.$refs.msg.textContent))
    }
    
    • 1
    • 2
    • 3
    • 4

    使用浏览器在此访问页面,单击“修改内容”按钮,在 Console 窗口中的输出为 “无限曙光”。

    除了使用全局的 Vue.nextTick() 方法外,在组件内部还可以使用实例的 nextTick() 方法,这样在回调函数中的 this 会自动绑定到当前组件实例上,而不是像上面的代码需要使用箭头函数来绑定 this 到组件实例。

    change() {
      this.message = '无限曙光';
      this.$nextTick(function () {
        console.log(this.$refs.msg.textContent);
      })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    10.11.4 Teleport

    Vue 可以通过将 UI 和相关行为封装到组件中构建 UI ,组件之间可以嵌套,从而构成一个 UI 数。

    然而,有事组件模板的一部分在逻辑上属于该组件,但从技术角度来看,应该将模板的这一部分移到 DOM 中的其它地方,位于 Vue 应用程序实例之外。

    一个常见的场景是创建一个包含全屏模态的组件。在大多数情况下,模态的逻辑都存在于组件中的,但是我们会发现,模态的定位很难通过 CSS 来解决,我们不得不考虑对组件进行拆分。

    Vue 3.0 官网给出了一个例子,有如下的 HTML 结构:

    <body>
        <div id="app" style="position: relative;">
            <h3>Tooltips with Vue 3 Teleporth3>
            <div>
                <modal-button>modal-button>
            div>
        div>
    body>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    modal-button 组件在嵌套很深的 div 元素中渲染。modal-button 组件代码如下:

    const app = Vue.createApp({});
      app.component('modal-button', {
        template: `
            
    
            
        `,
        data() {
            return { 
                modalOpen: false
            }
        }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    运行结果如下:
    在这里插入图片描述

    modal-button 组件有一个 button 元素触发模态的打开,以及一个具有 .modal 样式类的 div 元素,它包含模态的内容和一个用于自我关闭的按钮。

    .modal 样式类使用了一个样式表属性“position:absolute”,当 modal-button 组件在上面的 HTML 结构中渲染时,会发现由于模态在嵌套很深的 div 中渲染,样式属性 position:absolute 将相对于父级 div 元素应用。为了解决这个问题,Vue 3.0 给出了一个内置组件 teleport ,该组件允许控制在 DOM 中的哪个节点下渲染 HTML 片段。

    teleport 组件有两个 prop ,如下:

    • to:字符串类型,必须的 prop 。其值必须是有效的查询选择器或 HTML 的元素名(如果在浏览器的环境中使用)。teleport 组件的内容将被移动到指定的目标元素中。
    • disabled:布尔类型,可选的 prop 。disabled 可以用于禁用 teleport 组件的功能,这意味着它的插槽内容将不会被移动到任何位置,而是在周围父组件中指定 teleport 的地方渲染。

    修改 modal-button 组件的代码,使用 teleport 来告诉 Vue “将这个 HTML 传送到 body 标签下”。代码所示如下:

    const app = Vue.createApp({});
    app.component('modal-button', {
        template: `
            
    
            
                
            
        `,
        data() {
            return { 
                modalOpen: false
            }
        }
    })
    
    • 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

    现在,当单机“Open full screen modal! (With teleport!)”按钮,Vue 会正确地将模态的内容在body标签下渲染。运行结果如下:
    在这里插入图片描述

    如果 teleport 的内容中包含了 Vue 组件,那么该组件在逻辑上仍是 teleport 父组件的子组件。代码如下:

    const app = Vue.createApp({
        template: `
        

    Root instance

    `
    }); app.component('parent-component', { template: `

    This is a parent component

    `
    }) app.component('child-component', { props:['name'], template: `
    hello,{{ name }}
    `
    }) app.mount('#app');
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    不管 child-component 组件在什么位置渲染,它仍是 parent-component 组件的子组件,并且从父组件接收 name prop 。这意味着来自父组件的注入将按预期工作,并且子组件将嵌套在 Vue Devtools 中父组件之下,而不是放在实际内容移动到的位置。

    一个常见的用例场景是一个可重用的 Modal 组件,其中可能同时有多个活动实例。对于这种情况,多个 teleport 组件可以将他们的内容挂载到同一个目标元素下。挂载顺序将是一个简单的追加,代码如下:

    <teleport to="#modals">
        <div>Adiv>
    teleport>
    <teleport to="modals">
        <div>Bdiv>
    teleport>
    
    <div id="modals">
        <div>Adiv>
        <div>Bdiv>
    div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • 相关阅读:
    SAP abap json序列化
    Springboot整合Redis的Cluster集群进行API限流
    无人机飞手教员组装、调试高级教学详解
    MVCC究竟是什么?
    代码优化实例
    KWin、libdrm、DRM从上到下全过程 —— drmModeAddFBxxx(8)
    Mysql大表修改表结构
    【100天精通Python】Day65:Python可视化_Matplotlib3D绘图mplot3d,绘制3D散点图、3D线图和3D条形图,示例+代码
    Hadoop组成、HDFS、YARN、 MapReduce、 Hadoop环境搭建
    dns安全策略
  • 原文地址:https://blog.csdn.net/GXL_1012/article/details/127834310