如何在webpack中作預渲染下降首屏空白時間

1、瀏覽器渲染過程

一、用戶打開頁面,空白屏,等待html的返回css

二、html下載完畢,開始解析html,初始渲染html

三、下載css、js等資源,執行js渲染虛擬DOMvue

四、發起請求、獲取數據,渲染內容webpack

下面咱們主要是討論一下如何經過預渲染的方式下降空白屏的時間git

2、傳統頁面開發

在React、Vue這種數據驅動的框架還沒盛行的時候,通常咱們都是直接在html上寫dom結構的,要不就是直接服務端直出,因此咱們在下載完html頁面後,空白屏的時間是很是短的,由於dom是在html中的,並非像如今以虛擬dom的方式寫在js中,因此,咱們不須要等待js下載完畢後纔開始渲染頁面,而是html下載完畢後直接渲染出dom結構。github

現在咱們運用Vue等框架進行開發的時候,通常在html結構都是下面這樣的web

<!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資源沒有下載完畢的狀況下,頁面一直都是處於空白的頁面,一直要等到虛擬dom插入到id爲app的div中,這時候白屏才消失開始展示頁面,反正就是讓人感受特別慢就是了!瀏覽器

既然知道了白屏是怎麼產生的,那咱們下面就來嘗試一下如何在webpack中集成預渲染的功能,來下降白屏的時間。bash

3、在webpack中集成預渲染功能

github:webpack中如何集成預渲染功能app

這裏咱們嘗試將一個使用vue編寫的loading組件在webpack編譯過程當中將虛擬dom預渲染到html中,下面是loading組件的內容

<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__是用於後面圖片插入的標記,這裏先不用管,其實這個組件就是一個簡單的loading組件

最終咱們想要的效果是,將這個vue組件的虛擬dom預渲染到html文件當中

<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;
        <!-- 這裏咱們會將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頁面返回時編譯成base64內嵌到html中的loading就會立刻顯示,大大下降了白屏的時間,基本能夠達到秒開頁面,這時候咱們不須要等待js資源的下載以及虛擬dom的插入,固然這裏loading中的內容能夠是任何你想要預先渲染的模板

由於這裏咱們的loading組件是用vue寫的,因此咱們試着看看如何來作預渲染並集成到webpack中(能夠合着倉庫的代碼一塊兒看,代碼挺簡單的,只是一個demo)

這裏咱們先把vue單文件中的html與css單獨抽離出來

// 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)
複製代碼

這裏咱們經過正則的方式將template與style標籤中匹配到的內容單獨抽離了出來,接下來咱們須要將gif圖轉成base64並插入到咱們抽出的css代碼當中

let gifPath = resolvePath('./src/loading/imgs/loading.gif')

const transGifToCSSFile = (imgPath) => {
  let ext = path.extname(imgPath).slice(1)
  let preStr = `data:image/${ext};base64,`  // 根據尾綴自動拼接對應base64前綴
  let bitDate = fs.readFileSync(imgPath)
  let base64Str = bitDate.toString('base64')
  let dataURL = preStr + base64Str

  return dataURL
}

let dataURL = transGifToCSSFile(gifPath)
複製代碼

上面咱們經過extractAssetsInVueTpl函數抽離出了css,這裏咱們經過一個簡單的函數將佔位符替換成base64圖片

const injectDataURLToCSS = (cssStr, dataURL) => {
  return cssStr.replace(/__inline__/, dataURL)
}

let cssStr = injectDataURLToCSS(vueAssets.css, dataURL)
複製代碼

下面咱們就導出loading配置,包含了html模板與style樣式字符串

loading.html = vueAssets.html
loading.css = '<style>' + cssStr + '</style>'

module.exports = loading
複製代碼

簡單寫一個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中咱們經過模板語法將loading的內容插入到html模板中對應的位置了

<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>
複製代碼

4、總結

這裏只是寫一個demo介紹一下原理,更復雜的可使用vue-server-render來作同構直出或者使用一些像handlebars的模板引擎來生成模板,其實就是將服務端的渲染工做放到了編譯的過程中。

相關文章
相關標籤/搜索