关于datav源码排名轮播表bug的一次优雅解决

1,052 阅读4分钟

前段时间公司要求做一版数据大屏,然后延续上一代的技术选型,仍然是vue2,可视化的组件选择了echarts和datav,想比于echarts,datav就有点名不经传了,但是上一代用的这个,咱也不搞什么革新了就依然延续,还好官方文档还算清晰,相比echarts复杂的配置项简直是清晰明了。

我们有个模块用到了“排名轮播表”,刚开始给做的是单条轮播,就是一次往上跳一条然后轮播。但是领导看后不满意,提出要整页轮播,一次轮播一页10条。我一看这个需求不就是改下配置项吗,官方都给出答案了。

image.png

然后我一改,一看效果傻眼了,确实轮播了一页但是下一页变成了一条,然后整个轮播就变得有些鬼畜,再看官网给的示例,竟然一样的问题。。。

image.png 我当机立断立刻放弃该组件,转投swiper的怀抱。在实现了效果后,时间buffer还有一些,想要一探究竟。

其实发现这个问题的人很多,我肯定不是第一个,事实上网上也有很多给出答案的。 这个bug就出现在源码@jiaminghi/data-view/src/compoments/scrollRankingBoard/src/main.vue 第217行。

image.png 然后看看datav内有个同类型组件“轮播表”没有这个bug是怎么写的

image.png 很明显排名轮播表给了carousel传page的选项但是在数据slice时并没有进行校验。

错误原因已经找到了,剩下的就是怎么改了,简单粗暴的就是复制文件copy一个新的组件来用,但是他源码里有mixin,还有一些工具函数要完整复制就要创建很多文件,显得不那么优雅。

image.png

然后就分析下他的源码,从入口文件开始,我们main.js使用其实就是use了一下,看他源码内部的入口也是导出了一个函数,接收Vue构造函数,执行依次Vue.use

image.png

然后找到我们要改的组件的入口

image.png 其实一下就简单明了了,首先Vue.use的源码我们都知道,传进来的是对象的话有install方法就执行install方法,将Vue构造函数传进去,如果是函数的话就执行函数本身将Vue构造函数当做参数传进去。这里显然是第2种。

他这里要Vue的构造函数其实是为了执行Vue.compoment这个静态方法,这个方法源码大伙也应该知道,第一个参数是接收一个字符串当做name,第二个参数是传进来的options或者Vue.extend(options)方法,如果是对象他给你执行extend方法,该方法会执行mergeOptions会将当前传进来的选项和原本的options进行合并,然后返回一个继承了Vue原型方法的构造函数。

源码大概:

image.png

核心就是在这里了,既然他执行了Vue.components,必然在静态方法Vue.options(注意这里是静态属性options不是内部的$options)里就会有以key value形式存在的:组件名称=>options,至此我们主要目的达成,获取原先的options。

然后我们extend他的options 再将原先的animation方法重新修改为正确的覆盖掉。

大功告成

以下附上源码

<script>
import { scrollRankingBoard } from '@jiaminghi/data-view';
import Vue from 'vue'
scrollRankingBoard(Vue) // 这里可以用官方推荐的Vue.use但实际上他就是为了获取Vue构造函数这样调用一样 中间省去了不必要的步骤

const dvOptions = Vue.options.components.scrollRankingBoard
export default {
  name: 'newScrollPage',
  extends: dvOptions,
  methods: {
    async animation (start = false) {
      let { avgHeight, animationIndex, mergedConfig, rowsData, animation, updater } = this

      const { waitTime, carousel, rowNum } = mergedConfig

      const rowLength = rowsData.length

      if (rowNum >= rowLength) return

      if (start) {
        await new Promise(resolve => setTimeout(resolve, waitTime))
        if (updater !== this.updater) return
      }

      const animationNum = carousel === 'single' ? 1 : rowNum

      let rows = rowsData.slice(animationIndex)
      rows.push(...rowsData.slice(0, animationIndex))
      // 这一行源码有问题哟
      this.rows = rows.slice(0, carousel === 'page' ? rowNum * 2 : rowNum + 1)
      this.heights = new Array(rowLength).fill(avgHeight)

      await new Promise(resolve => setTimeout(resolve, 300))
      if (updater !== this.updater) return

      this.heights.splice(0, animationNum, ...new Array(animationNum).fill(0))

      animationIndex += animationNum

      const back = animationIndex - rowLength
      if (back >= 0) animationIndex = back

      this.animationIndex = animationIndex
      this.animationHandler = setTimeout(animation, waitTime - 300)
    },
  }
}
</script>

然后交由领导验收,领导沉吟片刻,“我觉的还是左右按时间维度来进行滚动比较好”

得白忙活了。