详解如何在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 相关文章推荐
javascript 出生日期和身份证判断大全
Nov 13 Javascript
jquery Firefox3.5中操作select的问题
Jul 10 Javascript
js中的值类型和引用类型小结 文字说明与实例
Dec 12 Javascript
js跨浏览器实现将字符串转化为xml对象的方法
Sep 25 Javascript
javascript生成img标签的3种实现方法(对象、方法、html)
Dec 25 Javascript
基于jQuery实现交互体验社会化分享代码附源码下载
Jan 04 Javascript
TypeScript入门-接口
Mar 30 Javascript
Angular2.js实现表单验证详解
Jun 23 Javascript
webpack+react+antd脚手架优化的方法
Apr 02 Javascript
微信小程序实现自定义picker选择器弹窗内容
May 26 Javascript
Layui动态生成select下拉选择框不显示的解决方法
Sep 24 Javascript
小程序瀑布流组件实现翻页与图片懒加载
May 19 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
日本十大最佳动漫,全都是二次元的神级作品
2019/10/05 日漫
用穿越火线快速入门php面向对象
2012/02/22 PHP
WordPress开发中的get_post_custom()函数使用解析
2016/01/04 PHP
PHP PDOStatement::setFetchMode讲解
2019/02/03 PHP
解决laravel5.4下的group by报错的问题
2019/10/16 PHP
学习JS面向对象成果 借国庆发布个最新作品与大家交流
2009/10/03 Javascript
利用了jquery的ajax实现二级联互动菜单
2013/12/02 Javascript
JavaScript在for循环中绑定事件解决事件参数不同的情况
2014/01/20 Javascript
讲解JavaScript的Backbone.js框架的MVC结构设计理念
2016/02/14 Javascript
jQuery插件ImageDrawer.js实现动态绘制图片动画(附源码下载)
2016/02/25 Javascript
JS操作COOKIE实现备忘记录的方法
2016/04/01 Javascript
浅谈 Vue 项目优化的方法
2017/12/16 Javascript
JS实现前端页面的搜索功能
2018/06/12 Javascript
javascript显示动态时间的方法汇总
2018/07/06 Javascript
这样回答继承可能面试官更满意
2019/12/10 Javascript
微信小程序canvas截取任意形状的实现代码
2020/01/13 Javascript
Node 模块原理与用法详解
2020/05/13 Javascript
浅谈vue获得后台数据无法显示到table上面的坑
2020/08/13 Javascript
[02:19]2018年度DOTA2最佳核心位选手-完美盛典
2018/12/17 DOTA
[01:10:58]KG vs TNC 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/16 DOTA
jupyter安装小结
2016/03/13 Python
Python中的sort()方法使用基础教程
2017/01/08 Python
Python基于identicon库创建类似Github上用的头像功能
2017/09/25 Python
python 获取url中的参数列表实例
2018/12/18 Python
Python参数解析模块sys、getopt、argparse使用与对比分析
2019/04/02 Python
Python异常原理及异常捕捉实现过程解析
2020/03/25 Python
Django如何重置migration的几种情景
2021/02/24 Python
Html5原生拖拽相关事件简介以及基础实现
2020/11/19 HTML / CSS
韩国休闲女装品牌网站:ANAIS
2016/08/24 全球购物
日本7net购物网:书籍、漫画、杂志、DVD、游戏邮购
2017/02/17 全球购物
导游实习生自荐书
2014/01/28 职场文书
入伍通知书
2015/04/23 职场文书
圣诞晚会主持词
2015/07/01 职场文书
2015年音乐教研组工作总结
2015/07/22 职场文书
2016入党积极分子党校培训心得体会
2016/01/06 职场文书
2016大学生社会实践心得体会范文
2016/01/14 职场文书