从0到1发布一个Vue Collapse组件

需求背景

最近在项目中遇到了一个类似Collapse的交互需求,因此到github上找了一圈关于Vue Collapse的相关轮子,但是多少都有些问题。有的是实现问题,例如vue2-collapse,伸缩部分采用max-height指定动画,存在缺陷;还有的是扩展性问题,遇到定制场景比较棘手。因此,决定自己撸一个Collapse组件。从项目中的一个需求,到目前已将它开源并发布到npm,还是踩了许多坑的。代码虽然简单,但是过程却不太容易。因此这篇文章不是安利这款组件r-collapse-vue,仅仅是想记录一下整个开发生命周期,需要做什么,以及遇到什么问题。当然了,如果这个组件或是这篇文章对你有帮助,劳烦点进去给个star,万分感谢~

开发流程

我们的整个开发流程,可以简单的总结如下:

  1. 项目脚手架搭建(Vue CLI3)
  2. 组件功能开发
  3. 单元测试(Vue Test Utils + Jest)
  4. 文档编写(Vue Styleguidist + Github Pages)
  5. 发布NPM
  6. 持续集成配置(TravisCI)

我们来详细聊一聊每个过程是如何实施的,且遇到了哪些问题。

脚手架搭建

脚手架我们直接使用Vue CLI来搭建即可,其已经提供了丰富的功能,并且可以通过vue.config.js扩展webpack的能力。但是要注意的是,我们的构建产物是一个模块,而不是我们平时在项目中构建出一个应用。我们希望构建出来的模块是一个兼容CommonJs或是UMD,以便于使用者在不同的环境中引用。所幸,Vue CLI3也给我提供了这样一个功能,详细可参考文档

其次,本次开发我选择了TypeScript,脚手架默认集成了vue-property-decorator。使用之后直观的感受就是,Vue的整个生态对TS的支持还不够完善,但整体还是比较爽的,期待官方在3.0中能够彻底支持TS。本文主题不是讨论TS,因此简单罗列下使用时遇到的问题:

  • 在template中无法做到智能提示,需要智能提示只能使用tsx,这一点是比较痛苦的
  • 定义Prop时需要加非空断言(!:),否则会报错,例如:
@Prop({ required: true })
public value!: String;
  • 使用Vue Test Utils写单测时,无法对自定义的Vue组件进行类型推导,见下文
  • 使用Vue Styleguidist编写文档demo不支持TS,见下文

组件功能开发

在日常写业务的时候,我们可能会在组件当中耦合很多的业务逻辑。但是作为一个通用组件,我们在开发的时候要尽可能保证它的扩展性,因此我们希望达到的一个目标就是:在保证开发体验的前提下提高扩展性。对于Collapse组件,UI方面一般都是按照各自的设计稿来自行编写的,因此我们只需要提供功能即可。更好的方式是提供默认的UI,但又可以支持完全定制,这个是目前r-collapse-vue可以完善的一个点。
在进行功能设计的过程中,我们要先确定我们需要支持哪些功能,以r-collapse-vue举例,需要提供的功能包括:

  • 基本的展开/收缩(支持动画)
  • 手风琴模式
  • 自定义点击事件
  • Collapse嵌套

在实现的过程中,我们也需要思考很多细节,举几个例子:

  1. 使用者如何控制每一个Collapse的状态?

最简单的想法是传递一个类似叫做status的prop,在每一个Collapse内部去维护这个状态。但是这样会有一个问题,我们如何去支持手风琴模式,即一个展开另外的都需要收起。按照这种做法,需要用一个父组件包裹,去获取每一个Collapse子组件的实例,调用实例方法去控制。这样做不是不行,vue2-collapse就是这么做的,但是我认为不够优雅。因此我们重新整理思路,每一个Collapse之间的状态可能会互相影响,我们常用的解决方法是状态提升,因此我的做法是抽象两个组件,Collapse和CollapsePanel,Collapse即是父组件,提供状态控制,将状态传递给其内部嵌套的CollapsePanel,在内部消化掉所有的逻辑,这更加符合单向数据流的思想,站在使用者角度来看,写法也能够相对统一,使用时我们只需这么写:

<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>
  1. 实际场景中经常会对展开和收缩进行样式区分,如何帮助使用者提升开发体验?

见上面的代码,我们在CollapsePanel中传入了一个name属性作为唯一标识,此时使用者可结合activeKeys自行判断当前panel是否展开:

<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>

这种方法虽然可以,但是存在两个问题:

  • 用户需要自行添加逻辑,体验不够友好
  • 每次重新渲染都会执行额外的逻辑判断,性能不够友好

因此,可以提供一个activeClass的prop,让使用者可以自定义展开状态的类名,就可以避免以上的问题。

这些细节问题看似简单,但是作为一个通用组件的开发者,我们应该经常站在使用者的角度看问题,才能不断地提升组件的开发体验。

单元测试

