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>
如果想要使用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>
如果想要使用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>
通过布尔值 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>
当文本框展示出来之后,如果希望它立即获得焦点,则可以为其添加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>
但是因为这个操作是异步的,所以会报错,可以使用下一个方法来解决。
组件的$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>
动态组件指的是动态切换组件的显示与隐藏。vue提供了一个内置的 <component>
组件,专门用来实现组件的动态渲染。
<component>
是组件的占位符<component is="要渲染的组件的名称"></component>
<template>
//点击按钮,动态切换组件的名称
<button @click="comName='MyNamic1'">组件1</button>
<button @click="comName='MyNamic2'">组件2</button>
//通过is属性,动态指定要渲染的组件的名称
<component :is="comName"></component>
</template>
data(){
return{
comName:'MyNamic1'//当前渲染的组件名称
}
}
默认情况下,切换动态组件时无法保持组件的状态,此时可以使用vue内置的 <keep-alive>
组件保持动态组件的状态。
<keep-alive>
<component :is="comName"></component>
</keep-alive>
插槽(slot)是vue为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望由用户指定的部分定义为插槽。
可以把插槽认为是组件封装期间,为用户预留的内容的占位符。
在封装组件时,可以通过 <slot>
元素定义插槽,从而为用户预留内容占位符。
<template>
<p>这是MyCom1组件的第一个p标签</p>
<!-- 通过slot标签,为用户预留内容占位符(插槽) -->
<slot></slot>
<p>这是MyCom1组件的最后一个p标签</p>
</template>
<MyCom1>
<!-- 在使用MyCom1组件时,为插槽指定具体的内容 -->
<p>用户自定义内容</p>
</MyCom1>
如果在封装组件时没有预留任何 <slot>
插槽,则用户提供的任何自定义内容都会被丢弃。
<template>
<p>这是MyCom1组件的第一个p标签</p>
<!-- 封装组件时,没有预留任何插槽 -->
<p>这是MyCom1组件的最后一个p标签</p>
</template>
<MyCom1>
<!-- 下面用户自定义的内容会被丢弃 -->
<p>用户自定义内容</p>
</MyCom1>
封装组件时,可以为预留的 <slot>
插槽提供后备内容(默认内容)。如果组件的使用者没有为插槽提供任何内容,则后备内容会生效。
<template>
<p>这是MyCom1组件的第一个p标签</p>
<slot>后备内容</slot>
<p>这是MyCom1组件的最后一个p标签</p>
</template>
如果在封装组件时需要预留多个插槽节点,则需要为每个 插槽指定具体的 name 名称。这种带有具体 名称的插槽叫做“具名插槽”。
<template>
<header>
<!-- 页头 -->
<slot name="header"></slot>
</header>
<main>
<!-- 主体 -->
<slot name="main"></slot>
</main>
<footer>
<!-- 页脚 -->
<slot name="footer"></slot>
</footer>
</template>
注意:没有指定name名称的插槽,会有隐含的名称叫做"default"
在向具名插槽提供内容的时候,我们可以在一个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>
根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>
在封装组件的过程中,可以为预留的插槽绑定 props 数据,这种带有 props 数据的 <slot>
叫做“作用域插槽”。
<!-- 预留插槽 -->
<slot :info="infomation"></slot>
<!-- 使用自定义组件 -->
<MyTest>
<template v-slot:default="scope">
{{scope}}
</template>
</MyTest>
作用域插槽对外提供的数据对象,可以使用解构赋值简化数据的接收过程。
<MyTable>
<template #default="{ user }">
<!-- 使用作用域插槽的数据 -->
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
</template>
</MyTable>
在封装MyTable组件的过程中,可以通过作用域插槽把表格每一行的数据传递给组件的使用者。
<tbody>
<!-- 循环渲染数据 -->
<tr v-for="item in list" :key="item.id">
<!-- 下面的slot是一个作用域插槽 -->
<slot :user="item"></slot>
</tr>
</tbody>
在使用MyTable组件时,自定义单元格的渲染方式,并接收作用域插槽对外提供的数据。
<MyTable>
<!-- 接收作用域插槽对外提供的数据 -->
<template #default="scope">
<!-- 使用作用域插槽的数据 -->
<td>{{ scope.user.id }}</td>
<td>{{ scope.user.name }}</td>
</template>
</MyTable>
vue官方提供了v-for、v-model、v-if等常用的内置指令。除此之外vue还允许开发者自定义指令。
vue中的自定义指令分为两类,分别是:
在每个vue组件中,可以在directives节点下声明私有自定义指令。
directives:{
//自定义一个指令
focus:{
//当被绑定的元素插入到DOM元素中时,自动触发mounted函数
mounted(el){
el.focus()//让被绑定的元素自动获得焦点
}
}
}
在使用自定义指令时,需要加上v-前缀。
<!-- 声明自定义指令时,指令的名字是focus -->
<!-- 使用自定义指令时,需要加上v- 指令前缀 -->
<input v-focus>
全局共享的自定义指令需要通过“单页面应用程序的实例对象”进行声明,示例代码如下:
const app = Vue.createApp({})
//注册一个全局自定义指令 ‘v-focus’
app.directive('focus',{
//当被绑定的元素插入到DOM中时,自动触发mounted函数
mounted(el){
el.focus()
}
})
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】
如果mounted和updated函数中的逻辑完全相同,则可以简写如下格式:
app.directive('focus',(el)=>{
el.focus()
})
在绑定指令时,可以通过“等号”的形式为指令绑定具体的参数值。
<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
})