Bootstrap

vite+vue3+ts父子组件传值,及属性监听watch用法

在上一篇文章的demo中,我们成功的制作了简单的todolist,本篇我们来了解下父子组件传值的操作。
本篇依旧采用vite+vue3+ts的组合,以及使用ant-design-vue的UI组件,对于这套组合和UI组件的项目部署,本篇不再讲解有需要了解的可以看《vite+vue3+ts简单例子todolist》。
下边我们直接来看下父子组件传值。

父子组件传值

父传子 defineProps用法

在vue2中我们使用属性和props的组合,在vite+vue3+ts里边也是一样用使用属性,只是在接收时候使用的是defineProps。
我们先来父组件的代码Father.vue

<template>
  <div>
    <h2>这是父组件</h2>
    <h4>下方是子组件的内容</h4>
    <SonVue :msg="msg" />
  </div>
</template>

<script setup lang="ts">
import SonVue from './Son.vue';
import { ref } from 'vue';
// 传给子组件的内容
const msg = ref('这是父组件传给子组件的内容');

</script>

<style scoped></style>

然后就是子组件中获取传入的值了,也就是defineProps在vite+vue3中的使用了,Son.vue代码

<template>
  <div>
    <h3>这是子组件接收的父组件信息</h3>
    <p>{{props.msg}}</p>
  </div>
</template>

<script setup lang="ts">
// defineProps 接收父组件传入的值,使用方式分为ts的用法和非ts的用法
// 非ts的用法
const props = defineProps({
  msg: {
    type: String,
    default: ''
  }
});

</script>

<style scoped></style>

上边是defineProps非ts的用法,下边使用ts的写法

<template>
  <div>
    <h3>这是子组件接收的父组件信息</h3>
    <p>{{props.msg}}</p>
  </div>
</template>

<script setup lang="ts">
// defineProps 接收父组件传入的值,使用方式分为ts的用法和非ts的用法
// 非ts的用法
/* const props = defineProps({
  msg: {
    type: String,
    default: ''
  }
}); */
// ts的用法
interface PropsType {
  msg: string
}
const props = withDefaults(defineProps<PropsType>(), {
  msg: '' // 默认值
});


</script>

<style scoped></style>
子传父 defineEmits用法

上边是父组件传值到子组件defineProps的使用,下边我们再来看下子组件传值父组件的方式,与vue2也是一样使用事件的方式,只是这里使用了defineEmits,下边我们看下defineEmits的使用。
Father.vue

<template>
  <div>
    <h2>这是父组件</h2>
    <div>
      <h4>子组件触发父组件接收的值</h4>
      <p>{{receiveMsg}}</p>
    </div>
    <h4>下方是子组件的内容</h4>
    <SonVue @onReceiveMsg="onReceiveMsg" />
  </div>
</template>

<script setup lang="ts">
import SonVue from './Son.vue';
import { ref } from 'vue';
// 接收子组件的传入的值
const receiveMsg = ref('');
// 子组件触发的方法
const onReceiveMsg = (params:string) => {
  console.log('接收子组件的值', params);
  receiveMsg.value = params;
}


</script>

<style scoped></style>

子组件使用defineEmits触发父组件的方法
非ts的用法,Son.vue代码:

<template>
  <div>
    <h3>这是子组件接收的父组件信息</h3>
    <a-input v-model:value="inputVal" placeholder="请输入传给父组件的值" />
    <a-button @click="sendMsg">点击传值给父组件参数</a-button>
  </div>
</template>

<script setup lang="ts">
import { Button as AButton, Input as AInput } from "ant-design-vue";
import { ref } from 'vue'
const inputVal = ref('');
// 子组件通过defineEmits触发父组件的方法
// 非ts的用法
const emit = defineEmits(["onReceiveMsg"]);
const sendMsg = () => {
  // 传input值给父组件
  emit("onReceiveMsg", inputVal.value);
}

</script>

<style scoped></style>

使用ts的用法,Son.vue

<template>
  <div>
    <h3>这是子组件接收的父组件信息</h3>
    <a-input v-model:value="inputVal" placeholder="请输入传给父组件的值" />
    <a-button @click="sendMsg">点击传值给父组件参数</a-button>
  </div>
</template>

<script setup lang="ts">
import { Button as AButton, Input as AInput } from "ant-design-vue";
import { ref } from 'vue'
const inputVal = ref('');
// 子组件通过defineEmits触发父组件的方法
// 非ts的用法
// const emit = defineEmits(["onReceiveMsg"]);
// ts的用法
interface EmitType {
  (e: "onReceiveMsg", params: string): void
}
const emit = defineEmits<EmitType>();

const sendMsg = () => {
  // 传input值给父组件
  emit("onReceiveMsg", inputVal.value);
}

</script>

<style scoped></style>
暴露组件自己的属性 defineExpose用法

在vue3中增加了暴露组件自己属性的方法,让子组件暴露自己的属性,然后父组件应用,也类似子组件直接传值给父组件了。
这里我们先来子组件的代码
Son.vue:

