父组件传参数到子组件 - props
在 <script setup>
中用 defineProps()
声明组件的属性,在父组件模板中引用子组件时,就可以给通过属性给子组件传参数(可以是值、变量、函数、对象等)。
defineProps
是编译器宏,Vue3 编译器宏不需要导入,并且只能在 <script setup>
中使用。
ex: 子组件声明、调用属性
1 2 3 4 5 6 7 8 9 10 11 12 13
| <script setup>
defineProps([{ msg: String }]) </script>
<template> <div> {{ msg }} </div> </template>
|
ex: 父组件使用属性
1 2 3 4 5 6 7 8
| <script setup> import HelloWorld from './HelloWorld.vue' </script>
<template> <HelloWorld msg="Welcome"/> </template>
|
父组件传递事件到子组件
在 <script setup>
中用编译器宏 defineEmits()
声明组件提供的事件,并且触发事件后,在父组件引用子组件时就可以给子组件传递事件。
模板中用可直接用 $emit 变量触发事件,不需要用 defineEmits()
声明,但是实现触发事件的同时还需要做其他事情时,就要用 defineEmits()
。
ex: 子组件声明 & 触发事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <script setup>
const emits = defineEmits(['my-event-a', 'myEventB'])
const onEventA = () => { emits('my-event-a'); } const onEventB = () => { emits('my-event-b', '参数1', '参数2', '参数...'); } </script>
<template> <div> <button @click="$emit('my-event-1')">$emit 触发事件</button> <button @click="$emit('my-event-2', '参数1', '参数2')">$emit 触发事件并传参</button> <button @click="onEventA">按钮a</button> <button @click="onEventB">按钮b</button> </div> </template>
|
ex: 父组件传递事件到子组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <script setup> import HelloWorld from './HelloWorld.vue';
const handle1 = () => { console.log('hi') } const handle2 = (arg1, arg2) => { console.log(arguments) } </script>
<template> <HelloWorld msg="Welcome" @my-event-1="handle1" @my-event-2="handle2" @my-event-a="handle1" @my-event-b="handle2" /> </template>
|
父组件通过插槽给子组件分配内容
使用 <slot />
作为一个占位符,父组件传递进来的内容就会渲染在这里。
1 2 3 4 5 6 7
| <template> <div class="alert-box"> <strong>This is an Error for Demo Purposes</strong> <slot /> </div> </template>
|
1 2 3 4 5 6 7 8 9 10
| <script setup> import AlertBox from './AlertBox.vue' </script>
<template> <AlertBox> Something bad happened. </AlertBox> </template>
|
父组件访问子组件实例
父组件通过模板引用或者 $parent
链获取到的组件的公开实例,默认不会暴露任何在 <script setup>
中声明的绑定,我们可以通过 defineExpose
编译器宏来显式指定要暴露哪些绑定。
ex: 子组件显式暴漏绑定
1 2 3 4 5 6 7 8 9 10 11 12
| <script setup> import { ref } from 'vue'
const a = 11 const b = ref(2) const c = () => { console.log('im foo') }
defineExpose({ a, b, c }) </script>
|
当父组件通过模板引用的方式获取到当前组件的实例,获取到的实例会像这样 { a: number, b: number } (ref 会和在普通实例中一样被自动解包)
ex: 父组件通过模板引用访问子组件暴漏的绑定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <script setup> import { ref } from 'vue'; import Foo from './Foo.vue';
const fooRef = ref();
const abc = () => { console.log(fooRef.value.a) fooRef.value.c() } </script>
<template> <Foo ref="fooRef" /> <p>{{ $refs['fooRef'].a }}</p> </template>
|
子组件访问父组件
方式1 使用 defineExpose
父组件用 defineExpose()
显式暴露绑定后,在子组件的 <script setup>
中用 getCurrentInstance().parent.exposed
访问,在子组件的模板中用 $parent
访问。
推荐在子件的模板中调用父组件时使用该方式,在子组件的 <script setup>
中推荐用依赖注入更方便。
ex: 父组件引用子组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <script setup> import { ref } from 'vue'; import Foo from './Foo.vue';
const a = 111 const bar = () => { console.log('im done.') } defineExpose({ a, bar }) </script>
<template> <Foo ref="fooRef" /> </template>
|
ex: 子组件访问父组件显式暴露的绑定
1 2 3 4 5 6 7 8 9 10 11 12 13
| <script setup> import { getCurrentInstance } from 'vue';
const parentExposed = getCurrentInstance().parent.exposed; console.log(parentExposed.a) parentExposed.bar() </script>
<template> {{ $parent.a }} </template>
|
方式2 使用 Vue3 的依赖注入
用 provide()
在父组件提供依赖,用 inject()
在子组件 <script setup>
中注入。
在 setup
中访问父组件,使用依赖注入更便捷。
inject()
必须在组件的 setup()
阶段同步调用。
ex: 父组件提供依赖
1 2 3 4 5 6 7 8 9 10 11 12 13
| <template> <Foo /> </template>
<script setup> import { provide } from 'vue' import Foo from './Foo.vue';
provide('bar', () => { console.log('Hello') }) </script>
|
ex: 子组件注入依赖
1 2 3 4 5 6
| <script setup> import { inject } from 'vue'; const abc = inject('bar'); abc() </script>
|
兄弟组件互相访问
兄弟组件加上 ref
属性后,通过父组件的 refs
就可以调用到兄弟组件用 defineExpose()
暴漏的绑定,
ex: 父组件显式暴漏绑定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <template> <Foo ref="fooRef" /> </template>
<script setup> import Foo from './Foo.vue';
const a = 1; const bar = () => { console.log('im bar') }
defineExpose({ a, bar }) </script>
|
ex: 子组件访问父组件暴漏的绑定
1 2 3 4 5 6 7 8 9 10 11 12 13
| <script setup> import { getCurrentInstance } from 'vue'; const parent = getCurrentInstance().parent; parent.refs.fooRef.a parent.refs.fooRef.bar() </script>
<template> {{ $parent.$refs.fooRef.a }} <button @click="$parent.$refs.fooRef.bar()">点我</button> </template>
|
使用状态管理
比较复杂的应用,强烈推荐使用该方法。
我们可以用 响应式 API
做简单状态管理。
原理
根据 js 变量的作用域的特性,在一个单页面应用(SPA)中,如果有一个文件定义了全局变量,SPA 中任意一个文件 import 该文件,都会共享该变量。代码如下:
1 2 3 4 5 6
| let a1 = 0; export function pr() { ++a1; console.log(a1) }
|
如果你有一部分状态需要在多个组件实例间共享,你可以使用 reactive()
或 ref()
来创建一个响应式对象,并将它导入到多个组件中:
1 2 3 4 5 6 7 8 9
| import { ref } from 'vue'; export const todoList = ref([]); export function loadData() (list) { fetch('/todo').then(function (res) { todoList.value = res.json().items; }) }
|
父组件或子组件都可以引入 todoListStore.js
,共同维护其中的响应式变量 todoList
的状态。
更复杂的状态管理,推荐使用 Pinia
。
使用 window 变量
在组件中把变量赋值给 windows
变量,父组件、兄弟组件、子组件都能访问到,子组件 setup()
之外也能访问到。此方法违背了 Vue3 的设计思想,容易引起混乱,慎用。