完结篇!一步一步实现一个专业的前端组件库~

11,058 阅读11分钟

组件库完结篇,附带线上演示!捣鼓了半个月,终于像点样子了。从架构设计、组件开发到文档建设,整个组件库的0-1过程笔者通过文章的方式进行记录、分享,有兴趣的朋友赶紧动起手来,搞一个属于自己的组件库吧~

别看历时比较久,不是all in的。而且笔者在不断的踩坑浪费了比较多时间~其实快速完成一个基础版本的组件库还是比较轻松的,耗时久的更多是在一些样式隔离、开发提效上面。这篇,笔者会接着前两篇文章,对最后步骤的开发进行记录和分享,顺便跟大家一起瞄一瞄 element-plus 的源码。

我知道大家喜欢看得见,摸得着的!!!不啰嗦,戳组件库在线演示地址。这里笔者屏蔽掉了一些公司的内容,所以可能会跟笔者写过的文章中的内容、截图有点对应不上~大家见谅见谅

额,组件库是发布在 netlify 上的~用脚手架的形式,具体的可以参考一下 vite的文档 的介绍。目测了一下国内的访问速度还行,如果实在是卡、打不开的话,可以用vpn访问试试~

源码了解

往期回顾

本文完全建立在之前笔者已经发布的两篇文章的基础上编写。主要是记录笔者从0-1研发一个组件库的核心过程,算是做成一个系列的笔记进行分享和记录吧。这里还是想感谢一下开源项目: element-plus ,给到的帮助实在是太多了。笔者的组件库基本上都是以其为教科书进行展开的,其中很多源码实现给了笔者很多启发,特别是在 demo引入简化 这些在插件、工程化上的实现。

这里贴出该组件库的往期文章:

  1. 组件库架构:快上车!从零开始搭建一个属于自己的组件库!
  2. 组件库文档踩坑记:组件库——如何实现一个跨框架的组件库文档?

而本文的主题,主要围绕三点

  1. 组件开发。最终确定组件库的组件开发流程,包括本地开发调试、文档调试。
  2. 样式隔离。使用 iframe 做沙箱,实现跨框架的组件库文档。
  3. demo组件引入简化。实现插件读取源码文件,完成文档中的demo组件引入和源码导入。

至此,一个完善的组件库就差不多完工了,本文也是组件库系列的最后一篇啦。剩下的还有些比如:版本号的管理问题;一些自动化工具的使用;组件的单元测试等等吧就不再以文章形式进行记录和分享了。然后呢,笔者自己的想法就是等组件库中沉淀的组件到一定量的时候就考虑开源吧,看看能不能帮助到更多的朋友实现早点下班的梦想哈哈~

一、组件开发流程

整体架构、思路基本都是:快上车!从零开始搭建一个属于自己的组件库!这篇文章所介绍的内容。这里不会太啰嗦,只提下跟第一篇不同的地方。

1. dev server

组件开发时各自的 dev server 关系图如下:

组件库dev.png

其中,在第一篇文章中,笔者对 element-plus 的组件开发调试是放在 docs项目 中的,因为 vitepress 天然支持 vue3 。但是,为了沙箱机制隔离各框架之间的特性,最终笔者决定把 element-plus 的组件开发也放在当前项目中启动 dev server 完成,再通过 iframe 的形式在文档中展示。

如上图所示,不同的 ui框架 启动的 dev server 端口号不同。在文档项目的开发环境中,即是通过不同的端口号在 iframe 中导入用不同的 ui框架 组件库,所以如果要正确预览组件,也需要把对应项目的 dev server 给启动起来。

  • 3333: element-plus
  • 3633: element-ui
  • 3933: ant-design

上述端口号区分,通过 .env 文件配合 vite环境变量 进行区分处理。dev 和 prod 的 .env 文件配置如下:

  1. dev: image.png

  2. prod(prod为何是如下配置,后文会提到): image.png

2. 组件的项目结构

项目结构最终形式如下:

image.png

如图所示:

  1. 蓝色 的为 组件demo
    • 开发调试组件。最后需要打包进文档项目中,供文档中预览、在线演示
    • 维护自己的路由。一个 demo 对应一个 路由,文档中通过 iframe 加载对应的 组件demo 以在线演示
  2. 红色 的为 组件
    • 纯组件。供用户在业务项目中安装、导入、使用
    • 仅打包组件本身代码。externals 所有第三方包

最终会通过打包命令的 mode 区分两种不同的打包配置:

  • 蓝色demo组件 :打包成正常的单页应用,入口是 index.html
  • 红色组件 :打包成 lib 形式,入口是 /components/index.(js/ts)

最后提一下,为了方便,笔者把各组件库的 demo单页面应用 打包进 docs 项目的 .vitepress/dist 目录中,以各自的 库名 为文件夹名输出打包后产物(这里也就是上述环境变量中 prod 的配置原理)。这样发包上线时,只需要把整个 docs项目 中的 dist包 丢上去cdn仓库就可以了。

文档打包后的产物dist的结构大致如下:

dist/
    element-plus/
        ...
    element-ui/
        ...
    ant-design/
        ...
    assets/
        ...
    css/
        ...
    ...    

二、样式隔离

这个可以说是踩了最多坑的地方了。一开始头铁,非想着使用 web component 的 shadow dom 做沙箱实现样式隔离,却陷进了一个死循环里,最终笔者也是放弃了。果然,年轻还是头铁......

