這里給大家分享我在網(wǎng)上總結(jié)出來的一些知識,希望對大家有所幫助
最近,我剛剛完成了一個(gè)閱讀器的txt文件閱讀功能,但在處理大文件時(shí),遇到了文本內(nèi)容過多導(dǎo)致瀏覽器崩潰的問題。
一般情況下,沒有任何樣式渲染時(shí)不會(huì)出現(xiàn)什么問題,15MB的文件大約會(huì)有3秒的空白時(shí)間。
<div id="content"></div>
fetch('./dp.txt').then(resp => resp.text()).then(text => { document.getElementById('content').innerText = text })
盡管目前還沒有嚴(yán)重的問題,但隨著文件繼續(xù)增大,肯定會(huì)超過瀏覽器內(nèi)存限制而導(dǎo)致崩潰。
在開發(fā)閱讀器的過程中,我添加了下面的樣式,結(jié)果導(dǎo)致瀏覽器直接崩潰:
* { margin: 0; padding: 0; } html, body { width: 100%; height: 100%; overflow: hidden; } body { column-fill: auto; column-width: 375px; overflow-x: auto; }
預(yù)期結(jié)果應(yīng)該是像下面這樣分段顯示:
?然而,實(shí)際出現(xiàn)了下面的問題:
因此,文件內(nèi)容太多會(huì)導(dǎo)致瀏覽器崩潰。即使進(jìn)行普通的渲染,我們也要考慮這個(gè)問題。
如何解決
解決這個(gè)問題的方法有點(diǎn)經(jīng)驗(yàn)的前端開發(fā)工程師應(yīng)該都知道可以使用虛擬滾動(dòng),重點(diǎn)是怎么對文本分段分段,最容易的可能就是按照一定數(shù)量字符劃分,但是這個(gè)導(dǎo)致文本銜接不整齊出現(xiàn)文本跳動(dòng)。如圖,橙色和藍(lán)色表示兩端文本的銜接,虛擬滾動(dòng)必然會(huì)提前移除橙色那塊內(nèi)容,那么就會(huì)導(dǎo)致藍(lán)色文本位置發(fā)生改變。
要解決這個(gè)問題,我們需要想辦法用某個(gè)元素替代原來的位置。當(dāng)前頁橙色部分刪除并計(jì)算位置,問題會(huì)變得復(fù)雜并且誤差比較大,因此這一部分直接保留,把這部分前面的內(nèi)容移除,然后用相同長度的元素占據(jù),接下來重點(diǎn)就是怎么獲取到橙色部分與前面內(nèi)容的分界點(diǎn)。
獲取分界點(diǎn)可以使用document.createRange()
,document.createRange()
是 JavaScript 中用于創(chuàng)建Range
對象的方法。Range對象表示一個(gè)包含節(jié)點(diǎn)與文本節(jié)點(diǎn)之間一定范圍的文檔片段。這個(gè)范圍可以橫跨單個(gè)節(jié)點(diǎn)、部分節(jié)點(diǎn)或者多個(gè)節(jié)點(diǎn)。
// 創(chuàng)建 Range 對象 const range = document.createRange(); range.setStart(textNode, 0); // 第一個(gè)參數(shù)可以是文本節(jié)點(diǎn),第二個(gè)參數(shù)表示偏移量 range.setEnd(textNode, 1); const rect = range.getBoundingClientRect(); // 獲取第一個(gè)字符的位置信息
利用Range對象的特性,我們可以從橙色部分的最后一個(gè)字符開始計(jì)算,直到找到分界點(diǎn)的位置。
閱讀器如果僅僅只是從左往右閱讀,按照上面的方法已經(jīng)可以實(shí)現(xiàn),但是閱讀器可能會(huì)出現(xiàn)頁面直接跳轉(zhuǎn),跳轉(zhuǎn)之后的文本起點(diǎn)你并不知道,并且頁面總頁碼你也無法知道。因此從一開始就要知道每一頁的分界點(diǎn),也就是需要做預(yù)渲染。以下是一個(gè)簡單的示例:
let text = '...' const step = 300 let end = Math.min(step, value.length) // 獲取結(jié)束點(diǎn) while (text.length > 0) { node.innerText = value.substring(0, end) // 取部分插入節(jié)點(diǎn) const range = document.createRange() range.selectNodeContents(node) const rect = range.getBoundingClientRect() // 獲取當(dāng)前內(nèi)容的位置信息 if (rect.height > boxHeight) { // 判斷當(dāng)前內(nèi)容高度是否會(huì)超出顯示區(qū)域的高度 // 如果超出,從 end 最后一個(gè)字符開始計(jì)算,直到不超出范圍 while (bottom > boxHeight) { // node.childNodes[0] 表示文本節(jié)點(diǎn) range.setStart(node.childNodes[0], end - 1) range.setEnd(node.childNodes[0], end) bottom = range.getBoundingClientRect().bottom end-- } } else { // 如果沒有超出,end 繼續(xù)增加 // ... } }
上面只是簡單的實(shí)現(xiàn)原理,可以達(dá)到精確區(qū)分每一頁的字符,但是計(jì)算量有點(diǎn)太大,15MB文本大約500多萬字,循環(huán)次數(shù)估計(jì)也在幾十萬到上百萬。在本人的電腦上測試大約需要20秒,每個(gè)人設(shè)備的性能不同,所需時(shí)間也會(huì)有所不同。很明顯,這種實(shí)現(xiàn)方式并不太理想。
后來我對這個(gè)方案進(jìn)行了優(yōu)化,實(shí)際上我們不需要計(jì)算每一頁的分界點(diǎn),可以計(jì)算出多頁的分界點(diǎn),例如10頁、20頁、50頁等。優(yōu)化后的代碼是將step
增大,比如設(shè)為10000,然后將不能組成一頁的尾部內(nèi)容去掉。優(yōu)化后,15MB的文本大約需要4秒左右。需要注意的是,step
并不是越大越好,太大會(huì)導(dǎo)致渲染頁面占用時(shí)間過長。
這就是我目前用來解決頁面渲染大量文本的方法。如果你有更好的方案,歡迎留言。文章來源:http://www.zghlxwxcb.cn/news/detail-638009.html
本文轉(zhuǎn)載于:
https://juejin.cn/post/7261231729523965989
如果對您有所幫助,歡迎您點(diǎn)個(gè)關(guān)注,我會(huì)定時(shí)更新技術(shù)文檔,大家一起討論學(xué)習(xí),一起進(jìn)步。
?文章來源地址http://www.zghlxwxcb.cn/news/detail-638009.html
到了這里,關(guān)于記錄--瀏覽器渲染15M文本導(dǎo)致崩潰怎么辦的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!