antd函数组件_如何打造⼀套vue组件库
开篇
组件库能帮我们节省开发精⼒,⽆需所有东西都从头开始去做,通过⼀个个⼩组件拼接起来,就得到了我们想要的最终页⾯。在⽇常开发中如果没有特定的⼀些业务需求,使⽤组件库进⾏开发⽆疑是更便捷⾼效,⽽且质量也相对更⾼的⽅案。
⽬前的开源组件库有很多,不管是react还是vue的体系⾥都有很多⾮常优秀的组件库,⽐如我经常使⽤的就有elementui和iview。当然也还有其他的⼀些组件库,他们的本质其实都是为了节省重复造基础组件这⼀轮⼦的过程。也有的公司可能会对⾃⼰公司的产品有特别的需求,不太愿意使⽤开源的组件库的样式,或者⾃⼰有⼀些公司内部的业务项⽬需要⽤到,但开源项⽬⽆法满⾜的组件需要沉淀下来的时候,⾃建⼀套组件库就成为了⼀个作为业务驱动所需要的项⽬。
本⽂会从 ”准备“ 和 ”实践“ 两个阶段来阐述,⼀步步完成⼀个组件库的打造。⼤致内容如下:
准备:主要讲了搭建组件库之前我们需要先提及⼀下⼀些基础知识,为实践阶段做铺垫。
1. 准备
实践:有了⼀些基本概念,咱们就直接通过⼀个实践案例来动⼿搭建⼀套基础的组件库。从做的过程中去感受组件库的设计。
2. 实践
希望通过本⽂的分享以及包含的⼀个简单的 实际操作案例
实际操作案例,能让你从组件库使⽤者的⾓⾊向组件库创造者的⾓⾊迈进那么⼀⼩步,在⽇常使⽤组件库的时候⼼⾥有个底,那我的⽬的也就达到了。
对应的 repo也就是:arronKler/lime-ui
准备:打造组件库之前你应该知道些什么?
这⼀个章节主要是想先解析清楚⼀些在组件库的建⽴中会⽤到的⼀些平时在业务概念中很少去关注的概念。我会分为⼯程和组件两个⽅⾯来阐述,把我所知道的⼀些其中的技巧和坑点都交付出来,以帮助我们在实际去做的过程中可以有所准备。
项⽬:做⼀个组件库项⽬有哪些额外需要考虑的事?
做组件库项⽬和常规业务项⽬肯定还是有⼀些事情是我们业务项⽬不怎么需要,但是类库项⽬⼀般都会考虑的事,这⼀⼩节就是介绍说明⼀下,那些我们在做组件库的过程中需要额外考虑的事。
组件测试
很多开发者平时业务项⽬都⽐较赶,然后就是⼀般业务项⽬中都不怎么写测试脚本。但在做⼀个组件库项⽬的过程中,最好还是有对应的组件测试的脚本。⾄少有两点好处:
1. ⾃动化测试你写的组件的功能特性
2. 改动代码不⽤担⼼会影响之前的使⽤者。(测试脚本会告诉你有没有出现未预料到的影响)
对于类库型项⽬,我觉得第⼆点好处还是很重要的,这才能保证你在不断推进项⽬升级迭代的过程中,确保不会出现影响已经在⽤你所创造的类库的那些⼈,毕竟你要是升级⼀次让他的项⽬出现⼤问题,那可真保不准别⼈饭碗都能丢。(就像之前的antd的圣诞节雪花事件⼀样)我们这⾥主要想提的是 组件测试到底要测什么?
组件测试到底要测什么?
我们这⾥给到⼀张很直观的图,看到这张图其实你应该也清楚了这个问题的答案
所以回过头来,组件测试,实际需要我们不仅仅作为创造者的⾓度对组件的功能特性进⾏测试。更要从使⽤者的⾓度来看,把组件当做⼀个“⿊盒⼦”,我们能给到它的是⽤户的交互⾏为、props数据等,这个“⿊盒⼦”也会对应的反馈出⼀定的事件和渲染的视图可以被使⽤者所捕获和观察。通过对这些位置的检查,我们就能获知⼀个组件的⾏为是否如我们所愿的去进⾏着,确保它的⾏为⼀定是⼀致不出⼳蛾⼦的。
契约精神。作为组件的使⽤者,我使⽤你的组件,等于咱们签订⼀个契约,这个组件的所有⾏为应该是和另外还想提的⼀点偏的话题就是 契约精神
你描述的是⼀致的,不会出现第三种意料之外的可能。毕竟对于企业项⽬来说,我们不喜欢surprise。antd的彩蛋事件也是给各位都提个醒,咱们搞技术可以这么玩也挺有创意,但是这种公⽤类库,特别是企业使⽤的也⽐较多的,还是把创意收⼀收,讲究契约,不讲surprise。就算是⾃家企业内部使⽤的组件库,除⾮是业务上的⼈都是认可的,否则也不要做这种危险试探。
好的组件测试也是能够帮助我们识别出那些我们有意或⽆意创造的surprise,有意的咱就不说了,就怕是那种⽆意中出现的surprise那就⽐较要命了,所以写好组件测试还是挺有必要的。
⽂档⽣成
vuepress是⼀个⽂档⽣成⼯具,默认的样式和vue官⽅⽂档⼏乎是⼀致的,因为创造它的初衷就是想为vue和相关的⼦项⽬提供⽂档⽀持。它内置了 Markdown的扩展,写⽂档的时候就是⽤ markdown来写,最让⼈省⼼的是你可以直接在 Markdown ⽂件中使⽤Vue组件,意味着我们的组件库中写的⼀个个组件,可以直接放到⽂档⾥去⽤,展⽰组件的实际运⾏效果。 我们的案例⽹站也就是通过vuepress来写的,⽣成静态⽹站后,⽤ gh-pages 直接部署到github上。
vuepress更好的⼀点在于你可以⾃定义其webpack配置和主题,意味着你可以让你⾃⼰的⽂档站点在开发阶段有更多的功能特性的⽀持,同时可以把站点风格改成⾃⼰的⼀套主题风格。这就⽆需我们重头开始去做⼀套了,对于咱们想要快速完成组件库⽂档建设这⼀需求来说,还是挺有效的。
不过这只是咱们要做的事情的⼀个辅助性的东西,所以具体的使⽤咱们在实践阶段再说明,这⾥就不赘述了。
⾃定义主题
⾃定义主题的功能对于⼀个开源类库来说肯定还是挺有好处的,这样使⽤者就可以⾃⼰使⽤组件库的功能⽽在界⾯设计上使⽤⾃⼰的设计风格。其实⼤部分组件库的功能设计都是挺好挺完善的,所以⼀般来说中⼩型公司即使想要实现⾃⼰的⼀套组件风格的东西,直接使⽤开源类库如 element、iview或者基于react的Antd 所提供的功能和交互逻辑,然后在其上进⾏主题定制基本就满⾜需求了(除⾮你家设计师很有想法。。。)。
⾃定义主题的功能⼀般的使⽤⽅式是这样的
1. 通过主题⽣成⼯具。(制作者需要单独做⼀个⼯具)
2. 引⼊关键主题⽂件,覆盖主题变量。(这种⽅式⼀般都需要适配制作者所使⽤的css预处理器)
对于第⼀种⽅式往往都是组件库的制作者通过把⽣成组件样式的那⼀套东西做成⼀个⼯具,然后提供给使⽤者去根据⾃⼰的需要来调整,最后⽣成⼀套特定的样式⽂件,引⼊使⽤。
作为使⽤者来说,你主要做的其实是覆盖了组件库中的⼀些主题变量,因为具体的组件的样式⽂件不是写死的固定样式
第⼆种⽅式,作为使⽤者来说,你主要做的其实是覆盖了组件库中的⼀些主题变量
值,⽽是使⽤了定义好的变量,所以你的⾃定义主题就⽣效了。但是这也会引⼊⼀个⼩问题就是你必须适配组件库的创造者所使⽤的样式预处理器,⽐如你⽤iview,那你的项⽬就要能解析Less⽂件,你⽤ElementUI,你的项⽬就必须可以解析SCSS。
其实对于第⼀种⽅式也主要是以调整主题变量为主。所以当咱们⾃⼰要做⼀套组件库的时候,不难看出,⼀个核⼼点就是需要把主题变量
把主题变量⽂件和样式⽂件拆开来,后⾯的就简单了。
⽂件和样式⽂件拆开来
webpack打包
类库项⽬的构建这⾥提两点:
1. 暴露⼊⼝
2. 外部化依赖
先谈第⼀点 “暴露接⼝”。业务项⽬中,我们的整个项⽬通过webpack或其他打包⼯具打包成⼀个或多个bundle⽂件,这些⽂件被浏览器载⼊后就会直接运⾏。但是⼀个类库项⽬往往都不是单独运⾏的,⽽是通过暴露⼀个 “⼊⼝”,然我在业务项⽬中去调⽤它。 在webpack 配置⽂件⾥,可以通过定义 output 中的 library 和 libraryTarget 来控制我们要暴露的⼀个 “⼊⼝变量” ,以及我们要构建的⽬标代码。
// other config
output: {
library: "MyLibName",
libraryTarget: "umd",
umdNamedDefine: true
}
}
再说⼀下 “外部化依赖”,我们做⼀个vue组件库项⽬的时候,我们的组件都是依赖于vue的,当我们组件库项⽬中的某个地⽅引⼊了vue,那么打包的时候vue的运⾏时也是会被⼀块⼉打包进⼊最终的组件库bundle⽂件的。这样的问题在于,我们的vue组件库是被vue项⽬使⽤的,那么项⽬中已经有运⾏时了,我们就没必要在组件库中加⼊运⾏时,这样会多增加组件库bundle的体积。使⽤webpack的externals可以将vue依赖 "外部化"。
// other config
externals: {
vue: {
root: 'Vue',
commonjs: 'vue',
commonjs2: 'vue',
amd: 'vue'
}
}
}
按需加载
组件库的按需加载功能还是很实⽤的, 这样可以避免我们在使⽤组件库的过程中把所有的⽤到和没⽤到的内容都打包到业务代码中去,导致最后的bundle⽂件过⼤影响⽤户体验。
在业务项⽬中我们的按需加载都是把需要按需加载的地⽅单独⽣成为⼀个chunk,然后浏览器运⾏我们的打包代码的时候发现我们需要这⼀块⼉资源了,再发起请求获取到对应的所需代码。
在组件库⾥边,我们就需要改变⼀下引⼊的⽅式,⽐如⼀开始我们引⼊⼀个组件库的时候是直接将组件库和样式全部引⼊的。如下⾯这样
import LimeUI from 'lime-ui' // 引⼊组件库
import 'lime-ui/styles/index.css' // 引⼊整个组件库的样式⽂件
Vue.use(LimeUI)
那么,换成⼿动的按需加载的⽅式就是
import { Button } from 'lime-ui' // 引⼊button组件
import 'lime-ui/styles/button.css' // 引⼊button的样式
Vueponent('l-button', Button) // 注册组件
这种⽅式的确是按需引⼊的,但也⼀个不舒服的地⽅就是每次我们引⼊的时候都需要⼿动的引⼊组件和样式。⼀般来说⼀个项⽬⾥⾯⽤到的组件少说也有⼗多个,这就⽐较⿇烦了。组件库是怎么解决这个问题的呢?
通过babel插件的⽅式,将引⼊组件库和组件样式的模式⾃动化,⽐如antd、antd-mobile、material-ui都在使⽤的babel-plugin-import、还有ElementUI使⽤的 babel-plugin-component。在业务项⽬中配置好babel插件之后,它内部就可以给你做⼀个这样的转换(这⾥以babel-plugin-component)
// 原始代码
import { Button } from 'components'
// 转换代码
var button = require('components/lib/button')
require('components/lib/button/style.css')
OK,那既然代码可以做这样的转换的话,其实我们所要做的⼀点就是在我们打造组件库的时候,把我们的组件库的打包代码放到对应的⽂件⽬录结构之下就可以了。使⽤者可以选择⼿动载⼊组件,也可以使⽤babel插件的⽅式优化这⼀步骤。
组件:⽐起⽇常的组件设计,做组件库你还需要知道些什么?
做组件库中的组件的技巧和在项⽬中⽤到的还是有⼀些区别的,这⼀⼩节就是告诉⼤家,组件库中的组件设计,我们还应该知道哪些必要的知识内容。
组件通信:除了上下级之间进⾏数据通信,还有什么?
我们常规⽤到的组件通信的⽅法就是通过 props 和 $emit 来进⾏⽗组件和⼦组件之间的数据传递,如
下⾯的⽰意图中展⽰的那样:⽗组件通过 props 将数据给⼦组件、⼦组件通过 $emit 将数据传递给⽗组件,顶多通过eventBus或Vuex来达到任意组件之间数据的相互通信。这些
如何处理跨级组件之⽅法在常规的业务开发过程中是⽐较有效的,但是在组件库的开发过程中就显得有点⼒不从⼼了,主要的问题在于: 如何处理跨级组件之间的数据通信呢?
如果在⽇常项⽬中,我们当然可以使⽤像 vuex 这样的将组件数据直接 ”外包“ 出去的⽅式来实现数据的跨级访问,但是vuex 始终是⼀个外部依赖项,组件库的设计肯定是不能让这种强依赖存在的。下⾯我们就来说说两个在组件库项⽬中我们会⽤到的数据通信⽅式。
内置的provide/inject
provide/inject 是vue⾃带的可以跨级从⼦组件中获取⽗级组件数据的⼀套⽅案。 这⼀对东西类似于react⾥⾯的 Context ,都是为provide/inject 是vue⾃带的可以跨级从⼦组件中获取⽗级组件数据的⼀套⽅案。
了处理跨级组件数据传递的问题。
使⽤的时候,在⼦组件中的 inject 处声明需要注⼊的数据,然后在⽗级组件中的某个含有对应数据的地⽅,提供⼦级组件所需要的数据。不管他们之间跨越了多少个组件,⼦级组件都能获取到对应的数据。(参考下⾯的伪代码例⼦)
// 引⽤关系 CompA --> CompB --> CompC --> ... --> ChildComp
// CompA.vue
export default {
provide: {
theme: 'dark'
}
}
// CompB.vue
// CompC.vue
// ...
// ChildComp.vue
export default {
inject: ['theme'],
mounted() {
console.log(this.theme) // 打印结果: dark
}
}
不过provide/inject的⽅式主要是⼦组件从⽗级组件中跨级获取到它的状态,却不能完美的解决以下问题:
1. ⼦级组件跨级传递数据到⽗级组件
2. ⽗级组件跨级传递数据到⼦级组件
派发和⼴播: ⾃制dispatch和broadcast功能
dispatch和broadcast可以⽤来做⽗⼦级组件之间跨级通信。在vue1.x⾥⾯是有dispatch和broadcast功能的,不过在vue2.x中被取dispatch和broadcast可以⽤来做⽗⼦级组件之间跨级通信
消掉了。这⾥可以参考⼀下下⾯链接给出的v1.x中的内容。
dispatch⽂档(v1.x): /api/# vm-dispatch
broadcast⽂档(v1.x): /api/# vm-broadcast
根据⽂档,我们得知
react组件之间通信dispatch会派发⼀个事件,这个事件⾸先在⾃⼰这个组件实例上去触发,然后会沿着⽗级链⼀级⼀级的往上冒泡,直到触发了某个⽗级中声明的对这个事件的后就停⽌,除⾮是这个返回了true。当然也是可以通过回调函数获取到事件派发的时候传递的所有参数的。这⼀点很像我们在DOM中的事件冒泡机制,应该不难理解。
⽽broadcast就是会将事件⼴播到⾃⼰的所有⼦组件实例上,⼀层⼀层的往下⾛,因为组件树的原因,往下⾛的过程会遇到 “分叉”,也就可以看成是⼀条条的多个路径。事件沿着每⼀个⼦路径向下冒泡,每个路径上触发了就停⽌,如果返回的是true那就继续向下再传播。
dispatch派发事件往上冒泡,broadcast⼴播事件往下散播,遇到处理对应事件的就处理,没有返回简单总结⼀下。dispatch派发事件往上冒泡,broadcast⼴播事件往下散播,遇到处理对应事件的就处理,没有返回true就停⽌
跨层级进⾏数据通信。
需要注意的是,这⾥的派发和⼴播事件都是 跨层级的
跨层级的 , ⽽且可以携带参数,那也就意味着可以跨层级进⾏数据通信

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。