一、ref引用
ref用来辅助开发者在不依赖jQuery的情况下,获取DOM元素或组件的引用
注:
- 在每个vue实例上,都包含一个$refs对象
- 默认情况下,组件的$refs指向一个空对象
1、使用ref引用页面上的DOM元素
步骤
- 为要引用的DOM添加ref属性,并提供引用的名称
- 通过this.$refs.引用的名称,可获取DOM元素的引用
<template>
<div>
<!-- 为要引用的DOM添加ref属性,并提供引用的名称 -->
<h1 ref="myh1">App 根组件</h1>
<button type="button" class="btn btn-primary" @click="getRefs">获取 $refs 引用</button>
</div>
</template>
<script>
export default {
name: 'MyApp',
methods: {
getRefs() {
// 通过this.$refs.引用的名称,可获取DOM元素的引用
console.log(this.$refs.myh11)
//操作DOM
this.$refs.myh11.style.color = 'red'
},
},
}
</script>
2、使用ref引用组件
步骤
- 为要引用的组件添加ref属性,并提供引用的名称
- 通过this.$refs.引用的名称,可引用组件的实例
- 引用到组件的实例后,可以调用组件上的methods方法
<template>
<div>
<!--为要引用的组件添加ref属性,并提供引用的名称 -->
<my-counter ref="counterRef"></my-counter>
<button type="button" class="btn btn-primary" @click="getRefs">获取 $refs 引用</button>
</div>
</template>
<script>
import MyCounter from './Counter.vue'
export default {
name: 'MyApp',
methods: {
getRefs() {
// 通过this.$refs.引用的名称,可引用组件的实例
console.log(this.$refs.counterRef)
// 引用到组件的实例后,可以调用组件上的methods方法
this.$refs.counterRef.reset()
},
},
components: {
MyCounter,
},
}
</script>
counter.vue中
<template>
<div class="counter-container">
<h3>Counter 组件 --- {{ count }}</h3>
<hr />
<button type="button" class="btn btn-info" @click="count += 1">+1</button>
</div>
</template>
<script>
export default {
name: 'MyCounter',
data() {
return {
count: 0,
}
},
methods: {
reset() {
this.count = 0
},
},
}
</script>
3、$nextTick方法
用于解决:
DOM更新是异步的,当函数中涉及到更新DOM,并马上利用ref对DOM进行操作时,会undefined的问题
组件的$nextTick(cb)方法,会把cb回调推迟到下一个DOM更新操作之后执行。从而能保证cb回调函数可以操作到最新的DOM元素
<template>
<div>
<h1>App 根组件</h1>
<hr />
<input type="text" class="form-control" v-if="inputVisible" ref="ipt" />
<button type="button" class="btn btn-primary" v-else @click="showInput">展示 input 输入框</button>
</div>
</template>
<script>
export default {
name: 'MyApp',
data() {
return {
inputVisible: false,
}
},
methods: {
showInput() {
// 要展示文本框
this.inputVisible = true
console.log(this.$refs.ipt)//此时为undefined
// 把对input文本的操作,推迟到下次DOM更新之后
this.$nextTick(() => {
console.log(this.$refs.ipt)//此时有值
// 获取到文本框的引用对象,然后调用 focus() 方法
this.$refs.ipt.focus()
})
},
},
}
</script>
二、动态组件
动态组件,指动态切换组件的显示与隐藏
vue提供了内置的<component>组件,专门用来实现组件的动态渲染
注:
- <component>为要动态渲染的组件的占位符
- 通过is属性动态的指定要渲染的组件名称
<component is="要渲染的组件的名称"></component>
步骤:
- 在data中定义当前要渲染的组件的名称
- 通过is属性,动态指定要渲染的组件的名称
- 可进行动态切换组件名称操作
示例:
<template>
<div>
<h1 class="mb-4">App 根组件</h1>
<!-- 点击按钮动态切换组件名称 -->
<button type="button" class="btn btn-primary" @click="comName = 'MyHome'">首页</button>
<button type="button" class="btn btn-info ml-2" @click="comName = 'MyMovie'">电影</button>
<hr />
<!-- 使用组件 通过is属性,动态指定要渲染的组件的名称 -->
<component :is="comName"></component>
</div>
</template>
<script>
// 导入组件
import MyHome from './Home.vue'
import MyMovie from './Movie.vue'
export default {
name: 'MyApp',
data() {
return {
comName: 'MyHome'//当前要渲染的组件的名称
}
},
// 注册组件
components: {
MyHome,
MyMovie,
},
}
</script>
<style lang="less" scoped></style>
使用 keep-alive保持状态
默认情况下,切换动态组件时,被切换掉的组件会被卸载,无法保持组件的状态(对组件的数据等进行的一些操作),可使用<keep-alive>保持动态组件的状态
<keep-alive>
<component :is="组件名"></component>
</keep-alive>
示例:
<template>
<div>
<h1 class="mb-4">App 根组件</h1>
<!-- 点击按钮动态切换组件名称 -->
<button type="button" class="btn btn-primary" @click="comName = 'MyHome'">首页</button>
<button type="button" class="btn btn-info ml-2" @click="comName = 'MyMovie'">电影</button>
<hr />
<!-- 使用keep-alive保持组件状态 -->
<keep-alive>
<component :is="comName"></component>
</keep-alive>
</div>
</template>
<script>
import MyHome from './Home.vue'
import MyMovie from './Movie.vue'
export default {
name: 'MyApp',
data() {
return {
comName: 'MyHome'//当前要渲染的组件的名称
}
},
components: {
MyHome,
MyMovie,
},
}
</script>
三、插槽
在开发者封装组件时,把不确定的、希望由用户指定的部分定义为插槽。
可以把插槽认为是组件封装期间,为用户预留的内容的占位符。
1、插槽的基础用法
可通过<slot>元素定义插槽,从而为用户预留内容占位符
<template>
<div class="com-container">
<p>这是第一个 p 标签</p>
<!-- 通过slot标签,为用户预留内容占位符 -->
<slot></slot>
<p>这是最后一个 p 标签</p>
</div>
</template>
注:
- 如果在组件封装的过程中,没有预留任何<slot>插槽,则用户提供的任何自定义内容都会被丢弃
示例:
App.vue
<template>
<div>
<h1>App 根组件</h1>
<!-- 使用组件 -->
<my-com> 向组件插槽中传递的内容 </my-com>
</div>
</template>
<script>
// 导入组件
import MyCom from './MyCom.vue'
export default {
name: 'MyApp',
// 注册组件
components: {
MyCom,
},
}
</script>
Mycount.vue
<template>
<div class="com-container">
<p>这是第一个 p 标签</p>
<!-- 通过slot标签,为用户预留内容占位符 -->
<slot></slot>
<p>这是最后一个 p 标签</p>
</div>
</template>
<script>
export default {
name: 'MyCom',
}
</script>
2、插槽的后备内容
在封装组件时,可为预留的<slot>插槽提供后背内容(默认内容),如果组件的使用者没有为插槽提供任何内容,则后备内容会生效。
<slot>这是后备内容</slot>
示例:
App.vue
<template>
<div>
<h1>App 根组件</h1>
<!-- 使用组件,并未指定插槽内容 -->
<my-com></my-com>
</div>
</template>
<script>
// 导入组件
import MyCom from './MyCom.vue'
export default {
name: 'MyApp',
// 注册组件
components: {
MyCom,
},
}
</script>
Mycount.vue
<template>
<div class="com-container">
<p>这是第一个 p 标签</p>
<!-- 此时页面显示后备内容 -->
<slot>后备内容 </slot>
<p>这是最后一个 p 标签</p>
</div>
</template>
<script>
export default {
name: 'MyCom',
}
</script>
3、具名插槽
在封装组件时,需要预留多个插槽节点,则需要为每个插槽指定具体的name名称,
这种带有具体名称的插槽叫做具名插槽。
创建具名插槽
<slot name="header">标题内容</slot>
向组件标签的内容节点中,插入具名插槽的内容
<template v-slot:header>
<h1>标题</h1>
</template>
注:
- 在声明插槽时,若没有指定名称,则会有隐含的名称叫做“default”
- 只有默认插槽可省略外层的template,插入具名插槽时,template不能被省略
- v-slot:可简写为#
示例:
App.vue
<template>
<div>
<h1>App 根组件</h1>
<hr />
<!-- 使用组件 -->
<my-article>
<template v-slot:header>
<h1>标题</h1>
</template>
<template #default>
<p>内容</p>
</template>
<template #footer>
<p>页脚</p>
</template>
</my-article>
</div>
</template>
<script>
// 导入组件
import MyArticle from './MyArticle.vue'
export default {
name: 'MyApp',
components: {
// 注册组件
MyArticle,
},
}
</script>
Mycount.vue
<template>
<div>
<!-- 页头 -->
<header>
<slot name="header"></slot>
</header>
<!-- 主要内容 -->
<main>
<slot></slot>
</main>
<!-- 页脚 -->
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
<script>
export default {
name: 'MyArticle',
}
</script>
4、作用域插槽
在封装组件的过程中,可以为预留的<slot>插槽绑定props数据,这种带有props数据的<slot>叫做作用域插槽
<slot :info="infomation">为插槽动态的传递props数据</slot>
在使用自定义组件时接收props数据
<!-- 利用等号接收props数据,名字自定义即可 -->
<template #default="scope">
<p>{{scope}}</p>
</template>
注:
- scope默认接收的是一个对象,可通过scope.属性名查看具体属性
解构作用域插槽的prop
利用解构赋值即可
<template #default="{ msg, info }">
<p>{{ msg }}</p>
<p>{{ info.address }}</p>
</template>
四、自定义指令
当v-for、v-if等内置指令无法满足开发者的需求时,开发者可自定义指令
分类:
- 私有自定义指令
- 全局自定义指令
注:
- 自定义指令在使用时,必须以v-前缀开头,在声明时,不需要加
1、私有自定义指令
可在组件中,通过directives节点声明私有自定义指令
示例:
<template>
<!-- 使用v-focus自定义事件 -->
<input type="text" v-focus />
</template>
<script>
export default {
directives: {
// 自定义一个私有指令
//自定义指令名称:{}
focus: {
// 当被绑定的元素渲染到DOM中后,自动触发mounted函数
//el即为当前指令所绑定的DOM元素
mounted(el) {
el.focus()
},
},
},
}
</script>
2、全局自定义指令
全局自定义指令需要在main.js中进行声明,全部组件都可使用
示例:
main.js中
const app = createApp(App)
// 声明全局自定义指令
// app.directive(自定义指令名,配置对象)
app.directive('focus', {
mounted(el) {
el.focus()
},
})
其他组件中
<!-- 使用v-focus自定义事件 -->
<input type="text" v-focus />
updated函数
mounted函数只在元素第一希插入DOM时被调用,当DOM更新时不会触发
updated函数会在每次DOM更新完成后被调用
注:
- 在vue2中使用自定义指令时,两个函数的名称有变化mounted->bind updated->update
- 当mounted与 updated逻辑完全相同时,可简写
// 声明全局自定义指令v-focus
// app.directive(自定义指令名,配置对象)
app.directive('focus', {
mounted(el) {
//DOM初次渲染时,自动获取焦点
el.focus()
},
updated(el) {
//DOM更新后,依旧能够自动获取焦点
el.focus()
},
})
简写形式
app.directive('focus', (el) => {
//在mounted和updated函数中都会触发
el.focus()
})
3、指令的参数值
在绑定指令时,可以通过等号的形式为指令绑定具体的参数值
// binding.value接收具体的参数值
app.directive('color', (el, binding) => {
el.style.color = binding.value
})
<input type="text" v-color="'cyan'" />