1. 父组件向子组件传值 props

  1. //父组件
  2. <template>
  3. <div id="app">
  4. <users v-bind:users="users"></users> //前者自定义名称便于子组件调用,后者要传递数据名
  5. </div>
  6. </template>
  7. <script>
  8. import Users from "./components/Users"
  9. export default {
  10. name: 'App',
  11. data(){
  12. return{
  13. users:["Henry","Bucky","Emily"]
  14. }
  15. },
  16. components:{
  17. "users":Users
  18. }
  19. }

在子组件中使用props(可以是数组也可以是对象)接收即可。可以传多个属性。

  1. //users子组件
  2. <template>
  3. <div class="hello">
  4. <ul>
  5. <li v-for="user in users">{{user}}</li>//遍历传递过来的值,然后呈现到页面
  6. </ul>
  7. </div>
  8. </template>
  9. <script>
  10. export default {
  11. name: 'HelloWorld',
  12. props:{
  13. users:{ //这个就是父组件中子标签自定义名字
  14. type:Array,
  15. required:true
  16. }
  17. },
  18. data() {
  19. return {
  20. name : this.users // 把传过来的值赋值给新的变量
  21. }
  22. },
  23. }
  24. </script>
  25. /*
  26. props: ['users']
  27. props: { users: String } //这样指定传入的类型,如果类型不对会警告
  28. props: { users: [String, Number] } // 多个可能的类型
  29. prosp: { users: { type: String, requires: true } } //必填的的字符串
  30. props: {
  31. childMsg: {
  32. type: Array,
  33. default: () => []
  34. }
  35. } // default指定默认值
  36. 如果 props 验证失败,会在控制台发出一个警告。
  37. */

子组件接收的父组件的值分为引用类型和普通类型两种:
  普通类型:字符串(String)、数字(Number)、布尔值(Boolean)、空(Null)
  引用类型:数组(Array)、对象(Object)

单向数据流

image.png

vue不推荐直接在子组件中修改父组件传来的props的值,会报错
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop’s value. Prop being mutated: “result” (found in component )

  1. export default {
  2. name:"round-search-bar",
  3. props:['search'], //父组件传来的值
  4. data(){
  5. return {
  6. currentSearch: this.search //通过data, 定义新变量currentSearch, 这样currentSearch的值变更时,不会影响父组件传来的search的值
  7. }
  8. },
  9. methods: {
  10. doSearch(){
  11. Util.searchAPI(this.$router,this.currentSearch)
  12. }
  13. },
  14. }

2. 子组件向父组件传值

(通过 emit 事件形式)

  1. // 子组件
  2. <template>
  3. <h1 @click="clicked">绑定一个点击事件</h1>
  4. </template>
  5. <script>
  6. export default {
  7. data() {
  8. return {
  9. info: '爸爸的爱好好收下'
  10. };
  11. },
  12. methods: {
  13. clicked() {
  14. this.$emit("childChanged", this.info); //子组件的自定义事件
  15. },
  16. },
  17. };
  18. </script>
  1. // 父组件
  2. <template>
  3. <div>
  4. <my-child v-on:childChanged="getInfo" />
  5. <h2>{{title}}</h2>
  6. </div>
  7. </template>
  8. <script>
  9. import MyChild from "./components/MyChild"
  10. export default {
  11. name: 'App',
  12. components:{
  13. MyChild,
  14. },
  15. data(){
  16. return{
  17. title:"我才是你爸爸"
  18. }
  19. },
  20. methods:{
  21. getInfo(e){ //声明这个函数
  22. this.title = e;
  23. }
  24. },
  25. }
  26. </script>

父组件通过 refs 获取子组件实例

在使用选项式API时,我们可以通过this.$refs.name的方式获取指定元素或者组件,但是组合式API中就无法使用哪种方式获取。如果我们想要通过ref的方式获取组件或者元素,需要定义一个同名的Ref对象,在组件挂载后就可以访问了。

  1. <template>
  2. <ul class="parent list-group">
  3. <li class="list-group-item" v-for="i in childRefs?.list" :key="i">
  4. {{ i }}
  5. </li>
  6. </ul>
  7. <!-- 子组件 ref的值与<script>中的保持一致 -->
  8. <child-components ref="childRefs"></child-components>
  9. <!-- 父组件 -->
  10. </template>
  11. <script setup>
  12. import { ref } from 'vue'
  13. import ChildComponents from './child.vue'
  14. const childRefs = ref(null)
  15. </script>

3. 任何组件间的通信 $bus 事件总线

  1. // main.js
  2. Vue.prototype.$eventBus = new Vue()
  3. ------------------------------------------
  4. // 需要订阅的地方 mounted 、created 等
  5. this.$eventBus.$on('update', val => {})
  6. // 需要发布信息的地方
  7. this.$eventBus.$emit('update', '更新信息')
  8. //移除事件监听
  9. this.$eventBus.$off('update', {})
  • 通过 $on(eventName, eventHandler) 侦听一个事件
  • 通过 $once(eventName, eventHandler) 一次性侦听一个事件
  • 通过 $off(eventName, eventHandler) 停止侦听一个事件

Vue3中移除了事件总线,但是可以借助于第三方工具来完成,Vue官方推荐mitt[2]tiny-emitter[3]
在大多数情况下不推荐使用全局事件总线的方式来实现组件通信,虽然比较简单粗暴,但是长久来说维护事件总线是一个大难题,所以这里就不展开讲解了,具体可以阅读具体工具的文档

4. vue3 v-model方式 组件中使用

v-model是Vue中一个比较出色的语法糖,就比如下面这段代码

  1. <ChildComponent v-model:title="pageTitle" />

就是下面这段代码的简写形势

  1. <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />

v-model确实简便了不少,现在我们就来看一下上面那个demo,如何用v-model实现。

子组件:

  1. <template>
  2. <div class="child-wrap input-group">
  3. <input
  4. v-model="value"
  5. type="text"
  6. class="form-control"
  7. placeholder="请输入"
  8. />
  9. <div class="input-group-append">
  10. <button @click="handleAdd" class="btn btn-primary" type="button">
  11. 添加
  12. </button>
  13. </div>
  14. </div>
  15. </template>
  16. <script setup>
  17. import { ref, defineEmits, defineProps } from 'vue'
  18. const value = ref('')
  19. const props = defineProps({
  20. list: {
  21. type: Array,
  22. default: () => [],
  23. },
  24. })
  25. const emits = defineEmits(['update:list'])
  26. // 添加操作
  27. const handleAdd = () => {
  28. const arr = props.list
  29. arr.push(value.value)
  30. emits('update:list', arr)
  31. value.value = ''
  32. }
  33. </script>

在子组件中我们首先定义props和emits,然后添加完成之后emit指定事件。

注:update:是Vue中的固定写法,表示props中的某个属性名。

父组件中使用就比较简单,代码如下:

  1. <template>
  2. <!-- 父组件 -->
  3. <ul class="parent list-group">
  4. <li class="list-group-item" v-for="i in list" :key="i">{{ i }}</li>
  5. </ul>
  6. <!-- 子组件 -->
  7. <child-components v-model:list="list"></child-components>
  8. </template>
  9. <script setup>
  10. import { ref } from 'vue'
  11. import ChildComponents from './child.vue'
  12. const list = ref(['JavaScript', 'HTML', 'CSS'])
  13. </script>