赞
踩
组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。在实际应用中,组件常常被组织成层层嵌套的树状结构:
和嵌套 HTML 元素的方式类似,Vue 实现了自己的组件模型,使我们可以在每个组件内封装自定义内容与逻辑。Vue 同样也能很好地配合原生 Web Component
Vue 的单文件组件 (即 *.vue 文件,英文 Single-File Component,简称 SFC) 是一种特殊的文件格式,使我们能够将一个 Vue 组件的模板、逻辑与样式封装在单个文件中。
<script setup>
import { ref } from 'vue'
const greeting = ref('Hello World!')
</script>
<template>
<p class="greeting">{{ greeting }}</p>
</template>
<style>
.greeting {
color: red;
font-weight: bold;
}
</style>
Vue 的单文件组件是网页开发中 HTML、CSS 和 JavaScript 三种语言经典组合的自然延伸。、
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
return { count }
},
template: `
<button @click="count++">
You clicked me {{ count }} times.
</button>`
// 也可以针对一个 DOM 内联模板:
// template: '#my-template-element'
}
上面的例子中定义了一个组件,并在一个 .js 文件里默认导出了它自己,但你也可以通过具名导出在一个文件中导出多个组件。
要使用一个子组件,需要在父组件中导入它。
<script setup>
import ButtonCounter from './ButtonCounter.vue'
</script>
<template>
<h1>Here is a child component!</h1>
<ButtonCounter />
</template>
<script setup>
,导入的组件都在模板中直接可用。方式一:父组件给子组件传值时,通过v-on绑定属性实现,子组件通过props进行接收
// 父组件 <template> <div>父组件</div> <Childs :msg="msg" :count="count"></Childs> </template> <script lang="ts"> import { defineComponent, ref } from 'vue' import Childs from './childCom.vue' export default defineComponent({ components: { Childs }, setup () { const msg = ref('这是传递给子组件的') const count = ref(10) return { msg, count } } }) </script> // 子组件 <template> 子组件 <div>{{ msg }}</div> <div>{{ count }}</div> </template> <script lang="ts"> import { defineComponent, ref } from 'vue' export default defineComponent({ props: { msg: { type: String, default: '' }, count: { type: Number, default: 0 } }, setup () { return { } } }) </script>
方式二:父组件通过插槽(slot)向子组件传递参数
子组件中通过slot标签传递参数
父组件中通过template插入内容,通过v-slot可以接收插槽传递的数据
// 父组件 <template> <div>父组件</div> <Childs> <template #default="{ message }"> {{ message }} </template> </Childs> </template> <script lang="ts"> import { defineComponent, ref } from 'vue' import Childs from './childCom.vue' export default defineComponent({ components: { Childs }, setup () { return { } } }) </script> // 子组件 <template> 子组件 <slot :message="message"></slot> </template> <script lang="ts"> import { defineComponent, ref } from 'vue' export default defineComponent({ setup (_, { emit }) { const message = ref('子组件message') return { message } } }) </script> ``` ![上述代码执行效果](https://img-blog.csdnimg.cn/direct/537301cf18ac42a3a0eb7fac10f955c6.png) 注意:插槽只能用于传递静态内容,如果需要传递动态内容,则需要使用props或者provide/inject来实现。此外,插槽还可以定义多个,并且可以通过name属性来区分不同的插槽。(插槽后面会有具体的文章介绍)
v-model,子组件可直接修改父组件传递过来的值
在Vue3中,可以使用v-model指令来实现子组件直接修改父组件传递过来的值。具体来说,可以在父组件中使用v-model指令将一个变量与子组件的一个prop绑定起来,然后在子组件中使用emit方法触发一个名为update:加上prop名字的事件,并传递一个新的值作为参数。
// 父组件 <template> <div>父组件</div> <Childs v-model:isShow="isShow"></Childs> </template> <script lang="ts"> import { defineComponent, ref } from 'vue' import Childs from './childCom.vue' export default defineComponent({ components: { Childs }, setup () { const isShow = ref(true) return { isShow } } }) </script> // 子组件 <template> 子组件 <div>{{ count }}</div> --> <div>{{ isShow }}</div> </template> <script lang="ts"> import { defineComponent, ref } from 'vue' export default defineComponent({ props: { isShow: { type: Boolean, default: false } }, setup (_, { emit }) { return { } } }) </script>
```cpp // 父组件 <template> <div>父组件</div> <Childs :msg="msg" :count="count" @handleClick="handleClick"></Childs> <div>子组件传入父组件:{{ childMsg }}</div> </template> <script lang="ts"> import { defineComponent, ref } from 'vue' import Childs from './childCom.vue' export default defineComponent({ components: { Childs }, setup () { const msg = ref('这是传递给子组件的') const count = ref(10) const childMsg = ref<string>('') const handleClick = (data: string) => { console.log(data) childMsg.value = data } return { msg, count, handleClick, childMsg } } }) </script> // 子组件 <template> 子组件 <div>{{ msg }}</div> <div>{{ count }}</div> <button @click="handleClick">按钮</button> </template> <script lang="ts"> import { defineComponent, ref } from 'vue' export default defineComponent({ props: { msg: { type: String, default: '' }, count: { type: Number, default: 0 } }, setup (_, { emit }) { const handleClick = () => { emit('handleClick', '我是子组件的信息') } return { handleClick } } }) </script> ```
Vue 3 中移除了 eventBus,但可以借助第三方工具来完成。Vue 官方推荐使用mitt或 tiny-emitter。
yarn add mitt -S
// eventBus.ts
import mitt from 'mitt'
const mitter = mitt();
export default mitter
<template> <div>父组件</div> <EventBusA /> <EventBusB /> </template> <script lang="ts"> import { defineComponent, ref } from 'vue' import EventBusA from './components/eventBusA.vue' import EventBusB from './components/eventBusB.vue' export default defineComponent({ components: { EventBusA, EventBusB }, setup () { return { } } }) </script>
<template> <div>A组件</div> <div>{{ message }}</div> </template> <script lang="ts"> import { defineComponent, ref } from 'vue' import mitter from '../eventBus' export default defineComponent({ components: { }, setup () { const message = ref<string>('') mitter.on('msgB', (data) => { // msgB是emit传的字段, data是传的字段值 message.value = data }) return { message } } }) </script>
<template> <div>B组件</div> <button type="button" @click="handleClick">点击传参A</button> </template> <script lang="ts"> import { defineComponent, ref } from 'vue' import mitter from '../eventBus' export default defineComponent({ components: { }, setup () { const handleClick = () => { // emit(key, value) key: 字段, value:字段值 mitter.emit('msgB', '我是B组件传给A组件') } return { handleClick } } }) </script>
provide/inject来实现跨级组件之间的通信
创建parent.vue
<template> <div>父组件</div> <brotherOne /> <brotherTwo /> </template> <script lang="ts"> import { defineComponent, ref, provide } from 'vue' import brotherOne from './components/brotherOne.vue' import brotherTwo from './components/brotherTwo.vue' export default defineComponent({ components: { brotherOne, brotherTwo }, setup () { const msg = ref('给孙组件传递的值') provide('msg', msg) return { } } }) </script>
创建brotherOne.vue
<template> <div>老大组件: {{ message }}</div> <oneSon /> </template> <script lang="ts"> import { defineComponent, inject, ref } from 'vue' import oneSon from './oneSon.vue' export default defineComponent({ components: { oneSon }, setup () { const message = inject('msg') return { message } } }) </script>
创建brotherTwo.vue
<template> <div>老二组件: {{ message }}</div> <twoSon /> </template> <script lang="ts"> import { defineComponent, inject, ref } from 'vue' import twoSon from './twoSon.vue' export default defineComponent({ components: { twoSon }, setup () { const message = inject('msg') return { message } } }) </script>
创建oneSon.vue
<template>
<div>老大Son组件: {{ message }}</div>
</template>
<script lang="ts">
import { defineComponent, inject, ref } from 'vue'
export default defineComponent({
setup () {
const message = inject('msg')
return {
message
}
}
})
</script>
创建twoSon.vue
<template>
<div>老二Son组件: {{ message }}</div>
</template>
<script lang="ts">
import { defineComponent, inject, ref } from 'vue'
export default defineComponent({
setup () {
const message = inject('msg')
return {
message
}
}
})
</script>
Vuex 和 Pinia 是 Vue 3 中的状态管理工具,使用这两个工具可以轻松实现组件通信。后面会写文章详细讲解。
在Vue中,子组件不能直接修改父组件传递的prop,这是为了确保单向数据流的原则和组件间的数据流动清晰。
单向数据流:Vue遵循单向数据流的设计原则,即数据从父组件流向子组件。这种设计使得数据的流动方向明确,便于追踪和调试。如果允许子组件直接修改父组件传递的prop,会导致数据的变更不可预测,增加代码的复杂性和维护成本。
可维护性和可预测性:当子组件修改了父组件传递的prop时,父组件的状态可能会被不可预测地改变,导致代码难以维护和调试。通过限制子组件对prop的修改,可以确保父组件的状态不会被子组件意外地改变,提高代码的可维护性和可预测性。
如果子组件需要修改父组件的数据,可以通过触发事件(使用$emit
)或者使用Vuex进行全局状态管理来实现。父组件可以监听子组件触发的事件或者从Vuex中获取数据变更,然后在相应的处理函数中进行状态的更新。这样可以保持数据流的单向性,同时实现子组件对父组件数据的影响。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。