詳解如何在webpack中做預(yù)渲染降低首屏空白時(shí)間
一、瀏覽器渲染過(guò)程
1、用戶(hù)打開(kāi)頁(yè)面,空白屏,等待html的返回
2、html下載完畢,開(kāi)始解析html,初始渲染
3、下載css、js等資源,執(zhí)行js渲染虛擬DOM
4、發(fā)起請(qǐng)求、獲取數(shù)據(jù),渲染內(nèi)容
下面我們主要是討論一下如何通過(guò)預(yù)渲染的方式降低空白屏的時(shí)間
縮小首屏載時(shí)間是一個(gè)重要的優(yōu)化項(xiàng),總結(jié)來(lái)主要有以下幾種方式:
1、盡可能的縮小webpack或者其他打包工具生成的包的大小
2、使用服務(wù)端渲染的方式
3、使用預(yù)渲染的方式
4、使用gzip減小網(wǎng)絡(luò)傳輸?shù)牧髁看笮?
5、按照頁(yè)面或者組件分塊懶加載
二、傳統(tǒng)頁(yè)面開(kāi)發(fā)
在React、Vue這種數(shù)據(jù)驅(qū)動(dòng)的框架還沒(méi)盛行的時(shí)候,一般我們都是直接在html上寫(xiě)dom結(jié)構(gòu)的,要不就是直接服務(wù)端直出,所以我們?cè)谙螺d完html頁(yè)面后,空白屏的時(shí)間是非常短的,因?yàn)閐om是在html中的,并不是像現(xiàn)在以虛擬dom的方式寫(xiě)在js中,所以,我們不需要等待js下載完畢后才開(kāi)始渲染頁(yè)面,而是html下載完畢后直接渲染出dom結(jié)構(gòu)。
如今我們運(yùn)用Vue等框架進(jìn)行開(kāi)發(fā)的時(shí)候,一般在html結(jié)構(gòu)都是下面這樣的
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>title</title> </head> <body> <div id="app"></div> <script src="/bound.js"></script> </body> </html>
在js資源沒(méi)有下載完畢的情況下,頁(yè)面一直都是處于空白的頁(yè)面,一直要等到虛擬dom插入到id為app的div中,這時(shí)候白屏才消失開(kāi)始展現(xiàn)頁(yè)面,反正就是讓人感覺(jué)特別慢就是了!
既然知道了白屏是怎么產(chǎn)生的,那我們下面就來(lái)嘗試一下如何在webpack中集成預(yù)渲染的功能,來(lái)降低白屏的時(shí)間。
三、在webpack中集成預(yù)渲染功能
github:webpack中如何集成預(yù)渲染功能
這里我們嘗試將一個(gè)使用vue編寫(xiě)的loading組件在webpack編譯過(guò)程中將虛擬dom預(yù)渲染到html中,下面是loading組件的內(nèi)容
<template>
<div class="loading-img"></div>
</template>
<script>
export default {}
</script>
<style>
.loading-img {
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
margin: auto;
display: inline-block;
width: 60px;
height: 60px;
background: url(__inline__) no-repeat center center;
background-size: contain;
}
</style>
上面__inline__是用于后面圖片插入的標(biāo)記,這里先不用管,其實(shí)這個(gè)組件就是一個(gè)簡(jiǎn)單的loading組件
最終我們想要的效果是,將這個(gè)vue組件的虛擬dom預(yù)渲染到html文件當(dāng)中
<html>
<head>
<meta charset="UTF-8">
<title>test</title>
<!-- pre-render-loading抽出的css -->
<style>
.loading-img {
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
margin: auto;
display: inline-block;
width: 60px;
height: 60px;
<!-- 這里我們會(huì)將loading圖編譯成base64直接插入到html中 -->
background: url(data:image/gif;base64,.....) no-repeat center center;
background-size: contain;
}
</style>
...
</head>
<body>
<div id="app">
<!-- loading base64圖 -->
<div class="loading-img"></div>
</div>
...
</body>
</html>
向上面那樣,在html頁(yè)面返回時(shí)編譯成base64內(nèi)嵌到html中的loading就會(huì)馬上顯示,大大降低了白屏的時(shí)間,基本可以達(dá)到秒開(kāi)頁(yè)面,這時(shí)候我們不需要等待js資源的下載以及虛擬dom的插入,當(dāng)然這里loading中的內(nèi)容可以是任何你想要預(yù)先渲染的模板