一个优秀的开源组件一定少不了单元测试,例如Ant Design等开源库都有着很高的单测覆盖率。一开始写单测可能会觉得耗时、没有必要,但其实单测能够带来诸多的好处:

  1. 单测相较手动测试,能够减少bug率,覆盖的场景更全,且测试较为方便
  2. 开源的组件可能会有很多的维护者,单测能够降低模块之间互相影响产生bug的概率
  3. 使用者一般都会选择单测覆盖率较高的轮子

因此,单测必不可少,目前前端常见的选择包括:

  • Jest,FaceBook出品,配置简单,使用JSDOM模拟测试环境,当遇到操作真实DOM的场景,如获取scrollHeight等比较乏力
  • Karma + Mocha,Mocha同Jest都是测试框架,而Karma为框架提供了真实的浏览器测试环境,如果代码中对DOM操作较多,建议使用这种组合。但是Mocha配置较复杂,且需要自行安装断言库

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时能够同时展示源码,它也能够做到。我们可以通过两种方式:

  • 在Vue组件中使用<docs></docs>标签来写demo,这样做对组件有侵入,感觉不太好
  • 新建一个markdown文件,内部通过特殊的标记写入vue代码即可

我们选用第二种方式,但是又遇到了许多坑。比如写入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来作为我们的静态资源服务器展示文档,因为它方便部署且免费。过程如下:

  1. 将styleguide.config.js中的styleguideDir选项改为"docs",即将build的目标目录设置为docs
  2. 在Github对应仓库的settings中将GitHub Pages的Source选项设置为master branch/docs folder,意味着会自动从仓库的docs目录获取静态资源

这样每次更新docs会自动部署更新文档。

说完文档,我们还需要编写在Github上展示的README,这里推荐一个生成README的库,readme-md-generator,格式非常简洁且美观。在README中,我们可以添加如下的小图标:

这个可以使用shields生成,它能关联你的NPM、Github等等,实时更新icon信息,有了它文档逼格瞬间高多了。

发布NPM

要将包发布到NPM,我们需要做如下的准备工作:

  1. https://www.npmjs.com/上注册一个NPM的账号
  2. 本地执行
npm login --registry=https://registry.npmjs.org

注意,这边加上registry为了防止在全局或当前环境覆写.npmrc,导致登录的不是NPM源。

  1. 修改package.json的配置,可以参考v-collapse-vue的部分配置:
{
  "name": "r-collapse-vue",
  "version": "1.0.0",
  "description": "a collapse component for VueJs",
  "author": {
    "name": "Ray",
    "email": "[email protected]"
  },
  "main": "dist/r-collapse-vue.common.js",
  "files": [
    "dist"
  ],
  "keywords": [
    "Vue",
    "collapse"
  ],
  "publishConfig": {
    "registry": "https://registry.npmjs.org"
  },
  "repository": {
    "type": "git",
    "url": "[email protected]:DanceOnBeat/r-collapse-vue.git"
  }
}
  1. 最后执行npm publish即可完成发布

每次发布新版本之前,我们可以通过

npm version major/minor/patch -m 'xxx'

来修改版本号并且打上tag,此tag非NPM的dist-tag,而是Git的tag。一个版本对应一个tag,并通过

git push origin master --tags

将tag也推到远程仓库,这样在仓库中我们就能清楚地看到发布的记录,方便日后回滚之类的操作。具体的版本规则可以参考semver规范

持续集成(CI)

当开发结束后,我们需要跑测试,测试通过后,还需要构建生成dist目录,最后发布到NPM。每次修改都做这样一套操作实在繁琐,并且容易遗漏步骤,这时候我们就需要使用CI将我们的流程自动化,我在这边选择了TravisCI。同时,我们还可以通过Codecov,将我们的单测报告上传至Codecov服务器,这样就能同步更新Codecov的icon。

在配置CI时,我原本将生成docs的步骤也添加了进去,此时我们在deploy中会有两个步骤,如下:

deploy:
  - provider: npm
    email: [email protected]
    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才触发一次自动化构建,这样基本上就大功告成了。

总结

这是一次非常有趣的造轮体验,代码虽然不难,但是过程中又学习到了很多新的东西,包括单元测试、文档编写等等,希望这篇文章能给准备造轮或想要造轮的小伙伴提供一点帮助。

原文地址:https://www.cnblogs.com/danceonbeat/p/11063773.html

时间: 2024-11-05 15:46:38

从0到1发布一个Vue Collapse组件的相关文章

如何对第一个Vue.js组件进行单元测试 (上)

首先,为什么要单元测试组件? 单元测试是持续集成的关键.通过专注于小的.独立的实体,确保单元测试始终按预期运行,使代码更加可靠,你可以放心地迭代你的项目而不必担坏事儿. 单元测试不仅限于脚本.可以独立测试的任何东西都是可单元测试的,只要你遵循一些好的做法.这些实例包括单一责任.可预测性和松散耦合. 作为我们应用程序的可重用实体,Vue.js组件是单元测试的理想选择.我们将用不同的输入和交互测试做好的单个单元,并确保它始终按照我们的预期运行. 在开始之前 Vue CLI 3发布了.Vue Test

