Vue09 五一前 组件通信

store组合式写法

count.ts组合式写法

import { reactive } from 'vue'
export const useLoveTalkStore = defineStore('talk', () => {

    const talkList = reactive(
        JSON.parse(localStorage.getItem('talkList') as string) || []
    )

    //getATalk函数相当于action
    async function getATalk() {
        //发请求,下面这行写法是:连续解构+重命名
        let { data: { content: title } } = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
        // //把请求回来的字符,包装成一个对象
        let obj = { id: nanoid(), title } //简单写成title:content 上面let可以result改成 {data:{content}};还可以进一步 title  {data:{content:title}}
        // //放到数组中
        talkList.unshift(obj)
    }
    //必须有return,不然无效
    return { talkList, getATalk }
})

选项式写法

import { defineStore } from 'pinia'
import axios from 'axios'
import { nanoid } from 'nanoid'

export const useLoveTalkStore = defineStore('talk', {
    actions: {
        async getATalk() {
            //发请求,下面这行写法是:连续解构+重命名
            let { data: { content: title } } = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
            // //把请求回来的字符,包装成一个对象
            let obj = { id: nanoid(), title } //简单写成title:content 上面let可以result改成 {data:{content}};还可以进一步 title  {data:{content:title}}
            // //放到数组中
            this.talkList.unshift(obj)
        }
    },
    //state真正存储数据的地方
    state() {
        return {
            talkList: JSON.parse(localStorage.getItem('talkList') as string) || []
            // talkList: [
            //     { id: 'ftrfasdf01', title: '今天天气好' },
            //     { id: 'ftrfasdf02', title: '今天天气好2' },
            //     { id: 'ftrfasdf03', title: '今天天气好3' }
            // ]
        }
    }
})

组件通信_方式1_props

  • 父传子:属性值是非函数
<template>
  <div class="father">
    <h3>父组件</h3>
		<h4>汽车:{{ car }}</h4>
		<!-- 传递,子需要接收 -->
		<Child :car="car" :sendToy="getToy"/>
  </div>
</template>

子组件需要接收

<script setup lang="ts" name="Child">
	import {ref} from 'vue'
	// 数据
	let toy = ref('奥特曼')
	// 声明接收props
	defineProps(['car','sendToy'])
</script>
  • 子传父:属性值是函数
<template>
  <div class="father">
    <h3>父组件</h3>
		<h4>汽车:{{ car }}</h4>
		<!-- v-show、v-if条件渲染 不传递参数就不显示 -->
		<h4 v-show="toy">子给的玩具:{{ toy }}</h4>
		<Child :car="car" :sendToy="getToy"/>
  </div>
</template>

<script setup lang="ts" name="Father">
	import Child from './Child.vue'
	import {ref} from 'vue'
	// 数据
	let car = ref('奔驰')
	let toy = ref('')
	// 方法
	function getToy(value:string){
		toy.value = value
	}
</script>
<template>
  <div class="child">
    <h3>子组件</h3>
		<h4>玩具:{{ toy }}</h4>
		<h4>父给的车:{{ car }}</h4>
		<button @click="sendToy(toy)">把玩具给父亲</button>
  </div>
</template>

<script setup lang="ts" name="Child">
	import {ref} from 'vue'
	// 数据
	let toy = ref('奥特曼')
	// 声明接收props
	defineProps(['car','sendToy'])
</script>

组件通信_方式2__自定义事件

<template>
  <div class="father">
    <h3>父组件</h3>
		<h4>{{ str }}</h4>
		<button @click="test">点我</button>
		<!-- 给子组件Child绑定事件 -->
		<Child/>
  </div>
</template>

<script setup lang="ts" name="Father">
    import Child from './Child.vue'
	import { ref } from "vue";
	// 数据
	let str = ref('你好')

	function test() {
		str.value = '哈哈'
	}
</script>

可以简化写法

<template>
  <div class="father">
    <h3>父组件</h3>
		<h4>{{ str }}</h4>
		<button @click="str = '哈哈'">点我</button>
		<!-- 给子组件Child绑定事件 -->
		<Child/>
  </div>
</template>

<script setup lang="ts" name="Father">
    import Child from './Child.vue'
	import { ref } from "vue";
	// 数据
	let str = ref('你好')
</script>

$seven 特殊占位符 这是一个事件对象

<template>
  <div class="father">
    <h3>父组件</h3>
		<h4>{{ str }}</h4>
		<button @click="str = $event">点我</button>
		<!-- 给子组件Child绑定事件 -->
		<Child/>
  </div>
</template>

子传父

