Vue(二)——组件
选项式API和组合式API
选项式API式一种通过包含多个选项的对象来描述组件逻辑的API,如data、methods、computed、watch等。在组件的初始化阶段,Vue会处理这些选项,吧选项中定义的数据、方法、计算属性、侦听器等内容添加到组件实例上,当页面渲染完成后,通过this关键字可以访问组件实例。
1 | <script> |
组合式API式将组件中的数据、方法、计算属性、侦听器等代码全部组合在一起,写在setup()函数中。
1 | <script> |
语法糖
1 | <script setup> |
Vue提供的选项式API和组合式API是同一底层系统所提供的两套不同的接口,选项式API是在组合式API的基础上实现的。使用组合式API可以讲项目的每个功能的数据和方法放到一起,能够快速定位到功能区域的相关代码,便于阅读和维护。同时,组合式API可以通过函数来实现高效的逻辑复用。
生命周期钩子函数
组件的生命周期是指每个组件从创建到被销毁的整个过程,每个组件都有生命周期,如果想要在某个特定的时机进行特定的处理,可以使用生命周期钩子函数。
Vue2生命周期 | Vue3生命周期 | 说明 |
---|---|---|
beforeCreate | - | 实例对象创建前 |
created | - | 实例对象创建后 |
beforeMount | onBedoreMount | 挂载前 |
mounted | onMounted | 挂载后 |
beforeUpdate | onBeforeUpdate | 更新前 |
updated | onUpdated | 更新后 |
beforeDestroy | onBeforeUnmount | 销毁前 |
destroyed | onUnmounted | 销毁后 |
组件的注册和引用
组件是Vue的基本结构单元,开发者可以将页面中独立的、可重用的部分封装成组件,对组件的结构、样式和行为进行设置,组件之间的可以相互引用。
注册组件
全局注册
在main.js中注册全局组件,可以在任何组件中使用。
1
2
3
4
5
6import { createApp } from 'vue'
import App from './App.vue'
import MyComponent from './components/MyComponent.vue' // 引入组件
const app = createApp(App)
app.component('MyComponent', MyComponent) //在挂载前注册组件
app.mount('#app')局部注册
只能在当前注册范围内使用
1
2
3
4
5
6
7
8<script>
import MyComponent from './components/MyComponent.vue'
export default {
components: {
MyComponent : MyComponent // 可以简写为 'MyComponent',组件名和组件对象名一致
}
}
</script>如果使用setup语法糖,导入的组件会被自动注册,无须手动注册,导入后可以直接在模板中使用。
引用组件
被引用的组件需要写成标签的形式,标签名与组件名对应,命名可以使用短横线分隔命名法,如my-component
,也可以使用驼峰命名法,如MyComponent
。
1 | <template> |
解决组件之间的样式冲突
scoped属性
为<style>
标签添加scoped属性后,Vue会自动为当前组件的DOM元素添加一个唯一的自定义属性,比如data-v-xxxxxx
,然后在CSS选择器中添加该属性,以确保样式只作用于当前组件的DOM元素。
深度选择器
如果添加了scoped属性后还需要让某些样式对子组件生效,则可以通过深度选择器实现。被编译后的选择器为[data-v-xxxxxx] .child
1 | <style scoped> |
父组件向子组件传递数据(props)
在子组件中声明props属性,接收父组件传递的数据
1
2
3
4
5
6
7<script>
export default {
props: {
msg: String
}
}
</script>可以不限制props类型,写成字符串数组形式的props,如
props: ['msg']
。如果使用setup语法糖:
1
2
3<script setup>
const props = defineProps(['msg']) // 可以写成字符串数组形式或多个对象参数
</script>在父组件中传递数据
如果数据时固定不变的,则通过静态绑定props方法。
1
<MyComponent msg="Hello"></MyComponent>
使用v-bind动态绑定props,可以将父组件的数据动态传递给子组件。
1
<MyComponent :msg="msg"></MyComponent>
在Vue中,所有的props都遵循单项数据流原则,props数据因父组件的更新而变化,变化后的数据将向下流向子组件,而且不会逆向传递
这样可以防止子组件意外变更props导致数据流向难以理解的问题。每次父组件绑定的props发生变更时,子组件中的props都将会刷新为最新的值。验证props
- 基础类型检查
- 必填项校验
- 属性默认值
- 自定义验证函数validator()
1 | props: { |
子组件向父组件传递数据
子组件向父组件传递数据,可以通过自定义事件来实现
在子组件中声明自定义事件
1
2
3<script>
const emit = defineEmits(['change']) // 字符串数组
</script>在子组件中触发自定义事件
第一个参数为自定义时间的名称,第二个参数为需要传递的数据。
1
<button @click="$emit('change', 'Hello')">点击</button>
setup函数
1
2
3
4
5
6
7
8export default {
setup(props, ctx) {
const handleClick = () => {
ctx.emit('change', 'Hello')
}
}
return { handleClick }
}setup语法糖
1
2
3
4
5<script setup>
cosnt update = () => {
emit('change', 'Hello')
}
</script>在父组件中监听自定义事件
1
<MyComponent @change="handleChange"></MyComponent>
1
2
3
4
5<script>
const handleChange = (value) => {
console.log(value)
}
</script>
跨级组件之前的数据传递
通过依赖注入实现跨级组件之间的数据传递,父组件作为依赖提供者,使用provide()函数,子组件如果想注入上层组件或整个应用提供的数据,需要使用inject()函数。
provide()函数
1
2
3
4
5
6
7
8<script>
import { provide } from 'vue'
export default {
setup() {
provide('name', 'Tom') // 第一个参数是注入名,第二个参数是注入的值
}
}
</script>使用语法糖
1
2
3
4<script setup>
import { provide } from 'vue'
provide('name', 'Tom')
</script>全局依赖
1
2const app = createApp(APP)
app.provide('name', 'Tom')inject()函数
通过inject可以注入上层组件或者整个应用提供的数据,第一个参数是注入值,第二个参数是默认值,第三个参数是布尔值,代表是否将默认值视为工厂函数。
1
2
3
4
5
6
7
8
9
10
11
12
13<script>
import { inject } from 'vue'
export default {
setup() {
// 注入一个值
const name = inject('name', 'Jerry') // 第一个参数是注入名,第二个参数是默认值。
// 当没有匹配到注入的值时,默认值可以是工厂函数
const baz = inject('baz', () => new Map())
// 注入时为了表明提供的默认值是函数,需要传入第三个参数
const fn = inject('function', () => { }, false)
}
}
</script>
工厂函数
- 把对象的创建过程封装在一个函数里;
- 显式返回一个对象(可以是字面量对象、也可以是其它构造结构);
- 相比于构造函数或类,使用工厂函数无需 new,避免了构造函数中 this 指向的问题。
动态组件
定义动态组件
使用<component>
标签可以动态切换不同的组件,通过绑定is属性决定哪个组件被渲染。
1 | <component :is="currentComponent"></component> |
利用KeepAlive组件缓存动态组件
使用动态组件实现组件之间的按需切换时,隐藏的组件会被销毁,展示出来的组件会被重新创建,这样会导致组件的状态丢失。为了解决这个问题,可以使用<KeepAlive>
组件对动态组件进行缓存。
1 | <KeepAlive> |
相关生命周期
- onActivated:被包裹的动态组件被激活时触发
- onDeactivated:被包裹的动态组件被停用时触发
KeepAlive组件的常用属性
属性 | 类型 | 说明 |
---|---|---|
include | String、RegExp | 只有名称匹配的组件会被缓存 |
exclude | String、RegExp | 名称匹配的组件不会被缓存 |
max | Number | 缓存组件的最大数量 |
插槽
组件封装期间为组件的使用者预留的占位符,允许组件的使用者在组件内展示特定的内容
默认插槽
定义插槽
1 | <template> |
使用插槽
1 | <MyComponent> |
具名插槽
当Vue中需要定义多个插槽时,可以通过具名插槽来区分不同的插槽。具名插槽是给每一个插槽定义一个name名称,默认插槽的名称是default。
定义具名插槽
1 | <template> |
使用具名插槽
1 | <MyComponent> |
作用域插槽
作用域插槽时带有数据的插槽,子组件提供一部分数据给插槽,父组件接受子组件的数据进行页面渲染。
定义数据
1 | <slot :data="data"></slot> |
使用数据
1 | <MyComponent> |
自定义指令
自定义指令方便开发者通过直接操作DOM元素来实现业务逻辑
- 私有自定义指令:组件内不定义,只在当前组件内使用
- 全局自定义指令:在main.js中定义,全局使用
自定义指令的生命周期函数
函数名 | 说明 |
---|---|
created | 在绑定元素的属性前调用 |
beforeMount | 绑定元素挂载之前调用 |
mounted | 绑定元素的父组件及其自身所有子节点都挂载完成后调用 |
beforeUpdate | 绑定元素的父组件更新之前调用 |
updated | 绑定元素的父组件更新完成后调用 |
beforeUnmount | 绑定元素的父组件卸载之前调用 |
unmounted | 绑定元素的父组件卸载完成后调用 |
常用的自定义指令声明周期函数的参数
- el:指令所绑定的元素,可以用来直接操作DOM
- binding:一个对象,包含多个属性,用于接收属性的参数值
- value: 传递给指令的值
- arg:传递给指令的参数
- oldValue:之前的值,仅仅在update相关中可用
- modifiers:一个包含修饰符的对象
- instance:指令所绑定的组件实例
- dir: 指令的定义对象
- vnode:代表绑定元素底层的虚拟节点
- prevNode:之前页面渲染中指令所绑定元素的虚拟节点
私有自定义指令
声明
1 | <template> |
使用语法糖则不需要声明directives
1 | <script setup> |
全局自定义指令
声明
1 | // main.js |
为自定义指令绑定参数
1 | <template> |
对于自定义指令来说,通常仅需要在mounted和updated函数中操作DOM元素,如果这两个函数中的操作逻辑相同,可以将操作逻辑提取出来,封装成一个函数,然后在mounted和updated函数中调用。
引用静态资源
- public目录用于存放不可以编译的静态资源文件,该目录下的文件会被复制到打包目录,该目录下的文件需要使用绝对路径访问。
- src\assets目录用于存放可编译的静态资源文件,例如土坯啊样式文件等,该目录下需要使用相对路径访问。