美文网首页
手把手教你Vue3自定义组件(setup语法糖版)

手把手教你Vue3自定义组件(setup语法糖版)

作者: ZT_Story | 来源:发表于2022-04-28 16:34 被阅读0次
    Vue3 setup Cmp.png

    Vue 3.0 在今年2月7日已经正式转正,经过这两年的尝鲜和测试,已经比较稳定,个人建议在支持现代浏览器的项目中都可以使用Vue 3.0+来进行开发,原生支持TypeScript这点是真的香,Vue 3的好处还是很多的,好了话不多说,这次我们就来聊一聊 <script setup> 语法糖里,究竟该如何自定义组件?

    目录

    自定义组件,我们一般需要实现这几个点:

    • props —— 定义属性
    • events —— 定义事件
    • slots —— 插槽
    • expose —— 定义组件可供外部访问的内容
    • v-model —— 自定义组件实现双向数据绑定
    • provide 与 inject

    为什么使用 script setup ?

    刚开始尝试Vue 3的时候用的组合式API都是这样的写法

    export default {
      props: {
        title: String
      },
      setup(props) {
        console.log(props.title)
        ...
        return {
            ...
        }
      }
    }
    

    乍一看感觉不如原来的Options API啊,什么逻辑都写到setup里面了,好臃肿的一个方法。

    但事实上,Vue 3在对响应式重新设计之后,让我们可以通过refreactive方法来创建声明一个响应式变量,也就意味着我们很多逻辑可以不依赖this.data进行开发和编写,甚至一些响应式逻辑都可以多组件复用。

    在了解了这些点之后,即便我们可以将逻辑拆分独立,通过解构的方式导入setup中,让我们代码更加高内聚低耦合,但我们依然避免不了,复杂组件需要return无数的方法或者变量提供给模板使用。

    但是,在<script setup> 语法糖出现之后,这个问题得到了极大的改善,不管是import的组件也好,还是声明的变量也罢,都可以不用一个个return了。

    编译器会帮助我们转换成setup()函数的内容,这意味着与普通的 <script> 只在组件被首次引入的时候执行一次不同,<script setup> 中的代码会在每次组件实例被创建的时候执行

    所以,还不赶紧学起来?

    自定义组件

    props 与 events

    <script setup> 中必须使用 definePropsdefineEmits API 来声明 propsemits ,它们具备完整的类型推断并且在 <script setup> 中是直接可用的:

    <script setup>
    const props = defineProps({
      foo: String
    })
    
    const emit = defineEmits(['change', 'delete'])
    // setup code
    </script>
    
    • definePropsdefineEmits 都是只在 <script setup> 中才能使用的编译器宏。他们不需要导入且会随着 <script setup> 处理过程一同被编译掉。
    • defineProps 接收与 props 选项相同的值,defineEmits 也接收 emits 选项相同的值。
    • definePropsdefineEmits 在选项传入后,会提供恰当的类型推断。
    • 传入到 definePropsdefineEmits 的选项会从 setup 中提升到模块的范围。因此,传入的选项不能引用在 setup 范围中声明的局部变量。这样做会引起编译错误。但是,它可以引用导入的绑定,因为它们也在模块范围内。

    以上是官方文档对于定义Props和Emits的相关介绍,笔者觉得说的还是很清楚的,这里在圈一下重点

    • <script setup>不需要导入definePropsdefineEmits
    • 定义props时传入的参数与options APIprops选项一致
    • 在TS中可以直接纯类型声明
    interface Props {
        foo: string
        bar?: number 
    }
    const props = defineProps<Props>();
    

    这里肯定很多小伙伴有疑问了,那如果用TS做纯类型的声明,默认值该怎么定义呢?

    呐,看这里!

    interface Props {
      msg?: string
      labels?: string[]
    }
    
    const props = withDefaults(defineProps<Props>(), {
      msg: 'hello',
      labels: () => ['one', 'two']
    })
    

    还有一个withDefaults编译器宏

    上面代码会被编译为等价的运行时 props 的 default 选项。此外,withDefaults 辅助函数提供了对默认值的类型检查,并确保返回的 props 的类型删除了已声明默认值的属性的可选标志。

    Slots

    大部分情况,我们可能需要根据外部的slots的传入情况来决定组件内部的展示部分,在模板中我们可以通过$slots来访问所有的默认插槽以及具名插槽

    比如:Auth组件校验不通过时,隐藏slots的内容

    那么我们可以在模板中这样来做

    // page
    <Auth auth="commit">
        <button>提交<button>
    </Auth>
    // components
    <template>
        <slot v-if="condition" />
    </template>
    

    这样既不会增加dom节点也可以增加逻辑来处理按钮权限的问题

    再比如:组件内部有多个插槽及具名插槽的时候

    form表单中的form-item组件是可以自定义插槽来覆盖默认的input内容的,在模板中就可以通过$slots来访问具体的插槽对象

    // page
    <form-item>
        <template #input>
            自定义form input内容
        </template>
    </form-item>
    
    // components
    <template>
        <slot v-if="$slots.input" name="input" />
        <input v-else />
    </template>
    

    我们很少情况会在setup中操作Slots,但是它依然提供了useSlots方法来帮我们操作组件的Slots

    <script setup>
    
    import { useSlots } from 'vue'
    
    const slots = useSlots()
    
    </script>
    

    Expose

    使用 <script setup> 的组件是默认关闭的,也即通过模板 ref 或者 $parent 链获取到的组件的公开实例,不会暴露任何在 <script setup> 中声明的绑定。

    我们组件内部的状态和方法可能会很多,比如一些复杂的组件,但是有些状态外部或许需要在适当时候操作或访问的时候,我们就需要考虑那些属性和方法是可以暴露给外部的

    这个时候我们就可以使用defineExpose来声明绑定

    <script setup>
    import { ref } from 'vue'
    
    const a = 1
    const b = ref(2)
    
    defineExpose({
      a,
      b
    })
    </script>
    

    当父组件通过模板 ref 的方式获取到当前组件的实例,获取到的实例会像这样 { a: number, b: number } (ref 会和在普通实例中一样被自动解包)

    v-model

    v-model其实是一个语法糖

    它代表声明了一个modelValue的属性以及一个update:modelValue的事件

    Vue 3 中你可以通过 propsName + update:propsName 来自定义v-model

    也就是说:一个组件里可以定义多个v-model

    // page
    <cmp v-model:foo="xxx" v-model:bar="xxxx" />
    
    // components
    <script setup>
    
    interface Props {
        foo: string
        bar: string
    }
    
    const props = defineProps<Props>();
    const emits = defineEmits(["update:foo", "update:bar"]);
    
    </script>
    

    provide 与 inject

    这里需要用到 provide() 与 inject()

    父组件:

    <script setup>
    import { provide } from "vue";
    
    const userObj = ref<User>(...);
    
    provide("user", userObj);
    
    const fn = () => {
        ...
    }
    
    provide("change", fn);
    
    </script>
    

    子组件:

    <script setup>
    import { inject } from "vue";
    
    const injectUserObj = inject("user");
    
    const injectFn = inject("change");
    </script>
    

    总结

    目前笔者整理的就这么多,我自己在开发组件的过程中常用到的目前也就这些知识点

    当然还有函数式组件相关的写法,这个可能大部分人不常会用到,组件库考虑到动态性或许会选择

    不过我们做业务组件时我还是建议大家使用单文件组件

    维护性还是高了不少的

    喜欢的就点赞收藏起来吧~

    相关文章

      网友评论

          本文标题:手把手教你Vue3自定义组件(setup语法糖版)

          本文链接:https://www.haomeiwen.com/subject/jhedyrtx.html