目录
4.1 动态组件
4.1.1 定义动态组件
4.1.2 利用KeepAlive组件实现组件缓存
4.1.3 组件缓存相关的生命周期函数
4.1.4 KeepAlive组件的常用属性
4.2 插槽
4.2.1 什么是插槽
编辑 4.2.2 具名插槽
4.2.3 作用域插槽
4.3 自定义指令
4.3.1 什么是自定义指令
4.3.2 私有自定义指令的声明与使用
4.3.3 全局自定义指令的声明与使用
4.3.4 为自定义指令绑定参数
编辑 4.3.5 自定义指令的函数形式
4.4 引用静态资源
学习目标:
掌握具名插槽的使用方法,能够通过name属性定义具名插槽
掌握私有自定义指令的使用方法,能够独立完成私有自定义指令的声明和使用
掌握为自定义指令绑定参数的使用方法,能够通过等号(=)的方式,为当前 指令绑定参数
掌握自定义指令的函数形式,能够使用函数形式简化自定义指令的声明等等
4.1 动态组件
4.1.1 定义动态组件
利用动态组件可以动态切换页面中显示的组件。使用<component>标签可以定义动态组件,语法格式如下。
<component is="要渲染的组件"></component>
上述语法格式中,<component>标签必须配合is属性一起使用,is属性的属性值表示要渲染的组件,当该属性值发生变化时,页面中渲染的组件也会发生变化。
is属性的属性值可以是字符串或组件,当属性值为组件时,如果要实现组件的切换,需要调用shallowRef()函数定义响应式数据,将组件保存为响应式数据。shallowRef()函数只处理对象最外层属性的响应,它比ref()函数更适合于将组件保存为响应式数据。
代码如下:
创建Myleft.vue文件:
<template>
Myleft组件
<div>
count的值:{{值}}
<button @click="count+1">+1</button>
</div>
</template>
<script setup>
import {onMounted,onUnmounted,onActivated,onDeactivated,ref} from 'vue'
const count = ref(0);
onMounted(()=>{
console.log("Myleft组件被挂载成功")
}
)
onUnmounted(()=>{
console.log("Myleft组件被销毁成功")
}
)
onActivated(()=>{
console.log("MyRight组件被缓存了")
}
)
onDeactivated(()=>{
console.log("MyRight组件被缓存了")
}
)
</script>
<style>
</style>
创建Myright.vue文件:
<template>
Myright组件
</template>
<script>
</script>
<style>
</style>
创建DynamicComponet.vue文件:
<template>
<button @click="showComponent=Myleft">展示MyLeft组件</button>
<button @click="showComponent=Myright">展示MyRight组件</button>
<KeepAlive>
<div>
<component :is="showComponent"></component></div>
</KeepAlive>
</template>
<script setup>
import {shallowRef} from 'vue'
import Myleft from'./Myleft.vue'
import Myright from './Myright.vue'
const showComponent = shallowRef(Myleft)
</script>
<style>
</style>
修改main.js文件:
import { createApp } from 'vue'
import App from './components/DynamicComponet.vue'
const app=createApp(App)
//import GlobalComponent from './components/GlobalComponent.vue'
// app.component("GlobalComponent", GlobalComponent)
app.mount('#app')
运行结果:
4.1.2 利用KeepAlive组件实现组件缓存
概念:使用动态组件实现组件之间的按需切换时,隐藏的组件会被销毁,展示出来的组件会被重新创建。因此,当一个组件被销毁后又重新创建时,组件无法保持销毁前的状态。
如果在多个组件之间进行动态切换时想要保持这些组件的状态,以及避免重复渲染导致的性能问题,可以通过组件缓存来实现。
组件缓存可以使组件创建一次后,不会被销毁。在Vue中可以通过KeepAlive组件来实现组件缓存。 KeepAlive组件可通过<KeepAlive>标签来定义,定义时使用<KeepAlive>标签包裹需要被缓存的组件。运行结果:
从单击“展示MyRight组件”按钮后的页面效果和控制台可以看出,控制台中没有输出“MyLeft组件被销毁了”的信息,说明此时MyLeft组件已经被缓存了。 再次单击“展示MyLeft组件”按钮,页面效果与步骤1中单击“+1”按钮后的页面效果相同,说明通过添加<KeepAlive>标签可以实现组件缓存。
4.1.3 组件缓存相关的生命周期函数
概念:在Vue开发中,有时需要在组件被激活或者被缓存时执行某些操作。为此,Vue提供了组件缓存相关的生命周期函数,可以监听组件被激活和组件被缓存的事件。
当组件被激活时,会触发组件的onActivated()生命周期函数;当组件被缓存时,会触发组件的onDeactivated()生命周期函数。这两个生命周期函数的语法格式如下。
// onActivated()生命周期函数
onActivated(() => { })
// onDeactivated()生命周期函数
onDeactivated(() => { })
MyLeft.vue文件新增代码:
<script setup>
import { ref, onMounted, onUnmounted, onActivated, onDeactivated } from 'vue'
onActivated(() => {
console.log('MyLeft组件被激活了')
})
onDeactivated(() => {
console.log('MyLeft组件被缓存了')
})
</script>
MyRight.vue文件新增代码:
<script setup>
import { onActivated, onDeactivated } from 'vue'
onActivated(() => {
console.log('MyRight组件被激活了')
})
onDeactivated(() => {
console.log('MyRight组件被缓存了')
})
</script>
运行结果:
单击“展示MyRight组件”按钮后的页面效果和控制台如下图所示。
单击“展示MyLeft组件”按钮后的页面效果和控制台如下图所示。
4.1.4 KeepAlive组件的常用属性
概念:在默认情况下,所有被<KeepAlive>标签包裹的组件都会被缓存。如果想要实现特定组件被缓存或者特定组件不被缓存的效果,可以通过KeepAlive组件的常用属性include、exclude属性来实现。
KeepAlive组件的常用属性如下表所示。
在<KeepAlive>标签中使用include属性和exclude属性时,多个组件名之间使用英文逗号分隔,以include属性为例,语法格式如下。
<KeepAlive include="组件名1, 组件名2">
被缓存的组件
</KeepAlive>
4.2 插槽
4.2.1 什么是插槽
概念:Vue为组件的封装者提供了插槽(slot),插槽是指开发者在封装组件时不确定的、希望由组件的使用者指定的部分。也就是说,插槽是组件封装期间为组件的使用者预留的占位符,允许组件的使用者在组件内展示特定的内容。通过插槽,可以使组件更灵活、更具有可复用性。
插槽需要定义后才能使用,下面对定义插槽和使用插槽分别进行讲解。
1. 定义插槽
在封装组件时,可以通过<slot>标签定义插槽,从而在组件中预留占位符。假设项目中有一个MyButton组件,在MyButton组件中定义插槽的示例代码如下。
<template>
<button>
<slot></slot>
</button>
</template>
在<slot>标签内可以添加一些内容作为插槽的默认内容。如果组件的使用者没有为插槽提供任何内容,则默认内容生效;如果组件的使用者为插槽提供了插槽内容,则该插槽内容会取代默认内容。 另外,如果一个组件没有预留任何插槽,则组件的使用者提供的任何插槽内容都会不起作用。
2. 使用插槽
概念:使用插槽即在父组件中使用子组件的插槽,在使用时需要将子组件写成双标签的形式,在双标签内提供插槽内容。例如,使用MyButton组件的插槽的示例代码如下。
因为插槽内容是在父组件模板中定义的,所以在插槽内容中可以访问到父组件的数据。插槽内容可以是任意合法的模板内容,不局限于文本。例如,可以使用多个元素或者组件作为插槽内容,示例代码如下。
<MyButton>
<span style="color: yellow;">按钮</span>
<MyLeft />
</MyButton>
创建src\components\SlotSubComponent.vue文件,用于展示子组件的相关内容。
<template>
<div>测试插槽的组件</div>
<slot></slot>
</template>
创建src\components\MySlot.vue文件,用于展示插槽的相关内容。
<template>
父组件-----{{ message }}
<hr>
<SlotSubComponent>
<p>{{ message }}</p>
</SlotSubComponent>
</template>
<script setup>
import SlotSubComponent from './SlotSubComponent.vue'
const message = '这是组件的使用者自定义的内容'
</script>
修改src\main.js文件,切换页面中显示的组件。
import App from './components/MySlot.vue'
运行结果:
演示插槽的默认内容
演示插槽的默认内容,实现当组件的使用者没有自定义内容时默认内容生效的效果。 注释MySlot组件中插槽内容。
<!-- <p>{{ message }}</p> -->
在SlotSubComponent组件中为<slot>标签提供默认内容。
<slot>
<p>这是默认内容</p>
</slot>
运行结果:
将MySlot组件中的插槽内容取消注释,保存上述代码后,页面效果如下图所示。
4.2.2 具名插槽
概念:在Vue中当需要定义多个插槽时,可以通过具名插槽来区分不同的插槽。具名插槽是给每一个插槽定义一个名称,这样就可以在对应名称的插槽中提供对应的数据了。
插槽通过<slot>标签来定义,<slot>标签有一个name属性,用于给各个插槽分配唯一的名称,以确定每一处要渲染的内容。添加name属性的<slot>标签可用来定义具名插槽。 定义具名插槽的语法格式如下。
<slot name="插槽名称"></slot>
在父组件中,如果要把内容填充到指定名称的插槽中,可以通过一个包含v-slot指令的<template>标签来实现,语法格式如下。
<组件名>
<template v-slot:插槽名称></template>
</组件名>
与v-on和v-bind类似,v-slot也有简写形式,即把v-slot:替换为#。例如,v-slot:title可以简写为#title。
演示具名插槽的使用
创建src\components\ArticleInfo.vue文件,用于展示文章内容模板。
<template>
<div class="article-container">
<div class="header-box"><slot name="header"></slot></div>
<div class="content-box"><slot name="content"></slot></div>
<div class="footer-box"><slot name="footer"></slot></div>
</div>
</template>
<style>
.article-container > div { border: 1px solid black; }
</style>
创建src\components\MyArticle.vue文件,用于提供文章数据,在MyArticle组件中导入并使用ArticleInfo组件,并在<ArticleInfo>标签中为不同插槽添加不同的信息。
<template>
<ArticleInfo>
<template v-slot:header><p>这是文章的头部区域</p></template>
<template v-slot:content><p>这是文章的内容区域</p></template>
<template #footer><p>这是文章的尾部区域</p></template>
</ArticleInfo>
</template>
<script setup>import ArticleInfo from './ArticleInfo.vue' </script>
修改src\main.js文件,切换页面中显示的组件。
import App from './components/MyArticle.vue'
运行结果:
4.2.3 作用域插槽
概念:一般情况下,在父组件中不能使用子组件中定义的数据。如果想要在父组件中使用子组件中定义的数据,则需要通过作用域插槽来实现。作用域插槽是带有数据的插槽,子组件提供一部分数据给插槽,父组件接收子组件的数据进行页面渲染。
作用域插槽的使用分为定义数据和接收数据两个部分。
1. 定义数据
在封装组件的过程中,可以为预留的插槽定义数据,供父组件接收并使用子组件中的数据。在作用域插槽中,可以将数据以类似传递props属性的形式添加到<slot>标签上。 例如,在封装MyHeader组件时,在插槽中定义数据供父组件使用,示例代码如下。
<slot message="Hello Vue.js"></slot>
2. 接收数据
使用默认插槽和具名插槽接收数据的方式不同,接下来分别进行讲解。
下面讲解默认插槽:
在Vue中,每个插槽都有name属性,表示插槽的名称。在定义插槽时虽然省略了<slot>标签的name属性,但是name属性默认为default,这样的插槽属于默认插槽。
在父组件中可以通过v-slot指令接收插槽中定义的数据,即接收作用域插槽对外提供的数据。通过v-slot指令接收到的数据可以在插槽内通过Mustache语法进行访问。 例如,在父组件中使用MyHeader组件中的插槽时,通过v-slot指令的值接收传递的数据的示例代码如下。
作用域插槽对外提供的数据对象可以使用解构赋值以简化数据的接收过程,示例代码如下。
下面讲解具名插槽: 在Vue中,通过<slot>标签添加name属性来定义具名插槽,在具名插槽中也可以向父组件中传递数据。 例如,在封装MyHeader组件时,向具名插槽中传入数据的语法格式如下。
<slot name="header" message="hello"></slot>
具名插槽和作用域插槽可以作用在同一个<slot>标签上且并不冲突。<slot>标签的name属性不会作为数据传递给插槽,所以最终传递给组件的数据只有message属性。
在使用具名插槽时,插槽属性可以作为v-slot的值被访问到,基本语法格式为“v-slot:插槽名称="形参"”,简写形式为“#插槽名称="形参"”,使用简写形式使用插槽的示例代码如下。
<MyHeader>
<template #header="{ message }">
{{ message }}
</template>
</MyHeader>
如果在一个组件中同时定义了默认插槽和具名插槽,并且它们均需要为父组件提供数据,这时就需要为默认插槽使用显式的<template>标签来接收数据,示例代码如下。
<MyHeader>
<template #default="{ message }">
{{ message }}
</template>
</MyHeader>
演示作用域插槽的使用
创建src\components\SubScopeSlot.vue文件,用于展示作用域插槽。
<template>
<slot message="Hello 默认插槽"></slot>
<hr>
<slot message="Hello Vue.js" name="header"></slot>
<hr>
<slot :user="user" name="content"></slot>
</template>
<script setup>
import { reactive } from 'vue'
const user = reactive({ name: 'xiaoyuan', age: '15' })
</script>
创建src\components\ScopeSlot.vue文件,用于为作用域插槽提供数据。
<template>
<SubScopeSlot>
<template v-slot:default="scope"><p>{{ scope }}</p></template>
<template v-slot:header="scope">
<p>{{ scope }}</p><p>{{ scope.message }}</p>
</template>
<template #content="{ user }">
<p>{{ user.name }}</p><p>{{ user.age }}</p></template>
</SubScopeSlot>
</template>
<script setup>import SubScopeSlot from './SubScopeSlot.vue'</script>
修改src\main.js文件,切换页面中显示的组件。
import App from './components/ScopeSlot.vue'
运行结果:
4.3 自定义指令
4.3.1 什么是自定义指令
当内置指令不能满足开发需求时,可以通过自定义指令来拓展额外的功能。自定义指令的主要作用是方便开发者通过直接操作DOM元素来实现业务逻辑。
Vue中的自定义指令分为两类,分别是私有自定义指令和全局自定义指令。 私有自定义指令是指在组件内部定义的指令。私有自定义指令可以在定义该指令的组件内部使用。例如,在组件A中自定义了指令,只能在组件A中使用,组件B、组件C中不能使用。 全局自定义指令是指在全局定义的指令。全局自定义指令可以在全局使用,例如,在src\main.js文件中定义了全局自定义指令,这个指令可以用于任何一个组件。
一个自定义指令由一个包含自定义指令生命周期函数的参数来定义。常用的自定义指令生命周期函数如下表所示。
常用的自定义指令生命周期函数的参数如下表所示。
binding中包含以下6个常用属性。
value:传递给指令的值。
arg:传递给指令的参数。
oldValue:之前的值,仅在beforeUpdate()函数和updated()函数中可用,无论值是否更改都可用。 modifiers:一个包含修饰符的对象 (如果有)。例如,在v-my-directive.foo.bar 中,修饰符对象是{ foo: true, bar: true }。
instance:使用该指令的组件实例。 dir:指令的定义对象。
4.3.2 私有自定义指令的声明与使用
如果没有使用setup语法糖,可以在directives属性中声明私有自定义指令。例如,声明一个私有自定义指令color,示例代码如下。
export default {
directives: {
color: {}
}
}
在上述代码中,color为自定义指令的名称,指令名称可以自定义。名称为color的指令指向一个配置对象,对象中可以包含自定义指令的生命周期函数,可通过这些函数来操作DOM元素。
在使用自定义指令时,需要以“v-”开头,示例代码如下。
<h1 v-color>标题</h1>
如果使用setup语法糖,任何以“v”开头的驼峰式命名的变量都可以被用作一个自定义指令,示例代码如下。
<template>
<span v-color></span>
</template>
<script setup>
const vColor = { }
</script>
驼峰式命名:驼峰式命名规则是一种在编程中广泛使用的命名约定。它有两种主要形式:
小驼峰式命名法:这种命名方式中,第一个单词的首字母小写,从第二个单词开始,每个单词的首字母都大写。例如,“firstName”和“lastName”。
大驼峰式命名法:在这种命名法中,每个单词的首字母都大写,而第一个单词的首字母小写。例如,“FirstName”、“LastName”和“CamelCase”。大驼峰式也被称为Pascal命名法,这种命名法常用于类名和常量名。
演示私有自定义指令的使用方法
创建src\components\DirectiveComponent.vue文件。
<template>
<p v-fontSize>DirectiveComponent组件</p>
</template>
<script setup>
const vFontSize = {}
</script>
修改src\main.js文件,切换页面中显示的组件。
import App from './components/DirectiveComponent.vue'
修改DirectiveComponent组件,添加mounted()函数,实现元素挂载完成后文本字号的改变。
const vFontSize = {
mounted: el => {
el.style.fontSize = '24px'
}
}
运行结果:
4.3.3 全局自定义指令的声明与使用
全局自定义指令需要通过Vue应用实例的directive()方法进行声明,语法格式如下。
directive('自定义指令名称', 对象)
在上述语法格式中,directive()方法的第1个参数类型为字符串,表示全局自定义指令的名称;第2个参数类型为对象或者函数,可以是对象或者函数形式,用于接收指令的参数值。
下面演示全局自定义指令的使用方式。 修改src\main.js文件,声明全局自定义指令fontSize。
import { createApp } from 'vue'
import './style.css'
import App from './components/DirectiveComponent.vue'
const app = createApp(App)
app.directive('fontSize', {
mounted: el => {
el.style.fontSize = '24px'
}
})
app.mount('#app')
4.3.4 为自定义指令绑定参数
在使用自定义指令时,开发人员可以通过自定义指令的参数改变元素的状态,传递的参数由自定义指令的生命周期函数的第2个参数接收。 在标签中使用自定义指令时,通过等号(=)的方式可以为当前指令绑定参数,示例代码如下。
<h1 v-color="color"></h1>
如果指令需要多个值,可以传递一个对象,示例代码如下。
<div v-demo="{ color: 'red', text: 'hello' }"></div>
演示自定义指令参数的使用方法
创建src\components\CustomDirective.vue文件。
<template>
<p v-fontSize="fontSize">DirectiveComponent组件</p>
<button @click=“fontSize = ‘24px’”>更改字号大小</button>
</template>
<script setup>
import { ref } from 'vue'
const fontSize = ref('12px')
const vFontSize = {
mounted: (el, binding) => { el.style.fontSize = binding.value },
}
</script>
修改src\main.js文件,切换页面中显示的组件。
运行结果:
演示自定义指令参数的使用方法
在自定义指令fontSize中添加updated()函数,实现自定义指令绑定的参数改变时,页面进行同步更改。
const vFontSize = {
// 原有代码……
updated: (el, binding) => {
el.style.fontSize = binding.value
}
}
运行结果:
4.3.4 为自定义指令绑定参数
在使用自定义指令时,开发人员可以通过自定义指令的参数改变元素的状态,传递的参数由自定义指令的生命周期函数的第2个参数接收。
在标签中使用自定义指令时,通过等号(=)的方式可以为当前指令绑定参数,示例代码如下。
<h1 v-color="color"></h1>
如果指令需要多个值,可以传递一个对象,示例代码如下。
<div v-demo="{ color: 'red', text: 'hello' }"></div>
演示自定义指令参数的使用方法
创建src\components\CustomDirective.vue文件。
<template>
<p v-fontSize="fontSize">DirectiveComponent组件</p>
<button @click=“fontSize = ‘24px’”>更改字号大小</button>
</template>
<script setup>
import { ref } from 'vue'
const fontSize = ref('12px')
const vFontSize = {
mounted: (el, binding) => { el.style.fontSize = binding.value },
}
</script>
修改src\main.js文件,切换页面中显示的组件。
import App from './components/CustomDirective.vue'
运行效果:
演示自定义指令参数的使用方法
在自定义指令fontSize中添加updated()函数,实现自定义指令绑定的参数改变时,页面进行同步更改。
const vFontSize = {
// 原有代码……
updated: (el, binding) => {
el.style.fontSize = binding.value
}
}
运行结果:
单击“更改字号大小”按钮后的页面效果如下图所示。
4.3.5 自定义指令的函数形式
对于自定义指令来说,通常仅需要在mounted()函数和updated()函数中操作DOM元素,除此之外,不需要其他的生命周期函数。
例如,4.3.4小节CustomDirective 组件中的mounted()函数和updated()函数中的代码完全相同。此时,可以将自定义指令简写为函数形式。 将私有自定义指令简写为函数形式的示例代码如下。
const vFontSize = (el, binding) => {
el.style.fontSize = binding.value
}
将全局自定义指令简写成函数形式的示例代码如下。
app.directive('fontSize', (el, binding) => {
el.style.fontSize = binding.value
})
4.4 引用静态资源
1. 引用public目录中的静态资源
public目录用于存放不可编译的静态资源文件,该目录下的文件会被复制到打包目录,该目录下的文件需要使用绝对路径访问。 例如,在组件中引用public目录中的demo.png文件,示例代码如下。
<img src="/demo.png" >
演示引用public目录中静态资源的方法
创建src\components\Image.vue文件。
<template>
<img src="/vite.svg" >
</template>
修改src\main.js文件,切换页面中显示的组件,示例代码如下。
import App from './components/Image.vue'
运行效果:
2. 引用src\assets目录中的静态资源
src\assets目录用于存放可编译的静态资源文件,例如图片、样式文件等。该目录下的文件需要使用相对路径访问。
在引用src\assets目录中的图片时,首先将图片保存到本地,然后使用import语法将图片导入需要的组件,最后通过img元素的src属性添加图片的路径。
演示引用src\assets中静态资源的方法
创建src\components\Icon.vue文件。
<template>
<img :src="icon">
</template>
<script setup>
import icon from '../assets/vue.svg'
</script>
修改src\main.js文件,切换页面中显示的组件,示例代码如下。
import App from './components/Icon.vue'
运行效果:
今天就分享到此,感谢预览~