Skip to content
This repository was archived by the owner on Jan 21, 2021. It is now read-only.

Commit 845bf91

Browse files
authored
Merge pull request #29 from laysent/master
Support preloading additional types
2 parents f97c5e7 + 7a57710 commit 845bf91

File tree

3 files changed

+210
-51
lines changed

3 files changed

+210
-51
lines changed

README.md

Lines changed: 59 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,34 @@ preload-webpack-plugin
99
A Webpack plugin for automatically wiring up asynchronous (and other types) of JavaScript
1010
chunks using `<link rel='preload'>`. This helps with lazy-loading.
1111

12-
Note: This is an extension plugin for [html-webpack-plugin](https://github.com/ampedandwired/html-webpack-plugin) - a plugin that
12+
Note: This is an extension plugin for [html-webpack-plugin](https://github.com/ampedandwired/html-webpack-plugin) - a plugin that
1313
simplifies the creation of HTML files to serve your webpack bundles.
1414

15-
This plugin is a stop-gap until we add support for asynchronous chunk wiring to
15+
This plugin is a stop-gap until we add support for asynchronous chunk wiring to
1616
[script-ext-html-webpack-plugin](https://github.com/numical/script-ext-html-webpack-plugin/pull/9).
1717

1818
Introduction
1919
------------
2020

21-
[Preload](https://w3c.github.io/preload/) is a web standard aimed at improving performance
21+
[Preload](https://w3c.github.io/preload/) is a web standard aimed at improving performance
2222
and granular loading of resources. It is a declarative fetch that can tell a browser to start fetching a
2323
source because a developer knows the resource will be needed soon. [Preload: What is it good for?](https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/)
2424
is a recommended read if you haven't used the feature before.
2525

2626
In simple web apps, it's straight-forward to specify static paths to scripts you
27-
would like to preload - especially if their names or locations are unlikely to change. In more complex apps,
28-
JavaScript can be split into "chunks" (that represent routes or components) at with dynamic
27+
would like to preload - especially if their names or locations are unlikely to change. In more complex apps,
28+
JavaScript can be split into "chunks" (that represent routes or components) at with dynamic
2929
names. These names can include hashes, numbers and other properties that can change with each build.
3030

3131
For example, `chunk.31132ae6680e598f8879.js`.
3232

33-
To make it easier to wire up async chunks for lazy-loading, this plugin offers a drop-in way to wire them up
33+
To make it easier to wire up async chunks for lazy-loading, this plugin offers a drop-in way to wire them up
3434
using `<link rel='preload'>`.
3535

3636
Pre-requisites
3737
--------------
38-
This module requires Webpack 2.2.0 and above. It also requires that you're using
39-
[html-webpack-plugin](https://github.com/ampedandwired/html-webpack-plugin) in your Webpack project.
38+
This module requires Webpack 2.2.0 and above. It also requires that you're using
39+
[html-webpack-plugin](https://github.com/ampedandwired/html-webpack-plugin) in your Webpack project.
4040

4141
Installation
4242
---------------
@@ -68,30 +68,68 @@ and finally, configure the plugin in your Webpack `plugins` array after `HtmlWeb
6868
plugins: [
6969
new HtmlWebpackPlugin(),
7070
new PreloadWebpackPlugin()
71-
]
71+
]
72+
```
73+
74+
When preloading files, the plugin will use different `as` attribute depends on the type of each
75+
file. For each file ends with `.css`, the plugin will preload it with `as=style`, for each file ends
76+
with `.woff2`, the plugin will preload it with `as=font`, while for all other files, `as=script`
77+
will be used.
78+
79+
If you do not prefer to determine `as` attribute depends on suffix of filename, you can also
80+
explicitly name it using `as`:
81+
82+
```javascript
83+
plugins: [
84+
new HtmlWebpackPlugin(),
85+
new PreloadWebpackPlugin({
86+
rel: 'preload',
87+
as: 'script'
88+
})
89+
]
7290
```
7391

74-
By default, the plugin will assume async script chunks will be preloaded with `as=script`.
75-
This is the equivalent of:
92+
In case you need more fine-grained control of the `as` atribute, you could also provide a function here.
93+
When using it, entry name will be provided as the parameter, and function itself should return a
94+
string for `as` attribute:
95+
96+
```javascript
97+
plugins: [
98+
new HtmlWebpackPlugin(),
99+
new PreloadWebpackPlugin({
100+
rel: 'preload',
101+
as(entry) {
102+
if (/\.css$/.test(entry)) return 'style';
103+
if (/\.woff$/.test(entry)) return 'font';
104+
if (/\.png$/.test(entry)) return 'image';
105+
return 'script';
106+
}
107+
})
108+
]
109+
```
110+
111+
Notice that if `as=font` is used in preload, crossorigin will be added, otherwise the font resource
112+
might be double fetched. Explains can be found in [this article](https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf).
113+
114+
By default, the plugin will assume async script chunks will be preloaded. This is the equivalent of:
76115

77116
```js
78117
plugins: [
79118
new HtmlWebpackPlugin(),
80119
new PreloadWebpackPlugin({
81120
rel: 'preload',
82-
as: 'script',
83121
include: 'asyncChunks'
84122
})
85123
]
86124
```
87125

88-
For a project generating two async scripts with dynamically generated names, such as
126+
For a project generating two async scripts with dynamically generated names, such as
89127
`chunk.31132ae6680e598f8879.js` and `chunk.d15e7fdfc91b34bb78c4.js`, the following preloads
90128
will be injected into the document `<head>`:
91129

92130
```html
93-
<link rel="preload" href="chunk.31132ae6680e598f8879.js" as="script">
94-
<link rel="preload" href="chunk.d15e7fdfc91b34bb78c4.js" as="script">
131+
<link rel="preload" as="script" href="chunk.31132ae6680e598f8879.js">
132+
<link rel="preload" as="script" href="chunk.d15e7fdfc91b34bb78c4.js">
95133
```
96134

97135
You can also configure the plugin to preload all chunks (vendor, async, normal chunks) using
@@ -102,7 +140,6 @@ plugins: [
102140
new HtmlWebpackPlugin(),
103141
new PreloadWebpackPlugin({
104142
rel: 'preload',
105-
as: 'script',
106143
include: 'all'
107144
})
108145
]
@@ -114,7 +151,6 @@ plugins: [
114151
new HtmlWebpackPlugin(),
115152
new PreloadWebpackPlugin({
116153
rel: 'preload',
117-
as: 'script',
118154
include: ['home']
119155
})
120156
]
@@ -123,7 +159,7 @@ plugins: [
123159
will inject just this:
124160

125161
```html
126-
<link rel="preload" href="home.31132ae6680e598f8879.js" as="script">
162+
<link rel="preload" as="script" href="home.31132ae6680e598f8879.js">
127163
```
128164

129165
Filtering chunks
@@ -186,7 +222,7 @@ submitting a pull request through GitHub.
186222
Contributing workflow
187223
---------------------
188224

189-
`index.js` contains the primary source for the plugin, `test` contains tests and `demo` contains demo code.
225+
`index.js` contains the primary source for the plugin, `test` contains tests and `demo` contains demo code.
190226

191227
Test the plugin:
192228

@@ -203,16 +239,16 @@ $ npm run lint-fix # fix linting issues
203239
```
204240

205241
The project is written in ES2015, but does not use a build-step. This may change depending on
206-
any Node version support requests posted to the issue tracker.
242+
any Node version support requests posted to the issue tracker.
207243

208244
Additional Notes
209245
---------------------------
210246

211-
* Be careful not to `preload` resources a user is unlikely to need. This can waste their bandwidth.
247+
* Be careful not to `preload` resources a user is unlikely to need. This can waste their bandwidth.
212248
* Use `preload` for the current session if you think a user is likely to visit the next page. There is no
213-
100% guarantee preloaded items will end up in the HTTP Cache and read locally beyond this session.
249+
100% guarantee preloaded items will end up in the HTTP Cache and read locally beyond this session.
214250
* If optimising for future sessions, use `prefetch` and `preconnect`. Prefetched resources are maintained
215-
in the HTTP Cache for at least 5 minutes (in Chrome) regardless of the resource's cachability.
251+
in the HTTP Cache for at least 5 minutes (in Chrome) regardless of the resource's cachability.
216252

217253
Related plugins
218254
--------------------------

index.js

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
'use strict';
1818

1919
const objectAssign = require('object-assign');
20+
21+
const flatten = arr => arr.reduce((prev, curr) => prev.concat(curr), []);
22+
2023
const defaultOptions = {
2124
rel: 'preload',
22-
as: 'script',
2325
include: 'asyncChunks',
2426
fileBlacklist: [/\.map/]
2527
};
@@ -40,21 +42,14 @@ class PreloadPlugin {
4042
// get wired up using link rel=preload when using this plugin. This behaviour can be
4143
// configured to preload all types of chunks or just prefetch chunks as needed.
4244
if (options.include === undefined || options.include === 'asyncChunks') {
43-
let asyncChunksSource = null;
4445
try {
45-
asyncChunksSource = compilation
46-
.chunks.filter(chunk => !chunk.isInitial())
47-
.map(chunk => chunk.files);
46+
extractedChunks = compilation.chunks.filter(chunk => !chunk.isInitial());
4847
} catch (e) {
49-
asyncChunksSource = compilation.chunks
50-
.map(chunk => chunk.files);
48+
extractedChunks = compilation.chunks;
5149
}
52-
extractedChunks = [].concat.apply([], asyncChunksSource);
5350
} else if (options.include === 'all') {
5451
// Async chunks, vendor chunks, normal chunks.
55-
extractedChunks = compilation
56-
.chunks
57-
.reduce((chunks, chunk) => chunks.concat(chunk.files), []);
52+
extractedChunks = compilation.chunks;
5853
} else if (Array.isArray(options.include)) {
5954
// Keep only user specified chunks
6055
extractedChunks = compilation
@@ -66,19 +61,30 @@ class PreloadPlugin {
6661
return false;
6762
}
6863
return options.include.indexOf(chunkName) > -1;
69-
})
70-
.map(chunk => chunk.files)
71-
.reduce((prev, curr) => prev.concat(curr), []);
64+
});
7265
}
7366

7467
const publicPath = compilation.outputOptions.publicPath || '';
7568

76-
extractedChunks.filter(entry => {
69+
flatten(extractedChunks.map(chunk => chunk.files)).filter(entry => {
7770
return this.options.fileBlacklist.every(regex => regex.test(entry) === false);
7871
}).forEach(entry => {
7972
entry = `${publicPath}${entry}`;
8073
if (options.rel === 'preload') {
81-
filesToInclude+= `<link rel="${options.rel}" href="${entry}" as="${options.as}">\n`;
74+
// If `as` value is not provided in option, dynamically determine the correct
75+
// value depends on suffix of filename. Otherwise use the given `as` value.
76+
let asValue;
77+
if (!options.as) {
78+
if (entry.match(/\.css$/)) asValue = 'style';
79+
else if (entry.match(/\.woff2$/)) asValue = 'font';
80+
else asValue = 'script';
81+
} else if (typeof options.as === 'function') {
82+
asValue = options.as(entry);
83+
} else {
84+
asValue = options.as;
85+
}
86+
const crossOrigin = asValue === 'font' ? 'crossorigin="crossorigin" ' : '';
87+
filesToInclude+= `<link rel="${options.rel}" as="${asValue}" ${crossOrigin}href="${entry}">\n`;
8288
} else {
8389
// If preload isn't specified, the only other valid entry is prefetch here
8490
// You could specify preconnect but as we're dealing with direct paths to resources

0 commit comments

Comments
 (0)