10、Vue-组合式-组件

137 阅读6分钟

对于一些公共性比较强的UI,可以使用组件的方式对其进行存储。方便多次调用

一、基础使用

1、定义组件

components目录下新建一个.vue文件, 用于存放组件内容

组件内简单写一点东西:

<script setup>

</script>
<template>
    <h3>这是一个自定义的组件</h3>
</template>

2、使用组件

替换app.vue内代码如下:

<script setup>
// 第一步:引入组件
import component from "./components/conmonent.vue"

</script>

<template>
    <!--第二步:使用组件-->
    <component/>
</template>

二、组件的注册方式

1、局部注册

上述的例子中就属于局部注册,要在那个文件中使用组件,就在那个文件中import对应组件

2、全局注册

修改main.js为下图所示:

然后使用下述代码替换掉app.vue中的代码

<script setup>

</script>

<template>
    <!--第二步:使用组件-->
    <global-component/>
</template>

可以看到,此时app.vue中已经取消了之前的import。 实际效果仍跟基础使用中的示例一样。

后面使用到组件的例子中都会采用局部注册的方式去使用组件。方便阅读理解。实际使用的时候按需即可

三、动态组件

有些使用场景需要在多个组件之间来回切换,这里就需要用到动态组件的概念,使用:is关键字来完成

1、新建两个component组件

component1:

<script setup>

</script>
<template>
    <h3>第一个自定义组件</h3>
</template>

component2:

<script setup>

</script>
<template>
    <h3>第二个自定义组件</h3>
</template>

代码很简单,只是一行打印而已

3.1、使用下述代码替换掉app.vue中的代码

<script setup>

import { ref } from 'vue'

// 引入组件
import component1 from './components/conmonent1.vue'
import component2 from './components/component2.vue'

// 声明一个默认组件
const currentTab = ref('component1')

// 将组件放入一个字典中
const tabs = {
  component1,
  component2,
}
</script>

<template>
    <!--点击按钮切换到对应组件-->
    <button v-for="(_, tab) in tabs" :key="tab" @click="currentTab = tab">{{ tab }}</button>
    <component :is="tabs[currentTab]"></component>
</template>

3.2 或者使用下述代码替换app.vue中的代码:

<script setup>

import { ref } from 'vue'

// 引入组件
import component1 from './components/conmonent1.vue'
import component2 from './components/component2.vue'

// 声明一个默认组件
const currentTab = ref('component1')

// 将组件放入一个字典中
const tabs = {
  "component1":component1,
  "component2":component2
}

function changeComponent() {
    currentTab.value = currentTab.value == "component1" ? "component2" : "component1"
}
</script>

<template>
    <!--点击按钮切换到对应组件-->
    <button @click="changeComponent">切换组件</button>
    <component :is="tabs[currentTab]"></component>
</template>

四、组件数据传递1:defineProps

使用组件过程中可能会涉及到数据的传递,不同组件之间的数据是没办法直接使用的,使用defineProps关键字完成数据传递

先新建一个组件,并在app.vue中使用该组件

组件代码:

<script setup>
</script>
<template>
    <h3>自定义的组件</h3>
</template>

app.vue代码:

<script setup>
import component1 from "./components/conmonent1.vue"
</script>

<template>
    <component1/>
</template>

此时访问主页效果应该是这样的:

app.vue中向组件传递数据:

组件中接收并使用数据:

最终效果如下图:

特别注意:

  1. 上述的数据传递方式只能从父组件传递给子组件,无法反向传递
  2. 如果要传递的是一个动态可变的数据,则需要使用:对变量进行绑定,例如:
<component1 :param1="从父组传递过过来的第一个参数"/>

3. 不同类型的数据传递,传递的时候都是使用双引号对数据进行包裹

<component1 :name="张三" :age="18"/>

4. 接收数据的时候可以将defineProps赋值给变量,例如:

<script setup>
// 第一步:接收从父组件传递过来的数据并赋值给变量
const props=defineProps(['param1', 'param2'])
</script>
<template>
    <h3>自定义的组件</h3>
    <!--第二步:使用从父组件传递过来的数据-->
    <p>{{props.param1}}</p>
    <p>{{props.param2}}</p>
</template>

五、数据传递2:子组件向父组件传递数据

1、第一种方式:props

正常来讲props是没有办法反向传递数据给父组件的。但是可以通过使用props向子组件传递一个函数。让函数使用到子组件的参数并传递回来。

