DataV?全屏容器組件源碼解析
引言
數(shù)據(jù)可視化頁(yè)面一般在瀏覽器中進(jìn)行全屏展示,全屏容器將根據(jù)屏幕比例及當(dāng)前瀏覽器窗口大小,自動(dòng)進(jìn)行縮放處理。瀏覽器全屏后,全屏容器將充滿屏幕。
接下來(lái)我們來(lái)一起閱讀下DataV中關(guān)于全屏容器的源碼,看下它是如何實(shí)現(xiàn)的。
源碼閱讀(vue2版本)
它的源碼位置

看下它的DOM結(jié)構(gòu),很簡(jiǎn)單
<div id="dv-full-screen-container" :ref="ref">
<template v-if="ready">
<slot></slot>
</template>
</div>
源碼分析
接下來(lái)重點(diǎn)看下它的JS實(shí)現(xiàn),它的代碼中混入了autoResize.js,所以我們需要兩個(gè)文件一起看,不然會(huì)對(duì)突然出現(xiàn)的變量很奇怪。(如果覺(jué)得分開不便于閱讀,其實(shí)我們可以把它合在一起來(lái)閱讀,是一樣的)
梳理下它的執(zhí)行邏輯:
第1步、mounted(組件掛載時(shí),這一時(shí)期可對(duì)dom進(jìn)行操作)
mounted () {
const { autoResizeMixinInit } = this
autoResizeMixinInit()
},
第2步、autoResizeMixinInit函數(shù)
methods: {
async autoResizeMixinInit () {
const { initWH, getDebounceInitWHFun, bindDomResizeCallback, afterAutoResizeMixinInit } = this
await initWH(false)
getDebounceInitWHFun()
bindDomResizeCallback()
if (typeof afterAutoResizeMixinInit === 'function') afterAutoResizeMixinInit()
},
}
這其中調(diào)用了幾個(gè)函數(shù),我們來(lái)看看這些函數(shù)的作用。
2.1、initWH函數(shù)
// 初始化寬高
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()
})
})
},
這個(gè)函數(shù)的作用很簡(jiǎn)單,就是在DOM渲染成功后,獲取這個(gè)組件dom的寬高,返回一個(gè)Promise異步函數(shù),作用就是:保證這個(gè)dom已經(jīng)渲染好了,再去執(zhí)行其他函數(shù)。
2.1.1、onResize
onResize () {
const { setAppScale } = this
setAppScale()
}
resize為true,即窗口大小變化、dom的style改變時(shí)要重新設(shè)置dom的縮放比例。
2.2、getDebounceInitWHFun
getDebounceInitWHFun () {
const { initWH } = this
this.debounceInitWHFun = debounce(100, initWH)
},
獲取一個(gè)經(jīng)過(guò)防抖的initWH函數(shù)。即debounceInitWHFun。
2.3、bindDomResizeCallback
// 監(jiān)聽dom元素變化
bindDomResizeCallback () {
const { dom, debounceInitWHFun } = this
this.domObserver = observerDomResize(dom, debounceInitWHFun)
window.addEventListener('resize', debounceInitWHFun)
},
很重要的一步,其中使用了observerDomResize來(lái)對(duì)這個(gè)組件dom進(jìn)行監(jiān)聽。
需要監(jiān)聽的DOM變化:
- 窗口大小改變時(shí)觸發(fā)的事件
- dom的樣式改變時(shí)觸發(fā)
第一個(gè)直接使用window監(jiān)聽resize事件即可。
第二個(gè)對(duì)dom元素的監(jiān)聽,我們就需要使用MutationObserver來(lái)做了。
MutationObserver用來(lái)監(jiān)視 DOM 變動(dòng)。DOM 的任何變動(dòng),比如節(jié)點(diǎn)的增減、屬性的變動(dòng)、文本內(nèi)容的變動(dòng)都會(huì)觸發(fā)MutationObserver事件。
封裝一個(gè)observerDomResize函數(shù)來(lái)對(duì)dom的style屬性變化進(jìn)行監(jiān)聽
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:屬性的變動(dòng)
- attributeFilter:表示需要觀察的特定屬性
- attributeOldValue:布爾值,表示觀察attributes變動(dòng)時(shí),是否需要記錄變動(dòng)前的屬性值。
2.4、afterAutoResizeMixinInit
afterAutoResizeMixinInit () {
const { initConfig, setAppScale } = this
initConfig()
setAppScale()
this.ready = true
},
組件dom節(jié)點(diǎn)渲染出來(lái)后,設(shè)置ready為true,再渲染插槽中的DOM元素。
2.4.1、initConfig
initConfig () {
const { dom } = this
// 當(dāng)前屏幕分辨率
const { width, height } = screen
this.allWidth = width
dom.style.width = `${width}px`
dom.style.height = `${height}px`
},
作用:
- 獲取當(dāng)前設(shè)備屏幕的分辨率
- 將這個(gè)分辨率寬高設(shè)置為組件DOM的寬高
2.4.2、setAppScale
setAppScale () {
const { allWidth, dom } = this
const currentWidth = document.body.clientWidth
dom.style.transform = `scale(${currentWidth / allWidth})`
},
它的作用是改變當(dāng)前DOM的縮放比率。
縮放比率 = 當(dāng)前窗口的可視寬度 / 當(dāng)前設(shè)備的屏幕分辨率(寬度)
第3步、beforeDestroy(組件卸載時(shí))
beforeDestroy () {
const { unbindDomResizeCallback } = this
unbindDomResizeCallback()
}
我們之前對(duì)于dom的style屬性和window的resize都做了監(jiān)聽,所以當(dāng)我們組件卸載時(shí)這些監(jiān)聽事件也需要移除,如果不移除,那么到其他頁(yè)面,做這些操作,監(jiān)聽事件仍然存在,但其實(shí)我們已經(jīng)不需要再對(duì)他監(jiān)聽了,反而會(huì)造成性能浪費(fèi)。
3.1、unbindDomResizeCallback
unbindDomResizeCallback () {
let { domObserver, debounceInitWHFun } = this
if (!domObserver) return
domObserver.disconnect()
domObserver.takeRecords()
domObserver = null
window.removeEventListener('resize', debounceInitWHFun)
}
disconnect方法用來(lái)停止觀察。調(diào)用該方法后,DOM 再發(fā)生變動(dòng),也不會(huì)觸發(fā)觀察器。
takeRecords用來(lái)清除變動(dòng)記錄,即不再處理未處理的變動(dòng)。該方法返回變動(dòng)記錄的數(shù)組。
小結(jié)
通過(guò)這個(gè)源碼閱讀,我們學(xué)習(xí)到了
1、函數(shù)的拆分(每個(gè)函數(shù)都只執(zhí)行一個(gè)功能)
2、使用對(duì)象解構(gòu)賦值,避免濫用this去讀取data和methods中的數(shù)據(jù)和方法
3、使用MutationObserver來(lái)監(jiān)聽DOM的變動(dòng)
其實(shí)在閱讀源碼時(shí),也發(fā)現(xiàn)了一個(gè)小問(wèn)題,就是使用mixin,會(huì)不利于代碼的閱讀,雖然他可以讓我們復(fù)用一部分代碼邏輯,但也增加了代碼的可閱讀性難度。(它的一些變量和方法的調(diào)用,需要我們多個(gè)文件來(lái)回尋找)
正好閱讀到一篇文章,vue3的自定義hook可以很好的解決mixin的問(wèn)題,那么我們就用vue3來(lái)重寫下這個(gè)組件吧。
以上就是DataV 全屏容器組件源碼解析的詳細(xì)內(nèi)容,更多關(guān)于DataV 全屏容器組件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
ant desing vue table 實(shí)現(xiàn)可伸縮列的完整例子
最近在使用ant-design-vue做表格時(shí),遇到要做一個(gè)可伸縮列表格的需求,在網(wǎng)上一直沒(méi)有找到好的方法,于是小編動(dòng)手自己寫個(gè)可以此功能,下面小編把a(bǔ)nt desing vue table 可伸縮列的實(shí)現(xiàn)代碼分享到腳本之家平臺(tái)供大家參考2021-05-05
基于vue-cli配置lib-flexible + rem實(shí)現(xiàn)移動(dòng)端自適應(yīng)
這篇文章主要介紹了基于vue-cli配置lib-flexible + rem實(shí)現(xiàn)移動(dòng)端自適應(yīng),需要的朋友可以參考下2017-12-12
淺談validator自定義驗(yàn)證及易錯(cuò)點(diǎn)
這篇文章主要介紹了validator自定義驗(yàn)證及易錯(cuò)點(diǎn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
vue.js中window.onresize的超詳細(xì)使用方法
這篇文章主要給大家介紹了關(guān)于vue.js中window.onresize的超詳細(xì)使用方法,window.onresize 是直接給window的onresize屬性綁定事件,只能有一個(gè),文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-12-12

