1、什么是动态组件
指的是动态切换组件的显示与隐藏,vue提供了一个内置的<component>
组件,专门用来实现组件的动态渲染。
1、<component>
是组件的占位符
2、通过is属性动态来指定要渲染的组件名称
3、<component is="要渲染的组件的名称"></component
关键代码:
<component :is="comName"></component>
源码
grandFather.vue
<template>
<h3>here is grandfather component</h3>
</template>
<script>
export default {
name: 'GrandFather',
data(){
return{
}
},
methods:{
},
components:{
}
}
</script>
<style scoped>
</style>
Father.vue
<template>
<h3>here is Father component</h3>
<hr/>
</template>
<script>
export default {
name: 'Father',
components:{
}
}
</script>
<style scoped>
</style>
app.vue
<template>
<div>
<component :is="comName"></component>
<button @click="changeGrandFather">切换组件grandfather</button>
<button @click="changeFather">切换组件father</button>
<hr/>
</div>
</template>
<script>
import GrandFather from './components/GrandFather.vue'
import Father from './components/Father.vue'
export default {
name: 'App',
data(){
return {
comName:''
}
},
methods:{
changeGrandFather(){
console.log('grandfather')
this.comName='GrandFather'
},
changeFather(){
console.log('father')
this.comName='Father'
}
},
components: {
GrandFather,
Father
}
}
</script>
<style scoped>
</style>
2、keep-alive保持状态
默认情况下,切换动态组件无法保持组件的状态。此时可以使用vue内置的<keep-alive>
组件保持动态组件的状态
方法:
使用<keep-alive>
包裹<component>
占位组件
关键代码
<keep-alive>
<component :is="comName"></component>
</keep-alive>
当切换组件的时候,组件内绑定的值不会回到初始状态
grandfather.vue
<template>
<h3>here is grandfather component{{count}}</h3>
<button @click="addcount">add</button>
</template>
<script>
export default {
name: 'GrandFather',
data(){
return{
count:0
}
},
methods:{
addcount(){
this.count++
}
},
components:{
}
}
</script>
<style scoped>
</style>
3、插槽的基础用法
在封装组件时,可以通过<slot>
元素定义插槽,从而为用户预留内容占位符
如果在封装组件时没有预留任何<slot>
插槽,则用户提供的任何自定义内容都会被丢弃
1、在子组件中申明插槽位置进行占位
2、在父组件的组件标签内进行填充
grandfather.vue
<template>
<h3>here is grandfather component</h3>
<slot></slot>
<h3>here is grandfather component</h3>
</template>
<script>
export default {
name: 'GrandFather',
data(){
return{
}
},
methods:{
},
components:{
}
}
</script>
<style scoped>
</style>
app.vue
<template>
<div>
<GrandFather>
<p>我是把插槽占位填满的内容</p>
</GrandFather>
<hr/>
</div>
</template>
<script>
import GrandFather from './components/GrandFather.vue'
export default {
name: 'App',
data(){
return {
}
},
methods:{
},
components: {
GrandFather,
}
}
</script>
<style scoped>
</style>
4、插槽的默认内容
封装组件时,可以为插槽提供默认内容,如果组件的使用者没有为插槽提供任何内容,则默认内容会生效。
grandfather.vue
<template>
<h3>here is grandfather component</h3>
<slot>我是插槽的默认内容</slot>
<h3>here is grandfather component</h3>
</template>
<script>
export default {
name: 'GrandFather',
data(){
return{
}
},
methods:{
},
components:{
}
}
</script>
<style scoped>
</style>
app.vue
<template>
<div>
<GrandFather>
</GrandFather>
<hr/>
</div>
</template>
<script>
import GrandFather from './components/GrandFather.vue'
export default {
name: 'App',
data(){
return {
}
},
methods:{
},
components: {
GrandFather,
}
}
</script>
<style scoped>
</style>
5、具名插槽
1、封装组件时需要预留多个插槽节点,则需要为每个<slot>
指定具体的name名称。这种带有具体名称的查插槽叫做具名插槽。
形式为:
<slot name=“插槽名”>
2、如果不为插槽指定插槽名,也会有一个默认的插槽名:default
3、在父组件中使用具名插槽使用<template v-slot:插槽名></template>
4、具名插槽可以用#来简写,简写成#header之类
grandfather.vue
<template>
<h3>header</h3>
<slot name="headerSlot">我是header插槽的默认内容</slot>
<h3>body</h3>
<slot name="bodySlot">我是body插槽的默认内容</slot>
<h3>defualt</h3>
<slot >不指定插槽名称会有一个默认的名称叫做defualt</slot>
<h3>Footer</h3>
<slot name="FooterSlot">我是Footer插槽的默认内容</slot>
</template>
<script>
export default {
name: 'GrandFather',
data(){
return{
}
},
methods:{
},
components:{
}
}
</script>
<style scoped>
</style>
app.vue
<template>
<div>
<GrandFather>
<template v-slot:headerSlot>
我是header的内容,从父组件传来的
</template>
<template v-slot:bodySlot>
我父组件传来的body内容
</template>
我是父组件传来的默认内容
<template v-slot:FooterSlot>
我是父组件传来的footer内容
</template>
</GrandFather>
<hr/>
</div>
</template>
<script>
import GrandFather from './components/GrandFather.vue'
export default {
name: 'App',
data(){
return {
}
},
methods:{
},
components: {
GrandFather,
}
}
</script>
<style scoped>
</style>
6、作用域插槽
在封装组件的过程中,可以为预留的<slot>
插槽绑定props数据,这种带有props数据的插槽叫做‘作用域插槽’
- 1、在子组件中定义具名插槽,并在插槽中通过绑定方式传入相应的数据,这个数据可以是多个属性
ps:注意,虽然是子组件,但是由于在父组件中使用插槽,事实上,这里的插槽承担起了传值的功能,事实上扮演了一个父组件(父传子)的角色。也就需要在slot上添加属性,绑定子组件中的数据(通常是一个对象)
<template>
<h3>header</h3>
<slot name="headerSlot" :info="information" :msg="mymsg"></slot>
</template>
data(){
return{
information:{
name:'zhangsan',
age:12
},
mymsg:{
address:'beijing',
major:'computer'
}
}
},
- 2、在父组件中使用插槽。由于是在组件中使用插槽,事实上承担了子组件的功能。所以。从插槽传来的数据,要在父组件中使用
关键代码:
<template>
<div>
<GrandFather>
<template v-slot:headerSlot="scope">
我是header的内容,从父组件传来的:
{{scope}}
<br/>
{{scope.info.name}}
</template>
</GrandFather>
<hr/>
</div>
</template>
ps:这里cope接受组件中slot传来的数据,scope是一个约定俗成的单词,不是默认,直接使用具名插槽使用等号接收数据,打印内容如下
header
我是header的内容,从父组件传来的: { "info": { "name": "zhangsan", "age": 12 }, "msg": { "address": "beijing", "major": "computer" } }
7、结构作用域插槽的scope
上例app.vue
<template>
<div>
<GrandFather>
<template v-slot:headerSlot="{info,msg}">
我是header的内容,从父组件传来的:
{{info}}
<br/>
{{info.name}}
</template>
</GrandFather>
<hr/>
</div>
</template>
<script>
import GrandFather from './components/GrandFather.vue'
export default {
name: 'App',
data(){
return {
}
},
methods:{
},
components: {
GrandFather,
}
}
</script>
<style scoped>
</style>
打印内容如下:
header
我是header的内容,从父组件传来的: { "name": "zhangsan", "age": 12 }
zhangsan
8、自定义指令
1、私有自定义指令:组件内的自定义指令
2、全局自定义指令:main.js声明的自定义指令,全局可用
9、创建私有自定义指令
1、在每个vue组件中的directives节点声明私有自定义指令,directives里面每个自定义组件是一个自定义指令对象,自定义指令对象中是一个mouted(){}生命周期函数,在这个函数里面可以创建自定义指令的具体逻辑。
ps:这里mouted的参数el就是自定义指令所在的dom元素
directives:{
//自定义一个私有指令
focus:{
//让被绑定的元素插入到dom中时,自动触发mounted函数
mounted(el){
el.focus()
}
}
} ,
2、在组件内使用v-xxx自定义指令
<template>
<h3>header</h3>
<input type="text" v-focus>
</template>
完整代码
<template>
<h3>header</h3>
<input type="text" v-focus>
</template>
<script>
export default {
name: 'GrandFather',
data(){
return{
information:{
name:'zhangsan',
age:12
},
mymsg:{
address:'beijing',
major:'computer'
}
}
},
directives:{
//自定义一个私有指令
focus:{
//让被绑定的元素插入到dom中时,自动触发mounted函数
mounted(el){
el.focus()
}
}
} ,
methods:{
},
components:{
}
}
</script>
<style scoped>
</style>
10、创建全局自定义指令
在main.js中的app对象上创建全局自定义指令
语法格式如下
app.directive(‘指令名’,{指令对象})
关键代码
app.directive('focus',{
//让被绑定的元素插入到dom中时,自动触发mounted函数
mounted(el){
el.focus()
}
})
完整代码
main.js
import { createApp } from 'vue'
import App from './App.vue'
import './index.css'
const app=createApp(App)
app.directive('focus',{
//让被绑定的元素插入到dom中时,自动触发mounted函数
mounted(el){
el.focus()
}
})
app.mount('#app')
app.js
<template>
<div>
<input type="text" v-focus>
</div>
</template>
<script>
import GrandFather from './components/GrandFather.vue'
export default {
name: 'App',
data(){
return {
}
},
methods:{
},
components: {
GrandFather,
}
}
</script>
<style scoped>
</style>
11、updated函数
上例在自定义指令中指定了mouted方法,mounted函数只在元素第一次插入dom时被调用,当dom更新时mounted函数不会被触发,updated函数会在每次dom更新完成后被调用。
在directive里面生命updated函数
关键代码
app.directive('focus',{
//让被绑定的元素插入到dom中时,自动触发mounted函数
mounted(el){
el.focus()
},
//每次dom更新时都会触发update函数
updated(el){
el.focus()
}
})
完整代码
grandfather.vue
<template>
<h3>GrandFather组件 {{count}}</h3>
<input type="text" v-focus>
<button @click="addone">add1</button>
</template>
<script>
export default {
name: 'GrandFather',
data(){
return{
count:0
}
},
methods:{
addone(){
this.count++
}
},
components:{
}
}
</script>
<style scoped>
</style>
app.vue
<template>
<GrandFather></GrandFather>
</template>
<script>
import GrandFather from './components/GrandFather.vue'
export default {
name: 'App',
data(){
return {
}
},
methods:{
},
components: {
GrandFather,
}
}
</script>
<style scoped>
</style>
main.js
import { createApp } from 'vue'
import App from './App.vue'
import './index.css'
const app=createApp(App)
app.directive('focus',{
//让被绑定的元素插入到dom中时,自动触发mounted函数
mounted(el){
el.focus()
},
//每次dom更新时都会触发update函数
updated(el){
el.focus()
}
})
app.mount('#app')
12、自定义指令的两个注意点
1、vue3使用mouted方法,vue2使用bind方法
2、mouted和update函数中的逻辑完全相同,可以进行简写
app.directive('focus',(el)=>{
//在mounted和updated时都会触发相同的业务处理
el.focus()
})
13、自定义指令的参数值
在绑定指令是,可以通过等号的形式为指令绑定具体的参数值
关键代码为:
子组件
grandfather.vue
<template>
<h3>GrandFather组件</h3>
<div v-color="'red'">通过自定义指令传参,给文字添加颜色</div>
</template>
<script>
export default {
name: 'GrandFather',
data(){
return{
}
},
methods:{
},
components:{
}
}
</script>
<style scoped>
</style>
自定义指令代码
main.js
import { createApp } from 'vue'
import App from './App.vue'
import './index.css'
const app=createApp(App)
app.directive('color',{
mounted(el,params){
//parmas.value就是通过等号为指定绑定的值
el.style.color=params.value
}
})
app.mount('#app')
自己写的一个穿多个参数的情形
grandfather.vue
<template>
<h3>GrandFather组件</h3>
//这里传入了一个数组
<div v-color="['red','green']">通过自定义指令传参,给文字添加颜色</div>
</template>
<script>
export default {
name: 'GrandFather',
data(){
return{
}
},
methods:{
},
components:{
}
}
</script>
<style scoped>
</style>
app.vue
<template>
<GrandFather></GrandFather>
</template>
<script>
import GrandFather from './components/GrandFather.vue'
export default {
name: 'App',
data(){
return {
}
},
methods:{
},
components: {
GrandFather,
}
}
</script>
<style scoped>
</style>
main.js
app.directive('color',{
mounted(el,params){
console.log(params)
//parmas.value就是通过等号为指定绑定的值
for (let index in params) {
el.style.color=params.value[0]
el.style.backgroundColor=params.value[1]
}
}
})