手摸手带你封装Vue组件库(4)在开发组件之前原本该节准备开始开发组件,但是我希望大家能够思考一下,现在大多数的文章可能
原本该节准备开始开发组件,但是我希望大家能够思考一下,现在大多数的文章可能只会教你写一个 button 然后结束,但是在你自己想做一个组件库的过程中你会发现很多的坑和注意点需要前期来考虑,那我带大家来思考一下我们前期需要准备些什么呢?
组件状态类型
我们在开发的时候经常用Element Plus
、Ant Design
以及其他组件库的过程中我们会发现有很多的组件都拥有状态,例如 button、message、alert、tag 等等,这些状态有代表其状态特有的颜色来表示,比如红色我们会当做报错、危险等状态,黄色代表警告之类的,一般我们会分为四种常用状态 info
、success
、warning
、error
,当然你也可以为你的组件库定义多种状态,我在这只是为了让大家理解。
组件使用(生成)方式
同样我们以我们常用的Element Plus
、Ant Design
组件库来说,我们认为的组件一般都是直接注册组件然后直接使用组件的名称来在 template 中使用,但是在我们使用这些组件库的时候我们会发现除了这种普通的组件,还有一些通过方法调用来生成组件的、通过指令来生成组件的:
- 普通组件 Button、Table、Input 等
- 通过函数生成组件 Message(消息)、MessageBox(消息弹出框)、Notification(通知)等
- 通过指令生成组件 loading(加载)等
组件的构思
大多数我们使用的组件都是在我们写入附近的 dom 层级生成,比如 button 组件,这是是最常见的。
但是有一些场景这种是会出现问题的,我也发现有些个人的组件库存在这种问题,我举个栗子 🌰,我们有一个 div,div 中存在一个一个 select
组件,我们 hover 在 select 组件上的时候他的 option 会显示出来,我们会想到我们的 select 组件在外层给一个 realtive
,然后选项的 option 绝对定位到下面,又或者有些人严谨一些,使用 position:fixed
,但是这种都存在一个问题,就是如果在我刚开始提到的 div 中我设置了一个 css 属性: overflow:hidden
,这两种都就寄了,我们总不能局限使用者的场景,所以我们必须想办法支持这种场景,我给你看一下 Element Plus
的处理你就知道了
大家是否发现,他的 select
的框是在 id 为 app 的里面渲染的,但是渲染的 option 部分的代码是在 #app
的外面,这样你的 select 所在位置再怎么折腾都不会影响我 option 的显示,麻烦的一点就是我们每次都需要计算我们的 option 位置,其实单论一个 select 组件,有特别多需要注意的地方,在开发的过程中我也会给大家提出,希望大家持续点赞关注(ps.嘤嘤嘤,全是白嫖收藏的,没人点赞没人关注 😭)
这种组件比较多,比如 Select、Cascader、ColorPicker、DatePicker 等,都是这种思路,所以我们可以将这种“外挂”的逻辑进行封装,到时候直接调用 utils 中的方法。
这个方法需要考虑一些问题,比如出现的位置、滚动的时候需要跟随滚动,还需要注意放的位置是否在可视区域,没在可视区域要计算是否能正常放置,放置不了需要改变位置之类的;有些组件也可以直接指定位置,比如 tooltips,我传入一个 placement 属性来决定我要现在是上方或者下方等等,这个在我们写的时候具体讨论实现,我说的不一定全,也不一定对,写的逻辑也可能存在 bug,大家大胆提,有建议直接说,或者贴代码,大家都是相互学习,我也比较菜,写文章一是为了记录,二是为了分享,三是为了相互学习,看大家有什么好的方法,以及指出自己的补足。
组件注册
我们在第二节中也说过,我们的 button 组件在外部写了一个 index.js
,然后抛出去一个携带 install
的对象,我们可以直接将组件抛出去,但是我们加了这个,就意味着我们在使用的是可以单独对某一个组件进行 use
,对此我们也可以将这些注册组件的逻辑封装为一个方法。
主题色相关
前面也提到过状态,状态这些基本一类状态颜色在任何拥有状态的组件中都可以使用,我们一般只定一个 primary 主题色,其他的为各个状态颜色的主题色,但是由于有些场景比如 button,文字是主题色,边框是主题色,但是背景色是比较浅的主题色,可以通过计算 rgba 添加透明色来实现,也可以通过十六进制计算,我比较推荐十六进制计算颜色,这时候我们需要计算一下通过主题色计算出由浅到深不同等级的主题色色阶。Element Plus 跟我们说的一样的这种由浅到深主题色。
我直接把方法放在这
// 处理主题样式
export const handleThemeStyle = (theme: string) => {
document.documentElement.style.setProperty("--themeColor", theme);
document.documentElement.style.setProperty("--el-color-primary", theme);
for (let i = 1; i <= 9; i++) {
document.documentElement.style.setProperty(
`--el-color-primary-light-${i}`,
`${getLightColor(theme, i / 10)}`
);
}
for (let i = 1; i <= 9; i++) {
document.documentElement.style.setProperty(
`--el-color-primary-dark-${i}`,
`${getDarkColor(theme, i / 10)}`
);
}
};
// hex颜色转rgb颜色
export const hexToRgb = (str: string): string[] => {
str = str.replace("#", "");
const hexs = str.match(/../g);
for (let i = 0; i < 3; i++) {
if (hexs) {
hexs[i] = String(parseInt(hexs[i], 16));
}
}
return hexs ? hexs : [];
};
// rgb颜色转Hex颜色
export const rgbToHex = (r: string, g: string, b: string) => {
const hexs = [Number(r).toString(16), Number(g).toString(16), Number(b).toString(16)];
for (let i = 0; i < 3; i++) {
if (hexs[i].length == 1) {
hexs[i] = `0${hexs[i]}`;
}
}
return `#${hexs.join("")}`;
};
// 变浅颜色值
export const getLightColor = (color: string, level: number) => {
const rgb = hexToRgb(color);
for (let i = 0; i < 3; i++) {
const s = (255 - Number(rgb[i])) * level + Number(rgb[i]);
rgb[i] = String(Math.floor(s));
}
return rgbToHex(rgb[0], rgb[1], rgb[2]);
};
// 变深颜色值
export const getDarkColor = (color: string, level: number) => {
const rgb = hexToRgb(color);
for (let i = 0; i < 3; i++) {
rgb[i] = String(Math.floor(Number(rgb[i]) * (1 - level)));
}
return rgbToHex(rgb[0], rgb[1], rgb[2]);
};
图标
图标在我们组件库中出现的也比较多,最能想到的就是 icon
组件,但是像我们一些 Menu、Select 等组件都会有一些箭头、感叹号之类的出现,有些组件我们也可以将这类组件的 icon 部分设置为插槽,让用户自定义,这些图标我建议使用 font
文件进行引入,这类图标我们可以在阿里巴巴矢量图 中查找并导出,如果有必要,我到时候将导出的操作流程给大家做以补充。
测试
一般来说开源的组件都会做测试,可以使用一些单元测试的框架,比如 Jest、Cypress 等等,因为相对来说比较麻烦,我也讲不了那么细,大家可以去看一些热门组件库的源码以及这些单元测试框架的使用,我自己没有使用过,我这个手摸手教程也不会给大家写之类的文章,感兴趣的可以自己去研究一下,我只是给大家提一下这点
目前在组件正式开发之前我就想到这么多,我觉得有必要让大家去思考,而不是直接开始写代码,希望大家指正,相互学习,本人也比较菜,不对的地方以及有更好的方法希望大家友善提出,或者可以一起讨论,有必要的话我们可以讨论,感兴趣的大家可以相互学习。希望您的关注和点赞,感谢 🤝
转载自:https://juejin.cn/post/7406999328769064969