preload 让加载和解析解耦
TL;DR
- preload本质:preload 是声明式的 fetch,可以改变浏览器加载资源的优先级,强制浏览器请求资源,同时不阻塞文档 onload 事件,也因此可以将 load 事件与脚本解析过程解耦
- prefetch本质:让浏览器空闲的时候加载下一页可能需要的资源,同样的load和解析解耦
- dns-fetch,让浏览器提前做dns预解析,当静态资源和html不在同一个域的时候,特别好用
- async,defer是script的属性,其让html解析与下载并行,但是async下载完之后立即执行,而defer是html解析完之后再执行,一般按照顺序执行,但据说不是百分百靠谱
概述
一般写html页面,script写在body结束标签前,这样只有当遇到script标签的时候,才会加载执行,费加载时间不科学吖!尽管大多数基于标记语言的资源能被浏览器的预加载器(Preloader)提前加载,但还是不尽如人意!
理想情况是,先从文档需要的资源,根据优先级,下载到本地,等到需要的时候,就可以直接使用或者解析执行!
然后!对!preload就可以干这个事!可随时关注preload的动态,就目前很多现代浏览器已经支持!
preload大白话的意思是,”嗨,浏览器!这个资源在这个页面后面会用到,现在先加载它吧。“
好处
好处远不止上面的:
- 本质!preload 是声明式的 fetch,可以改变浏览器加载资源的优先级,强制浏览器请求资源,同时不阻塞文档 onload 事件,也因此可以将 load 事件与脚本解析过程解耦
- 资源的提前加载。
<link rel="preload" href="late_discovered_thing.js" as="script">
- 字体的提前加载。
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
- 动态加载,但不执行。加载某一资源但却不想立即执行它,而是想在特定的时机执行。后面细说代码。
- 基于标记语言的异步加载。
<link rel="preload" as="style" href="asyncstyle.css" onload="this.rel='stylesheet'">
,加载完之后就可以应用到文档里了,script也同理,这里的onload是指window的onload事件,在加一些统计脚本的时候挺合适,不影响页面的onload,<link rel="preload" as="script" href="async_script.js" onload="var script = document.createElement('script'); script.src = this.href; document.body.appendChild(script);">
- 响应式加载。preload是在link标签上,也就是还可以配合media属性,能让浏览器加载自己需要的资源。
<link rel="preload" as="image" href="map.png" media="(max-width: 600px)"> <link rel="preload" as="script" href="map.js" media="(min-width: 601px)">
- 检测浏览器是不是支持preload,
var isSupportPreload = document.createElement("link").relList && document.createElement("link").relList.supports('preload')
怎么使用preload
先说说link标签
先说下link标签的使用。
- link规定了外部资源与当前文档的关系!
- 于是link有不可或缺的两个属性,
href
外部资源的路径,rel
外部资源与当前文档的关系,rel其实relation的缩写,但因为多数用于css,所以rel是stylesheet
的时候,是可以省略的 - link标签只能出现在
head
里 - 有全局属性和onload事件,onload事件是指文档所有资源加载完之后
- media:这个属性规定了外部资源适用的媒体类型。它的值必须是"媒体查询"。这个属性使得用户代理能选择最适合设备运行的媒体类型。
<link rel="stylesheet" media="(max-width: 800px)" href="example.css" />
- type这个属性被用于定义链接的内容的类型。这个属性的值应该是像text/html,text/css等MIME类型。这个属性常用的用法是定义链接的样式表,最常用的值是表明了CSS的text/css。
preload是在link上的
- 使用preload很简单,就是link的
rel="preload"
,href是仍然是资源路径,然后加上as来说明加载的内容的类型就可以了,常用的"script" "style" "image" "media" "document"
。 - preload加载字体的时候,crossorigin 属性是必须的,即便是字体资源在自家服务器上,因为用户代理必须采用匿名模式来获取字体资源。type 属性可以确保浏览器只获取自己支持的资源。比如Chrome 支持 WOFF2,看到这个就下载了,遇到不识别的类型就忽略
<!-- js --> <link rel="preload" href="static.js" as="script" onload="preloadFinished()"> <!-- css --> <link rel="preload" href="static.css" as="style" onload="preloadFinished()"> <!-- 字体,注意crossorigin和type的增加 --> <link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
细说,动态加载,但不执行
其实动态加载就是插入带preload的link,执行的时候,动态的创建script。
function loadScript(url){ var link = document.createElement("link"); link.href = url link.rel = "preload"; link.as = "script"; document.head.appendChild(link); } function execScript(url){ var script = document.createElement("script"); script.src = url; document.body.appendChild(script); } // 加载这个js,但不执行 loadScript("myscript.js") // 某个时机执行 setTimeout(function(){ execScript("myscript.js") },1000)
preload的坑
注意吖,不是说加载了就是执行!!!!!
也就是preload只管加载!!!
换句话说,你买东西,preload相当于快递到你家门口了,但是你只有拆了包裹才算是使用!!!
代码的体现就是,以前的<link href="xx.css">
和<script src="xxx.js">
还是要写在相应的位置的!
然后再在head头里加上<link rel="preload" href="xx.css" as="style"><link rel="preload" href="xxx.js" as="script">
,preload是锦上添花的!
浏览器加载机制
我觉得吧,先去知道浏览器加载机制之后,才能更加体会到preload的美!
这边简单的总结下,浏览器的加载机制:
- 资源分类。html,css,js...
- 安全策略检查。是否加载某处的资源
- 资源优先级计算。html、css、font资源 => preload资源、script、xhr请求 => 图片、语音、视频 => prefetch预读取的资源(多用于dns)
- 综合安全策略和优先级,开始下载资源
- 谷歌控制台查看资源优先级的方法:打开开发者工具 -> 点击network -> 刷新页面 -> 在出现的表格的表头那行右击(就是Name,type,size...)选择
Priority
prefetch与preload
prefetch相当于说,“嗨,浏览器,下个页面可能要用到这个资源,你闲着无聊就帮我加载下呗~”。 用法跟preload差不离,link的rel="prefetch"
和preload区别:
- preload是让浏览器提前加载当前页面比较重要的资源,,prefetch是让浏览器没活的时候加载下个页面可能用到的资源
- prefetch并没有同域的限制,但preload有
- 都不会影响onload事件,准备说有点load事件和解析解耦的感觉
- 当页面上使用到这个资源时候 preload 资源还没下载完,这时候不会造成二次下载,会等待第一次下载并执行脚本。prefetch差不多吧。
- 对于 preload 来说,一旦页面关闭了,它就会立即停止 preload 获取资源,而对于 prefetch 资源,即使页面关闭,prefetch 发起的请求仍会进行不会中断。
随手说下dns-prefetch
先说域名只是服务器ip地址的美化,也就是虽然请求域名,但实际是先请求dns服务器,dns服务器返回域名所代表的真正的服务器ip,浏览器再向服务器发送请求。
dns-prefetch就是让浏览器先将域名发给dns服务器,让浏览器知道,域名所指的真正的服务器,之后在发送请求的时候,就直接发给服务器了。
简单的一行就能让支持的浏览器提前解析DNS。也就是说在浏览器请求资源时,DNS查询就已经准备好了。
使用方式<link rel="dns-prefetch" href="//example.com">
使用场景:
- 项目的静态资源和html不在同一个域上
- 有重定向的域名
还有async,defer
直接引用这个回答,当浏览器碰到 script 脚本的时候:
- 没有 defer 或 async,浏览器会立即加载并执行指定的脚本,“立即”指的是在渲染该 script 标签之下的文档元素之前,也就是说不等待后续载入的文档元素,读到就加载并执行。
<script src="script.js"></script>
- 有 async,加载和渲染后续文档元素的过程将和 script.js 的加载与执行并行进行(异步)。
<script async src="script.js"></script>
- 有 defer,加载后续文档元素的过程将和 script.js 的加载并行进行(异步),但是 script.js 的执行要在所有元素解析完成之后,DOMContentLoaded 事件触发之前完成。
<script defer src="myscript.js"></script>