最近在项目中遇到了一个相似Collapse的交互需求,所以到github上找了一圈关于Vue Collapse的相关轮子,可是多少都有些问题。有的是实现问题,例如vue2-collapse,伸缩部分采用max-height指定动画,存在缺陷;还有的是扩展性问题,遇到定制场景比较棘手。所以,决定本身撸一个Collapse组件。从项目中的一个需求,到目前已将它开源并发布到npm,仍是踩了许多坑的。代码虽然简单,可是过程却不太容易。所以这篇文章不是安利这款组件r-collapse-vue,仅仅是想记录一下整个开发生命周期,须要作什么,以及遇到什么问题。固然了,若是这个组件或是这篇文章对你有帮助,劳烦点进去给个star,万分感谢~javascript
咱们的整个开发流程,能够简单的总结以下:html
咱们来详细聊一聊每一个过程是如何实施的,且遇到了哪些问题。前端
脚手架咱们直接使用Vue CLI来搭建便可,其已经提供了丰富的功能,而且能够经过vue.config.js扩展webpack的能力。可是要注意的是,咱们的构建产物是一个模块,而不是咱们平时在项目中构建出一个应用。咱们但愿构建出来的模块是一个兼容CommonJs或是UMD,以便于使用者在不一样的环境中引用。所幸,Vue CLI3也给我提供了这样一个功能,详细可参考文档。vue
其次,本次开发我选择了TypeScript,脚手架默认集成了vue-property-decorator。使用以后直观的感觉就是,Vue的整个生态对TS的支持还不够完善,但总体仍是比较爽的,期待官方在3.0中可以完全支持TS。本文主题不是讨论TS,所以简单罗列下使用时遇到的问题:java
@Prop({ required: true }) public value!: String;
在平常写业务的时候,咱们可能会在组件当中耦合不少的业务逻辑。可是做为一个通用组件,咱们在开发的时候要尽量保证它的扩展性,所以咱们但愿达到的一个目标就是:在保证开发体验的前提下提升扩展性。对于Collapse组件,UI方面通常都是按照各自的设计稿来自行编写的,所以咱们只须要提供功能便可。更好的方式是提供默认的UI,但又能够支持彻底定制,这个是目前r-collapse-vue能够完善的一个点。 在进行功能设计的过程当中,咱们要先肯定咱们须要支持哪些功能,以r-collapse-vue举例,须要提供的功能包括:webpack
在实现的过程当中,咱们也须要思考不少细节,举几个例子:git
最简单的想法是传递一个相似叫作status的prop,在每个Collapse内部去维护这个状态。可是这样会有一个问题,咱们如何去支持手风琴模式,即一个展开另外的都须要收起。按照这种作法,须要用一个父组件包裹,去获取每个Collapse子组件的实例,调用实例方法去控制。这样作不是不行,vue2-collapse就是这么作的,可是我认为不够优雅。所以咱们从新整理思路,每个Collapse之间的状态可能会互相影响,咱们经常使用的解决方法是状态提高,所以个人作法是抽象两个组件,Collapse和CollapsePanel,Collapse便是父组件,提供状态控制,将状态传递给其内部嵌套的CollapsePanel,在内部消化掉全部的逻辑,这更加符合单向数据流的思想,站在使用者角度来看,写法也可以相对统一,使用时咱们只需这么写:github
<r-collapse v-model="activeKeys"> <r-collapse-panel name="a">xxxx</r-collapse-panel> <r-collapse-panel name="b">xxxx</r-collapse-panel> </r-collapse>
见上面的代码,咱们在CollapsePanel中传入了一个name属性做为惟一标识,此时使用者可结合activeKeys自行判断当前panel是否展开:web
<r-collapse v-model="activeKeys"> <r-collapse-panel name="a" :class="activeKeys.includes('a') ? 'active': ''" > xxxx </r-collapse-panel> <r-collapse-panel name="b">xxxx</r-collapse-panel> </r-collapse>
这种方法虽然能够,可是存在两个问题:vue-cli
所以,能够提供一个activeClass的prop,让使用者能够自定义展开状态的类名,就能够避免以上的问题。
这些细节问题看似简单,可是做为一个通用组件的开发者,咱们应该常常站在使用者的角度看问题,才能不断地提高组件的开发体验。
一个优秀的开源组件必定少不了单元测试,例如Ant Design等开源库都有着很高的单测覆盖率。一开始写单测可能会以为耗时、没有必要,但其实单测可以带来诸多的好处:
所以,单测必不可少,目前前端常见的选择包括:
Vue当中已经给咱们提供了单测相关的工具Vue Test Utils,它提供了不少功能,如组件挂载,获取实例等等,使用它配合Jest或者Mocha可以比较方便的完成单测,详情参考文档。
在编写单测时,咱们须要注意,对于UI组件来讲,不该一味追求行级覆盖率,应当只关注输入输出,避免涉及过多的实现细节,从而避免琐碎的测试。例如,咱们测试展开功能,只须要触发click,检测status是否为true便可,无需关注过程当中是触发了xxx事件仍是发生了其余事情,这样当咱们的逻辑修改后可以保证单测还能有效。同时,在用TS编写单测时,经过Vue Test Utils建立的wrapper是普通的Vue类型,所以自定义的Vue组件没法进行类型推导,此时要获取实例属性时须要经过(wrapper.vm as any).xxx来获取。经查阅资料,官方表示目前无法解决这个问题,只能使用这种方式。
一个好的文档可以方便使用者明白你的设计理念,所以咱们想要的文档不只须要有完整的API描述,而且在展现demo时可以同时展现源码,相似于在Ant Design或Element中那样。咱们这边使用的是Vue Styleguidist。
它经过vue-docgen-api,可以将注释转换成属性描述展示在页面上。所以咱们只须要写注释,就可以生成组件属性相关的文档。而咱们的另外一个需求,在展现demo时可以同时展现源码,它也可以作到。咱们能够经过两种方式:
咱们选用第二种方式,可是又遇到了许多坑。好比写入md的Vue代码不支持TS,试了不少的方法都没有解决,后来仍是改为了JS写法;还有SCSS使用嵌套时,嵌套的内容未被正确解析,后改为了CSS。其实这个东西的实现难度并不高,在md中写Vue无非就是写个webpack插件解析.md格式的文件,取出Vue的部分经过vue-loader处理,鉴于bug这么多且样式我认为不够美观,以后有时间能够再造个轮子玩一玩。
在Vue CLI3中使用Vue Styleguidist十分方便,只要运行:
vue add styleguidist
而后在package.json的scripts中添加:
"serve:doc": "vue-cli-service styleguidist", "build:doc": "vue-cli-service styleguidist:build"
就能够拆箱即用了。
文档编写完成,咱们执行yarn build:doc构建文档,发现输出的是一个html文件,此时咱们能够选择使用Github Pages来做为咱们的静态资源服务器展现文档,由于它方便部署且免费。过程以下:
这样每次更新docs会自动部署更新文档,相似于这样https://danceonbeat.github.io/r-collapse-vue/,惟一的缺点就是国内打开有点慢。
说完文档,咱们还须要编写在Github上展现的README,这里推荐一个生成README的库,readme-md-generator,格式很是简洁且美观。在README中,咱们能够添加以下的小图标:
这个可使用shields生成,它能关联你的NPM、Github等等,实时更新icon信息,有了它文档逼格瞬间高多了。
要将包发布到NPM,咱们须要作以下的准备工做:
npm login --registry=https://registry.npmjs.org
注意,这边加上registry为了防止在全局或当前环境覆写.npmrc,致使登陆的不是NPM源。
{ "name": "r-collapse-vue", "version": "1.0.0", "description": "a collapse component for VueJs", "author": { "name": "Ray", "email": "zhurui0904@gmail.com" }, "main": "dist/r-collapse-vue.common.js", "files": [ "dist" ], "keywords": [ "Vue", "collapse" ], "publishConfig": { "registry": "https://registry.npmjs.org" }, "repository": { "type": "git", "url": "git@github.com:DanceOnBeat/r-collapse-vue.git" } }
每次发布新版本以前,咱们能够经过
npm version major/minor/patch -m 'xxx'
来修改版本号而且打上tag,此tag非NPM的dist-tag,而是Git的tag。一个版本对应一个tag,并经过
git push origin master --tags
将tag也推到远程仓库,这样在仓库中咱们就能清楚地看到发布的记录,方便往后回滚之类的操做。具体的版本规则能够参考semver规范。
当开发结束后,咱们须要跑测试,测试经过后,还须要构建生成dist目录,最后发布到NPM。每次修改都作这样一套操做实在繁琐,而且容易遗漏步骤,这时候咱们就须要使用CI将咱们的流程自动化,我在这边选择了TravisCI。同时,咱们还能够经过Codecov,将咱们的单测报告上传至Codecov服务器,这样就能同步更新Codecov的icon。
在配置CI时,我本来将生成docs的步骤也添加了进去,此时咱们在deploy中会有两个步骤,以下:
deploy: - provider: npm email: zhurui0904@gmail.com api_key: $AUTH_TOKEN on: tags: true branch: master skip_cleanup: true - provider: pages skip_cleanup: true github_token: $GITHUB_TOKEN keep_history: true target_branch: master on: branch: master
这会形成一个问题是provider: pages会将CI服务器生成的新的docs目录push到咱们的Github仓库,这又会触发一次CI,以致于无限循环。后来也没找到合适的解决方案,又考虑到文档不常常更新,就将文档部署相关的部分从CI中移除了。若是你们有合适的解决方案,能够留言告诉我一下,不胜感激。
以前咱们提到一个NPM发布版本对应一个tag,所以咱们能够在配置中添加
if: tag IS present
限定只在提交了tag才触发一次自动化构建,这样基本上就大功告成了。
这是一次很是有趣的造轮体验,代码虽然不难,可是过程当中又学习到了不少新的东西,包括单元测试、文档编写等等,但愿这篇文章能给准备造轮或想要造轮的小伙伴提供一点帮助。