因?yàn)檫@里我們的loading組件是用vue寫(xiě)的,所以我們?cè)囍纯慈绾蝸?lái)做預(yù)渲染并集成到webpack中(可以合著倉(cāng)庫(kù)的代碼一起看,代碼挺簡(jiǎn)單的,只是一個(gè)demo)
這里我們先把vue單文件中的html與css單獨(dú)抽離出來(lái)
// render-loading.js
let vueAssets = null
let vueTplPath = resolvePath('./src/loading/pre-render-loading.vue')
const extractAssetsInVueTpl = (vueTplPath) => {
let vueTpl = clearEnter(fs.readFileSync(vueTplPath).toString())
let html = /<template>(.*)<\/template>/g.exec(vueTpl)[1]
let css = /<style>(.*)<\/style>/g.exec(vueTpl)[1]
return {
html,
css
}
}
vueAssets = extractAssetsInVueTpl(vueTplPath)
這里我們通過(guò)正則的方式將template與style標(biāo)簽中匹配到的內(nèi)容單獨(dú)抽離了出來(lái),接下來(lái)我們需要將gif圖轉(zhuǎn)成base64并插入到我們抽出的css代碼當(dāng)中
let gifPath = resolvePath('./src/loading/imgs/loading.gif')
const transGifToCSSFile = (imgPath) => {
let ext = path.extname(imgPath).slice(1)
let preStr = `data:image/${ext};base64,` // 根據(jù)尾綴自動(dòng)拼接對(duì)應(yīng)base64前綴
let bitDate = fs.readFileSync(imgPath)
let base64Str = bitDate.toString('base64')
let dataURL = preStr + base64Str
return dataURL
}
let dataURL = transGifToCSSFile(gifPath)
上面我們通過(guò)extractAssetsInVueTpl函數(shù)抽離出了css,這里我們通過(guò)一個(gè)簡(jiǎn)單的函數(shù)將占位符替換成base64圖片
const injectDataURLToCSS = (cssStr, dataURL) => {
return cssStr.replace(/__inline__/, dataURL)
}
let cssStr = injectDataURLToCSS(vueAssets.css, dataURL)
下面我們就導(dǎo)出loading配置,包含了html模板與style樣式字符串
loading.html = vueAssets.html loading.css = '<style>' + cssStr + '</style>' module.exports = loading
簡(jiǎn)單寫(xiě)一個(gè)webpack入口配置,這里我們需要使用html-webpack-plugin將loading插入到html中(這里用到了插件的自定義模板)
const HtmlWebpackPlugin = require('html-webpack-plugin')
const loading = require('./render-loading')
module.exports = {
entry: './src/index.js',
output: {
path: __dirname + '/dist',
filename: 'index_bundle.js'
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
loading: loading
})
]
}
在html中我們通過(guò)模板語(yǔ)法將loading的內(nèi)容插入到html模板中對(duì)應(yīng)的位置了
<html> <head> <meta charset="UTF-8"> <title>test</title> ... <%= htmlWebpackPlugin.options.loading.css %> </head> <body> <div id="app"> <!-- loading base64圖 --> <%= htmlWebpackPlugin.options.loading.html %> </div> ... </body> </html>
四、總結(jié)
這里只是寫(xiě)一個(gè)demo介紹一下原理,更復(fù)雜的可以使用vue-server-render來(lái)做同構(gòu)直出或者使用一些像handlebars的模板引擎來(lái)生成模板,其實(shí)就是將服務(wù)端的渲染工作放到了編譯的過(guò)程當(dāng)中。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
js對(duì)象數(shù)組和對(duì)象的使用實(shí)例詳解
在本篇文章里小編給大家整理了關(guān)于js對(duì)象數(shù)組和對(duì)象的使用實(shí)例相關(guān)知識(shí)點(diǎn),有需要的朋友們學(xué)習(xí)下。2019-08-08
一問(wèn)了解JavaScript中的元數(shù)據(jù)
本文主要介紹了一問(wèn)了解JavaScript中的元數(shù)據(jù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
JavaScript中用字面量創(chuàng)建對(duì)象介紹
這篇文章主要介紹了JavaScript中用字面量創(chuàng)建對(duì)象介紹,本文直接給出代碼實(shí)例,并講解了一些技巧,需要的朋友可以參考下2014-12-12
javascript 單例模式詳解及簡(jiǎn)單實(shí)例
這篇文章主要介紹了javascript 單例模式詳解及簡(jiǎn)單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-02-02
js傳中文參數(shù)controller里獲取參數(shù)亂碼問(wèn)題解決方法
js傳中文參數(shù),在controller里獲取參數(shù)亂碼的問(wèn)題在本文有個(gè)不錯(cuò)的解決方法,感興趣的朋友可以參考下2014-01-01
javascript獲取ckeditor編輯器的值(實(shí)現(xiàn)代碼)
這篇文章主要介紹了javascript獲取ckeditor編輯器的值,用于表單驗(yàn)證。需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-11-11
JS實(shí)現(xiàn)數(shù)字格式千分位相互轉(zhuǎn)換方法
下面小編就為大家?guī)?lái)一篇JS實(shí)現(xiàn)數(shù)字格式千分位相互轉(zhuǎn)換方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-08-08

