vue 3 props 与 组件的变化,持续更新.....

24,184 阅读4分钟

组件之间的数据传递

父传子props

  1. 父子之间的数据传,父组件的数据,通过props 传递给子组件,传递的方式与之前的vue2 的语法没有变化,但是在子组件接收父组件传递过来的数据,接收的方式发生了一些变化。
// 父组件
<template>
    <Menu :arr="arr"></Menu>
</template>
<script setup>
    const arr = [1,2,3,4,5]
</script>
// 子组件 Menu.vue
<script setup>
    import { defineProps } from 'vue'
    const props = defineProps({
        arr: {
            type: Array,
            required: true
        }
    })
    
    console.log(props.arr) // [1,2,3,4,5]
</script>
  1. 上面的传递数据的方式,将父组件里面的数组传递给子组件,但有时候,会在父组件里给引用的子组件加上一些类,或者是一些其他的值,但是不是以props的方式进行传递的,在子组件接收的时候,像是class等,都会被子组件的根元素接收,如果我们外面还包含了一层 div ,这些类不会被绑定到想要的位置,所以在接收的时候,我们需要将这种默认设置到根元素的方式禁用掉
// 子组件
<script>
    export default {
        inheritAttrs: false
    }
</script>
  1. 将不是以 props 方式传递的值,要统一放到指定的元素上
<template>
    <div v-bind="$attrs"></div>
</template>
  1. 如果不想全部放在一个元素上,可以指定放置某一个属性
<template>
    <div :id="$attrs.id" ></div>
</template>

v-model与组件

  1. 在某些情况下,父组件中的值和子组件中的值是共用的,那么会使用 v-model 双向绑定的方式,在 vue2 中,v-model 是 @input事件与v-bind:value的结合体,实现双向绑定,但是他很局限,所以在 vue 3中 重写了他,变得更加灵活
    • 放弃使用 input的接收方式
    • 改用update:modelValue,modelValue替代了value的方式
