废话:异步加载和预加载一直是前端优化的必备技能之一。今天我们就来深入分析几个常用的关键点。异步加载废话不多说,任何啰嗦的教程都不值得一张清晰明了的高清大图:以及预加载(preload,prefetch,dns-prefetch,preconnect,prerender)")从这张图上,我们看到了什么可以概括为以下四点:默认先解析HTML,再加载JS,此时先中断HTML解析,再执行JS,最后JS执行完毕,恢复HTML解析。defer的情况下,HTML和JS齐头并进,最后执行JSasync情况下,HTML和JS齐头并进,可能在HTML解析之前就完成了JS的执行,最终的模块情况和defer类似,除了提取过程中会加载多个JS文件,基本上已经区分了,我懂了,那我怎么记呢?默认的情况我们已经很熟悉了,就不用再记了。defer的翻译意思是延迟,即pr拖沓,所以是懒惰,也就是说,我什么都不想做,就是你把饭摆在我面前,我也懒得动嘴。这样一想,我们不就记住了,就算你客户端下载了JS文件,我也懒得去执行。到头来,真的是大家都干完了,才舍不得执行JS文件。async翻译过来就是异步,异步,异步的意思,不就是一步一步来吗,一步到位,就是我只要下载了,马上执行,至于其他的,我想都别想。Module翻译过来就是模块的意思。用过es6的人基本都懂这个关键字。加载和defer类似,只是可以加载多个js文件。我们来看看这几个加载DOM的事件时机:和预加载(preload,prefetch,dns-prefetch,preconnect,prerender)")从这张图我们可以看出以下几点:async会在加载完JS后立即执行,最迟会在load事件之前执行,defer会在HTML解析完成后执行,最迟会在DOMContentLoaded事件之前执行。从上面我们可以看出,如果你的脚本依赖DOM是否构建完成,可以使用defer;如果不需要DOM构建,可以放心使用async。deferdefer属性只适用于外部脚本,即只有当src属性存在时才会生效;如果一个script标签上有defer属性和async属性,浏览器会如何解决这种情况呢?我们通过一段代码来验证结果,详情请点击这里。也就是说defer的优先级没有async高。让我们看看规范如何处理这种情况。即使指定了async属性,也可以指定defer属性,以导致仅支持defer(而不是async)的旧版Web浏览器回退到defer行为,而不是默认的阻塞行为。规范只是写明在不支持async的情况下,浏览器会回退支持defer,但并没有明确说明两者都支持的情况,也就是说这种情况是浏览器自己处理的。经过测试,各浏览器表现如下:Chrome浏览器的性能解析为异步特性。Safari浏览器的表现是异步特性。Opera浏览器的表现是异步特性。最高。兼容性我们来看看defer的兼容性。移动端绿色环保,放心使用。IE10以上可以放心使用。IE6-9有个小问题就是不会按照script标签的执行顺序执行。脚本库不用管,但是依赖库就不行了。比如你的项目依赖jQuery,之后马上使用jQuery的方法可能会出现问题。和preloading(preload,prefetch,dns-prefetch,preconnect,prerender)")async和defer一样,只适用于外部脚本,即只有src属性存在时才会生效。兼容性async兼容性在移动端同样是大绿,IE只支持IE10+。和预加载(preload,prefetch,dns-prefetch,preconnect,prerender)")module在现代浏览器中,我们可以声明acript标签type='module'属性来包含ES6的模块导入和导出语法,像这样:看起来不像人很兴奋,貌似对开发者也很友好,但是和传统脚本也有几点不同:模块默认使用“usestrict”模式,也就是不能使用arguments.callee等语法,模块会只加载一次,不管前后写多少次。不支持注解。模块有自己的词法作用域,比如定义一个vara=1,并且没有创建全局变量,所以不能通过window.a访问它的值。模块导入方式目前只支持以下几种模式:supportimport{math}from'./math.mjs';import{math}from'../math.mjs';import{math}from'/modules/math.mjs';import{math}from'https://simple.example/modules/math.mjs';//不支持从“jquery”导入{math};当然,浏览器厂商也在考虑支持import{math}from'jquery'这种格式,但是还有很长的路要走。module默认是defer,所以不需要在module中加入defer很熟悉,不支持这种写法,但是支持async属性,其加载和渲染方式类似async,这里就不细说了。移动端兼容性还算不错,但是IE好像败下阵来了。加油,只要还是支持edge16+,对于不支持modules的浏览器,可以使用nomodule属性作为版本回退方案。以及预加载(preload,prefetch,dns-prefetch,preconnect,prerender)")最后说一下模块的使用。对于大型项目(超过100个模块)不建议直接使用模块语法。你应该使用Webpack、Rollup、Parcel等打包工具,由于静态导入或导出语法是静态可解析的,可以通过打包工具去除冗余模块,我们考虑如下场景:import{Modal}from'./util.js';Modal({title:'hello'})如果我们用打包工具打包这段代码,最终生成的js文件只会包含Modal函数,如果不使用打包工具,浏览器会下载整个utiljs文件,并且通过进一步分析,我们了解到使用了Modal函数,对于没有使用util中所有函数的方法来说,这是一种不必要的带宽浪费。预加载是我们浏览器加载资源的时候。对于每个资源都有它们自己的默认优先级,如果我们能够修改每个资源的默认优先级,我们几乎可以按照我们的预期加载我们想要加载的资源。以谷歌浏览器为例,我们打开控制台,切换到网络选项,点击刷新页面,在网络下的标题行点击鼠标右键,勾选优先级,可以看到加载资源的优先级。我们可以看到样式级别高于脚本优先级。毕竟页面Loadingin的一部分肯定是需要先渲染的样式,否则整个页面就会被撕裂,用户体验不好。而preloading(preload,prefetch,dns-prefetch,preconnect,prerender)")preloadpreload翻译为预加载。一旦启用,它会通知浏览器应该尽快加载资源。如果提取的资源不可用3s使用,在GoogleDevTools中会触发警告信息和preload(preload,prefetch,dns-prefetch,preconnect,prerender)")大概语法如下: