prefetch 和 preload 及 webpack 的相关处理

使用预取和预加载是网站性能和用户体验提升的一个很好的途径,本文介绍了使用 prefetch 和 prefetch 进行预取和预加载的方法,并使用 webpack 进行实现

Link 的链接类型

<link> 标签的 rel 属性可以定义链接类型,prefetch 是其中的一种,与 href 配合使用可以预取或预加载对应资源

<link rel="prefetch" herf="URL">

preload 是另外一种类型,同样用 href 定义资源地址,但其处理预取外,还会对资源进行解析,所以还要增加属性 as,说明资源的类型

<link rel="preload" href="URL" as="MIME_TYPE">

预取资源

prefetch 表示用户在接下来的浏览中(例如在下一个页面),有可能用到对应资源,提示浏览器要在闲时获取对应资源

先新建文件夹 prefetch-preload-demo(本文所有代码将在此创建),安装相关依赖,并新建文件夹 static

mkdir prefetch-preload-demo
cd prefetch-preload-demo
npm init -y
npm i -D http-server
mkdir static

在 static 中创建 prefetch.htmlmain.jsscript.js

prefetch.html 定义了一个 relprefetch 的链接

<html>
<head>
<title>Prefetch</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="prefetch" href="script.js">
</head>
<body>
<script src="main.js"></script>
</body>
</html>

main.js 创建了一个按钮,并绑定了点击事件

let button = document.createElement('button');
button.innerHTML = 'Add Script';
button.addEventListener('click', e => {
  let script = document.createElement("script");
  script.src = "script.js";
  document.head.appendChild(script);
});
document.body.appendChild(button);

script.js 只是简单的打印了一下

console.log('script run');

运行服务器(也可在 package.json 中增加 server 脚本)

npx http-server

访问 http://localhost:8080 并导航至 static 中,点击 prefetch.html,或者直接访问线上页面,初始状态下,查看控制台的网络选项卡下的内容如下(不要勾选 Disable Cache,点击右侧齿轮,勾选 Use large request rows

  • script.js 被 fetch 下来,size 列的两个数字,275 B 表示下载的字节大小,0 B 表示解析的字节大小(即目前并没有解析)
  • 控制台是空的,即脚本没有运行

点击页面上的 Add Script,会在页面增加地址为 script.js<script> 标签,此时网络选项卡会增加以下内容

  • 下载字节量为 (prefetch cache) ,即直接从预取缓存获取资源,下面的解析后的字节不再为 0
  • 控制台打印出脚本中的调试内容,即这时脚本才被解析并运行

预加载资源

preload 表示用户在当前的浏览中(往往是在当前页面),极有可以可能用到对应资源,提示浏览器要优先获取对应资源

将 prefetch.html 的 link 标签的 prefetch 改为 preload,并增加资源类型 asscript,即得 preload.html

<link rel="preload" href="script.js" as="script">

访问本地服务器对应的 prefetch.html,或者直接访问线上页面,初始状态下,查看控制台的网络选项卡下的内容如下

  • script.js 被优先下载, size 列的解压字节不再为 0,即 preload 除了把脚本下载了下来,还进行了解析
  • 控制台目前仍为空,即脚本虽然被解析,但并没有运行

点击 Add Script,网络选项卡并没有增加任何记录,但是控制台输出了脚本的打印内容

  • 因为脚本已经解析完成,所以连从缓存获取都不需要了,直接运行即可
  • 如果没有在 3 秒内点击 Add Script,控制台会进行警告,因为没有及时使用应该优先加载的资源

The resource https://chanvinxiao.com/demo/html/script.js was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate as value and it is preloaded intentionally.

webpack 的相关处理

运行以下命令安装相关依赖,并新建文件夹 src

npm i -D webpack webpack-cli html-webpack-plugin preload-webpack-plugin@3.0.0-beta.4
mkdir src

  • PreloadWebpackPlugin 的当前版本 2.x 与 webpack 当前版本 4.x 不兼容,所以需要指定版本号为最新的 3.x beta

main.jsscript.js 复制到 src 中,并将 main.js 的点击事件处理更新为

button.addEventListener('click', e => {
  import(/* webpackChunkName: "script" */ './script.js');
});

  • import() 为动态加载脚本,webpack 会生成类似以上动态创建 script 标签的代码
  • import 里的注释为特殊含义的魔法注释,如果不设置 webpackChunkName,加载的脚本将被按数字次序命名

增加 webpack.config.js 如下

const HtmlWebpackPlugin = require('html-webpack-plugin');
const PreloadWebpackPlugin = require('preload-webpack-plugin');

module.exports = {
  entry: './src/main.js',
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'preload.html'
    }),
    new PreloadWebpackPlugin()
  ]
}

  • HtmlWebpackPlugin 将自动生成相应的 html 文件,默认为 index.html,这里通过设置 filename 选项更改
  • PreloadWebpackPlugin 为 HtmlWebpackPlugin 的插件,默认为其动态加载资源增加链接类型为 preloadlink 标签,其 as 的值可根据后缀自动判断

PreloadWebpackPlugin 也支持 prefetch,需要增加 rel 选项为 prefetch

    new HtmlWebpackPlugin({
      filename: 'prefetch.html'
    }),
    new PreloadWebpackPlugin({
      rel: 'prefetch'
    })

不过要同时生成 preload.html 和 prefetch.html,需要在对应的 PreloadWebpackPlugin 中设置 excludeHtmlNames 排除对方,否则会同时产生 preload 和 prefetch 的 link 标签

    new HtmlWebpackPlugin({
      filename: 'preload.html'
    }),
    new HtmlWebpackPlugin({
      filename: 'prefetch.html'
    }),
    new PreloadWebpackPlugin({
      excludeHtmlNames: ['prefetch.html']
    }),
    new PreloadWebpackPlugin({
      rel: 'prefetch',
      excludeHtmlNames: ['preload.html']
    })

构建文件(也可在 package.json 中增加 build 脚本)

npx webpack

dist 文件夹中将生成 prefetch.html 和 preload.html,访问本地服务器对应地址,即可得到与以上静态页面同样的效果

总结

此文使用静态页面和 webpack 打包两种方式演示了预取和预加载的实现 ,主要技术点如下:

  • ELEMENT.appendChild 动态创建脚本
  • import() 动态加载脚本并设置魔法注释
  • html-webpack-plugin 及其插件的配置
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,743评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,296评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,285评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,485评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,581评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,821评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,960评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,719评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,186评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,516评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,650评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,936评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,757评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,991评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,370评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,527评论 2 349

推荐阅读更多精彩内容