写在最前
在博客魔改过程中,不可避免的会引入大量的第三方脚本(js),而基于页面读取js的加载顺序,每当浏览器在加载html的过程中遇到<script>js代码片段</script>
这样的标签时,浏览器会暂停继续构建html,而是优先执行当前的js脚本,等执行完毕后再继续加载后面的html。
至于外部脚本 <script src="js外链"></script>
这样的写法,更是要先下载脚本,然后再执行,之后才能继续处理剩余的页面。
无形中,多出了一大把的加载时间。这也是我们常说魔改是对博客速度的负优化的原因之一。
因此可以通过给<script></script>
添加defer
和ansyc
属性来实现异步加载,调整js的加载时间和顺序,确保浏览器构建HTML的过程一切顺利。
参考内容
原理分析
首先要清楚defer、async是什么,有什么区别。defer
和async
是<script>
标签的两个属性,用来控制js脚本的加载。
以下先引用参考教程的原文。
原理拆解
按照上面的原理我画了一张图来解释加载顺序和对HTML加载时间的影响。
可以看到,总的HTML加载时间,下载脚本的时间,执行脚本的时间是固定的。不同之处在于HTML阻塞的时间以及执行脚本的次序。
不加任何
HTML加载时间+下载脚本时间+执行脚本时间async
和defer
的情况,页面总加载时间最长,是加了
HTML加载时间+执行脚本时间async
和defer
的时间,在加载HTML时间足够长的情况下,所有静态资源总的加载时间都是
适用内容
然后,必须考虑到页面加载时间和脚本加载顺序,以及各个脚本直接存在的依赖关系。
从参考文档来看,
defer
特性除了告诉浏览器不要阻塞页面之外,还可以确保脚本执行的相对顺序。- 这个很适合使用到Vue和jquery等js框架的js脚本,给它们添加
defer
属性以后,可以确保HTML加载完毕,且js下载完毕后,各个js脚本继续按照引入的顺序执行,从而确保不会因为依赖缺失而报错。
- 这个很适合使用到Vue和jquery等js框架的js脚本,给它们添加
- 其他脚本不会等待
async
脚本加载完成,同样,async
脚本也不会等待其他脚本。- 这个适合使用原生js,没有引用任何js框架,自己独立就能运行,且体量相对较小的js脚本,随页面加载同步下载执行。
使用范例
此处以我使用的Butterfly
主题中添加的几个js为例。
这里首先说一下我个人的想法,不一定对,本着尽可能减少页面阻塞资源的情况下,我建议:
- 尽可能给每个script标签都加上
defer
和async
。 - 确定为独立脚本或原生脚本的情况下,使用
async
。这一条并不适用于大型js(例如busuanzi.js)。原因可以看上面的原理拆解图,大型js的执行时间依然会造成大面积的HTML阻塞。 - 如果实在不清楚应该添加哪个,则以
defer
求稳,确保依赖项不会缺失。 - 总的来说,
async
加在那些非必要的,起装饰或者优化效果的js上,defer
加在那些确保页面完整性的必要js上。 - 来自Heo的建议,不要给影响页面生成的js(例如
util.js、main.js、lazy_load.js、vue.js、jquery.js
)添加异步加载标签(不论是async
还是defer
都不要加),不然会造成大面积页面功能模块失效。推测是由于部分HTML元素需要由js动态写入,抑或部分整合在各个pug
中的script
片段无法添加defer
导致。
inject配置项
1 | bottom: |
CDN配置项
CDN配置项的引入先于inject,至于如何给script标签添加defer
和async
属性,则要先找到引入位置。
在[Blogroot]\themes\butterfly\layout\includes\additional-js.pug
中修改。
1 | div |
拓展内容,CSS的异步加载
页面载入并渲染的流程可以简单理解为以下情况:
与js的加载执行过程十分相似,加载CSS时也会造成HTML加载阻塞。
既然js可以异步加载,那CSS理论上应该也可以。虽然不能像js一样直接通过async
和defer
来定义加载顺序那么方便。
目前有效手段有两种,一种是通过定义一个无效media,使得该CSS引入优先级最低,再用onload="this.media='all'"
在页面加载完成后纠正media,并加载CSS。
写法如下:
未加入异步加载:
1 | <link rel="stylesheet" href="/example.css"> |
加入异步加载后:
1 | <link rel="stylesheet" href="/example.css" media="defer" onload="this.media='all'"> |
注意此处的media=”defer”中的defer并无意义,是个无效media(只是方便理解才这么写),目的是让浏览器认为这个CSS文件优先级非常低,从而在不阻塞的情况下进行加载。
加载完成以后,样式转为对所有设备生效是onload="this.media='all'"
,但是其实还有其他的设备可供选择。
值 | 描述 |
---|---|
screen | 计算机屏幕(默认)。 |
tty | 电传打字机以及类似的使用等宽字符网格的媒介。 |
tv | 电视机类型设备(低分辨率、有限的滚屏能力)。 |
projection | 放映机。 |
handheld | 手持设备(小屏幕、有限带宽)。 |
打印预览模式/打印页面。 | |
braille | 盲人点字法反馈设备。 |
aural | 语音合成器。 |
all | 适用于所有设备。 |
还有一种利用rel="preload"
属性的方案,但是目前只有Chrome浏览器可以完美支持,等推广还需要很长一段时间,写法如下:
1 | <link rel="preload" href="cssfile.css" as="style" onload="this.rel='stylesheet'"> |
CSS整合
以下内容仅针对butterfly主题。其他主题可以理解原理后进行操作。实际上就是使用@import引入自定义样式。
相信很多小伙伴在看了上述的CSS异步加载方案后,肯定迫不及待的去给自己博客的魔改自定义样式添加异步加载属性了,就算不是,现在也给我演一下,配合我的工作,这么做虽然可以减少HTML页面阻塞,但是很可能会出现首屏页面有那么几秒钟存在大片无样式的板块的情况。
所以我们可以确立一条原则,为了追求视觉体验,不要给index.css
等涉及首页样式的CSS文件添加异步加载。
然而事实上,相比于给CSS添加异步加载,不如将我们的魔改样式整合到index.css
文件内,减少对服务器的请求次数。这样更能节省加载时间。
我的做法如下:
在
[Blogroot]\themes\butterfly\source\css\
路径下新建_custom
文件夹,然后把魔改样式的CSS
文件拖动进去。文件目录层级可以表示为以下情况:1
2
3
4
5
6
7source
|__ css
|__ _custom
|__ custom1.css
|__ custom2.css
|__ custom3.css
|__ index.styl在
[[Blogroot]\themes\butterfly\source\css\index.styl
中新增一行代码:@import '_custom/*.css'
,表示引入_custom
文件夹下的所有CSS文件。(此处为了确保自定义样式不会被覆盖,放在最底下。)1
2
3
4
5
6
7
8
9
10
11@import 'nib'
@import '_third-party/*.css'
// project
@import 'var'
@import '_global/*'
@import '_highlight/highlight'
@import '_page/*'
@import '_layout/*'
@import '_tags/*'
@import '_mode/*'
+ @import '_custom/*.css'如果是使用的外链css,也可以在这里引入。同样是修改
[Blogroot]\themes\butterfly\source\css\index.styl
代码,使用@import
逐行引入(此处为了确保自定义样式不会被覆盖,放在最底下。):1
2
3
4
5
6
7
8
9
10
11@import 'nib'
@import '_third-party/*.css'
// project
@import 'var'
@import '_global/*'
@import '_highlight/highlight'
@import '_page/*'
@import '_layout/*'
@import '_tags/*'
@import '_mode/*'
+ @import 'https://cdn.jsdelivr.net/gh/username/repo/css/example.css'这样一来,每个魔改方案的CSS依然可以在独立的CSS文件中找到并修改(如果是手动添加整合的话,只能用注释分割,显然很不利于后续查找修改),而在每次提交时,运行hexo g的过程中就会将所有CSS文件都整合到
index.css
,可以在主题配置文件的CDN配置项里给index.css
加上jsdelivr
进一步提升加载速度(注意刷新jsdelivr的缓存)。
Use this card to join the candyhome and participate in a pleasant discussion together .
Welcome to 测试's candyhome,wish you a nice day .