一、浏览器渲染过程
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="https://www.gxlcms.com/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中的内容可以是任何你想要预先渲染的模板
因为这里我们的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的模板引擎来生成模板,其实就是将服务端的渲染工作放到了编译的过程当中。
温馨提示:内容为网友见解,仅供参考
详解如何在webpack中做预渲染降低首屏空白时间
一、浏览器渲染过程1、用户打开页面,空白屏,等待html的返回2、html下载完毕,开始解析html,初始渲染3、下载css、js等资源,执行js渲染虚拟DOM4、发起请求、获取数据,渲染内容下面我们主要是讨论一下如何通过预渲染的方式降低空白屏的时间缩小首屏载时间是一个重要的优化项,总结来主要有以下几种方式: ...
...js做splitChunks分包,从而减少首屏加载时间
为了优化性能,我们需要利用webpack的optimization.splitChunks功能进行拆分。首先,我们对打包后的dist文件夹进行观察,看看拆分后的效果。原本824kB的chunk-vendors.js被成功拆分成多个几十KB的小包,这样在生产环境加载时,能明显提升页面加载速度。以下是在vue.config.js中实际使用的分包代码片段,可以直接复...
webpack打包是如何优化的?
首先,关闭生产环境中的SourceMap。SourceMap虽便于调试代码,但在生产环境中并无必要。关闭SourceMap后,打包速度明显提升,体积也大幅缩减至几兆。其次,通过分析大文件,找出主要消耗资源的js文件。使用webpack-bundle-analyzer插件,帮助我们清楚地了解各个文件的大小和关系。发现其中五个文件占用了约4M的大小。
首屏秒开优化,可以这样做
针对图片资源,我们推荐使用在线压缩工具如tinypng进行压缩。它能将图片体积减小至原来的30%左右,几乎不影响图片清晰度,同时显著减少加载时间。在构建流程中加入压缩图片的步骤,可进一步提升效率。使用image-webpack-loader进行图片压缩的集成,不仅需要安装该插件,还需要在vue.config.js中进行配置。完成配置...
vue-cli项目中如何缩短首屏加载时间以提高效率
主要是首屏加载太慢。大文件定位我们可以使用webpack可视化插件Webpack Bundle Analyzer 查看工程js文件大小,然后有目的的解决过大的js文件。 安装npm install --save-dev webpack-bundle-analyzer在webpack中设置如下,然后npm run dev 的时候默认会在8888端口显示。const BundleAnalyzerPlugin = require('...
实战剖析-vue项目首屏加载时长优化
识别问题,针对性优化。查找过大文件,拆包或异步加载非即时资源。利用tree-shaking按需引入,实现代码压缩。gzip压缩降低文件大小。使用webpack或vite压缩代码。优化图片体积,使用tinify.cn工具。优化策略包括:1. 拆包压缩,减少引入重复模块。2. lodash按需引入,减少包体积。3. 异步引入非首屏必要资源。
Vue构建的页面怎么提高首屏渲染以及性能优化
通过分析和实践,提高Vue构建页面的首屏渲染和性能优化主要可以从以下几个方面着手:首先,使用webpack-bundle-analyzer插件帮助我们分析并了解打包文件的组成与大小,从而有针对性地进行模块化拆分,减少不必要的文件加载,提升加载速度。其次,合理运用路由懒加载,将静态引用方式改为动态加载。在Vue CLI 3中...
JS核心理论之《SPA、CSR、SSR、Prerender原理浅析》
Prerender的实现需要借助PrerenderSPAPlugin插件,通过webpack设置来指定需要预渲染的页面。Prerender原理基于Chrome官方的Puppeteer工具,它在构建阶段的最后,在本地启动Puppeteer服务,访问配置的路由,渲染页面并输出到HTML文件中,创建对应的目录。然而,Prerender的缺点在于渲染是在打包阶段进行的,如果页面包含...
为Vue 项目添加骨架屏
首先创建骨架屏入口文件,配置 webpack,将入口文件指定为 entry。然后通过参数将配置对象传入骨架屏插件中,插件运行时编译配置对象生成骨架屏 bundle。使用 bundle 文件创建 renderer,调用 renderToString 方法获取 HTML 渲染结果。默认情况下,样式内嵌在 JavaScript bundle 中,使用 ExtractTextPlugin 进行样式...
Vue最全性能优化方法
4. 利用http2:在多资源并发加载场景下,http2可突破TCP连接限制,显著提升网络环境下的性能。5. 懒加载:通过动态导入,减少首屏代码量,webpack会生成单独chunk.js。6. 预渲染与加载优化:减少白屏时间,使用loading或骨架屏改善用户体验。7. 优化第三方库:按需加载,降低打包体积。8. 分析与优化:...