结论
- props父传子
- defineEmits子传父
- mitt兄弟组件传参
- $attrs (爷孙)
- refs
- v-model(双向)
- provide/inject(多层)
- 路由传参
- vuex传参 (全局)
- pinia传参 (全局)
- 浏览器缓存 (全局)
- window (全局)
- app.config.globalProperties (全局)
一:父传子
defineProps 和 defineEmits 都是只能在 <script setup> 中使用的编译器宏。他们不需要导入,且会随着 <script setup> 的处理过程一同被编译掉。
TestPage.vue 父组件
<template>
<p>
111
<TestB :name="name">
</TestB>
</p>
<p>父组件中的值:{{ name }}</p>
<button @click="modify">点击</button>
</template>
<script lang="ts" setup>
import { ref } from "vue";
import TestB from '@/components/TestB.vue'
const name = ref('你好')
const modify = () => {
name.value += '1'
}
</script>
testB.vue 子组件
<template>
<div class="testContainer">
<div>
<span>{{ props.name }}</span>
</div>
</div>
</template>
<script setup>
import { watchEffect, reactive, ref, computed, defineProps, withDefaults } from "vue";
const props = defineProps(
{
name: {
type: String,
default: '默认值',
required: false // 是否必传
}
}
)
// ts写法(3.4及以下版本)
// const props = withDefaults(defineProps<{
// name?: string // 是否必传
// }>(), {
// name: '默认值'
// })
// ts写法(3.5版本)
// const { name= '默认值' } = defineProps<{
// name?: string // 是否必传
// }>()
watchEffect(() => {
// 在 3.5 之前仅运行一次
// 在 3.5+ 版本中会在 "name" prop 改变时重新运行
console.log(props)
})
</script>
<style scoped>
.testContainer {
background-color: #f8f8f8;
}
</style>
二:defineEmits子传父
testPage.vue父组件
<template>
<div>
这是父组件
<TestB @addEvent = "handle">
</TestB>
</div>
<p>子组件中的值:{{ childValue }}</p>
</template>
<script lang="ts" setup>
import { ref } from "vue";
import TestB from '@/components/TestB.vue'
const childValue = ref('')
const handle = (v) => {
childValue.value = v
}
</script>
testB.vue 子组件
<template>
<div class="testContainer">
<div>
<button @click="clickEvent">点击我</button>
</div>
</div>
</template>
<script setup>
import { watchEffect, reactive, ref, computed, defineProps, withDefaults } from "vue";
const name = ref('你好吖')
const emits = defineEmits(['addEvent'])
const clickEvent = () => {
emits('addEvent', name.value)
}
</script>
<style scoped>
.testContainer {
background-color: #f8f8f8;
}
</style>
三:兄弟组件传参(mitt)
testPage.vue父组件
<template>
<div>
这是父组件
<TestB >
</TestB>
<FancyButton>
</FancyButton>
</div>
</template>
<script lang="ts" setup>
import TestB from '@/components/TestB.vue'
import FancyButton from '@/views/aboutPage/FancyButton.vue'
</script>
testB.vue (发射事件)
<template>
<div class="testContainer">
<div>
<button @click="clickEvent">点击我</button>
</div>
</div>
</template>
<script setup>
import { watchEffect, reactive, ref, computed, defineProps, withDefaults,getCurrentInstance } from "vue";
import mitter from '@/utils/eventBus'
const name = ref('你好吖')
const {proxy } = getCurrentInstance()
const clickEvent = () => {
console.log(name.value);
// 方式一
// mitter.emit('foo', { a: 'b' })
// 方式二
proxy.$bus.emit('test', { a: 'xxx' })
}
</script>
<style scoped>
.testContainer {
background-color: #f8f8f8;
}
</style>
FancyButton.vue (监听事件)
<template>
<div class="fancy-btn">
2222
</div>
</template>
<script setup>
import {getCurrentInstance} from 'vue'
import mitter from '@/utils/eventBus'
const {proxy } = getCurrentInstance()
mitter.on('foo', (value)=>{
console.log(value);
})
mitter.on('*', (type,data) => {
console.log('Received in B:', type);
console.log('Received in B:', data);
});
// 方式二监听
proxy.$bus.on('test', (value)=>{
console.log(value);
})
</script>
采用全局挂载方式使用的时候记得在项目的main.js中提前挂载
import { createApp } from 'vue'
import mitt from "mitt"
const app = createApp(App)
app.config.globalProperties.$bus = new mitt()
app.mount('#app')
四:$attrs
实际上这是父传子的衍生,用于组件封装比较多
TestPage.vue父组件
<template>
<div>
这是父组件
<TestB :id="id" :name="name">
</TestB>
</div>
</template>
<script lang="ts" setup>
import TestB from '@/components/TestB.vue'
import FancyButton from '@/views/aboutPage/FancyButton.vue'
const name = '你好,这是父组件';
const id =123
</script>
testB子组件
<template>
<div class="testContainer">
<div>
<button @click="clickEvent">{{props.name}}</button>
</div>
</div>
</template>
<script setup>
import {useAttrs } from "vue";
const props = defineProps({
name: {
type: String
}
})
const myattrs = useAttrs()
console.log(props,19);
console.log(myattrs,20);// 可以看到这里面打印的是 props以外的属性
</script>
<style scoped>
.testContainer {
background-color: #f8f8f8;
}
</style>
五:refs传参
引入一下ref,没啥好说的
testPage.vue父组件
<template>
<div>
这是父组件
<TestB ref ="childRef">
</TestB>
<Button @click="btnClick">父组件按钮点击</Button>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import TestB from '@/components/TestB.vue'
const childRef = ref<InstanceType<typeof TestB>>(null)
const btnClick = () => {
childRef.value?.logEvent()
console.log(childRef.value?.name,17)
}
</script>
testB.vue子组件
<template>
<div class="testContainer">
<div>
子组件
</div>
</div>
</template>
<script setup>
import {defineExpose } from "vue";
const name ="testB"
const logEvent = () => {
console.log('我是子组件方法')
}
defineExpose({
logEvent,
name
})
</script>
<style scoped>
.testContainer {
background-color: #f8f8f8;
}
</style>
六:v-model
TestPage.vue父组件
<template>
<div>
这是父组件
<!-- <TestB v-model:name="name" v-model:num="num">
</TestB> -->
<TestB :name="name" @update:name="name = $event" />
<p>
姓名{{ name }}
<br>
数量{{ num }}
</br>
</p>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import TestB from '@/components/TestB.vue'
const name=ref('张三');
const num=ref(123);
</script>
testB.vue子组件
<template>
<div class="testContainer">
<div>
子组件
<button @click="myClick">点击</button>
</div>
</div>
</template>
<script setup>
import {defineEmits } from "vue";
const emit = defineEmits(["name","num"])
const myClick = () => {
emit("update:name", "改个新名字")
emit("update:num", 666)
}
</script>
<style scoped>
.testContainer {
background-color: #f8f8f8;
}
</style>
子组件通过 defineEmits获取到然后用emit("update:修改的属性", 修改的内容)进行修改父组件的内容,,注意:update:是固定写法。
v-model扩展:defineModel():
defineModel()宏的简单说明:父子组件的数据双向绑定,不用emit和props的繁重代码
版本要求:必须要3.4+
示例场景:父组件引入一个子组件弹窗,点击就父传子(props)弹出子组件弹窗,子组件里面有个按钮点击就子传父(emit)关闭
代码示例
TestPage.vue 父组件
<template>
<div>
这是父组件
<TestB v-if="showDevice" v-model="showDevice">
</TestB>
<p>
{{ showDevice }}
</br>
</p>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import TestB from '@/components/TestB.vue'
const showDevice = ref(true) // 控制子组件的显示和隐藏
</script>
TestB.vue子组件
<template>
<div class="testContainer">
<div>
子组件
<button @click="handleClickCancel">点击取消子组件弹窗</button>
</div>
</div>
</template>
<script setup>
import {defineModel } from "vue";
const model = defineModel();
console.log(model)
const handleClickCancel = () => {
console.log(model);
model.value = false;
}
</script>
<style scoped>
.testContainer {
background-color: #f8f8f8;
}
</style>
七:provide与indect注入传值
provide和inject叫依赖注入,是vue官方提供的API,它们可以实现多层组件传递数据,无论层级有多深,都可以通过这API实现。
TestPage.vue(这个可以是父组件也可以是子组件)
<template>
<div>
这是父组件
<TestB >
</TestB>
<p>
{{ showDevice }}
</br>
</p>
</div>
</template>
<script setup>
import { ref,provide } from 'vue'
import TestB from '@/components/TestB.vue'
provide('path', '/project/')
const showDevice = ref('xxxx') // 控制子组件的显示和隐藏
provide('name', showDevice)
</script>
TestB.vue(子组件或者孙组件)
<template>
<div class="testContainer">
<div>
子组件
</div>
</div>
</template>
<script setup>
import {inject } from "vue";
const path = inject('path')
console.log(path);
const name = inject('name')
console.log(name.value);
</script>
<style scoped>
.testContainer {
background-color: #f8f8f8;
}
</style>
八:路由传参
路由跳转分为query和params传参
query示例:
// 传递方
const query = { id: 9527, name: '天天鸭' }
router.push({ path: '/user', query })
// 接收方
import { useRoute} from 'vue-router'
const route = useRoute()
console.log(route.query)
param示例:
// 发送方
router.push({
name: 'test',
params: {
name: '天天鸭'
}
})
// 接收方
import { useRoute} from 'vue-router'
const route = useRoute()
console.log(route.params) // { name: '天天鸭' }
state传参示例:
// 发送方
const state= { name: '天天鸭' }
router.push({ path: '/user', state })
// 接收方直接使用
console.log(history?.state?.name)
九:vuex传参
十:pinia传参
十一:浏览器缓存传参
十二:通过window对象全局挂载全局对象或者属性
// 定义
window.duname = '天天鸭'
window.duObj = { test: '看看对象' }
// 引用
console.log( window.duname); // 天天鸭
console.log( window.duObj); // {test: '看看对象'}
十二:app.config.globalProperties
// main.js
import { createApp } from 'vue'
const app = createApp(App)
app.config.globalProperties.msg = 'hello'
// 其它页面引用
import { getCurrentInstance } from "vue";
const { proxy } = getCurrentInstance() // 使用proxy,类似于vue2的this
console.log(proxy.msg); // hello