引言
数据可视化页面一般在浏览器中进行全屏展示,全屏容器将根据屏幕比例及当前浏览器窗口大小,自动进行缩放处理。浏览器全屏后,全屏容器将充满屏幕。
接下来我们来一起阅读下DataV中关于全屏容器的源码,看下它是如何实现的。
源码阅读(vue2版本)
它的源码位置
看下它的DOM结构,很简单
<div id="dv-full-screen-container" :ref="ref">
<template v-if="ready">
<slot></slot>
</template>
</div>
源码分析
接下来重点看下它的JS实现,它的代码中混入了autoResize.js,所以我们需要两个文件一起看,不然会对突然出现的变量很奇怪。(如果觉得分开不便于阅读,其实我们可以把它合在一起来阅读,是一样的)
梳理下它的执行逻辑:
第1步、mounted(组件挂载时,这一时期可对dom进行操作)
mounted () {
const { autoResizeMixinInit } = this
autoResizeMixinInit()
},
第2步、autoResizeMixinInit函数
methods: {
async autoResizeMixinInit () {
const { initWH, getDebounceInitWHFun, bindDomResizeCallback, afterAutoResizeMixinInit } = this
await initWH(false)
getDebounceInitWHFun()
bindDomResizeCallback()
if (typeof afterAutoResizeMixinInit === 'function') afterAutoResizeMixinInit()
},
}
这其中调用了几个函数,我们来看看这些函数的作用。
2.1、initWH函数
// 初始化宽高
initWH (resize = true) {
const { $nextTick, $refs, ref, onResize } = this
return new Promise(resolve => {
$nextTick(_ => {
const dom = this.dom = $refs[ref]
this.width = dom ? dom.clientWidth : 0
this.height = dom ? dom.clientHeight : 0
if (!dom) {
console.warn('DataV: Failed to get dom node, component rendering may be abnormal!')
} else if (!this.width || !this.height) {
console.warn('DataV: Component width or height is 0px, rendering abnormality may occur!')
}
if (typeof onResize === 'function' && resize) onResize()
resolve()
})
})
},
这个函数的作用很简单,就是在DOM渲染成功后,获取这个组件dom的宽高,返回一个Promise异步函数,作用就是:保证这个dom已经渲染好了,再去执行其他函数。
2.1.1、onResize
onResize () {
const { setAppScale } = this
setAppScale()
}
resize为true,即窗口大小变化、dom的style改变时要重新设置dom的缩放比例。
2.2、getDebounceInitWHFun
getDebounceInitWHFun () {
const { initWH } = this
this.debounceInitWHFun = debounce(100, initWH)
},
获取一个经过防抖的initWH函数。即debounceInitWHFun。
2.3、bindDomResizeCallback
// 监听dom元素变化
bindDomResizeCallback () {
const { dom, debounceInitWHFun } = this
this.domObserver = observerDomResize(dom, debounceInitWHFun)
window.addEventListener('resize', debounceInitWHFun)
},
很重要的一步,其中使用了observerDomResize来对这个组件dom进行监听。
需要监听的DOM变化:
- 窗口大小改变时触发的事件
- dom的样式改变时触发
第一个直接使用window监听resize事件即可。
第二个对dom元素的监听,我们就需要使用MutationObserver来做了。
MutationObserver用来监视 DOM 变动。DOM 的任何变动,比如节点的增减、属性的变动、文本内容的变动都会触发MutationObserver事件。
封装一个observerDomResize函数来对dom的style属性变化进行监听
export function observerDomResize (dom, callback) {
const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver
const observer = new MutationObserver(callback)
observer.observe(dom, { attributes: true, attributeFilter: ['style'], attributeOldValue: true })
return observer
}
- attributes:属性的变动
- attributeFilter:表示需要观察的特定属性
- attributeOldValue:布尔值,表示观察attributes变动时,是否需要记录变动前的属性值。
2.4、afterAutoResizeMixinInit
afterAutoResizeMixinInit () {
const { initConfig, setAppScale } = this
initConfig()
setAppScale()
this.ready = true
},
组件dom节点渲染出来后,设置ready为true,再渲染插槽中的DOM元素。
2.4.1、initConfig
initConfig () {
const { dom } = this
// 当前屏幕分辨率
const { width, height } = screen
this.allWidth = width
dom.style.width = `${width}px`
dom.style.height = `${height}px`
},
作用:
- 获取当前设备屏幕的分辨率
- 将这个分辨率宽高设置为组件DOM的宽高
2.4.2、setAppScale
setAppScale () {
const { allWidth, dom } = this
const currentWidth = document.body.clientWidth
dom.style.transform = `scale(${currentWidth / allWidth})`
},
它的作用是改变当前DOM的缩放比率。
缩放比率 = 当前窗口的可视宽度 / 当前设备的屏幕分辨率(宽度)
第3步、beforeDestroy(组件卸载时)
beforeDestroy () {
const { unbindDomResizeCallback } = this
unbindDomResizeCallback()
}
我们之前对于dom的style属性和window的resize都做了监听,所以当我们组件卸载时这些监听事件也需要移除,如果不移除,那么到其他页面,做这些操作,监听事件仍然存在,但其实我们已经不需要再对他监听了,反而会造成性能浪费。
3.1、unbindDomResizeCallback
unbindDomResizeCallback () {
let { domObserver, debounceInitWHFun } = this
if (!domObserver) return
domObserver.disconnect()
domObserver.takeRecords()
domObserver = null
window.removeEventListener('resize', debounceInitWHFun)
}
disconnect方法用来停止观察。调用该方法后,DOM 再发生变动,也不会触发观察器。
takeRecords用来清除变动记录,即不再处理未处理的变动。该方法返回变动记录的数组。
小结
通过这个源码阅读,我们学习到了
1、函数的拆分(每个函数都只执行一个功能)
2、使用对象解构赋值,避免滥用this去读取data和methods中的数据和方法
3、使用MutationObserver来监听DOM的变动
其实在阅读源码时,也发现了一个小问题,就是使用mixin,会不利于代码的阅读,虽然他可以让我们复用一部分代码逻辑,但也增加了代码的可阅读性难度。(它的一些变量和方法的调用,需要我们多个文件来回寻找)
正好阅读到一篇文章,vue3的自定义hook可以很好的解决mixin的问题,那么我们就用vue3来重写下这个组件吧。
以上就是DataV 全屏容器组件源码解析的详细内容,更多关于DataV 全屏容器组件的资料请关注编程网其它相关文章!