app.vue代码如下:

<script setup>
import component1 from "./components/conmonent1.vue"
import {ref} from "vue";

// 第一步:声明一个变量用于存储从子组件传递过来的数据
const data = ref('')

// 第二步:声明一个方法用于传输给子组件,方法内将子组件参数进行赋值, 这里注意不能有返回值,否则子组件那边也会产生打印
function dataTransmission(param1){
    data.value = param1
}
</script>

<template>
    <!--第三步:将声明的方法传递给子组件。这里注意要使用冒号-->
    <component1 :title="dataTransmission"/>
    <!--最后:打印结果-->
    <p>这是从子组件传递过来的数据:{{data}}</p>
</template>

组件代码如下:

<script setup>
import {ref} from "vue";
// 第一步:获取子组件传递过来的方法
defineProps(['title'])

// 第二步:定义一个变量用于存储数据
const msg = ref('子组件数据')
</script>
<template>
    <h3>自定义的组件</h3>
    <!--第三步:使用从父组件传递过来的函数将定义的变量数据传递出去-->
    <p>{{title(msg)}}</p>
</template>

最终效果如下:

2、第二种方式:自定义事件:emit

通过使用$emit自定义事件的方式从子组件向父组件传递数据

为了看起来更加清晰和直观。这里用截图的方式存放代码

效果如下:

3、第三种方式:插槽:slot

这种方式的数据传递主要传递的是html

父组件调用子组件的时候,在组件块中传递html, 子组件使用slot来接收父组件传递过来的html并进行展示。结果如下:

slot 相关知识点如下:

  • 默认值:可以在子组件slot标签对中定义默认值。当父组件有数据传递过来的时候使用父组件传递过来的数据;当父组件没有传递数据过来恩等时候使用子组件自定义的默认值

  • 插槽名称:父组件传递数据的过程中可以使用v-slot关键字进行插槽命名。子组件使用name关键字来获取对应的插槽内容进行展示。使插槽的使用更加灵活。例如:

  • 插槽数据传递:当子组件既需要自己本身的数据,又需要父组件传递过来的模板内容的情况下:可以先将子组件中的数据传递给父组件,然后由父组件将数据通过插槽传递给子组件,例如:

  • 当插槽数据传递和插槽名称需要同时使用时,使用又有所不同:

六、组件保持存活:keep-avive

每次进行组件切换的时候都会走一遍组件的声明周期,如果组件切换频率较高的话,会造成不必要的消耗。因此,有了保持存活的需求。 保持组件存活使用keep-avive关键字

测试代码如下:

上述利用了动态切换组件,并且在其中一个组件中对组件内数据进行了修改,用于看效果使用

效果如下:

保持存活的组件

不保持存活的组件

通过上述两个示例的操作对比,应该很容易理解组件保持存活的概念

七、异步组件:defineAsyncComponent

有时候,我们希望组件是在需要的时候进行异步加载的。以此来减缓整个页面的加载时长,提高体验感,使用defineAsyncComponent 关键字来完成

效果预览:

异步加载组件

通过上述的截图可以看出:组件2是在切换的时候才进行加载的。再来看下同步组件的表现,做个对比

同步加载组件

通过上述的截图可以看出:组件1和组件2都是在页面一开始就加载好了的。

八、依赖注入:provideinject

假设有三个组件,组件1,组件2,组件3: 组件1引用组件2,组件2引用组件3,组件3要使用组件1传递的数据。按照前面学到的: 组件1要将数据传递给组件2,组件2要将数据传递给组件3,然后组件3再使用传递过来的数据 此时使用依赖注入就可以轻松解决此问题:基础使用语法如下:

效果展示:

注意:

  • 如果要修改传递过来的数据:传递的时候传递一个可变的变量过去,例如:
import {provide, ref} from "vue";
const message= ref('界面1传递过来的数据')
// 组件1使用provide方法传递数据, 第一个参数msg是用于接收的数据名, 第二个参数是具体数据
provide('msg', message)
  • 可以在main.js中进行全局注册,这样全局所有的组件都可以使用到该数据(不推荐)
const app = createApp(App)
// main.js中配置全局数据传递
app.provide('msg', 'main.js传递出来的数据')

app.mount('#app')
  • 任意一个子组件都可以使用父组件传递出来的数据