JavaScript前端實(shí)現(xiàn)頁(yè)面白屏檢測(cè)與解決
白屏通常指的是頁(yè)面打開后,瀏覽器上面的地址欄已經(jīng)顯示完整的 URL,但是頁(yè)面內(nèi)容無法渲染,只有白色的空白頁(yè)面。
導(dǎo)致白屏的原因大致可分為兩類:
資源加載問題
代碼執(zhí)行錯(cuò)誤
從現(xiàn)代前端視角來看,這兩種原因都跟當(dāng)前SPA框架的廣泛使用有關(guān)。
資源加載問題
這里的資源特指 JavaScript 腳本、樣式表、圖片等靜態(tài)資源,不包括接口調(diào)用等動(dòng)態(tài)資源。
資源加載問題導(dǎo)致的白屏可以分為可恢復(fù)的和不可恢復(fù)的兩大類
1.可恢復(fù)的白屏
可恢復(fù)的白屏常見于第一次進(jìn)人頁(yè)面時(shí),由于資源加載過慢或者接口請(qǐng)求未返回,所以瀏覽器無法執(zhí)行下一步驟。
這種白屏通常是網(wǎng)絡(luò)狀況太差或者設(shè)備性能太差等原因?qū)е碌?,一般在瀏覽器返回后,就能恢復(fù)頁(yè)面渲染,可以通過監(jiān)控首屏?xí)r間來發(fā)現(xiàn)。
如果生產(chǎn)環(huán)境的首屏?xí)r間呈異常上升趨勢(shì),那么一定是頁(yè)面白屏?xí)r間過長(zhǎng)導(dǎo)致的,開發(fā)人員應(yīng)該及時(shí)關(guān)注并排查近期改動(dòng)的代碼
2.不可恢復(fù)的白屏
不可恢復(fù)的白屏大多是由于網(wǎng)絡(luò)或緩存問題導(dǎo)致的。
常見的例子莫過于 React、Vue 等 SPA 框架構(gòu)建的 Web 應(yīng)用,一旦 [bundle|app].js 因?yàn)榫W(wǎng)絡(luò)原因訪問失敗,便會(huì)引發(fā)頁(yè)面白屏。
你可以訪問任意SPA框架構(gòu)建的Web應(yīng)用,按照如下步驟復(fù)現(xiàn):打開 DevTools > Network,找到 app.xxx.js,右鍵并選中 Block request URL,隨后刷新頁(yè)面。
另一個(gè)例子是SPA項(xiàng)目打包后,在非首次線上替換dist文件時(shí),某些手機(jī)/瀏覽器在之后首次打開頁(yè)面,可能出現(xiàn)白屏情況
在用戶端會(huì)默認(rèn)緩存index.html入口文件,而由于vue打包生成的css/js都是哈希值,跟上次的文件名都不同,因此會(huì)出現(xiàn)找不到css/js的情況,導(dǎo)致白屏的產(chǎn)生。在服務(wù)端更新包之后,由于舊的文件被刪除,而index.html所鏈接的路徑依然是舊文件路徑,因此會(huì)找不到文件,從而白屏。
代碼執(zhí)行錯(cuò)誤
代碼執(zhí)行錯(cuò)誤導(dǎo)致的白屏一般伴隨著功能流程的阻斷出現(xiàn),并且很難通過等待或者頁(yè)面刷新等方法修復(fù)。
這類問題出現(xiàn)的原因通常是前端代碼邏輯錯(cuò)誤,或是后端接口的臟數(shù)據(jù)導(dǎo)致的前端數(shù)據(jù)解析邏輯錯(cuò)誤,最終導(dǎo)致運(yùn)行異常的前端代碼觸發(fā)了頁(yè)面崩潰。
例如,如果 React 中的組件發(fā)生了異常,并且外部沒有使用 componentDidCatch 或者getDerivedStateFromError 捕錯(cuò)誤,那么 React 組件 render 掛載的目標(biāo)節(jié)點(diǎn)下的 DOM 樹會(huì)被移除、頁(yè)面就會(huì)出現(xiàn)白屏。
自React 16 起,任何未被錯(cuò)誤邊界捕獲的錯(cuò)誤將會(huì)導(dǎo)致整個(gè) React 組件樹被卸載。
白屏檢測(cè)
檢測(cè)根節(jié)點(diǎn)是否渲染
這種方法的原理是在當(dāng)前主流 SPA 框架下,DOM 一般掛載在一個(gè)根節(jié)點(diǎn)之下(比如 < div id=“app” > ),發(fā)生白屏后通常是根節(jié)點(diǎn)下所有 DOM 被卸載。
我們可以通過在頁(yè)面渲染完成后,檢查頁(yè)面根節(jié)點(diǎn)的子元素是否存在,如果不存在,則可以判斷頁(yè)面是白屏狀態(tài)。
這種方案簡(jiǎn)潔明了,但缺點(diǎn)也很明顯,項(xiàng)目有骨架屏渲染的情況下無法判斷是否白屏。
如在 Vue.js 中可以使用鉤子函數(shù) mounted 或 created 在頁(yè)面渲染完成后進(jìn)行檢測(cè)。
export default { created() { // 獲取頁(yè)面根節(jié)點(diǎn) const rootNode = document.querySelector('#app'); // 檢測(cè)根節(jié)點(diǎn)的子元素是否存在 if (rootNode.children.length === 0) { // 頁(yè)面是白屏狀態(tài) console.error('頁(yè)面白屏了!'); } else { // 頁(yè)面正常顯示 console.log('頁(yè)面正常顯示'); } }, }
由于 SPA 應(yīng)用通常使用異步加載方式加載組件和數(shù)據(jù),因此需要確保在頁(yè)面組件渲染完成后再進(jìn)行白屏檢測(cè)。如果在組件渲染之前進(jìn)行檢測(cè),可能會(huì)誤判為白屏。另外,SPA 應(yīng)用可能會(huì)存在某些異步請(qǐng)求失敗或加載超時(shí)等問題,也需要考慮這些因素對(duì)白屏檢測(cè)的影響。
Mutation Observer 監(jiān)聽 DOM 變化
Mutation Observer 是一種在 DOM 樹發(fā)生變化時(shí)進(jìn)行回調(diào)的方式,可以用于監(jiān)聽頁(yè)面元素的變化??梢酝ㄟ^ Mutation Observer 來監(jiān)聽 DOM 樹變化,從而判斷頁(yè)面是否白屏。
但這個(gè)方式的缺點(diǎn)也很明顯:
- 對(duì)性能的影響:使用 Mutation Observer 監(jiān)聽 DOM 變化會(huì)對(duì)瀏覽器性能造成一定影響,尤其是當(dāng) DOM樹變化頻繁時(shí),會(huì)導(dǎo)致回調(diào)函數(shù)頻繁執(zhí)行,影響頁(yè)面的響應(yīng)速度和用戶體驗(yàn)。
- 兼容性問題:Mutation Observer 的兼容性不是很好,不同瀏覽器支持程度不同,需要進(jìn)行兼容性處理。
- 誤判問題:有些情況下,頁(yè)面并不是真正的白屏,但是 Mutation Observer 仍然會(huì)誤判為白屏,例如頁(yè)面中有一個(gè)空白的 div元素占位,此時(shí)即使頁(yè)面內(nèi)容未加載,也不會(huì)被判定為白屏狀態(tài)。
具體實(shí)現(xiàn)步驟如下:
創(chuàng)建一個(gè) Mutation Observer 實(shí)例,并指定回調(diào)函數(shù)。
const observer = new MutationObserver((mutations) => { // mutations 表示 DOM 樹的變化列表 });
將 Mutation Observer 實(shí)例與根節(jié)點(diǎn)進(jìn)行綁定,并指定監(jiān)測(cè)的選項(xiàng)。
const rootNode = document.documentElement; const observerConfig = { childList: true, // 監(jiān)聽子節(jié)點(diǎn)的變化 subtree: true, // 監(jiān)聽所有后代節(jié)點(diǎn)的變化 attributes: true, // 監(jiān)聽節(jié)點(diǎn)屬性的變化 characterData: true, // 監(jiān)聽節(jié)點(diǎn)文本內(nèi)容的變化 attributeOldValue: true, // 記錄節(jié)點(diǎn)屬性變化前的值 characterDataOldValue: true, // 記錄節(jié)點(diǎn)文本內(nèi)容變化前的值 }; observer.observe(rootNode, observerConfig);
在回調(diào)函數(shù)中檢查 DOM 樹的變化,如果發(fā)現(xiàn)根節(jié)點(diǎn)的子元素存在,則可以判斷頁(yè)面不是白屏狀態(tài)。
const observer = new MutationObserver((mutations) => { // 檢查根節(jié)點(diǎn)的子元素是否存在 if (rootNode.children.length > 0) { console.log('頁(yè)面正常顯示'); } else { console.error('頁(yè)面白屏了!'); } });
頁(yè)面截圖
通過對(duì)網(wǎng)頁(yè)進(jìn)行截圖,對(duì)截圖進(jìn)行像素點(diǎn)分析,判斷頁(yè)面是否白屏。一般情況下,可以統(tǒng)計(jì)截圖中所有像素點(diǎn)的 RGB 值,如果所有像素點(diǎn)的 RGB 值都相同,且與白色(RGB(255, 255, 255))非常接近,那么就可以判斷頁(yè)面是白屏狀態(tài)。
這個(gè)方式的缺點(diǎn)如下:
頁(yè)面截圖需要包含足夠的像素點(diǎn),以便能夠準(zhǔn)確地檢測(cè)頁(yè)面是否白屏。如果截圖不夠清晰,可能會(huì)導(dǎo)致誤判。
有些情況下,頁(yè)面并不是真正的白屏,但是由于一些外部原因(例如網(wǎng)絡(luò)問題)導(dǎo)致頁(yè)面未加載,此時(shí)頁(yè)面截圖也會(huì)被判定為白屏狀態(tài)。
頁(yè)面存在骨架屏的情況下,需要對(duì)比骨架屏
解決辦法
思路:減小打包后的體積(sourceMap關(guān)掉,CDN引入, 路由懶加載,組件按需加載)
- 提高渲染速度
- 優(yōu)化用戶體驗(yàn)
- CDN資源優(yōu)化
將依賴的第三方npm包全部改為通過CDN鏈接獲取,在index.html里插入相應(yīng)鏈接
<body> <div id="app"></div> <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script> <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.min.js"></script> <script src="https://cdn.bootcss.com/vuex/3.1.0/vuex.min.js"></script> <script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.min.js"></script> <script src="https://cdn.bootcss.com/element-ui/2.6.1/index.js"></script> </body>
在vue.config.js里配置externals屬性
module.exports = { ··· externals: { 'vue': 'Vue', 'vuex': 'Vuex', 'vue-router': 'VueRouter', 'axios':'axios', 'element-ui': 'ElementUI' } }
卸載相關(guān)依賴的npm包
npm uninstall xxx
使用gzip壓縮
前端處理:
// npm i compression-webpack-plugin -S const CompressionPlugin = require('compression-webpack-plugin'); ???????module.exports = { productionSourceMap: false, configureWebpack: config => { if (process.env.NODE_ENV === 'production') { return { plugins: [ new CompressionPlugin({ // 匹配規(guī)格 test: /\.js$|\.html$|\.css$|\.png$/, // 文件超過多大進(jìn)行壓縮 單位Byte threshold: 10240, // 是否刪除源文件(建議不刪除) deleteOriginalAssets: false }) ], } } }, }
還需要在 nginx開啟gzip壓縮, 例如:
gzip on; gzip_static on; //當(dāng)存在.gzip格式的js文件時(shí),優(yōu)先使用靜態(tài)文件 gzip_min_length 10k; //開啟gzip壓縮的最小大小 gzip_buffers 4 16k; gzip_http_version 1.1; gzip_comp_level 6; gzip_types text/plain application/javascript application/x-javascript text/javascript text/css application/xml; gzip_vary on; gzip_proxied expired no-cache no-store private auth; gzip_disable "MSIE [1-6]\.";
注意:
當(dāng)nginx開啟gzip壓縮的時(shí)候,無論前端打包出來的文件是否壓縮,網(wǎng)站加載到的js文件都是經(jīng)過nginx實(shí)時(shí)壓縮過的 。
當(dāng)gzip_static off的時(shí)候,前端上傳的js壓縮文件(gzip格式那些)并沒有什么用。
當(dāng)gzip_static on時(shí),優(yōu)先加載前端打包的gzip壓縮文件,如果沒有找到該文件,那么nginx將實(shí)時(shí)壓縮之后傳給瀏覽器。
到此這篇關(guān)于JavaScript前端實(shí)現(xiàn)頁(yè)面白屏檢測(cè)與解決的文章就介紹到這了,更多相關(guān)JavaScript頁(yè)面白屏內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript 實(shí)現(xiàn)HTML DOM增刪改查操作的常見方法詳解
這篇文章主要介紹了JavaScript 實(shí)現(xiàn)HTML DOM增刪改查操作,結(jié)合實(shí)例形式分析了JavaScript針對(duì)HTML DOM元素增刪改查常見操作技巧與使用注意事項(xiàng),需要的朋友可以參考下2020-01-01學(xué)習(xí)javascript面向?qū)ο?理解javascript對(duì)象
這篇文章主要介紹了javascript對(duì)象,學(xué)習(xí)javascript面向?qū)ο螅信d趣的小伙伴們可以參考一下2016-01-01javascript 在firebug調(diào)試時(shí)用console.log的方法
當(dāng)你使用console.log()函數(shù)時(shí),下面的firebug一定要打開,不然這函數(shù)在用firefox運(yùn)行時(shí)無效且影響正常程序,如果用IE打開,將會(huì)出錯(cuò)2012-05-05js字符串去重復(fù)id的實(shí)現(xiàn)代碼
最近由于需要我們將相關(guān)id的重復(fù)的去掉,一個(gè)是客戶端一個(gè)后臺(tái)程序把關(guān),這里分享下js的去重復(fù)id的實(shí)現(xiàn)代碼2013-07-07小程序開發(fā)之uniapp引入iconfont圖標(biāo)以及使用方式
uniapp本身是有icon組件的,但由于icon組件各端表現(xiàn)存在差異,所以我們可以通過使用iconfont的字體圖標(biāo)來彌補(bǔ)這些差異,下面這篇文章主要給大家介紹了關(guān)于小程序開發(fā)之uniapp引入iconfont圖標(biāo)以及使用方式的相關(guān)資料,需要的朋友可以參考下2022-11-11