- 本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。
- 这是源码共读的第xx期,链接:juejin.cn/post/707971…
这是手把手仿element-plus组件的第三篇,今天来封装message组件。
还是分三部分,先讲解思路,然后看element-plus实现,最后再自己实现一版。
思路讲解
message组件,大家肯定都用过。
1.基本功能就是在视图窗口,动态创建一个组件,组件可以接收内容,图标等信息。
伪代码大概长这样。
<!-- 1.模板 -->
<div>
<icon name="success">
<slot>
{{message}}
</slot>
<icon name="close">
</div>
<!-- 2.动态创建 -->
h(compoennt, props, {
default: () => {}
})
2.考虑到可能有多个message,所以message的位置需要动态计算。
实现的思路就是我们把每个message组件存一下,根据个数计算一下展示的位置。如果有message组件销毁了,则需要通知其他message组件,重新计算位置。
伪代码大概长这样。
<!-- 1.存message组件 -->
let arr = []
arr.push(instance)
<!-- 2.计算位置 -->
computed(() => {
// 根据arr计算
})
3.销毁组件
既然有创建,那肯定就有销毁,这个肯定是成对出现的。
动态创建的组件如何销毁呢?
大概率还是使用render函数。
// dom为容器
render(null, dom)
2.elemment-plus源码阅读
先clone代码。
git clone https://github.com/element-plus/element-plus
cd element-plus
pnpm i
pnpm run docs:dev
1.参数处理
从截图可以看出,代码的入口就是method下的message方法。
message方法支持传入字符串,也支持传入对象,所以normalizeOptions方法的作用就是处理参数的。(这个暂时不管)
核心代码就是下面三行,跟我们分析的差不多,先创建组件,然后添加到数组中。
const instance = createMessage(normalized, context)
instances.push(instance)
return instance.handler
2.创建组件
createMessage函数。
在创建组件这,有一个自增的id,用来标记,跟查找组件。
container是容器,用于render函数挂载。
const props = {
...options,
// now the zIndex will be used inside the message.vue component instead of here.
// zIndex: nextIndex() + options.zIndex
id,
onClose: () => {
userOnClose?.()
closeMessage(instance)
},
// clean message element preventing mem leak
onDestroy: () => {
// since the element is destroy, then the VNode should be collected by GC as well
// we do not want cause any mem leak because we have returned vm as a reference to users
// so that we manually set it to false.
render(null, container)
},
}
props参数,加了id,onClose跟onDestroy方法。
这里的代码,跟分析的也差别不大。onDestroy里面的render是销毁组件,closeMessage是删除数组中对应的元素,重新计算位置。
然后就是渲染挂载组件的逻辑了。(如果message的值是函数,或者虚拟节点,则会当成默认插槽传入)
3.组件模板
看一下MessageConstructor这个组件。
先看一下template结构。
vue内置组件,transition添加动画,根据leave事件销毁组件。
有插槽则展示插槽,没有的话,则显示p标签。
组件位置的计算,也跟预想差不多,根据组件列表进行计算。
3.手写组件
手写逻辑跟源码几乎一样,去掉了一些非核心的判断。
代码也比较多,文章中就不贴了,可自行下载查看。(已实现核心功能,后续会完善功能以及样式)
git clone https://github.com/bubuui/bubu-ui
cd bubu-ui
npm i
npm run docs:dev
说几个知识点。
在message函数中,是先创建了组件,然后再push到全局instances中。
组件第一次计算值,是获取不到的,所以要给instances设置响应式-shallowReactive。
我们只关心组件个数,所以使用shallowReactive而不是reactive。
组件中的变量可以通过defineExpose暴露出来,以便在动态创建时修改。
const vm = vnode.component!;
const handler = {
close: () => {
vm.exposed!.visible.value = false;
},
};
3.
组件中用到了很多hook,均来自于vueuse三方库。
感兴趣的可自行查阅,后续也会出相关hook源码阅读文章。
3.总结一下
message组件看起来简单,但是想要实现一个完善的组件,还是有很多细节的。
包含ts内容,组件功能实现,代码组织,样式架构...。
如果看完有收获,欢迎点赞、评论、分享支持。你的支持和肯定,是我写作的动力