本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
这一章我们继续来看组件部分,这也是与之前的不同,这篇文章的重点是defineProps
组件注册
分为全局注册和局部注册,全局的话,链式调用,可以注册多个,然后所有在vue应用实例的全局使用,局部组件就不一样了,因为有了前一节上的SFC的存在,比如要在另外一个组件中引用其他组件,只要导入了这个 .vue 文件就可以自动暴露给当前组件来使用了,无需像全局注册那样使用component来在实例上进行注册,那既然有全局组件,有局部组件的注册之分,那也一定有优缺点之分,包括使用的异同,
- 全局组件可以在全局使用,局部组件则在不同SFC中进行使用,则需要进行多次引入,
- 全局组件放在src的一级components文件夹下,局部建议放在父组件文件夹下面的component下
- 全局组件不管是否使用,打包后都在,对tree-shaking等无效
props
之前的vue2文档中在入门教程中好像没有怎么提透传attribute,也有可能是我很久没有看过文档的原因,这次在props直接进行了说明,将props和透传attribute做了提醒和区分,透传后续文档中专门介绍我们再说,继续看props,
defineProps()宏
在SFC中,props使用defineProps()宏来声明,这个对于vue2来说,我直接没咋接触这里的宏的概念,通篇读下来,我没有在这里找到文案对他的解释,这里就先简单理解成不需要导入,不像ref或者reactive那样,需要导入的命令是宏命令
整个props还是一样的,可以设置必填,默认类型等校验,规则也是一定要遵循单向数据流原则
事件
注意我们这里谈论的都是组件中的事件,组件的事件除了普通事件外,更多的是涉及事件触发后,需要向上一级组件,即父组件进行通信,
重要说明是这里的事件和原生DOM事件不同,这里的事件不具备冒泡机制,只监听子组件,不会触发平级或者更高级组件的监听事件,
组件v-model
另外一个是针对v-model修饰符,我们可以自定义组件的自定义修饰符,这里只是做个提醒,其实和vue2中只是defineprops的时候有区别,其他没有
透传Attributes
这里我印象中也是没有在vue2的新手指南文档中列出来过的,读完后感觉vue3默认了好多大家对vue2文档或者说使用的熟悉,闲话不多说,继续看透传
这里最基础的就是比如class和style 还有id等原生HTML属性,都是可以直接在组件中添加,然后透传到子组件的的元素上的,举例:
<!-- <MyButton> 的模板 这里是子组件 -->
<button class="btn">click me</button>
<!-- 这里是在父组件中使用子组件,并加上了class属性 -->
<MyButton class="large" />
<!-- 这个时候子组件最后的模板渲染DOM就是 button上两个属性 -->
<button class="btn large">click me</button>
这就是透传 因为我没有在子组件中定义props,但是可以直接传过来,这里举例子的是子组件只有一个html元素,而且例子中自由class,其实style,id也一样,透传还可以一层一层,如果这里的button已经是我封装的另外一个button,也一样透传,
那这就会遇到另外一个问题,我如果不想要他透传,这也是支持的,直接的一个普通的,额外的,不同于**< script setup >** 的 < script > 来写就可以
<script>
// 使用普通的 <script> 来声明选项
export default {
inheritAttrs: false
}
</script>
<script setup>
// ...setup 部分逻辑
</script>
ok上面的全部都是我们设想的,组件很少只有一个根节点元素,更多的是多节点
多根节点的Attribute继承
首先就是多个根节点的时候,我们肯定不能直接把class和style直接每个节点都传过去,不传那肯定也不行,对的,那就是vue文档中说的那样,那就是我们在父组件的透传attribute在子组件中,vue不知道它该把透传回来的属性给那个节点元素,这个时候vue会抛出错误的,所以透传的属性,只能通过 $attrs 来显示的绑定到节点上
<div class="btn-wrapper">
<button class="btn" v-bind="$attrs">click me</button>
</div>
<script setup>
import { useAttrs } from 'vue'
const attrs = useAttrs()
</script>
插槽内容和出口
组件插槽主要是可以子组件只负责整个组件的外层渲染,内部结构由父组件提供,插槽主要是可以使得组件的灵活性和可复用性得到提升,然后是匿名插槽和具名插槽的区别,这些都是vue2就有的,这里就不做多记录了,只是记录下具名插槽的使用就可以
依赖注入
prop逐级透传问题
为什么先谈这个问题,是因为这是依赖注入的使用场景,就是可以解决这个痛点 那什么是props逐级透传,就是我有ABC三个组件,关系是A是父组件B是子组件,C是B的子组件,也就是A的孙子组件了,加入C这里需要一个props是从A传入的,B组件本身是不关心这个props的,但是肯定是需要通过A传给B,B再传给C的嘛,那这里其实B的组件中是不需要的,但是代码需要接收并再传给C,这是层级关系比较简单的了,那如果是中间还有10层,这个关系就会非常冗余,这就是props的逐级透传,是我们应该避免的情况,这个时候,依赖注入就闪亮登场了
还是使用上面的例子来看,A相当于依赖提供者:provide,之后的话,无论层级上有多少个B这样的角色,在c 中,我们都可以通过注入来取得Aprovide出来的内容,
A组件中
provide(/* 注入名 */ 'message', /* 值 */ 'hello!')
</script>
另外一个点是值可以使用响应式,这样后代组件和提供者就建立了响应式的联系
import { ref, provide } from 'vue'
const count = ref(0)
provide('key', count)
之后就来到了我们需要注入取值的地方了,C组件中
<script setup>
import { inject } from 'vue'
const message = inject('message')
</script>
可能有人会奇怪,provide我可以理解,但是这里为啥叫注入(inject),因为这里的角度是不一样的,这里是在C组件中,对于C组件本身来说,这里取得的值就是被注入到了组件中,之后我就可以使用message这里的值了
至于后面的注入默认值,和响应式配合,就不再记录了
异步组件
首先是我们的组件在封装完成后,可能有一些是需要从服务器获取接口,甚至直接是从服务器加载相关组件,这里异步组件也不是啥新概念了,只是这里vue3的定义不同了,使用[defineAsyncComponent] 来实现
下面代码就是一个简单的定义并注册的异步组件
app.component('MyComponent', defineAsyncComponent(() =>
import('./components/MyComponent.vue')
))
这里还有一些选项是可以进行设置的,是一些处理的选项api,做了解,如果需要使用还需要查询详细的api文档。
const AsyncComp = defineAsyncComponent({
// 加载函数
loader: () => import('./Foo.vue'),
// 加载异步组件时使用的组件
loadingComponent: LoadingComponent,
// 展示加载组件前的延迟时间,默认为 200ms
delay: 200,
// 加载失败后展示的组件
errorComponent: ErrorComponent,
// 如果提供了一个 timeout 时间限制,并超时了
// 也会显示这里配置的报错组件,默认值是:Infinity
timeout: 3000
})
总结
今天的文档阅读中遇到的vue2和vue3的区别就是这些了,其实整体看下来,在组件模块中,在已经阅读完文档第一个大的基础模块后,这里感觉更多的是教你封装组件,区别或者感官上,还行