// 父组件
<template>
    <Menu :bool="bool" @update:bool = "bool = $event"`></Menu> 方式一
    <Menu v-model:bool = "bool" /> 方式二
</template>
<script setup>
    const bool = true
</script>
// 子组件 (方式一) 与 (方式二)接收的方式是相同的
<script setup>
    import { defineProps,defineEmits} from 'vue'
    const emit = defineEmits(['update:bool'])
    const props = defineProps({  
        bool: {
            type: Boolean,
            required: true
        }
    })
    
    // 发送事件
    const boolChange = () => {
        emit('update:bool',参数)
    }

</script>

使用watch 侦听props值的变化

  1. 在vue3 中一些api 的写法发生了变化,但是实质性的内容还是那些操作,所以还是要注意一些写法的变化
<sctipt>
    import { watch,defineProps } from 'vue';
    const props = defineProps({
        bool: {
            type: Boolean,
            required: true
        }
    })
    
    watch(
        () => props.bool,
        (newValue,oldValue) => {
            console.log(newValue,oldValue)
        }
    )
</script>

子传父emit

  1. vue3 中子组件想要给父组件传递数据
import { defineEmits } from 'vue'
const emit = defineEmits(['事件名称']) 

const click = () => {
    emit('事件名',参数)
}
  1. 直接在html元素上触发
<template>
    <button @click="emit('事件名')" >点击触发按钮</button>
</template>
<script setup>
    import { defineEmits } from 'vue'
    const emit = defineEmits(['事件名称'])
</script>
  1. 父组件接收的方式与之前没有变化
<template>
    <Menu @子组件发送的事件名="父组件注册事件名"></Menu>
</template>
  1. 需要注意的是,如果事件的名是驼峰命名,需要注意要将驼峰改成 - 连字符
// 父组件
<cart :data="item" @on-open-details="onOpenDetails"></cart>
// 子组件    
 <span @click="emit('on-open-details')"></span>
<script setup>
import { ref, defineEmits, defineProps,  } from 'vue'
const emit = defineEmits(['on-open-details'])
</script>

插槽(slot)的props

GIF 2021-12-2 14-01-58.gif

  1. 举一个删除列表的例子,看一下插槽的的props,是如何进行使用的
// 父组件
<template>
    <About v-for="item in arrlist" :key="item.id" :item="item" >
       
       // 方式一 (slotProps接收所有值,也可以通过结构,取出对应值)
       <template v-slot:default="slotProps">  
           接收所有传递的数据{{ slotProps }}
       </template>
       
       // 方式二
       <template #default="{ id }">
          接收指定的值 {{ id }}
       </template> 
       
       
       <template #default="{ id }">
           <button @click="removeRow(id)"> 删除 </button>
       </template>
       
    </About>
</template>
<script setup>
    import About from './Home.vue'
    import { ref } from 'vue'
    const arr = [
        { id: 1, name: 'css' },
        { id: 2, name: 'html' },
        { id: 3, name: 'js' },
    ]
    const arrlist = ref([])
    
    const removeRow =(id) => {
      const idx = arrlist.value.findIndex(x => x.id == id)
      arrlist.value.splice(idx, 1)
    }
</script>
// 子组件
<template>
  <div>
    <p> {{ props.item.id }} --- {{ props.item.name }} </p>
    // 将id的值传递给父组件,父组件接收到,给子组件插槽传递按钮,完成操作
    <slot :id="props.item.id"></slot>
  </div>
</template>
<script setup>
    import { defineProps } from 'vue'
    const props = defineProps({
        item: {
            type: Object
        }
    })
</script>

$slots的属性

  1. vue3 中可以在template通过 $slots直接使用这个属性,官方:用来以编程方式访问通过插槽分发的内容
  2. $slots的官网位置(实例 property | Vue.js (vuejs.org))
  3. 具体的内容还是官网介绍额比较详细
// 来源于element-plus
<template v-if="!$slots.title">{{ title }}</template>
  1. 如果不想在元素的身上使用,可以在script里面引入的方式进行使用,但实际很少,一般多于在元素的身上进行使用
// 来源官网
<script setup>
    import { useSlots } from 'vue'
    const slots = useSlots()
</script>

递归组件

  1. vue2 中的递归组件,组件都会有一个name的选项,设置该选项,通过name 去调用组件,完成递归组件的调用,在数据多的时候,如果有这种需求,去循环很多层级的数据,可以使用组件调用自身的方式,调用,通过v-if在什么条件下不去调用等一系列的操作
// Menu.vue
<template>
    <menu></menu>
</template>
<script>
    export default {
        name: 'menu'
    }
</script>
  1. vue3 中递归组件
    • 方式一
    <template>
        <el-menu-item-group /> // element-plus
    </template>
    <script>
        import { defineComponent } from 'vue'
        export default defineComponent({
            name: 'ElMenuItemGroup'
        })
    </script>
    
    • 方式二,由于使用了setup语法糖,没办法设置 name选项,但是可以通过import别名的方式,将组件引入并使用
    // Menu.vue
    <template>
        <menu-child></menu-child>
    </template>
    <script setup>
        import { Menu as MenuChild } from './Menu.vue'
    </script>
    

跨组件的通信

  1. 在实际开发的时候,有时候两个根本不是父子甚至不是兄弟组件,我们需要他们之间进行通信,第一种选择就是vuex去共享状态,还有一种就是通过 eventBus去事件传递,然后传值,但是在 vue3 中,官方推荐使用的是 mitt 这个库,他是轻量级的一个工具库,还是比较好用的
1. 下载 npm install mitt 
2. 按需引入 
   import mitt from 'mitt'
   const emitter = mitt() // 跨组件事件传递
   export default emitter
// 组件A
<template>
    <button @click="tabsChange">按钮</button>
</template>
<script setup>
    import emitter from '@/event/bus'
    
    const tabsChange = () => {
        emitter.emit('事件名')
    }
</script>
// 组件B
<template>
    
</template>
<script setup>
    import emitter from '@/event/bus'
    
    emitter.on('事件名', (参数) => {
        事件处理程序
    })
</script>
  1. 更多的操作,还得看mitt 在npm 或者是github上查看更多用法 mitt - npm (npmjs.com)

动态组件

  1. vuejs 提供给我们一个 component 组件并且通过 is去切换不同的组件,让不同组件进行切换显示,其实可以同时引入,并且可以通过 v-if去控制组件的显示与隐藏,既然vuejs提供了动态组件,可以去看一下动态组件的方式,v-if频繁的是切换使用,在性能方面上不是很好,所以动态组件还是有必要的使用
<component :is="Home"></component>
<script setup>
    import Home from '@/components/Home.vue'
    import About from '@/components/About.vue'
</script>
  1. 在vue2 中的那个写法,去控制一个变量,但是现在也不知道是为什么,在setup 语法糖里面就失效了,所以还在寻找在setup语法糖里面是如何使用的.........

数据穿透 provide or inject

Snipaste_2021-12-06_12-20-20.png

  1. vue 考虑到这种多层次的组件嵌套的数据传递的问题,他提供provide 与 inject,去完成深层嵌套组件的数据穿透
  2. 由于在 vue3 中,provide or inject 被放到了setup里面,所以需要在vue里面将他们进行引入使用
// 顶层组件
<script setup >
    import { provide,ref } from 'vue'
    const name = ref('provide')
    provide('name',name)
</script>
// 底层组件
<template>
    <input type="text" v-model="name" / >
    {{ name }}
</template>
<script setup>
    import { inject } from 'vue'
    const name = inject('name') // name就是provide 里面第一个参数
</script>
  1. 传递方法,通过provide向子组件传递方法,子组件通过调用方法去改变父组件里面的值,顶层组件数据被更改的前提是,他必须是一个响应式数据
// 顶层组件
<template> {{ name }} </template>
<script setup >
    import { provide,ref } from 'vue'
    const name = ref('provide')
    provide('clickChange',(newValue) =>name.value = newValue )
</script>
// 底层组件
<template>
    <button @click="clickChange('子组件触发')">点击改变</button>
</template>
<script setup>
    import { inject } from 'vue'
    const clickChange = inject('clickChange') // name就是provide 里面第一个参数
</script>