<template>
  <div>
    <h3>这是子组件接收的父组件信息</h3>
    <a-input v-model:value="inputVal" placeholder="请输入传给父组件的值" />
  </div>
</template>

<script setup lang="ts">
import { Input as AInput } from "ant-design-vue";
import { ref } from 'vue'
const inputVal = ref('');

// 子组件通过defineExpose暴露自己的属性
// 子组件暴露的方法
const exposeFun = (name:string) => {
  console.log('子组件暴露自己的属性', name);
}
// 使用defineExpose暴露inputVal和exposeFun
defineExpose({
  inputVal,
  exposeFun
});

</script>

<style scoped></style>

然后是父组件调用子组件暴露出来的属性

<template>
  <div>
    <h2>这是父组件</h2>
    <div>
      <a-button @click="useSonExpose" >使用子组暴露的属性</a-button>
      <h4>子组件触发父组件接收的值</h4>
      <p>{{receiveMsg}}</p>
    </div>
    <h4>下方是子组件的内容</h4>
    <SonVue ref="sonRef" />
  </div>
</template>

<script setup lang="ts">
import {Button as AButton} from 'ant-design-vue';
import SonVue from './Son.vue';
import { ref } from 'vue';
// 接收子组件的传入的值
const receiveMsg = ref('');

// 父组件接收子组件暴露的方法,使用子组件的ref
const sonRef = ref<{
  inputVal: string;
  exposeFun(name:string): void;
}>();
// 使用子组件暴露的内容
const useSonExpose = () => {
  // 由于ts认为inputVal可能位undefined不能赋值给string的reveiveMsg
  // 因此使用了一个断言
  receiveMsg.value = sonRef.value?.inputVal as string;

  // 触发子组件暴露的方法
  sonRef.value?.exposeFun('父组件');
}
</script>

<style scoped></style>

以上就是vue3中父子组件传值的方法,defineProps和defineEmits以及defineExpose的使用。

watch用法

上边我们介绍了父子组件的传值方法,这里我们再来介绍下vue3中对属性监听watch的用法,我们先来构建一个组件的架子,里边有ref和reactive的方式声明的变量,因为两种方式的监听有点区别:
Watch.vue基础代码

<template>
  <div>
    <h3>Watch用法</h3>
    <a-input v-model:value="inputVal" @keydown.enter="onInput" />
    <ol>
      <li v-for="(item, index) in data.list" >{{item}}</li>
    </ol>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive } from 'vue';
import {Input as AInput} from 'ant-design-vue';

const inputVal = ref('');
const data = reactive<{
  list: string[]
}>({
  list: []
});

const onInput = () => {
  if(inputVal.value){
    data.list.push(inputVal.value);
    inputVal.value = '';
  }
};


</script>

<style scoped></style>

然后使用watch对inputVal和list的监听

<template>
  <div>
    <h3>Watch用法</h3>
    <a-input v-model:value="inputVal" @keydown.enter="onInput" />
    <ol>
      <li v-for="(item, index) in data.list" >{{item}}</li>
    </ol>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, watch } from 'vue';
import {Input as AInput} from 'ant-design-vue';

const inputVal = ref('');
const data = reactive<{
  list: string[],
  name: string
}>({
  list: [],
  name: ''
});

const onInput = () => {
  if(inputVal.value){
    data.list.push(inputVal.value);
    data.name = inputVal.value;
    inputVal.value = '';
  }
};
// watch监听ref声明属性的变化
watch(inputVal, (newVal, oldVal)=> {
  console.log('watch监听inputVal的变化 newVal ------>', newVal);
  console.log('watch监听inputVal的变化 oldVal ------>', oldVal);
});
// watch监听reactive声明属性的变化
watch(() => data.name, (newVal, oldVal)=> {
  console.log('watch监听data.name的变化 newVal ------>', newVal);
  console.log('watch监听data.name的变化 oldVal ------>', oldVal);
});
watch(()=> data.list, (newVal, oldVal)=> {
  console.log('watch监听data.list的变化 newVal ------>', newVal);
  console.log('watch监听data.list的变化 oldVal ------>', oldVal);
}, {deep: true});
// vue3的watch也可以同时监听多个
watch([inputVal, ()=> data.name],(newVal, oldVal) => {
  console.log('watch监听inputVal和data.name的变化 newVal ------>', newVal);
  console.log('watch监听inputVal和data.name的变化 oldVal ------>', oldVal);
});


</script>

<style scoped></style>

在对list数组的监听中需要加上deep才能监听到数组的变化,因此又加入了data.name可以和inputVal对比使用,这就是vue3中watch的用法了。


以上就是本文的内容了,后边有空在给路由的使用和生命周期钩子函数整理出来,对于vite+vue3+ts的这套组合开发项目也就不成问题了,对于vue3中使用的vuex感觉既然有了好用的pinia可以作为了解,项目中可以使用pinia来处理store,如果对pinia不了解可以来看《新的好朋友Pinia,引领状态管理新时代》进一步了解。

如有疑问可以留言,也可以到QQ群一起探讨:
QQ群1: 657011407, QQ群2: 492593055,也可以到微信找我 shenzhipeng1023

;