有两个组件A,B和父组件P,A想传参到B必须先传到父组件P中,再通过父组件P传给B。
上面这种方式麻烦。
Bus:发布订阅模式
vue3中 o n , on, on,off和$once实例方法已被移除,组件实例不再实现事件触发接口,因此大家熟悉的EventBus便无法使用了,我们可以使用Mitt库,其实就是发布订阅模式的设计。
安装
npm install mitt -S
在Main.ts中引入mitt
import { createApp } from 'vue'
import App from './App.vue'
import mitt from 'mitt'
const Mit = mitt()
const app = createApp(App)
// 让在TS中有提示
declare module 'vue' {
export interface ComponentCustomProperties {
$Bus: typeof Mit
}
}
app.config.globalProperties.$Bus = Mit
app.mount('#app')
在A组件中发布事件
<template>
<h2>A组件h2>
<button @click="emit">emitbutton>
template>
<script setup lang='ts'>
import { getCurrentInstance } from 'vue'
const instance = getCurrentInstance()
// 发布事件
const emit = () => {
instance?.proxy?.$Bus.emit('on-click1', 'mitt1')
instance?.proxy?.$Bus.emit('on-click2', 'mitt2')
}
script>
<style lang='scss' scoped>
style>
在B组件订阅发布的事件
<template>
<h2>B组件</h2>
{{str}}
</template>
<script setup lang='ts'>
import { ref, getCurrentInstance } from 'vue';
const instance = getCurrentInstance()
let str = ref('')
// 订阅事件
/*
instance?.proxy?.$Bus.on('on-click1', (strA)=>{
console.log(strA, 'B组件')
str.value = strA as string
})
*/
// type函数,str发布事件携带的数据
/*
instance?.proxy?.$Bus.on('*', (type, str) => {
console.log(type, str, 'B组件')
})
*/
const Bus = (str: any) => {
console.log(str, 'B组件')
}
instance?.proxy?.$Bus.on('on-click1', Bus)
instance?.proxy?.$Bus.off('on-click1', Bus)
instance?.proxy?.$Bus.all.clear()
</script>
<style lang='scss' scoped>
</style>
我们之前是用Template去写我们的模板,现在可以扩展一种TSX风格
安装插件
npm install @vitejs/plugin-vue-jsx -D
vite.config.ts中配置一下
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), vueJsx()]
})
tsconfig.json中配置一下
"jsx": "preserve",
"jsxFactory": "h",
"jsxFragmentFactory": "Fragment",
在src下建一个App.tsx
const renderDom = () => {
return(
<div>
<h2>hello, tsx</h2>
</div>
)
}
export default renderDom
在App.vue中使用这个tsx模板
<template>
<renderDom>renderDom>
template>
<script setup lang="ts">
import renderDom from './App'
script>
<style lang="scss" scoped>
style>
在tsx中一些指令的用法和template中有些许的不一样。
app.tsx
import { ref } from 'vue'
let v = ref<string>('')
let arr = [1, 2, 3]
const renderDom = () => {
return(
<div>
{/*
{v.value}
*/}
{/* v-for不支持,用js的思想来写 */}
{/* {
arr.map(v=>{
return ({v},)
})
} */}
</div>
)
}
export default renderDom
给自定义组件去绑定一个v-model
App.vue
<template>
<div>
<h1>我是app.vue父组件h1>
<p>isShow:{{isShow}}p>
<div><button @click="isShow=!isShow">开关button>div>
<p>{{text}}p>
<hr>
<vModela v-model="isShow" v-model:textValue="text">vModela>
div>
template>
<script setup lang="ts">
import { ref } from 'vue'
import vModela from './components/v-model.vue'
const isShow = ref<boolean>(true)
const text = ref<string>('hello')
script>
<style lang="scss" scoped>
style>
v-model.vue
<template>
<div class="model" v-if="modelValue">
<div class="close"><button @click="close">关闭button>div>
<h3>我是v-model子组件 dialogh3>
<div>内容: <input @input="change" :value="textValue" type="text">div>
div>
template>
<script setup lang='ts'>
import { ref, reactive } from 'vue'
// 使用modelValue来接受父组件传过来的v-model的值
defineProps<{
modelValue: boolean,
textValue: string
}>()
// 子组件点击关闭,将窗口关闭,并且将父组件的isShow改成false
const emit = defineEmits(['update:modelValue', 'update:textValue'])
const close = () => {
emit('update:modelValue', false)
}
const change = (e:Event) => {
const target = e.target as HTMLInputElement
emit('update:textValue', target.value)
}
script>
<style lang='scss' scoped>
style>
通过给子组件绑定自定义指令去获取子组件的一些数据,在挂载完成之后获取得到子组件的一些数据。
App.vue
<template>
<div>
<A v-move:aaa.kk="{ background: 'red' }">A>
div>
template>
<script setup lang="ts">
import { ref, Directive, DirectiveBinding } from 'vue'
import A from './components/A.vue'
type Dir = {
background: string
}
const vMove:Directive = {
created(){
console.log('created')
},
beforeMount(){
console.log('beforeMount')
},
// 可以通过这个指令拿到子组件的一些数据
// mounted(...args: Array){
// console.log('mounted')
// console.log(args)
// },
mounted(el:HTMLElement, dir:DirectiveBinding<Dir>){
el.style.background = dir.value.background
},
beforeUpdate(){
console.log('beforeUpdate')
},
updated(){
console.log('update')
},
beforeUnmount(){
console.log('beforeUnmount')
},
unmounted(){
console.log('unmounted')
}
}
script>
<style lang="scss" scoped>
style>
子组件A.vue
<template>
<div class="box">
A组件
div>
template>
<script setup lang='ts'>
script>
<style lang='scss' scoped>
.box{
width: 200px;
height: 200px;
border: 1px solid yellowgreen
}
style>
Vue3自定义Hook,主要用来处理利用代码逻辑的一些封装,将多个相同的逻辑抽离出来,各个组件只需要引入,就能实现一次写代码,多组件受益的效果。hooks是函数。
vueuse开源库
<template>
<div>
<A aa="888" title="title">A>
div>
template>
<script setup lang="ts">
import A from './components/A.vue'
script>
<style lang="scss" scoped>
style>
A.vue
<template>
<div class="box">
A组件
div>
template>
<script setup lang='ts'>
import { useAttrs } from 'vue'
let attr = useAttrs()
console.log(attr)
script>
<style lang='scss' scoped>
.box{
width: 200px;
height: 200px;
border: 1px solid yellowgreen
}
style>
Hooks就是定义一个函数,暴露给其他组件使用。