前段时间公司要求做一版数据大屏,然后延续上一代的技术选型,仍然是vue2,可视化的组件选择了echarts和datav,想比于echarts,datav就有点名不经传了,但是上一代用的这个,咱也不搞什么革新了就依然延续,还好官方文档还算清晰,相比echarts复杂的配置项简直是清晰明了。
我们有个模块用到了“排名轮播表”,刚开始给做的是单条轮播,就是一次往上跳一条然后轮播。但是领导看后不满意,提出要整页轮播,一次轮播一页10条。我一看这个需求不就是改下配置项吗,官方都给出答案了。
然后我一改,一看效果傻眼了,确实轮播了一页但是下一页变成了一条,然后整个轮播就变得有些鬼畜,再看官网给的示例,竟然一样的问题。。。
我当机立断立刻放弃该组件,转投swiper的怀抱。在实现了效果后,时间buffer还有一些,想要一探究竟。
其实发现这个问题的人很多,我肯定不是第一个,事实上网上也有很多给出答案的。 这个bug就出现在源码@jiaminghi/data-view/src/compoments/scrollRankingBoard/src/main.vue 第217行。
然后看看datav内有个同类型组件“轮播表”没有这个bug是怎么写的
很明显排名轮播表给了carousel传page的选项但是在数据slice时并没有进行校验。
错误原因已经找到了,剩下的就是怎么改了,简单粗暴的就是复制文件copy一个新的组件来用,但是他源码里有mixin,还有一些工具函数要完整复制就要创建很多文件,显得不那么优雅。
然后就分析下他的源码,从入口文件开始,我们main.js使用其实就是use了一下,看他源码内部的入口也是导出了一个函数,接收Vue构造函数,执行依次Vue.use
然后找到我们要改的组件的入口
其实一下就简单明了了,首先Vue.use的源码我们都知道,传进来的是对象的话有install方法就执行install方法,将Vue构造函数传进去,如果是函数的话就执行函数本身将Vue构造函数当做参数传进去。这里显然是第2种。
他这里要Vue的构造函数其实是为了执行Vue.compoment这个静态方法,这个方法源码大伙也应该知道,第一个参数是接收一个字符串当做name,第二个参数是传进来的options或者Vue.extend(options)方法,如果是对象他给你执行extend方法,该方法会执行mergeOptions会将当前传进来的选项和原本的options进行合并,然后返回一个继承了Vue原型方法的构造函数。
源码大概:
核心就是在这里了,既然他执行了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>
然后交由领导验收,领导沉吟片刻,“我觉的还是左右按时间维度来进行滚动比较好”
得白忙活了。