具体的踩坑日记在第二篇文章中详细展开了,感兴趣的可以戳:组件库——如何实现一个跨框架的组件库文档?

最终,笔者采用了比较稳妥又省力的方案—— iframe 。说实话,其实 iframe 算是一开始的方案之一了,为什么一开始没选择这个方案呢?一方面是笔者带着对 web componet 的未知,想学习、想尝鲜、搞突破;一方面是对自己过分自信和头铁,觉得直接用 iframe 搞个跨框架的组件库文档缺乏挑战......

好吧,不管怎么说吧,头铁完了还是得解决问题。借着这次踩坑的实践,也算是了解了一些关于 web componet 的知识,强行不亏吧。

1. 用 iframe 做沙箱

结合上文 dev server 的内容这里就很好理解了。如下图所示,蓝色框圈起的块其实就是一个 iframe 。为了美观一点,笔者进行了点样式美化,抄袭了一波 mac 的菜单栏风格~这也算是做前端最后的倔强了!

image.png

打开 f12 查看一哈,iframe 的地址就如笔者前面所描述的那样, dev 环境中通过端口号区分不同的ui框架(线上则通过 public path 区分)。如此一来,有了天然的沙箱机制,实现一个跨框架的组件库文档也不在话下了。 image.png

使用 iframe 是不是就真的完美?笔者感觉还是稍有不足吧~其中高度设置就是问题。

目前,笔者通过给 iframe 组件传入高度来控制高度,在这种有内容撑起来的组件看起来还挺和谐,但是一些弹窗组件就多少有点蛋疼。说得可能不够直白,那就直接上图大家感受下吧~

  1. 图一 image.png

  2. 图二 image.png

  3. 图三

image.png

这样一看,如果设置高度太多,弹窗隐藏的时候空白区域很多,显得很突兀;如果高度设置得小一点,弹窗出现的时候又显得很难受~emmm,也是暂时没想到很好的办法,所以我提供了一个全屏预览的功能,希望可以弥补一下弹窗组件的展示缺陷吧。要是你们有什么好的方案想法,赶紧来评论区指点指点哈哈哈~笔者来套方案!

三、文档简化

文档简化,最好的理解就是在文档中不需要过度写代码。这话怎么理解?让我们先看看 element-plus 的文档是怎么样写的。如下图: image.png

接着,我们再对比下其官方文档: image.png

可以发现一点就是,他的 demo组件 引入只是在文档中保留了一个路径,最终就能渲染成一个组件块。这就是笔者所说的文档简化。在编写组件库文档中,开发者不需要手动管理 demo组件 的在线演示、组件源代码等。

这块的源码实现其是基于 markdown-it-container 实现的。从简单的说,大家可以这么理解,其自行写了一个组件容器,里面实现了整个demo组件引入的逻辑。组件中包括:

  1. demo组件 展示区域
  2. 操作按钮。如复制代码、展开源代码、在线调试跳转等
  3. 源代码区域

整个组件的代码实现不难,大家感兴趣可以自己去看看 image.png

然后他的源码导入大致流程是 读取文件 -> prismjs实现代码高亮 -> encode源码 -> decode源码 。这里需要注意的是,encode 这一笔很重要。笔者一开实现的时候没有用 encode + decode 处理,直接使用 prismjs 的处理后结果,换行那些会有问题,所以大家要做这一步的话,可以用这个办法处理。核心源码截图: image.png

如图上的黄色字,其实 demo组件 简化引入的核心实现即是如此,感兴趣的同学可以自己去读一下源码。(截图中的 Demo 即上文中的 vp-demo 组件)

笔者大致是基于这样的思路,自己实现了一个 vue3组件 + vite插件 来达到 demo组件 引入简化这么一个效果。没有直接使用 element-plusmarkdown-it-container 的方案,因为笔者需要给组件传入高度的props,所以直接在文档中使用组件,这样后续需要传入一些其他属性也很方便。

如图所示,笔者在文档中直接用一个 <vp-demo> 组件,可以随意传入 propsimage.png

大家可能注意到了,其中有一个比较特殊的 props

source-code="element-plus:::table/table-custom-columns"

没错,笔者就是通过正则匹配这个 source-code 的属性,然后切割特殊标识符 ::: 获取 组件库项目demo组件的文件路径,然后进行文件读取、代码高亮、encode处理。所以,最终运行时传入 vp-demoprops 是已经处理好的可在 html 中展示的代码。(笔者也是使用了 prismjs 去做代码高亮)

而这一切,都是通过写一个 vite 插件,在 transform 钩子中去实现的,感兴趣的话可以看下 vite插件机制

源代码的最终效果就如大家在 组件库在线演示 中看到的那样。 image.png

写在最后

终于,从0-1建设一个组件库算是完工了,不敢说专业,只能说往专业上靠近了一点点~大家不要喷我标题党!接下来就是抽空往里面开发组件了,看看大家有没有什么组件开发的建议、需求,都可以在评论区说,笔者也正愁组件库目前空空如也~组件库还有一些版本、发布的细节,组件的单测还没搞,笔者会抽空完善~组件库系列文章就到这里结束啦~还是那句,自己或者团队里还没有搞组件库的,可以赶紧搭一个,把常用的组件沉淀下来,一定可以早点下班~ peace

系列文章:

  1. 快上车!从零开始搭建一个属于自己的组件库!
  2. 组件库建设——实现一个跨框架的「组件库文档」
  3. 完结篇!一步一步实现一个专业的前端组件库~