<template>
  <div class="father">
    <h3>父组件</h3>
		<h4 v-show="toy">子给的玩具:{{ toy }}</h4>
		<!-- 给子组件Child绑定事件 -->
    <Child @send-toy="saveToy"/>
  </div>
</template>

​ <Child @send-toy=“saveToy”/>

给组件绑定send-toy事件,只要这个事件被触发,saveToy就会被调用

如何触发事件

需要在子组件里面声明,用一个宏函数,defineEmits

还需要加上emit。这个可以放在任何想要触发的地方

<template>
  <div class="child">
    <h3>子组件</h3>
		<h4>玩具:{{ toy }}</h4>
		<button @click="emit('send-toy',toy)">测试</button>
  </div>
</template>

<script setup lang="ts" name="Child">
	import { ref } from "vue";
	// 数据
	let toy = ref('奥特曼')
	// 声明事件
	const emit =  defineEmits(['send-toy'])
    
	// onMounted(() => {
	// setTimeout(() => {
	// 	emit('send-toy')
	// }, 3000);
	// })
</script>

案例

父组件

<template>
  <div class="father">
    <h3>父组件</h3>
		<h4 v-show="toy">子给的玩具:{{ toy }}</h4>
		<!-- 给子组件Child绑定事件 -->
    <Child @send-toy="saveToy"/>
  </div>
</template>

<script setup lang="ts" name="Father">
    import Child from './Child.vue'
	import { ref } from "vue";
	// 数据
	let toy = ref('')

	// 用于保存传递过来的玩具
	function saveToy(value:string){
		console.log('saveToy',value)
		toy.value = value
	}
</script>

子组件

<template>
  <div class="child">
    <h3>子组件</h3>
		<h4>玩具:{{ toy }}</h4>
		<button @click="emit('send-toy',toy)">测试</button>
  </div>
</template>

<script setup lang="ts" name="Child">
	import { ref } from "vue";
	// 数据
	let toy = ref('奥特曼')
	// 声明事件
	const emit = defineEmits(['send-toy'])
</script>

组件通信_方式3__mitt

pubsub

$bus

mitt

接收数据:提前绑定好事件(提前订阅事件)

提供数据:在合适的时候触发事件(发布消息)

案例

哥哥传递玩具给弟弟

<template>
  <div class="child1">
    <h3>子组件1</h3>
		<h4>玩具:{{ toy }}</h4>
		<button @click="emitter.emit('send-toy',toy)">玩具给弟弟</button>
  </div>
</template>

<script setup lang="ts" name="Child1">
	import {ref} from 'vue'
	import emitter from '@/utils/emitter';

	// 数据
	let toy = ref('奥特曼')
</script>
<template>
  <div class="child2">
    <h3>子组件2</h3>
		<h4>电脑:{{ computer }}</h4>
		<h4>哥哥给的玩具:{{ toy }}</h4>
  </div>
</template>

<script setup lang="ts" name="Child2">
	import {ref,onUnmounted} from 'vue'
	import emitter from '@/utils/emitter';
	// 数据
	let computer = ref('联想')
	let toy = ref('')

	// 给emitter绑定send-toy事件
	emitter.on('send-toy',(value:any)=>{
		toy.value = value
	})
	// 在组件卸载时解绑send-toy事件;对内存友好
	 onUnmounted(()=>{
	 	emitter.off('send-toy')
	})
</script>

组件通信_方式4__v-model

双向绑定

v-model用在html标签上

<template>
  <div class="father">
    <h3>父组件</h3>

    <!-- v-model用在html标签上 -->
    <!-- <input type="text" v-model="username"> -->
    <!-- :value数据来到页面,@input页面来到数据 -->
    <input type="text" :value="username" @input="username = (<HTMLInputElement>$event.target).value">

  </div>
</template>

v-model用在组件标签上

father.Vue

<template>
  <div class="father">
    <h3>父组件</h3>
      
    <!-- v-model用在组件标签上 -->
    <AtguiguInput v-model="username"/>
    <!-- <AtguiguInput 
      :modelValue="username" 
      @update:modelValue="username = $event"
    /> -->

  </div>
</template>

<script setup lang="ts" name="Father">
	import { ref } from "vue";
  import AtguiguInput from './AtguiguInput.vue'
  // 数据
  let username = ref('zhansgan')
</script>
<template>
  <input 
    type="text" 
    :value="modelValue"
    @input="emit('update:modelValue',(<HTMLInputElement>$event.target).value)"
  >
</template>

