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