Vue3组件化开发习惯

组件命名

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

        1. 驼峰命名是合法的 JS 标识符,导入、注册组件会更简单,IDE 会自动完成。

        2. <Pascalcase /> 使 Vue 组件比原生 HTML 元素更醒目。

属性定义

使用更细节的方式定义属性:

<script setup>
// 不推荐
// const props = defineProps(['foo'])

// 推荐
const props = defineProps({
    foo: {
        type: String,
        default: ''
    }
})
console.log(props.foo)
</script>

事件定义

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

<!-- MyComponent -->
<button @click="$emit('someEvent')">click me</button>
<MyComponent @some-event="callback" />

script setup 中定义

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

透传特性

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

<MyButton class="large">
<button class="large">click me</ button>

如果不想自动继承特性,可以使用 inheritAttrs: false 禁止

<script>
export default {
    inheritAttrs: false
}
</script>

<script setup>
import { useAttrs } from 'vue'

const attrs = useAttrs() // 获取到透传特性
</script>

插槽

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

Vue3 中插槽的变化是移除 scopeSlots, 只需要访问 slots 对象,且包括 default 插槽

<script setup>
import { useSlots } from 'vue'

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

提供/注入

隔代传参,使用provide/inject API

<script setup>
import { provide} from 'vue'
// 祖代提供数据
provide(/* key */ 'message', /* value */ 'hello!')
</script>
<script setup>
import { inject} from 'vue'
// 后代注入数据
inject(/* key */ 'message', /* value */ 'hello!') // 第二个参数为默认值
</script>

Composables

利用 composition API 封装的可重用状态逻辑成为 composables

约定 composables 函数命名时加上 use 前缀,例如:

import { ref, onMounted, onUnmounted } from 'vue'


export function useMouse() {
    const x = ref(0)
    const y = ref(0)
    
    function update(event) {
        x.value = event.pageX
        y.value = event.pageY
    }

    onMounted(() => window.addEventListener('mousemove', update))
    onUnmounted(() => window.removeEventListener('mousemove', update))

    return { x, y }
}

传入 refs 参数代替原始值

import { unref } from 'vue'

function useFeature(maybeRef) {
    //如果maybeRef确定是ref,自动解包
    //否则直接return
    const value = unref(maybeRef)
}

总是返回包含 refs 的对象

const { x, y } = useMouse()

组件通信

组件通信常用方式,vue3中有很多变化:

  • props: 父子通信,传入一个属性
{
    props: { msg: String }
}
defineProps({
    model: { type: Object, required: true }
});
  • $emit:

现在只剩下派发时间的 emit 方法

this.$emit('add', good)
cosnt emit = defineEmits(['update:model-value', 'validate'])

const emit = defineEmits<{
    (e: "update:model-value", value: string): void;
    (e: "validate"): void
}>()
emit("update:model-value", inp.value)

$on,$once,$off 被移除了!

上述 3 个方法被认为不应该由 vue 提供,因此被移除了,可以使用其他库实现等效功能。

import mitt from 'mitt'

const emitter = mitt()

//发送事件
emitter.emit('foo', 'foooooooo')

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

vue2 中我们常常使用一个 vue 实例,来做事件总线,现在不行了

所以我们就使用上面的 mitt 方案来代替就好了

// vue2时代,现在因为没有$on不行了
Vue.prototype.$bus = new Vue()

// 组件里面
this.$bus.$emit('xxx')

//其他组件
this.$bus.$on('xxx', () => {})
  • vuex/pinia

vuex 4.x 中的 composition api 写法:

const store = useStore()
store.commit('add')
store..dispatch('add')
  • parent/root

兄弟组件之间通信可以通过共同祖辈搭桥。

// borther1
this.$parent.$on('foo', handle)
// brother2
this.$parent.$emit('foo')

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

  • $refs

获取指定元素或组件

// parent
<HelloWorld ref="hw" />
mounted() {
    this.$refs.hw.xx = 'xxx'
}
  • project/inject

能够实现祖先和后代之间传值

在 composition 中有对于的api

// ancestor
provide() {
    return {foo: 'foo'}
}
//descendant
inject: ['foo']
import { provide, inject } from 'vue'
// 提供数据
provide(key, value)
// 注入数据
inject(key)
  • $attrs:

$attrs会包含那些没有声明的组件特性,vue3中移除了 listeners,只剩下 $attrs

// child: 并未在props中声明foo
<p> {{$attrs.foo}}</p>
// parent
<HelloWorld foo="foo" />

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

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

// Child做展开,父辈
<Grandson v-bind="$attrs"></Grandson>

// Grandson使用,孙子辈
<div @click="$emit('some-event', 'msg from grandson')">
  {{msg}}
</div>

插槽

插槽语法是 Vue 实现的内容分发 API,用于复合组件开发。该技术在通用组件库中有大量应用。

匿名插槽

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

// comp1
<div>
    <slot></slot>
</div>
//parent
<comp>hello</comp>

具名插槽

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>

作用域插槽

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

// comp3
<div>
    <slot :foo="foo"></slot>
</div>
//parent
<Comp3>
// 默认插槽用default做参数
<template v-slot:default="slotProps">
  来自子组件数据: {{slotProps.foo}}
</template>
</Comp3>

Vue3 中组件相关 API 变化总结

global-api 改为实例方法

全局静态方法引发一些问题,vue3 将 global-api 改为 app 实例方法

移除.sync,统一为 v-model

以前.sync 和 v-model 功能有重叠,容易混淆,vue3做了统一。

<div id="app">
    <comp v-model="data"></comp>
    <comp :model-value="data" @update:model-value="onxxx">
    <comp v-model:data="data"></comp>
    <comp :data="data" @update:data="onxxx">
</div>
app.component('comp', {
    props: {
        modelValue
    }
    template: `
      <div @click="$emit('update: modelValue', 'new value')">
        i am comp, {{modelValue}}
      </div>
`,
    props: ['modelValue'],
}) // 注意派发事件和属性名称不一样!

渲染函数 api 修改

不再传入 h 函数,需要我们手动导入;拍平的 props 结构。scopeSlots 删掉了,统一到 slots

import {h} from 'vue'

export default {
    render() {
        const emit = this.$emit
        const onClick = this.onclick
        return h('div', [
            h('div', { onClick() { emit('update:modelValue', 'new value')} },
                `i am comp, ${this.modelValue}`),
                h('button', { onClick(){ onClick() }}, 'buy it!')
        ])
    },
}

 组件 emits 选项

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值