vue组件高级(下)

Vue
304
0
0
2022-12-04
标签   Vue组件

1. ref引用

1.1 ref引用

ref用来辅助开发者在不依赖jQuery的情况下,获取DOM元素或组件的引用。

每个vue的组件实例上,都包含一个 refs对象,里面存储着对应的DOM元素或组件的引用。默认情况下,组件的 refs指向一个空对象。

<template>
<h3>MyRef组件</h3>
<button @click="getRef">获取$refs引用</button>
</template>

<script>
export default{
    methods:{
        getRef(){console.log(this)} //this代表当前组件的实例对象,this.$rtefs默认指向空对象
    }
}
</script>

1.2 使用ref引用DOM元素

如果想要使用ref引用页面上的DOM元素,则可以按照如下的方式进行操作:

<!-- 使用ref属性,为对应的DOM添加引用名称 -->
<template>
<h3 ref="myh3">MyRef组件</h3>
<button @click="getRef">获取$refs引用</button>
</template>

<script>
export default{
    methods:{
        getRef(){
            console.log(this.$refs.myh3)
            this.$refs.myh3.style.color='red'
        } 
}
</script>

1.3 使用ref引用组件实例

如果想要使用ref引用页面上的组件实例,则可以按照如下的方式进行操作:

<!-- 使用ref属性为对应的组件实例添加引用名称 -->
<template>
<MyCounter ref="counterRef"></MyCounter>
<button @click="getRef">获取$refs引用</button>
</template>

<script>
export default{
    methods:{
        getRef(){
            //通过this.$refs.引用名称,可以引用组件的实例 
            console.log(this.$refs.counterRef)
            //引用到组件的实例之后,就可以调用组件上的methods方法 
            this,$refs.counterRef.add()
        }
    }
}
</script>

1.4 控制文本框和按钮的按需切换

通过布尔值 inputVisible来控制组件中的文本框与按钮的按需切换:

<template>
<input type="text" v-if="inputVisible">
<button v-else @click="showInput">展示input输入框 </button>
</template>

<script>
export default{
    data(){
        return{
            //控制文本框和按钮的按需切换 
            inputVisible:false
        }
    },
    methods:{
        showInput(){//切换布尔值,显示文本框 
            this.inputVisible = true
        }
    }
}
</script>

1.5 让文本框自动获得焦点

当文本框展示出来之后,如果希望它立即获得焦点,则可以为其添加ref引用,并调用原生DOM对象的 .focus()方法即可。

<template>
<input type="text" v-if="inputVisible" ref="ipt">
<button v-else @click="showInput">展示输入框</button>
</template>


<script>
methods:{
    showInput(){
        this.inputVisible = true 
        //获取文本框的DOM引用,并调用.focus()使其自动获得焦点 
        this.$refs.ipt.focus()
    },
}
</script>

但是因为这个操作是异步的,所以会报错,可以使用下一个方法来解决。

1.6 this.$nextTick(cb)方法

组件的$nextTick(cb)方法,会把cb回调推迟到下一个DOM更新周期之后执行。

通俗的理解就是:等组件的DOM异步重新渲染完成后,再执行cb回调函数,从而能保证cb回调函数可以操作到最新的DOM元素。

<template>
<input type="text" v-if="inputVisible" ref="ipt">
<button v-else @click="showInput">展示输入框</button>
</template>


<script>
data() {
    return {
      inputVisible: false,
    };
},  
methods:{
    showInput(){
        this.inputVisible = true 
        //把对input文本框的操作,推迟到下次更新DOM之后,否则页面上根本不存在文本框元素 
        this.$nextTick(()=>{
        this.$refs.ipt.focus()          
        })
    },
}
</script>

2. 动态组件

动态组件指的是动态切换组件的显示与隐藏。vue提供了一个内置的 <component>组件,专门用来实现组件的动态渲染。

  1. <component>是组件的占位符
  2. 通过is属性动态指定要渲染的组件名称
  3. <component is="要渲染的组件的名称"></component>

2.1 实现动态组件渲染

<template>
//点击按钮,动态切换组件的名称 
    <button @click="comName='MyNamic1'">组件1</button> 
    <button @click="comName='MyNamic2'">组件2</button>
//通过is属性,动态指定要渲染的组件的名称 
    <component :is="comName"></component>
</template>

data(){
    return{
    comName:'MyNamic1'//当前渲染的组件名称
    }
}

2.2 使用keep-alive保持状态

默认情况下,切换动态组件时无法保持组件的状态,此时可以使用vue内置的 <keep-alive>组件保持动态组件的状态。

<keep-alive> 
    <component :is="comName"></component>
</keep-alive>  

3. 插槽

插槽(slot)是vue为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望由用户指定的部分定义为插槽。

可以把插槽认为是组件封装期间,为用户预留的内容的占位符。

3.1 基础用法

在封装组件时,可以通过 <slot>元素定义插槽,从而为用户预留内容占位符。

<template>
<p>这是MyCom1组件的第一个p标签</p>
<!-- 通过slot标签,为用户预留内容占位符(插槽) -->
<slot></slot>
<p>这是MyCom1组件的最后一个p标签</p>
</template>
<MyCom1>
<!-- 在使用MyCom1组件时,为插槽指定具体的内容 -->  
    <p>用户自定义内容</p>
</MyCom1>

3.1.1 没有预留插槽的内容会被丢弃

如果在封装组件时没有预留任何 <slot>插槽,则用户提供的任何自定义内容都会被丢弃。

<template>
<p>这是MyCom1组件的第一个p标签</p>
<!-- 封装组件时,没有预留任何插槽 -->
<p>这是MyCom1组件的最后一个p标签</p>
</template>
<MyCom1>
<!-- 下面用户自定义的内容会被丢弃 -->  
    <p>用户自定义内容</p>
</MyCom1>

3.1.2 后备内容

封装组件时,可以为预留的 <slot>插槽提供后备内容(默认内容)。如果组件的使用者没有为插槽提供任何内容,则后备内容会生效。

<template>
<p>这是MyCom1组件的第一个p标签</p>
<slot>后备内容</slot>
<p>这是MyCom1组件的最后一个p标签</p>
</template>

3.2 具名插槽

如果在封装组件时需要预留多个插槽节点,则需要为每个 插槽指定具体的 name 名称。这种带有具体 名称的插槽叫做“具名插槽”。

<template>
<header> 
    <!-- 页头 --> 
    <slot name="header"></slot>
</header>
<main> 
    <!-- 主体 --> 
    <slot name="main"></slot>  
</main>
<footer> 
    <!-- 页脚 --> 
    <slot name="footer"></slot>  
</footer>
</template>

注意:没有指定name名称的插槽,会有隐含的名称叫做"default"

3.2.1 为具名插槽提供内容

在向具名插槽提供内容的时候,我们可以在一个template元素上使用v-slot指令,并以v-slot的参数的形式提供名称:

    <MyAritcle> 
      <template v-slot:header> <h1>静夜思</h1></template> 
      <template v-slot:default> 
        <p>床前明月光,疑是地上霜</p> 
        <p>举头望明月,低头思故乡</p> 
      </template> 
      <template v-slot:footer> <p>作者:李白</p></template> 
    </MyAritcle>

3.2.2 具名插槽的简写形式

根v-on和v-bind一样,v-slot也有缩写,即把参数之前的所有内容(v-slot:)替换为字符 #

    <MyAritcle> 
      <template #header> <h1>静夜思</h1></template> 
      <template #default> 
        <p>床前明月光,疑是地上霜</p> 
        <p>举头望明月,低头思故乡</p> 
      </template> 
      <template #footer> <p>作者:李白</p></template> 
    </MyAritcle>

3.3. 作用域插槽

在封装组件的过程中,可以为预留的插槽绑定 props 数据,这种带有 props 数据的 <slot> 叫做“作用域插槽”。

<!-- 预留插槽 -->
<slot :info="infomation"></slot>

<!-- 使用自定义组件 -->
<MyTest>
<template v-slot:default="scope">
{{scope}}  
</template>
</MyTest>

3.3.1 解构作用域插槽的prop

作用域插槽对外提供的数据对象,可以使用解构赋值简化数据的接收过程。

    <MyTable> 
      <template #default="{ user }"> 
      <!-- 使用作用域插槽的数据 --> 
        <td>{{ user.id }}</td> 
        <td>{{ user.name }}</td> 
      </template> 
    </MyTable>

3.3.2 声明作用域插槽

在封装MyTable组件的过程中,可以通过作用域插槽把表格每一行的数据传递给组件的使用者。

    <tbody> 
      <!-- 循环渲染数据 -->      
      <tr v-for="item in list" :key="item.id"> 
      <!-- 下面的slot是一个作用域插槽 -->        
        <slot :user="item"></slot> 
      </tr> 
    </tbody>

3.3.3 使用作用域插槽

在使用MyTable组件时,自定义单元格的渲染方式,并接收作用域插槽对外提供的数据。

    <MyTable> 
      <!-- 接收作用域插槽对外提供的数据 -->  
      <template #default="scope"> 
      <!-- 使用作用域插槽的数据 --> 
        <td>{{ scope.user.id }}</td> 
        <td>{{ scope.user.name }}</td> 
      </template> 
    </MyTable>

4. 自定义指令

vue官方提供了v-for、v-model、v-if等常用的内置指令。除此之外vue还允许开发者自定义指令。

vue中的自定义指令分为两类,分别是:

  • 私有自定义指令
  • 全局自定义指令

4.1 声明私有自定义指令的语法

在每个vue组件中,可以在directives节点下声明私有自定义指令。

directives:{
    //自定义一个指令
    focus:{
        //当被绑定的元素插入到DOM元素中时,自动触发mounted函数 
        mounted(el){
            el.focus()//让被绑定的元素自动获得焦点
        }
    }
}

4.2 使用自定义指令

在使用自定义指令时,需要加上v-前缀。

<!-- 声明自定义指令时,指令的名字是focus --> 
<!-- 使用自定义指令时,需要加上v- 指令前缀 -->
<input v-focus>

4.3 声明全局自定义指令的语法

全局共享的自定义指令需要通过“单页面应用程序的实例对象”进行声明,示例代码如下:

const app = Vue.createApp({})

//注册一个全局自定义指令 ‘v-focus’
app.directive('focus',{
    //当被绑定的元素插入到DOM中时,自动触发mounted函数 
    mounted(el){
        el.focus()
    }
})

4.4 updated函数

mounted函数只在元素第一次插入DOM时被调用,当DOM更新时mounted函数不会被触发。updated函数会在每次DOM更新完成后被调用。

app。directive('focus',{
    mounted(el){//第一次插入DOm时触发这个函数
        el.focus()
    },
    updated(el){//每次DOM更新时都会触发updated函数
        el.focus()
    }
})

注意:在vue2的项目中使用自定义指令时,【mounted -> bind】【updated -> update】

4.5 函数简写

如果mounted和updated函数中的逻辑完全相同,则可以简写如下格式:

app.directive('focus',(el)=>{
    el.focus()
})

4.6 指令的参数值

在绑定指令时,可以通过“等号”的形式为指令绑定具体的参数值。

<input type="text" v-model.number="count" v-focus v-color="'red'">
<p v-color="'blue'"> {{count}}</p>
<button @click="count++">+1</button>

//自定义v-color指令
app.directive('color',(el,binding)=>{
    //binding.value是通过等号为指令绑定的值
    el.style.color=binding.value
})