vue前端性能优化之预加载和懒加载示例详解
白水清风 人气:0预加载
预加载简单来说就是将所有所需的资源提前请求加载到本地,这样后面在需要用到时就直接从缓存取资源;我们使用该技术预先告知浏览器,等下某些资源可能要被使用,先把资源下载下来,不要等使用的时候再下载,可以看出这样的加载技术会增加服务器的压力,但是用户的体验会比较好,因为可以较快的看到后面的内容。
那看到这里很多问题的小明就说了,那预加载那么好用,以后我们就都用预加载了,服务器不是问题,一个字买;但如果在一个资源非常多,页面非常长的电商网站场景中,如淘宝,我们要等待所有的资源都预加载完,再呈现给用户么?那用户估计会直接关闭淘宝打开京东;切记,任何一个技术都有它适用的场景和不适用的场景,我们要学会技术选型。
图片预加载
HTML- img标签
img标签会在Html渲染解析到的时候,如果解析到img中src的值,则浏览器会立即开启一个线程去请求该资源,所以我们可以先将img标签隐藏但src写上对应的链接,这样皆可以把资源先请求回来了
// 预加载 <img src="https://img.qb5200.com/download-x/zhongzhuanl9/jb/20230320/ihlscl1nlaz.jpg" style="display: none" />
Image对象
const image = new Image() image.src = "https://img.qb5200.com/download-x/zhongzhuanl9/jb/20230320/mrhllakfg0g.jpg"
只要浏览器把图片下载到本地,同样的src就会使用缓存,这是最基本也是最实用的预载方法;除了以上方法还有一些利用 XMLHttpRequest
对象或者第三库如PerloadJS
库来实现
JS预加载
js的加载方式
<script src="index.js"></script>
JS 会阻塞 dom 渲染,浏览器必须等待 index.js
加载和执行完成后才能去做其它事情
<script async src="index.js"></script>
async模式下,它的加载时异步的,JS不会阻塞DOM渲染,async加载是无序的,当它加载完毕就会立即执行;可以用于与DOM无关系的操作,比如埋点统计
<script defer src="index.js"></script>
defer 模式下,JS 的加载也是异步的,defer资源会在DOMConentLoaded
执行之前,并且是有序的;如果多个script都设置了defer,会按引入顺序执行,如果需要控制资源加载顺序时,可以使用这个模式,如 element-ui.js
依赖于 vue
,所以必须先引入vue.js
,再引入 element-ui.js
<script type="module">import { a } from './a.js'</script>
在主流的现代浏览器中,script 标签的属性可以加上 type="module"
,浏览器会对其内部的 import 引用发起 HTTP 请求,获取模块内容。这时 script 的行为会像是 defer 一样,在后台下载,并且等待 DOM 解析
<link rel="preload" as="script" href="index.js" rel="external nofollow" rel="external nofollow" >
link 标签的 preload 属性:用于提前加载一些需要的依赖,这些资源会优先加载;vue2 项目打包生成的 index.html 文件,会自动给首页所需要的资源,全部添加 preload,实现关键资源的提前加载
<link rel="prefetch" as="script" href="index.js" rel="external nofollow" rel="external nofollow" >
prefetch 是利用浏览器的空闲时间,加载页面将来可能用到的资源的一种机制;通常可以用于加载其他页面(非首页)所需要的资源,以便加快后续页面的打开速度
async、defer 是 script 标签的专属属性,对于网页中的其他资源,可以通过 link 的 preload、prefetch 属性来预加载如今现代框架已经将 preload、prefetch 添加到打包流程中了,通过灵活的配置,去使用这些预加载功能,同时我们也可以审时度势地向 script 标签添加 async、defer 属性去处理资源,这样可以显著提升性能
preload
<!-- 使用 link 标签静态标记需要预加载的资源 --> <link rel="preload" href="/path/to/style.css" rel="external nofollow" as="style"> <!-- 或使用脚本动态创建一个 link 标签后插入到 head 头部 --> <script> const link = document.createElement('link'); link.rel = 'preload'; link.as = 'style'; link.href = '/path/to/style.css'; document.head.appendChild(link); </script>
preload
提供了一种声明式的命令,能让浏览器提前加载指定资源(如脚本或者样式表),并在需要执行的时候再执行。这在希望加快某个资源的加载速度时很有用。在 preload
下载完资源后,资源只是被缓存起来,浏览器不会对其执行任何操作。不执行脚本,不应用样式表。
注意:设置了 rel
属性的 link
标签 必须设置 as
属性来声明资源的类型,否则浏览器可能无法正确加载资源。常见的 as
属性包括:
script
: JavaScript 文件style
: CSS 样式文件font
: 字体文件image
: 图片文件fetch
: 通过fetch
或者XHR
获取的文件,例如ArrayBuffer
或者JSON
文件。
prefetch
<link rel="prefetch" href="static/img/delay_load_img.a5bb7c33.png" rel="external nofollow" >
prefetch
(链接预取)是一种浏览器机制,其利用浏览器空闲时间来下载或预取用户在不久的将来可能访问的文档。网页向浏览器提供一组预取提示,并在浏览器完成当前页面的加载后开始静默地拉取指定的文档并将其存储在缓存中。当用户访问其中一个预取文档时,便可以快速的从浏览器缓存中得到。prefetch
是一个低优先级的资源提示,允许浏览器在后台空闲时获将来可能用得到的资源,并且将他们存储在浏览器的缓存中。一旦一个页面加载完毕就会开始下载其他的资源,然后当用户点击了一个带有 prefetched 的连接,它将可以立刻从缓存中加载内容。
Preload & Prefetch 的区别
preload
的设计初衷是为了尽早加载首屏需要的关键资源,从而提升页面渲染性能;preload 是告诉浏览器页面必定需要的资源,浏览器一定会加载这些资源prefetch
声明的是将来可能访问的资源,因此适合对异步加载的模块、可能跳转到的其他路由页面进行资源缓存,浏览器不一定会加载这些资源- 大部分场景下无需特意使用
preload
;类似字体文件这种隐藏在脚本、样式中的首屏关键资源,建议使用preload
; - 异步加载的模块(典型的如单页系统中的非首页)建议使用
prefetch
,大概率即将被访问到的资源可以使用prefetch
提升性能和体验 - 注:preload 和 prefetch 混用的话,并不会复用资源,而是会重复加载。
不同资源加载的优先级规则
DevTools Priority 体现的优先级,一共分成五个级别:
- Highest 最高
- Hight 高
- Medium 中等
- Low 低
- Lowest 最低
其中
- html 主要资源,其优先级是最高的
- css 样式资源,其优先级也是最高的
- script 脚本资源,优先级不一
- font 字体资源,优先级不一
懒加载
懒加载也叫延迟加载;页面加载速度快、可以减轻服务器的压力、节约了流量,用户体验好;
懒加载的几种实现方式
- 纯粹的延迟加载,使用setTimeOut或setInterval进行加载延迟.
- 条件加载,符合某些条件,或触发了某些事件才开始异步下载
- 可视区加载,即仅加载用户可以看到的区域,这个主要由监控滚动条来实现
图片懒加载
对于一些图片比较多的页面,用户打开页面后,只需要呈现出在屏幕可视区域内的图片,当用户滑动页面时,再去加载出现在屏幕内的图片,以优化页面的加载效果;
实现:页面中的img元素,如果没有src属性,浏览器就不会发出请求去下载图片,只有通过javascript设置了图片路径,浏览器才会发送请求。懒加载的原理就是先在页面中把所有的图片统一使用一张占位图进行占位,把正真的路径存在元素的“data-url”(这个名字起个自己认识好记的就行)属性里,要用的时候就取出来,再设置。
获取屏幕可视窗口大小的几种方法
window.innerHeight // 标准浏览器及IE9+ document.documentElement.clientHeight // 标准浏览器及低版本IE标准模式 document.body.clientHeight // 低版本混杂模式
获取浏览器窗口顶部与文档顶部之间的距离,即滚动条滚动的距离
window.pagYoffset // 标准浏览器及IE9+ document.documentElement.scrollTop // 兼容ie低版本的标准模式 document.body.scrollTop // 兼容混杂模式;
路由懒加载
在SPA应用中,一个路由对应一个页面,如果我们不做任何处理,项目打包时,所有的页面都会打包成一个文件,当用户去打开首页时,就会去一次性加载所有的资源,这样首页加载就会慢,降低用户体验,那有什么办法不一次性加载所有资源?是用什么原理实现的?
在ES6中,有一个动态加载模块的方法:import();懒加载就是根据import()去实现的,调用import()之处,被作为分离模块的起点,意思是,被请求的模块和它引用的所有子模块,会被分离到一个单独chunk中;所以实现懒加载的方法,就是将需要进行懒加载的子模块分离出来,打包成一个单独的文件,这样就不会一次加载所有的资源了;
// 通过webpackChunkName设置分割后代码块的名字 const Home = () => import(/* webpackChunkName: "home" */ "@/views/home/index.vue"); const MetricGroup = () => import(/* webpackChunkName: "metricGroup" */ "@/views/metricGroup/index.vue"); const routes = [ { path: "/", name: "home", component: Home }, { path: "/metricGroup", name: "metricGroup", component: MetricGroup }, ]
注:webpackChunkName 作用是 webpack 在打包的时候,对异步引入的库代码(lodash)进行代码分割时,设置代码块的名字。webpack 会将任何一个异步模块与相同的块名称组合到相同的异步块中
组件懒加载
这个实现原理和路由懒加载是一样的,都是通过import()的方式实现的,那组件懒加载什么时候用呢?
假设有两个复杂页面都使用到了同一个组件,项目打包后,发现两个页面均包括了该组件的代码;且该组件又不是一进入页面就触发的,需要一定条件才触发的;这个时候就比较适合使用组件懒加载;即组件懒加载的使用场景有:
- 该页面文件体积大,导致页面打开慢,可以通过懒加载进行资源拆分,利用浏览器的并行下载提升速度
- 该组件又不是一进入页面就触发的,需要一定条件才触发的(比如弹窗)
- 该组件复用性高,很多页面引用,可以理由懒加载单独形成一个文件
其他时候不建议拆分过细,因为会造成浏览器http请求增多;
最后
预加载和懒加载都有各自的优缺点,我们要根据不同的情况选择不同的技术去解决问题。
加载全部内容