<script setup lang="ts" name="AtguiguInput">
  defineProps(['modelValue'])
  const emit = defineEmits(['update:modelValue')
</script>

$event到底是什么,什么时候可以用.target

对于原生事件,$event就是事件对象——能.target

对于自定义事件,$event就是触发事件时,所传递的数据——不能.target

组件通信_v-model的细节

update:modelValue名称太长 可以更改名字

如果value可以更换,可以再组件标签上多次使用v-model

<template>
  <div class="father">
    <h3>父组件</h3>
    <h4>{{ username }}</h4>
    <h4>{{ password }}</h4> 

    <!-- 修改modelValue -->
    <AtguiguInput v-model:ming="username" v-model:mima="password"/>
  </div>
</template>

<script setup lang="ts" name="Father">
	import { ref } from "vue";
  import AtguiguInput from './AtguiguInput.vue'
  // 数据
  let username = ref('zhansgan')
  let password = ref('123456')
</script>

组件

<template>
  <input 
    type="text" 
    :value="ming"
    @input="emit('update:ming',(<HTMLInputElement>$event.target).value)"
  >
  <br>
  <input 
    type="text" 
    :value="mima"
    @input="emit('update:mima',(<HTMLInputElement>$event.target).value)"
  >
</template>

<script setup lang="ts" name="AtguiguInput">
  defineProps(['ming','mima'])
  const emit = defineEmits(['update:ming','update:mima'])
</script>

组件通信_方式5__$attrs

概念:$attrs用于实现当前组件的父组件,向当前组件的子组件的通信(祖—孙)

祖-孙

father.vue

<template>
  <div class="father">
    <h3>父组件</h3>
		<h4>a:{{a}}</h4>
		<h4>b:{{b}}</h4>
		<h4>c:{{c}}</h4>
		<h4>d:{{d}}</h4>
		<Child :a="a" :b="b" :c="c" :d="d" v-bind="{x:100,y:200}" />
  </div>
</template>

<script setup lang="ts" name="Father">
	import Child from './Child.vue'
	import {ref} from 'vue'

	let a = ref(1)
	let b = ref(2)
	let c = ref(3)
	let d = ref(4)
    
</script>

child.vue 把祖所有值都传递给孙

<template>
	<div class="child">
		<h3>子组件</h3>
		<!-- <h4>a:{{ a }}</h4>
		<h4>其他:{{ $attrs }}</h4> -->
		<GrandChild v-bind="$attrs"/>
	</div>
</template>

<script setup lang="ts" name="Child">
	import GrandChild from './GrandChild.vue'
	// defineProps(['a'])
</script>

grandchild.vue

<template>
	<div class="grand-child">
		<h3>孙组件</h3>
		<h4>a:{{ a }}</h4>
		<h4>b:{{ b }}</h4>
		<h4>c:{{ c }}</h4>
		<h4>d:{{ d }}</h4>
		<h4>x:{{ x }}</h4>
		<h4>y:{{ y }}</h4>
	</div>
</template>

<script setup lang="ts" name="GrandChild">
	defineProps(['a','b','c','d','x','y'])
</script>

孙-组

father.vue

<template>
  <div class="father">
    <h3>父组件</h3>
		<h4>a:{{a}}</h4>
		<Child :a="a" :updateA="updateA"/>
  </div>
</template>

<script setup lang="ts" name="Father">
	import Child from './Child.vue'
	import {ref} from 'vue'

	let a = ref(1)

	function updateA(value:number){
		a.value += value
	}
</script>

grandchild.vue

<template>
	<div class="grand-child">
		<h3>孙组件</h3>
		<h4>a:{{ a }}</h4>
		<button @click="updateA(6)">点我将爷爷那的a更新</button>
	</div>
</template>

<script setup lang="ts" name="GrandChild">
	defineProps(['a','updateA'])
</script>

组件通信_方式6__ r e f s 与 refs与 refsparent

father.vue

<template>
	<div class="father">
		<h3>父组件</h3>
		<h4>房产:{{ house }}</h4>
		<button @click="changeToy">修改Child1的玩具</button>
		<button @click="changeComputer">修改Child2的电脑</button>
		<button @click="getAllChild($refs)">让所有孩子的书变多</button>
		<Child1 ref="c1"/>
		<Child2 ref="c2"/>
	</div>
</template>

<script setup lang="ts" name="Father">
	import Child1 from './Child1.vue'
	import Child2 from './Child2.vue'
	import { ref,reactive } from "vue";
	let c1 = ref()
	let c2 = ref()

	// 数据
	let house = ref(4)
	// 方法
	function changeToy(){
		c1.value.toy = '小猪佩奇'
	}
	function changeComputer(){
		c2.value.computer = '华为'
	}
	function getAllChild(refs:{[key:string]:any}){
		console.log(refs)
		for (let key in refs){
			refs[key].book += 3
		}
	}
	// 向外部提供数据
	defineExpose({house})
</script>

child1.vue

<template>
  <div class="child1">
    <h3>子组件1</h3>
		<h4>玩具:{{ toy }}</h4>
		<h4>书籍:{{ book }} 本</h4>
		<button @click="minusHouse($parent)">干掉父亲的一套房产</button>
  </div>
</template>

<script setup lang="ts" name="Child1">
	import { ref } from "vue";
	// 数据
	let toy = ref('奥特曼')
	let book = ref(3)

	// 方法
	function minusHouse(parent:any){
		parent.house -= 1
	}

	// 把数据交给外部
	defineExpose({toy,book})

</script>

child2.vue

<template>
  <div class="child2">
    <h3>子组件2</h3>
		<h4>电脑:{{ computer }}</h4>
		<h4>书籍:{{ book }} 本</h4>
  </div>
</template>

<script setup lang="ts" name="Child2">
		import { ref } from "vue";
		// 数据
		let computer = ref('联想')
		let book = ref(6)
		// 把数据交给外部
		defineExpose({computer,book})
</script>

$refs 父传子

父得到子数据

<template>
	<div class="father">
		<h3>父组件</h3>
		<h4>房产:{{ house }}</h4>
		<button @click="changeToy">修改Child1的玩具</button>
		<button @click="getAllChild($refs)">让所有孩子的书变多</button>
		<Child1 ref="c1"/>
	</div>
</template>

<script setup lang="ts" name="Father">
	import Child1 from './Child1.vue'
	import Child2 from './Child2.vue'
	import { ref,reactive } from "vue";
	let c1 = ref()

	// 数据
	let house = ref(4)
	// 方法
	function changeToy(){
		c1.value.toy = '小猪佩奇'
	}
	function getAllChild(refs:{[key:string]:any}){
		for (let key in refs){
			refs[key].book += 3
		}
	}
</script>

子必须把数据传递出去

<script setup lang="ts" name="Child1">
	import { ref } from "vue";
	// 数据
	let toy = ref('奥特曼')
	let book = ref(3)

	// 把数据交给外部
	defineExpose({toy,book})

</script>

$parent 子传父

<template>
  <div class="child1">
    <h3>子组件1</h3>
		<h4>玩具:{{ toy }}</h4>
		<h4>书籍:{{ book }} 本</h4>
		<button @click="minusHouse($parent)">干掉父亲的一套房产</button>
  </div>
</template>

<script setup lang="ts" name="Child1">
	import { ref } from "vue";
	// 数据
	let toy = ref('奥特曼')
	let book = ref(3)

	// 方法
	function minusHouse(parent:any){
		parent.house -= 1
	}

</script>
说明属性
$refs值为对象,包含所有被ref属性标识的DOM元素或组件实例
$parent值为对象,当前组件的父组件实例对象

一个注意点

<script setup lang="ts" name="Father">
// 注意点:当访问obj.c的时候,底层会自动读取value属性,因为c是在obj这个响应式对象中的
	let obj = reactive({
		a:1,
		b:2,
		c:ref(3)
	})
	let x = ref(4)

	console.log(obj.a)
	console.log(obj.b)
	console.log(obj.c) //自动拆包解包
	console.log(x) //这不是4
</script>

组件通信_方式7__provide_inject

如何不打扰儿子,直接传递给孙 区别attrs

father.vue

<template>
  <div class="father">
    <h3>父组件</h3>
    <h4>银子:{{ money }}万元</h4>
    <h4>车子:一辆{{car.brand}}车,价值{{car.price}}万元</h4>
    <Child/>
  </div>
</template>

<script setup lang="ts" name="Father">
  import Child from './Child.vue'
  import {ref,reactive,provide} from 'vue'

  let money = ref(100)
  let car = reactive({
    brand:'奔驰',
    price:100
  })
  function updateMoney(value:number){
    money.value -= value
  }

  // 向后代提供数据 不能是money.value 否则传递的就是个数字 无法改变
  provide('moneyContext',{money,updateMoney})
  provide('car',car)

</script>

GrandChild.vue

<template>
  <div class="grand-child">
    <h3>我是孙组件</h3>
    <h4>银子:{{ money }}</h4>
    <h4>车子:一辆{{car.brand}}车,价值{{car.price}}万元</h4>
    <button @click="updateMoney(6)">花爷爷的钱</button>
  </div>
</template>

<script setup lang="ts" name="GrandChild">
  import { inject } from "vue";

  let {money,updateMoney} = inject('moneyContext',{money:0,updateMoney:(param:number)=>{}})
  let car = inject('car',{brand:'未知',price:0})
</script>
  • 22
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值