老项目优化第二步——资源的懒加载和预加载
之前做前端项目性能优化的时候,为了使页面加载更快,用尽了手段,包括文件的合并、压缩,文件缓存和开启服务器端的 gzip 压缩等等。
但发现压缩总有个极限,并且遇到弱网环境时,用户在感知上依旧会有“慢”的概念,尤其是在首屏展示的时候,于是就选了两种方案(骨架屏和资源预加载,骨架屏下次再总结)。
预加载
预加载相信很多人都有听说过,引用Patrick Hamann的解释就是,预加载是浏览器对将来可能被使用资源的一种暗示,一些资源可以在当前页面使用到,一些可能在将来的某些页面中被使用。
简单理解就是,将页面加载时的资源提前加载到本地,等页面真正加载时直接从缓存获取资源。
我主要是使用在首屏加载时需要的资源,以及一个资源过大的文件上,避免页面长时间空白,减少等待时间,优化体验。
预加载可以细分为以下几个点:DNS-prefetch、subresource 、 prefetch、preconnect、prerender。
可根据自己项目需要去使用。
(1)DNS 预解析 DNS-Prefetch
通过 DNS 预解析来告诉浏览器未来我们可能从某个特定的 URL 获取资源,当浏览器真正使用到该域中的某个资源时就可以尽快地完成 DNS 解析。比如说,我们可能从 http://example.com 获取资源时(图片、音频等),就可以在<head> 标签中加入以下内容:
<link rel="dns-prefetch" href="//example.com" target="_blank" rel="external nofollow" >
当我们从该 URL 请求一个资源时,就不再需要等待 DNS 的解析过程。
(2)预连接 Preconnect
与 DNS 预解析类似,preconnect 不仅完成 DNS 预解析,同时还将进行 TCP 握手和建立传输层协议。可以这样使用:
<link rel="preconnect" href="http://example.com" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" >
(3)预获取 Prefetching
如果我们确定某个资源将来一定会被使用到,我们可以让浏览器预先请求该资源并放入浏览器缓存中,也就是说如果我们猜测用户接下来将要访问哪个具体的资源,那就可以用prefetching来预加载确定的资源了。
<link rel="prefetch" href="image.png" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" >
(4)Subresources
这是另一个预获取方式,这种方式指定的预获取资源具有最高的优先级,在所有 prefetch 项之前进行:
<link rel="subresource" href="styles.css" target="_blank" rel="external nofollow" >
prefetch 为将来的页面提供了一种低优先级的资源预加载方式,而subresource 为当前页面提供了一种高优先级的资源预加载。
如果资源是当前页面必须的,或者资源需要尽快可用,那么最好使用 subresource 而不是 prefetch。
(5)预渲染 Prerender
prerender 可以预先加载文档的所有资源,类似于在隐藏的tab 页中打开了某个链接 – 将下载所有资源、创建 DOM 结构、完成页面布局、应用 CSS 样式和执行 JavaScript 脚本等。
当用户真正访问该链接时,隐藏的页面就切换为可见,使页面看起来就是瞬间加载完成一样。
<link rel="prerender" href="http://example.com" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" >
需要注意的是不要滥用该特性,当你知道用户一定会点击某个链接时才可以进行预渲染,否则浏览器将无条件地下载所有预渲染需要的资源。
(6)新特性:Preloading
和prefetching不同,preloading会让浏览器无论如何都下载指定的资源,也就是说浏览器一定会预加载该资源。
<link rel="preload" href="image.png" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" >
所有预加载技术都存在一个潜在的风险:对资源预测错误。
而预加载的开销是高昂的,比如抢占 CPU 资源,消耗电池,浪费带宽等,所以必须谨慎行事。
虽然很难确定用户下一步将访问哪些资源,但高可信的场景确实存在:
- 如果用户完成一个带有明显结果的搜索,那么结果页面很可能会被加载。
- 如果用户进入到登陆页面,那么登陆成功的页面很可能会被加载。
- 如果用户阅读一个多页的文章或访问一个分页的结果集,那么下一页很可能会被加载。
(7)H5音乐预加载 preload=”auto”
<audio src="audio.mp3" autoplay="autoplay" loop preload="auto" id="sendid2"></audio>
(8)使用html标签
<img src="image.png" style="display:none"/>
(9)使用Image对象
<script src="./imagePreload.js"></script>
// imagePreload.js文件
var image= new Image()
image.src="https://xxx.xx.com/image.jpg"
优化网页性能的另一种方式, 懒加载 。
图片懒加载图片懒加载一般用于网页中延迟加载图像,用户滚动到它们之前,可视区域外的图像不会加载。这与图像预加载相反,在长网页上使用延迟加载将使网页加载更快。
使用懒加载是为了应对那些页面过长,图片资源过多的网页。如果要等整个页面一次性加载完成,那体验就太差了。
实现原理- 首先,不要将图片地址放到src属性中,而是放到其它属性(data-original)中。
- 页面加载完成后,根据scrollTop判断图片是否在用户的视野内,如果在,则将data-original属性中的值取出存放到src属性中。
- 在滚动事件中重复判断图片是否进入视野,如果进入,则将data-original属性中的值取出存放到src属性中。
// 图片懒加载
<html >
<head>
<meta charset="UTF-8">
<title>Lazyload</title>
<style>
.image-item {
display: block;
margin-bottom: 50px;
height: 200px; // 一定记得设置图片高度
}
</style>
</head>
<body>
<img src="" class="image-item" lazyload="true" data-original="images/1.png" />
<img src="" class="image-item" lazyload="true" data-original="images/2.png" />
<img src="" class="image-item" lazyload="true" data-original="images/3.png" />
<img src="" class="image-item" lazyload="true" data-original="images/4.png" />
<img src="" class="image-item" lazyload="true" data-original="images/5.png" />
<img src="" class="image-item" lazyload="true" data-original="images/6.png" />
<img src="" class="image-item" lazyload="true" data-original="images/7.png" />
<img src="" class="image-item" lazyload="true" data-original="images/8.png" />
<img src="" class="image-item" lazyload="true" data-original="images/9.png" />
<img src="" class="image-item" lazyload="true" data-original="images/10.png" />
<img src="" class="image-item" lazyload="true" data-original="images/11.png" />
<img src="" class="image-item" lazyload="true" data-original="images/12.png" />
<script>
var viewHeight = document.documentElement.clientHeight // 获取可视区高度
function lazyload() {
var eles = document.querySelectorAll('img[data-original][lazyload]')
Array.prototype.forEach.call(eles, function (item, index) {
var rect
if (item.dataset.original === "")
return
rect = item.getBoundingClientRect() // 用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置
if (rect.bottom >= 0 && rect.top < viewHeight) {
!function () {
var img = new Image()
img.src = item.dataset.original
img.onload = function () {
item.src = img.src
}
item.removeAttribute("data-original")// 移除属性,下次不再遍历
item.removeAttribute("lazyload")
}()
}
})
}
lazyload() // 刚开始还没滚动屏幕时,要先触发一次函数,初始化首页的页面图片
document.addEventListener("scroll",lazyload)
</script>
</body>
</html>
懒加载库
Github上也有很多懒加载库可以直接引用:
- lazysizes 是一个功能十分强大的懒加载库,主要用于加载图片和iframes。你只需要指定src/srcset属性,lazysizes会帮你自动懒加载内容。值得注意的是,lazysizes基于intersection observer,因此你需要一个polyfill。你还可以通过一些插件扩展库的功能以用于懒加载视频。
- lozad.js是一个轻量级、高性能的懒加载库,基于intersection observer,你同样需要提供一个相关的polyfill。
- blazy是一个轻量级的懒加载库,大小仅为1.4KB。相对于lazysizes,它不需要任何的外部依赖,并且兼容IE7+。你可能猜测到了,blazy不支持intersection observer,性能相对较差。
- react-lazyload基于react的懒加载组件。
Vue路由懒加载(组件按需加载)
在单页应用中,运用webpack打包后的文件随着项目的进行会变得很大,组件越来越多,首次启动项目时,需要加载的内容过多,延时过长,不利于用户体验。
运用懒加载可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载时间。
与webpack配合实现组件懒加载(1)在webpack配置文件中的output路径配置chunkFilename属性
output: {
path: resolve(__dirname, 'dist'),
filename: options.dev ? '[name].js': '[name].js?[chunkhash]',
chunkFilename: 'chunk[id].js?[chunkhash]',
publicPath: options.dev ? '/assets/': publicPath
},
// chunkFilename路径将会作为组件懒加载的路径
(2)配合webpack支持的异步加载方法
// resolve => require([URL], resolve), 支持性好
// () => system.import(URL) , webpack2官网上已经声明将逐渐废除, 不推荐使用
// () => import(URL), webpack2官网推荐使用, 属于es7范畴, 需要配合babel的syntax-dynamic-import插件使用, 具体使用方法如下
npm install --save-dev babel-core babel-loader babel-plugin-syntax-dynamic-importbabel-preset-es2015
use: [{
loader: 'babel-loader',
options: {
presets: [['es2015', {modules: false}]],
plugins: ['syntax-dynamic-import']
}
}]
具体实例中实现懒加载 (1)路由中配置异步组件
export default new Router({
routes: [
{
mode: 'history',
path: '/home,
name: home,
component: resolve => require(['../views/home.vue'], resolve) // 懒加载
},
]
})
(2)实例中配置异步组件
components: {
historyTab: resolve => {require(['../../component/page.vue'], resolve)} // 懒加载
}
(3)全局注册异步组件
Vue.component(‘header’, () => {
System.import('./component/header.vue')
})
以上是这次项目优化在加载这方面整理的材料,后续会持续更新欢迎各位朋友指正