用webpack发布一个vue插件包

创建库 本来以为很简单,结果配置了webpack之后,运行build就报错了,似乎不认识es6语法,于是先后安装了几个包: @babel/core @babel/preset-env babel-loader @babel/plugin-proposal-class-properties 进行了一些配置: // babel const presets = [ [ '@babel/env', { targets: '> 0.25%, not dead', useBuiltIns: 'usage',

如何对第一个Vue.js组件进行单元测试 (下)

我们的首次测试 让我们来写首个测试.我们首先需要使用shallowMount手动挂载我们的组件,并将其存储在我们将执行断言的变量中.我们还可以通过propsData属性传递道具作为对象. 已安装的组件是一个对象,它有一些实用方法: 然后,我们可以写第一个断言: 让我们来分析一下这里发生了什么.首先,我们使用Jest的expect函数,它将我们想要测试的值作为参数.在我们的例子中,在父级上用findAll方法来获取具有活动类的所有元素.这将返回一个WrapperArray,包含Wrappers数组

【转】制作并发布第一个vue组件的npm包

最近在网上找到一个网页制作辅助工具-jQuery标尺参考线插件,觉得在现在的一个项目中能用的上,插件是基于JQuery的,但是现在的项目是用vue写的.So...,就照葫芦画瓢改装成了Vue组件,总的来说算是一个用处较多的组件,于是乎,就想着把它上传到Npm上分享出来.以前只用过别人的包,这一次自己上传一个乐呵乐呵...顺便记录发布一下过程. 项目地址 https://github.com/gorkys/vue... 初始化项目 这里用的是webpack-simple,可以理解为精简版的vue-

介绍推荐优秀的Vue UI组件库

Vue 是一个轻巧.高性能.可组件化的MVVM库,API简洁明了,上手快.从Vue推出以来,得到众多Web开发者的认可.在公司的Web前端项目开发中,多个项目采用基于Vue的UI组件框架开发,并投入正式使用.开发团队在使用Vue.js框架和UI组件库以后,开发效率大大提高,自己写的代码也少了,很多界面效果组件已经封装好了.在选择Vue UI组件库的过程中,通过GitHub上根据star数量.文档丰富程度.更新的频率以及维护等因素,也收集整理了一些优秀的Vue UI组件库. 下载资源:www.yi

【转】优秀的Vue UI组件库

原文来源:https://www.leixuesong.com/3342 Vue 是一个轻巧.高性能.可组件化的MVVM库,API简洁明了,上手快.从Vue推出以来,得到众多Web开发者的认可.在公司的Web前端项目开发中,多个项目采用基于Vue的UI组件框架开发,并投入正式使用.开发团队在使用Vue.js框架和UI组件库以后,开发效率大大提高,自己写的代码也少了,很多界面效果组件已经封装好了.在选择Vue UI组件库的过程中,通过GitHub上根据star数量.文档丰富程度.更新的频率以及维护

如何使用@vue/cli 3.0在npm上创建,发布和使用你自己的Vue.js组件库

译者按: 你可能npm人家的包过成千上万次,但你是否有创建,发布和使用过自己的npm包? 原文: How to create, publish and use your own VueJS Component library on NPM using @vue/cli 3.0 译者: Fundebug 为了保证可读性,本文采用意译而非直译.另外,本文版权归原作者所有,翻译仅用于学习. 尽管我已经在工作上用了Vue.js一段时间,但我从不需要在npm上发布组件.但最近发现在不同的项目重写组件是件非

Vue 3.0源码发布,前端程序员:“我真的学不动了!”

那天刷知乎,发现超多人唱衰前端岗,搞的小白和刚入行的新人人心惶惶,不知道自己入行的决定到底是对是错. 前端知识的学习不像上学,只局限在书本上的知识,层出不穷的热点和事件,多到让我们分不清什么是过时和新潮.以前能讲得清 ES6 的人已经很厉害了,可现在 ES8 都出来很久了,真是一刻都不能停止学习. 要是没什么人带领,就算每天都能追上热点,知道潮流的语言,刷再多论坛看再多分享,缺少实操的机会,也是白费功夫. 那么,到底什么样的能力,才是前端开发必备的?你与阿里P6的程序员,差在哪里?前端岗位该如何

Vue v2.0.0-rc.6 发布,轻量级 JavaScript 框架

Vue v2.0.0-rc.6 发布了,Vue.js 是构建 Web 界面的 JavaScript 库,提供数据驱动的组件(基础教程qkxue.net),还有简单灵活的 API,使得 MVVM 更简单(腾云科技ty300.com).本次更新内容如下: Fixed #3610 fix data observation converting prototype keys#3642 fix incorrect duplicate slot warning detection#3657 fix styl