详解如何在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 相关文章推荐
js自定义方法通过隐藏iframe实现文件下载
Feb 21 Javascript
js实现二代身份证号码验证详解
Nov 20 Javascript
解决jQuery uploadify在非IE核心浏览器下无法上传
Aug 05 Javascript
JavaScript常用判断写法大全(推荐)
May 30 Javascript
JavaScript toUpperCase()方法使用详解
Aug 26 Javascript
vue插件tab选项卡使用小结
Oct 27 Javascript
解决vue-cli中stylus无法使用的问题方法
Jun 19 Javascript
vue.js异步上传文件前后端实现代码
Aug 22 Javascript
Vue页面骨架屏的实现方法
May 22 Javascript
JS数组去重的6种方法完整实例
Dec 08 Javascript
JS事件绑定的常用方式实例总结
Mar 02 Javascript
jquery获取并修改触发事件的DOM元素示例【基于target 属性】
Oct 10 jQuery
基于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
php Memcache 中实现消息队列
2009/11/24 PHP
采用memcache在web集群中实现session的同步会话
2014/07/05 PHP
php中限制ip段访问、禁止ip提交表单的代码分享
2014/08/22 PHP
PHP处理数组和XML之间的互相转换
2016/06/02 PHP
php 替换文章中的图片路径,下载图片到本地服务器的方法
2018/02/06 PHP
PHP ADODB生成HTML表格函数rs2html功能【附错误处理函数用法】
2018/05/29 PHP
DEFER怎么用?
2006/07/01 Javascript
JavaScript去掉数组中的重复元素
2011/01/13 Javascript
鼠标滑上去后图片放大浮出效果的js代码
2011/05/28 Javascript
jquery在Chrome下获取图片的长宽问题解决
2013/03/20 Javascript
jquery预览图片实现鼠标放上去显示实际大小
2014/01/16 Javascript
jQuery+html5+css3实现圆角无刷新表单带输入验证功能代码
2015/08/21 Javascript
详解Angular2 之 结构型指令
2017/06/21 Javascript
详解使用Typescript开发node.js项目(简单的环境配置)
2017/10/09 Javascript
vue项目中使用百度地图的方法
2018/06/08 Javascript
vue项目前端微信JSAPI与外部H5支付相关实现过程及常见问题
2020/04/14 Javascript
js节流防抖应用场景,以及在vue中节流防抖的具体实现操作
2020/09/21 Javascript
Python实现的Excel文件读写类
2015/07/30 Python
Python的requests网络编程包使用教程
2016/07/11 Python
Python基本socket通信控制操作示例
2019/01/30 Python
Python中一般处理中文的几种方法
2019/03/06 Python
Python搭建代理IP池实现接口设置与整体调度
2019/10/27 Python
Python多继承以及MRO顺序的使用
2019/11/11 Python
Python基于Dlib的人脸识别系统的实现
2020/02/26 Python
Python 解决火狐浏览器不弹出下载框直接下载的问题
2020/03/09 Python
Django 拼接两个queryset 或是两个不可以相加的对象实例
2020/03/28 Python
django的autoreload机制实现
2020/06/03 Python
python制作抽奖程序代码详解
2021/01/15 Python
python利用opencv实现颜色检测
2021/02/23 Python
西班牙国家航空官方网站:Iberia
2017/11/16 全球购物
匡威爱尔兰官网:Converse爱尔兰
2019/06/09 全球购物
俄罗斯外国汽车和国产汽车配件网上商店:Движком
2020/04/19 全球购物
华为python面试题
2016/05/03 面试题
英语专业学子个人的自我评价
2013/10/02 职场文书
搞笑获奖感言
2014/01/30 职场文书
小学毕业演讲稿
2014/04/25 职场文书