详解如何在webpack中做预渲染降低首屏空白时间


Posted in Javascript onAugust 22, 2018

一、浏览器渲染过程

1、用户打开页面,空白屏,等待html的返回

2、html下载完毕,开始解析html,初始渲染

3、下载css、js等资源,执行js渲染虚拟DOM

4、发起请求、获取数据,渲染内容

下面我们主要是讨论一下如何通过预渲染的方式降低空白屏的时间

缩小首屏载时间是一个重要的优化项,总结来主要有以下几种方式:

1、尽可能的缩小webpack或者其他打包工具生成的包的大小
2、使用服务端渲染的方式
3、使用预渲染的方式
4、使用gzip减小网络传输的流量大小
5、按照页面或者组件分块懒加载

二、传统页面开发

在React、Vue这种数据驱动的框架还没盛行的时候,一般我们都是直接在html上写dom结构的,要不就是直接服务端直出,所以我们在下载完html页面后,空白屏的时间是非常短的,因为dom是在html中的,并不是像现在以虚拟dom的方式写在js中,所以,我们不需要等待js下载完毕后才开始渲染页面,而是html下载完毕后直接渲染出dom结构。

如今我们运用Vue等框架进行开发的时候,一般在html结构都是下面这样的

<!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中集成预渲染的功能,来降低白屏的时间。

三、在webpack中集成预渲染功能

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

这里我们尝试将一个使用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中的内容可以是任何你想要预先渲染的模板

详解如何在webpack中做预渲染降低首屏空白时间

详解如何在webpack中做预渲染降低首屏空白时间

因为这里我们的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>

四、总结

这里只是写一个demo介绍一下原理,更复杂的可以使用vue-server-render来做同构直出或者使用一些像handlebars的模板引擎来生成模板,其实就是将服务端的渲染工作放到了编译的过程当中。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
asp javascript 实现关闭窗口时保存数据的办法
Nov 24 Javascript
让你的博文自动带上缩址的实现代码,方便发到微博客上
Dec 28 Javascript
理解JavaScript的prototype属性
Feb 11 Javascript
JQuery一种取同级值的方式(比如你在GridView中)
Mar 15 Javascript
jQuery列表拖动排列具体实现
Nov 04 Javascript
jquery 获取 outerHtml 包含当前节点本身的代码
Oct 30 Javascript
实例分析浏览器中“JavaScript解析器”的工作原理
Dec 12 Javascript
node.js的Express服务器基本使用教程
Jan 09 Javascript
JS实现鼠标拖拽盒子移动及右键点击盒子消失效果示例
Jan 29 Javascript
浅谈vuex中store的命名空间
Nov 08 Javascript
js实现从右往左匀速显示图片(无缝轮播)
Jun 29 Javascript
JavaScript字符串转数字的简单实现方法
Nov 27 Javascript
基于vue中对鼠标划过事件的处理方式详解
Aug 22 #Javascript
vue鼠标移入添加class样式,鼠标移出去除样式(active)实现方法
Aug 22 #Javascript
JS封装的模仿qq右下角消息弹窗功能示例
Aug 22 #Javascript
当vue路由变化时,改变导航栏的样式方法
Aug 22 #Javascript
element-ui 设置菜单栏展开的方法
Aug 22 #Javascript
详解vue-cli脚手架中webpack配置方法
Aug 22 #Javascript
element-ui使用导航栏跳转路由的用法详解
Aug 22 #Javascript
You might like
thinkphp配置连接数据库技巧
2014/12/02 PHP
php显示指定目录下子目录的方法
2015/03/20 PHP
详解WordPress中给链接添加查询字符串的方法
2015/12/18 PHP
是 WordPress 让 PHP 更流行了 而不是框架
2016/02/03 PHP
PHP闭包函数传参及使用外部变量的方法
2016/03/15 PHP
9个比较实用的php代码片段
2016/03/15 PHP
用js实现计算加载页面所用的时间
2010/04/02 Javascript
简单实用的反馈表单无刷新提交带验证
2013/11/15 Javascript
Javascript加载速度慢的解决方案
2014/03/11 Javascript
JS与Ajax Get和Post在使用上的区别实例详解
2016/06/08 Javascript
原生js实现jquery函数animate()动画效果的简单实例
2016/08/21 Javascript
js 简易版滚动条实例(适用于移动端H5开发)
2017/06/26 Javascript
vue引入js数字小键盘的实现代码
2018/05/14 Javascript
微信小程序自定义弹窗实现详解(可通用)
2019/07/04 Javascript
javascript实现倒计时提示框
2021/03/02 Javascript
[10:24]郎朗助力完美“圣”典,天籁交织奏响序曲
2016/12/18 DOTA
[53:20]2018DOTA2亚洲邀请赛 4.1 小组赛 A组加赛 VG vs OG
2018/04/03 DOTA
Python爬取网易云音乐热门评论
2017/03/31 Python
Python实现的破解字符串找茬游戏算法示例
2017/09/25 Python
Python网络爬虫中的同步与异步示例详解
2018/02/03 Python
python提取照片坐标信息的实例代码
2019/08/14 Python
Python如何基于smtplib发不同格式的邮件
2019/12/30 Python
Python中私有属性的定义方式
2020/03/05 Python
python中sympy库求常微分方程的用法
2020/04/28 Python
Python3爬虫中识别图形验证码的实例讲解
2020/07/30 Python
基于Jquery和Css3代码制作可以缩放的搜索框
2015/11/19 HTML / CSS
HTML5画渐变背景图片并自动下载实现步骤
2013/11/18 HTML / CSS
英国剑桥包中文官网:The Cambridge Satchel Company中国
2018/11/06 全球购物
SQL数据库笔试题
2016/03/08 面试题
Exception类的常用方法
2012/06/16 面试题
留学推荐信写作指南
2014/01/25 职场文书
2019交通安全宣传标语集锦!
2019/06/28 职场文书
如何理解Vue简单状态管理之store模式
2021/05/15 Vue.js
java如何实现获取客户端ip地址的示例代码
2022/04/07 Java/Android
Python matplotlib绘制条形统计图 处理多个实验多组观测值
2022/04/21 Python
MySQL的表级锁,行级锁,排它锁和共享锁
2022/07/15 MySQL