Vue3组件化知识概览 | 青训营笔记

124 阅读2分钟

这是我参与「第五届青训营 」笔记创作活动的第5天

目录概览

该文章仅限于在项目开始之前对Vue3知识体系的重点概览(帮助回忆)

  1. 组件命名
  2. 属性定义
  3. 事件定义
  4. 透传特性
  5. 插槽
  6. 提供provide/注入inject
  7. Composables
  8. 组件通信
  9. 相关API变化

介绍

一、组件命名

官方建议使用大驼峰命名法来注册组件,原因如下:

  1. 驼峰命名是合法的js标识符,导入、注册组件会更简单,IEDE会自动完成
  2. 在setup语法糖中不用注册组件,直接导入使用即可,Vue组件比原生HTML元素更醒目

举个栗子🌰

<template>
      <Menu />
</template>

<script setup lang='ts'>
import Menu from "@/components/layout/menu/index.vue";
</script>

二、属性定义

最好采用具体的定义方式:

<script setup lang='ts'>
const props = defineProps({
    name:{
        type:string,
        require:true
    }
})
console.log(props.name)
</script>

三、事件定义

自定义事件的名称会被自动做转换,通常使用驼峰做事件名,监听时需要转换为肉串方式a-b

<!-- MyComponet组件-->
<button @click="$emit('someEvent')">点击我触发事件</button>
<!-- 引入MyComponet组件-->
<MyComponet @some-event="callback">

script setup中定义

<script setup lang='ts'>
const emit = defimeEmits(['someEvent'])
emit('someEvent')
</script>

四、透传特性

在vue3中,那些没有在组件props和emits中声明的特性或事件监听器称为透传特性,以前叫非属性特性,比如class,styleid特性。当组件只有单根时,透传特性自动被添加到根元素上作为其特性 例如:

<button class="large">点击</button>

若不想自动继承属性,可以使用inheritAttrs:false禁止

访问透传特性:

<script setup lang='ts'>
import {useAttrs} from 'vue'

const attrs = useAttrs()
</script>

五、插槽

如果要传递模板内容给子组件,我们使用插槽。

特别注意的是,Vue3中插槽的变化是移除scopeSlots,只需要访问slots对象,且包括default插槽都是函数形式

<script setup lang='ts'>
import {useSlots} from 'vue'

const slots = useSlots()
const defaultContent = slots.default()
</script>

六、提供/注入

隔代传参时,使用provide/inject

<script setup lang='ts'>
import {provide} from 'vue'

//祖代提供数据
provide('message','hello') //key,value
</script>
<script setup lang='ts'>
import {inject} from 'vue'

//后代注入数据
inject('message','hello') //key,value
</script>

七、Composables

利用Composition API封装的可重用状态逻辑称为composables 约定composables函数命名时可加上use前缀 例如:

image.png

八、组件通信

  • props:父子通信,传入一个属性
{
 props:{msg:string}
}

在Vue3.2中,还出了script setup新写法

const props = defineProps({
    model:{
        type:Object,
        required:true
    }
})
  • $emit
this.$emit('add',good)

vue3.2中出了script setup新语法:

//不推荐使用
const emit = defineEmits(['event'])
//推荐使用
const emit = defineEmits<{
    (e:"updateValue",value:string):void;
    (e:"validate"):void;
}>()
emit("updateValue",inp.value)

on,once,$off被移除了!这三个方法被认为不应该由vue提供,因此被移除了,可以使用其他库实现等效功能

import mitt from 'mitt'
const emitter = mitt()

//发送事件
emitter.emit('foo','fooooo')
//监听事件
emitter.on('foo',msg=>console.log(msg))
  • event bus

在vue2中我们常常用一个Vue实例来做事件总线,现在不行了,所以可以使用上面的mitt方案来代替

  • vuex/pinia

在vue3中状态管理一般都用pinia,因为它对ts的支持更好

具体可参照官网简介 | Pinia (vuejs.org)

  • parent/root:兄弟组件之间通信可以通过共同祖辈搭桥
//brother1
this.$parent.$on('foo',handle)
//brother2
this.$parents.$emit('foo')
  • $children

vue3中移除了该选项,官方建议我们访问子组件时使用$refs

  • $refs:获取指定元素或者组件
//parent
<HelloWorld ref="hm">
mounted() {
this.$refs.ha.xx = 'xxx'
}
  • provide/inject:祖孙辈之间进行通信 *\
import {provid,inject} from vue
//提供数据
provide(key,value)
//注入数据
inject(key)
  • attrs:包含那些没有声明的组件特性,vue3中移除了listners,只剩下attrs:包含那些没有声明的组件特性,vue3中移除了listners,只剩下attrs:
//child:并未在props中声明foo
<p>{{$attrs.foo}}</p>
//parent
<Hello foo='foo' />

通过v-bind="$attrs"透传到内部组件--在创建高级别组件时非常有用

//给Grandson隔代传值,parent.vue
<Child2 msg="lalal" @some-event="onSomeEvent" />

//给child做展开
<Grandson v-bind="$attrs">

//Grandson使用
<div @click="$emit("some-event",'msg from grandson')>
    {{msg}}
</div>
  • 插槽:插槽语法是Vue实现的内容分发API,用于复合组件开发,该技术在通用组件库开发中有大量应用
  1. 匿名插槽

slot称为匿名插槽,作为占位符存在,将来被替换为传入内容

//comp1
<div>
    <slot></slot>
</div>
//parent
<comp1>hello</comp1>
  1. 具名插槽

slot加上name就称为具名插槽,可以将内容分发到子组件指定位置

//comp2
<div>
    <slot></slot>
     <slot name="content"></slot>
</div>
//parent
<comp2>
<!-- 默认插槽用default作为参数-->
<template v-slot:default>具名插槽</template>
<!-- 具名插槽用插槽名作为参数-->
<template v-slot:content>内容...</template>
</comp2>
  1. 作用域插槽

分发内容要用到子组件中的数据

//comp3
<div>
    <slot :foo="foo"></slot>
</div>
//parent
<comp3>
<!-- 把v-slot的值指定为作用域上下文对象-->
<template v-slot:default="slotProps">
来自子组件的数据{{slotProps.foo}}
</template>
</comp3>

Vue3中组件相关API变化总结

  • global-api改为实例方法
//vue2中
Vue.component()

//vue3中
const app = creatApp({})
.componet('comp',{template:'<div>i am comp</div>'})
.mount('#app')
  • 移除.sync,统一为v-model
<div id="app">
    <comp v-model="data"></comp>
    <!--v-model展开后-->
    <comp :data="data" @update:data="onxxx"></comp>
</div>
app.componet('comp',{

    template:'
    <div @click="$emit('update:modelValue','new value')"> 
    {{modeleValue}}
    </div>
    ',
    props:['modelValue']
}
  • 渲染函数api修改

不再传入h函数,需要我们手动导入:拍平的props结构,scopedSlots删掉了

  • 组件emits选项

该选项用于标注自定义事件及其校验等

createApp({}).componet("comp",{
    template:'...',
    //emits标明组件对外事件
    emits:['buy','..']
    //还能对事件进行校验
    emits:{
        'update:modeleValue':null, //不做校验
        buy(p){
            if(p==='nothing'){
                console.warn('参数非法')
                return false 
            }else {
                return true
            }
        }
    }



})