Appearance
组件之间的传参
父向子传参
父组件
vue
<template>
<div>
父级
<hr>
<Child :title="name"></Child>
</div>
</template>
<script setup lang="ts">
import Child from "./components/child.vue";
import {ref} from "vue";
let name = ref<string>('Gxx')
</script>子组件
vue
<script setup lang="ts">
const props = defineProps({
title: {
type: String,
default: '默认值'
}
})
console.log(props.title)
</script>
<template>
<div>
子集
<div>{{title}}</div>
</div>
</template>改用ts泛型的简写形式
ts
const props = defineProps<{
title: string
}>()使用ts简写后,增加默认值
ts
withDefaults(defineProps<{
title: string,
arr: number[]
}>(), {
arr: () => [1]
})子向父传参
自定义事件
子组件
vue
<template>
<div>
子集
<button @click="send">给父组件传值</button>
// 或者用下面的写法
<button @click="$emit('on-click','我是子组件的数据','23')">给父组件传值</button>
</div>
</template>
<script setup lang="ts">
const emit = defineEmits(['on-click'])
const send = () => {
// 第一个参数是自定义事件的名称
// 第二个参数是向父组件传递的值,可以有多个,用逗号割开就行
emit('on-click', '我是子组件的数据', 23)
}
</script>改用ts泛型简写 defineEmits
ts
const emit = defineEmits<{
(e: 'on-click', name: string, age: number): void
}>()
const send = () => {
emit('on-click', '我是子组件的数据', 23)
}父组件
vue
<template>
<div>
父级
<hr>
<Child @on-click="getName"></Child>
</div>
</template>
<script setup lang="ts">
import Child from "./components/child.vue";
import {reactive, ref} from "vue";
// 子组件传了几个参数,就用几个形参接受
const getName = (name: string, age: number) => {
//接受子组件的参数
console.log('我是父组件', name, age)
}
</script>通过ref来获取子组件的数据
子组件
ts
defineExpose({
name: 'Gxx',
open: (arr) => console.log(arr)
})父组件
vue
<template>
<div>
父级
<hr>
<Child ref="child"></Child>
</div>
</template>
<script setup lang="ts">
import Child from "./components/child.vue";
import {onMounted, reactive, ref} from "vue";
let arr = reactive<number[]>([1, 2, 3])
const child = ref<InstanceType<typeof Child>>()
onMounted(() => {
console.log(child.value)
console.log(child.value.name);
// console.log(child.value.open);
child.value.open(arr)
})
</script>父子组件的v-model传值
一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件, 但是像单选框、复选框等类型的输入控件可能会将 value attribute 用于不同的目的。model 选项可以用来避免这样的冲突:
TIP
prop传值:
Vue2 中的值是 value,Vue3 中的值是 modelValue
自定义事件名称:
Vue2 中的事件名是 input,Vue3 中的值是 update:modelValue(所以Vue3没有.sync)
在父组件中
html
<DomDialog v-model="isDomDialog"></DomDialog>等同于如下常规写法:
html
<DomDialog v-bind:value="isDomDialog" v-on:input="isDomDialog=$event">
</DomDialog>或者:
html
<DomDialog :value="isDomDialog" @input="isDomDialog=$event"></DomDialog>在子组件中的接收与传值
vue
<script>
export default {
props: {
value: {type: Boolean,}
},
data() {
return {
dialogVisible: false,
}
},
watch: {
value(val) {
this.dialogVisible = val
},
},
methods: {
// 关闭弹窗触发
confrim() {
this.$emit('input', false) // 通过 this.$emit() 向父组件传值
}
},
}
</script>在Vue3子组件中
vue
<script setup lang="ts">
defineProps<{
modelValue: boolean
}>()
const emit = defineEmits(['update:modelValue'])
const close = () => {
emit('update:modelValue', false)
}
</script>Vue3支持多个v-model
html
<show v-model:textVal="textVal" v-model="visibleValue"></show>子组件接收prop参数和自定义事件
ts
const props = defineProps<{
modelValue: boolean,
textVal: string,
}>()
const emit = defineEmits(['update:modelValue', 'update:textVal'])将子组件input输入框的内容回传给父组件
html
<div>内容:<input @change="input" :value="textVal" type="text"></div>ts
const input = (e: Event) => {
console.log(e)
// Event.target 读不到身上的value属性
const target = e.target as HTMLInputElement
console.log(target.value)
emit('update:textVal', target.value)
}给v-model绑定自定义修饰符
示例: 父组件
html
<show v-model:textVal.isBt="textVal" v-model="visibleValue"></show>子组件
ts
const props = defineProps<{
modelValue: boolean,
textVal: string,
textValModifiers?: {
isBt: boolean
}
}>()
const input = (e: Event) => {
console.log(e)
// Event.target 读不到身上的value属性
const target = e.target as HTMLInputElement
console.log(target.value)
emit('update:textVal', props?.textValModifiers?.isBt ? target.value + '变态' : target.value)
}Vue2.3 的.sync方法(类似v-model)
它只是作为一个编译时的语法糖存在。它会被扩展为一个自动更新父组件属性的 v-on 监听器。
html
<comp :foo.sync="bar"></comp>会被扩展为:
html
<comp :foo="bar" @update:foo="val => bar = val"></comp>当子组件需要更新 foo 的值时,它需要显式地触发一个更新事件:
js
this.$emit('update:foo', newValue)兄弟组件
借助父组件传参
例如父组件为App 子组件为A 和 B他两个是同级的
vue
<template>
<div>
<A @on-click="getFalg"></A>
<B :flag="Flag"></B>
</div>
</template>
<script setup lang='ts'>
import A from './components/A.vue'
import B from './components/B.vue'
import {ref} from 'vue'
let Flag = ref<boolean>(false)
const getFalg = (flag: boolean) => {
Flag.value = flag;
}
</script>A 组件派发事件通过App.vue 接受A组件派发的事件然后在Props 传给B组件 也是可以实现的
缺点就是比较麻烦 ,无法直接通信,只能充当桥梁
Event Bus
在Vue2 可以使用$emit 传递 $on监听 emit传递过来的事件
这个原理其实是运用了JS设计模式之发布订阅模式
简易版
ts
type BusClass<T> = {
emit: (name: T) => void
on: (name: T, callback: Function) => void
}
type BusParams = string | number | symbol
type List = {
[key: BusParams]: Array<Function>
}
class Bus<T extends BusParams> implements BusClass<T> {
list: List
constructor() {
this.list = {}
}
emit(name: T, ...args: Array<any>) {
let eventName: Array<Function> = this.list[name]
eventName.forEach(ev => {
ev.apply(this, args)
})
}
on(name: T, callback: Function) {
let fn: Array<Function> = this.list[name] || [];
fn.push(callback)
this.list[name] = fn
}
}
export default new Bus<number>()然后挂载到Vue config 全局就可以使用了
使用mitt(封装发布订阅的包)
安装依赖
shell
npm i mitt -Smain.ts 文件
ts
import {createApp} from 'vue'
// import './style.css'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
// @ts-ignore
import App from './App.vue'
// @ts-ignore
import mitt from "mitt";
const Mit = mitt()
// createApp(App).mount('#app')
const app = createApp(App)
declare module 'vue' {
export interface ComponentCustomProperties {
$Bus: typeof Mit
}
}
app.config.globalProperties.$Bus = Mit
app.use(ElementPlus)
app.mount('#app')子组件A
vue
<script setup lang="ts">
import {getCurrentInstance} from "vue";
const instance = getCurrentInstance()
const emit = () => {
instance?.proxy?.$Bus.emit('on-click', 'mitt')
instance?.proxy?.$Bus.emit('on-click2', 'mitt2')
}
</script>
<template>
<h1>我是one</h1>
<button @click="emit">emit</button>
</template>子组件B
vue
<script setup lang="ts">
import {getCurrentInstance} from "vue";
const instance = getCurrentInstance()
// 创建自定义事件
const Bus = (str) => {
console.log(str + '=====>B')
}
instance?.proxy?.$Bus.on('on-click', Bus)
//off 取消指定的mitt事件,取消的函数
instance?.proxy?.$Bus.off('on-click', Bus)
// clear 删除全部事件
instance?.proxy?.$Bus.all.clear()
// 监听多条
// instance?.proxy?.$Bus.on('*', (type, str) => {
// console.log(type, str + '=====>B')
// })
</script>
<template>
<h1> 我是Two</h1>
</template>TIP
getCurrentInstance
Vue3 的 setup 中没有this时需要使用 getCurrentInstance() 来获取。
Vue3 中 getCurrentInstance() 可以用来获取当前组件实例