本章带领大家理解组件、props、emits、slots、providers/injects,Vue 插件 等Vue组件使用的基础知识。
- 5.1 组件注册
- 5.2 Props
- 5.2.1 组件之间如何传值
- 5.2.2 参数绑定 v-bind
- 5.2.3 参数类型
- 5.2.4 props 默认与必填
- 5.2.5 验证设置
- 5.2.6 useAttrs 属性设置
第一章 Vue3项目创建 1 Vue CLI 创建vue项目
第一章 Vue3项目创建 2 使用 Webpack 5 搭建 vue项目
第一章 Vue3项目创建 3 Vite 创建 vue项目
第二章 Vue3 基础语法指令
第三章 Vue Router路由器的使用
第四章 VUE常用 UI 库 1 ( element-plus,Ant ,naiveui,ArcoDesign)
第四章 VUE常用 UI 库 2 ( ailwind 后台框架)
第五章 Vue 组件应用 1( Props )
第五章 Vue 组件应用 2 ( Emit )
第五章 Vue 组件应用 3( Slots )
第五章 Vue 组件应用 4 ( provide 和 inject )
第五章 Vue 组件应用 5 (Vue 插件)
5.1 组件注册
Vue 3中有一个非常重要的概念是组件注册,一个 Vue 文件可以被引用到别外一个Vue 文件中,在引用的Vue文件模板中可以使用到被引用到Vue 文件。Vue 文件会被“注册”别外的Vue文件中成为它的子组件,这些子组件会被 父 Vue文件中的模板渲染时找到其对应的实现。组件注册有两种方式:全局注册和局部注册。
1 全局注册
使用 vue 根实例进行注册的组件为全局注册。使用的方法app.component()
,简单的理解就是注册给createApp()的组件都是全局组件,它可以在 Vue 应用中全局可用。
import { createApp } from 'vue'
import App from './App.vue'
import index from './components/index.vue';//导入组件index.vue
const app = createApp(App)
app.component('index',index);//注册全局组件
app.mount('#app')
components目录中创建一个index.vue文件。
<script setup></script>
<template>
<div>欢迎来到zht代码世界</div>
</template>
App.vue中模板中可以使用这个全局组件功能。
<script setup>
</script>
<template>
<index/>------------使用了全局组件index
</template>
2 局部组件
全局注册虽然很方便,但有使用起来会有几个问题:
- 全局注册在打包的时候都会被打在一个包中(这种情况叫“tree-shaking”),如果你注册了很多的全局组,即使它并没有被实际使用到,但是仍然会被打包在一个 JS 文件中,浏览器使用的时候一次全部加载进入缓存中来。
- 全局注册在大型项目中使项目的依赖关系变得复杂。在父组件中使用子组件时,不太容易定位到子组件的实现。和程序中使用过多的全局变量一样,这会影响整个项目代码的可维护性。
相比之下,局部注册的组件是在使用它的父组件中显式导入,并且只能在该父组件中使用。它的优点是使组件之间的依赖关系更加明确,并且对 tree-shaking 更加友好。
App.vue中模板可以直接导入index.vue,将index.vue注册为它的子组件。
<script setup>
import index from './components/index.vue';//导入组件index.vue
</script>
<template>
<index/>------------使用了全局组件index
</template>
5.2 Props
Props作用是将字符串、数字、数组和对象等值从父组件传递给子组件。当你想到在 JavaScript 中传递参数的时候,首先想到的可能是使用函数传递参数,就像函数中使用arguments一样处理参数。在vue中Props也可以像arguments一样在组件间传值,两个父子组件通过 Props 传递参数。在 Props 传递参数的时候可以在组件内部设定一个初始化函数,对 Props中的参数进行一个初始化的预定处理。
Vue.js 有一种机制,通过设置要传递的值的数据类型和默认值,使用 Props 将正确的数据传递给组件,以免导致错误。必须将正确的数据传递给组件,才能使组件正常工作。在路由的使用中就介绍过路由之间用Props如何传递参数,现在要介绍的组件之间如何使用Props。
5.2.1 组件之间如何传值
让我们创建一个简单的例子,看看如何使用 props传递参数。首先在index.vue文件中进行修改,使用defineProps函数获得props中的参数用于接收和显示名称,defineProps函数中的参数类型为数组。
index.vue
在components目录中创建一个index.vue文件。
<script setup>
defineProps(['name']);
</script>
<template>
<h1>{{ name }}</h1>
</template>
<style></style>
App.vue
在index组件中使用name属性设置一个字符串参数,将这个字符串传递给index.vue文件中的defineProps([‘name’]);函数,在模板将这个参数字符串显示出来。
<script setup>
import index from './components/index.vue';
</script>
<template>
<index name="我是一个Props 参数" />
</template>
组件可以重复使用传递不同的参数字符串。
<script setup>
import index from './components/index.vue';
</script>
<template>
<index name="我是一个Props 参数" />
<index name="我是二" />
<index name="我是三" />
</template>
5.2.2 参数绑定 v-bind
很多情况下我们需要在script标签中定义参数,而这些script标签中的参数需要进行值传递。这种情况下会使用到v-bind 指令进行参数名称绑定。如果不使用v-bind 设置绑定,name属性中设置的字符串(“productName”)将原样传递这个字符串(“productName”)给子组件,而不是script中的productName对象。
App.vue
<script setup>
import index from './components/index.vue';
const productName="const传值";//传递的参数
</script>
<template>
//通过v-bind 指令绑定productName参数与name关系
<index v-bind:name="productName" />
<index name="我是一个Props 参数" />
<index name="我是二" />
<index name="我是三" />
</template>
ref 函数绑定
如果在 props 中传递的数据需要用户交互而发生变化,可以使用 ref 函数定义一个反应变量,并使用 v-bind 设置该变量。
<script setup>
import index from './components/index.vue';
import { ref } from 'vue';
const productName = ref('const 参数');
</script>
<template>
<index v-bind:name="productName" />
<index name="我是一个Props 参数" />
<index name="我是二" />
<index name="我是三" />
</template>
描述指令 v-bind:name,也可以使用缩写形式(语法糖):name来绑定。
5.2.3 参数类型
上面的示例中对Props 参数使用的非常简单就是以字符串为参数进行值传递。Props也可以将字符串以外的任何内容作为要传递的值进行参数传递。但是,随着代码变得越来越复杂,很多时候我们可能会传递一个数字而不是字符串,或者传递一个Json对象。Props 中传入的这些不同类型的数据都会被defineProps函数接收与处理。
在vue.js中为了定义不同类型的参数,可以通过defineProps函数中的type属性来设置props中输入了什么类型的值。例如下面中 index 组件的 props 名称中包含字符串,因此首先将类型设置为 String。String的首字母大写,小写会报错。这声明该name属性将包含一个 String(字符串)。id属性类型被定义为数字类型,type设置为 Number,这样代表id获得值为数字类型。
index.vue
在index.vue组件中,获得来在父组件传递过来的参数,使用defineProps函数获得并且设置出这三个参数的数据类型。之前,我们只是用 props 设置数组并设置获得name属性。当我们需要设置不同属性的类型时候,可以参照下面的写法进行设置。
<script setup>
//defineProps 接收父组件Props中的参数
//设置name 字符串类型 type: String
//设置id 数字类型 type: Number
//设置dept 对象类型 type: Object
const props =defineProps({
name: {
type: String,
},
id: {
type: Number,
},
dept: {
type: Object,
},
});
</script>
<template>
<h1>id:{{ id }}</h1>
<h1>名称:{{ name }}</h1>
<h1>部门:{{ dept.name }} 部门id:{{ dept.id }}</h1>
</template>
<style></style>
App.vue
App.vue中的index组件绑定三个参数name,id,dept到props中。注意这里的dept属性实际上是一个对象类型。
<script setup>
import index from './components/index.vue';
import { ref } from 'vue';
const productName = ref('const 参数');
const dpet = ref({name:"部门一",id:"10"});
</script>
<template>
<index v-bind:name="productName" id="我不是数字" :dept="dpet"/>
<index name="我是一个Props 参数" />
</template>
参数类型一览
-
String
-
Number
-
Boolean
-
Array
-
Object
-
Date
-
Function
-
Symbol
类型不对发出警告
让我们来看看如果props中定义的属性类型与传入的参数类型不对会发生什么?测试一下我们定义一个数子类型的参数,给它值传入一个字符类型,看看会有什么变化。
--------------------- index.vue ---------------------
<script setup>
//defineProps 接收父组件Props中的参数
//设置name 字符串类型 type: String
//设置id 数字类型 type: Number
//设置dept 对象类型 type: Object
const props =defineProps({
name: {
type: String,
},
id: {
type: Number,
},
});
</script>
<template>
<h1>id:{{ id }}</h1>
<h1>名称:{{ name }}</h1>
</template>
<style></style>
--------------------- App.vue ---------------------
<script setup>
import index from './components/index.vue';
import { ref } from 'vue';
const productName = ref('const 参数');
</script>
<template>
<index v-bind:name="productName" id=“我不是数字”/>
<index name="我是一个Props 参数" />
</template>
在浏览器中会看到这个参数正常显示出来了,但是在控制台中会出现警告,我们会看到下面的警告内容。
控制台警告显示
类型不对的时候会在控制台会出现一条警告,执行了类型检查并传递了一个数字而不是字符串。我们可以通过这条警告,来检查自己的代码中是否存在参数类型与参数不一致的情况。
5.2.4 props 默认与必填
在props对象的属性中可以设置这默认值,必添项。
1 默认值 default
如果在子组件中调用的时候没有给props属性中的值传递任何参数的时候,这个时候props就会在配置中找到这个属性的默认值进行赋值。例如下面,默认值设置为“默认名字”。
defineProps({
name: {
type: String,
default: '默认名字',
},
});
index.vue组件中中如果设置了name属性的默认值,App.vue中如果没有为index组件设置name属性,将使用默认值。
<index name=“我是一个参数” />
<index />------------没有设置属性值,子组件中将显示默认值
组件部分将显示name属性的默认值“默认名字”。
如果我们在组件props的参数中没也有设置默认值会反生什么?
defineProps({
name: {
type: String,
},
});
如果未设置默认值,控制台不会显示警告等消息。浏览器上没有显示任何内容,因为没有值通过 props 传递index 组件不会收到name参数的值。
2 设置必填项 Required
required 属性来设置是否需是必填项。继续前面的内容,下面的例子让我们设置props属性内容为 required 而不设置 default 并且不在 component 标签中设置 props内容。required 的值可以是 true 或 false,我们将 prop 的中name属性设置为 required :true。
defineProps({
name: {
type: String,
required: true,---必填项
},
});
在浏览器控制台,将看到一条警告,指出即使这次需要该名称,也未提供该名称。警告说缺少道具“名称”
将 required 设置为 true 会导致显示警告, required 的默认情况为 false。
3 设置默认和必填项
在项目开发中设置了default默认值,在组件中如果没有给props属性值,将会使用到默认值,所以不需要设置reuqired这个属性。换句话说,不需要像下面这样写 default 和 required 。
defineProps({
name: {
type: String,
default: '默认名字',
required: true,
},
});
虽然此处设置了String的初始值,但设置数组或对象时可能会出现以下信息。
[Vue warn]: Invalid default value for prop "[props name]": Props with type Object/Array must use a factory function to return the default value.
在这种情况下,您应该使用一个函数
defineProps({
name: {
type: String,
default: () => [],
default:[] //设置默认数组与对象
},
});
5.2.5 验证设置
现在我们轻松的理解了props中的类型、默认和必需的设置。但是我们还有一个重要的 Validation 功能没有介绍,下面让我们一起来深理解Validation使用。Validation主要的功能是验证和检查输入的值是否正确。在下面的例子中,来验证index组件name属性的值是否包含"默认名字", “zht”, "zhtbs"这几个字符串。如果不包含这些字符串,控制台发出警告。
index.vue
<script setup>
const props =defineProps({
name: {
type: String,
default: '默认名字',
//验证函数
validator: (value) => {
return ["默认名字", "zht", "zhtbs"].indexOf(value) !== -1;
},
}
});
</script>
<template>
<h1>名称:{{ name }}</h1>
</template>
<style></style>
App.vue
<script setup>
import index from './components/index.vue';
</script>
<template>
<index name="我是一个参数" />
<index name="zhtbs" />
<index />
</template>
由于验证器中指定的数组元素中不包含"我是一个参数",因此控制台日志中的消息将显示自定义验证器失败的警告。
validator 我们来详细解释一下验证函数返回值,validator可以通过返回true或者false来判断成功或者失败,所以return true就一定会成功,当返回值被设置为 false 时,就会在控制台日志中收到警告。
<script setup>
const props =defineProps({
name: {
type: String,
default: '默认名字',
validator: (value) => {
return false;//不管值是什么都会收到验证警告
},
}
});
例如 对Props中接收到的值的字符串长度进行校验,可以这样写设置校验。如果字符数大于 6,此验证将为 true,因此如果您输入 3 个字符的包,它将为 false 并显示警告。
defineProps({
name: {
type: String,
default: '默认名字',
validator: (value) => {
//验证name是否大于6个字符
return value.length > 6;
},
},
});
Slots也可以用在父组件与子组件的传值方面。Props 允许传递字符串、数字、数组和对象等参数,而 Slots 允许您传递 HTML 标签。为了更好的加深了对 Props 的理解,请在 Props 之后学习了解Slots 的使用。
5.2.6 useAttrs 属性设置
在script setup中useAttrs 函数返回了
a
t
t
r
s
对象的引用。
attrs对象的引用。
attrs对象的引用。attrs对象保存了父组件模板中引用的子组件设置的class和style样式值,在把它们传回给子组件使用的一个通信工具类。在vue程序中会自动将组件中引用的子组件设置的class和style属性的值装入到
a
t
t
r
s
对象。
attrs对象。
attrs对象。attrs也会像props一样装入其他的参数属性,在子组件中这些被装入的其他参数也会被$attrs对象引用到。
App.vue
在使用到的子组件中,设置class属性和,id 和 style 参数值,它将会传递给子组件。
<script setup>
import index from './components/index.vue';
</script>
<template>
<index id="main" style="color:red" class="active" />
</template>
index.vue
在子组件中通过useAttrs()获得attrs对象,将attrs对象中的class样式绑定到p标签中的class。
<script setup>
import { useAttrs } from 'vue';
const attrs = useAttrs();
console.log(attrs);
</script>
<template>
<p :class="attrs.class">class属性确认中</p>
</template>
<style></style>
浏览其中会看到字体变红,和attrs对象中的内容。
但是我们会发现一个问题,就是attrs中的其他参数没有被绑定到模板元素上,这个时候它们都都会变成p标签中属性。
我们来看修改一下index.vue中的代码,增加几个html标签在这些标签中使用useAttr对象中的属性。
<script setup>
import { useAttrs } from 'vue';
const attrs = useAttrs();
console.log(attrs);
</script>
<template>
<p :class="attrs.class">class属性确认中</p>
<h2 class="info">子页面</h2>
<p :style="attrs.style">style属性确认中</p>
</template>
<style></style>
浏览器中显示结果。
我们会看到在多个html标签中useAttr对象中的值没有变成为这些html标签中的属性。
- 当模板中只有一个html标签绑定attrs对象时候,attrs中所有的值都会成为这个html元素中的属性。
- 当模板中多个htm标签绑定attrs对象,attrs对象中的值不会成为它们元素属性