Skip to content

Commit 1a7fdd7

Browse files
committed
feat: add prefetchFiles prop, closes #1
1 parent b31b2a1 commit 1a7fdd7

File tree

8 files changed

+250
-1003
lines changed

8 files changed

+250
-1003
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ All [props](https://router.vuejs.org/api/#router-link-props) of `<router-link>`
5757

5858
Whether to prefetch the matched route component.
5959

60+
### prefetchFiles
61+
62+
- Type: `string[]`
63+
- Examples: `['/foo.css']`
64+
65+
A list of addtional files to prefetch. By default we only prefetch the route component.
66+
6067
## Credits
6168

6269
Inspired by [quicklink](https://github.com/GoogleChromeLabs/quicklink) and [`nuxt-link`](https://github.com/nuxt/nuxt.js/pull/4574/).

example/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ const Nav = {
2222
</ul>
2323
<ul style="margin-top: 110vh;">
2424
<li>
25-
<router-link to="/page/4">page 4</router-link>
25+
<router-link to="/page/4" prefetchFiles={['/foo']}>
26+
page 4
27+
</router-link>
2628
</li>
2729
<li>
2830
<router-link to="/page/5">page 5</router-link>

package.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"scripts": {
1111
"test": "npm run lint",
1212
"lint": "xo",
13-
"build": "bili --js buble --format es --filename index.js",
13+
"build": "rollup -c",
1414
"prepublishOnly": "npm run build",
1515
"example": "poi example/index.js --serve --jsx vue",
1616
"commit": "git-cz"
@@ -22,7 +22,6 @@
2222
"author": "egoist<0x142857@gmail.com>",
2323
"license": "MIT",
2424
"devDependencies": {
25-
"bili": "^3.4.2",
2625
"commitizen": "^3.0.5",
2726
"cz-conventional-changelog": "^2.1.0",
2827
"eslint-config-prettier": "^3.3.0",
@@ -33,6 +32,10 @@
3332
"lint-staged": "^7.2.0",
3433
"poi": "^12.4.6",
3534
"prettier": "^1.15.2",
35+
"rollup": "^1.1.2",
36+
"rollup-plugin-buble": "^0.19.6",
37+
"rollup-plugin-node-resolve": "^4.0.0",
38+
"rollup-plugin-terser": "^4.0.2",
3639
"semantic-release": "^15.13.3",
3740
"vue": "^2.5.22",
3841
"vue-router": "^3.0.2",
@@ -49,7 +52,8 @@
4952
"browser"
5053
],
5154
"rules": {
52-
"no-new": "off"
55+
"no-new": "off",
56+
"no-unused-expressions": "off"
5357
}
5458
},
5559
"husky": {

rollup.config.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export default {
2+
input: 'src/index.js',
3+
output: {
4+
format: 'es',
5+
file: 'dist/index.js'
6+
},
7+
plugins: [
8+
require('rollup-plugin-node-resolve')(),
9+
require('rollup-plugin-buble')({
10+
transforms: { dangerousForOf: true }
11+
}),
12+
require('rollup-plugin-terser').terser()
13+
]
14+
}

src/index.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
const supported = typeof window !== 'undefined' && window.IntersectionObserver
1+
import prefetch from './prefetch'
2+
import { inBrowser } from './utils'
3+
4+
const supported = inBrowser && window.IntersectionObserver
5+
6+
export { prefetch }
27

38
export default (Vue, { componentName = 'RouterLink' } = {}) => {
49
const observer =
@@ -26,6 +31,9 @@ export default (Vue, { componentName = 'RouterLink' } = {}) => {
2631
prefetch: {
2732
type: Boolean,
2833
default: true
34+
},
35+
prefetchFiles: {
36+
type: Array
2937
}
3038
},
3139
mounted() {
@@ -59,13 +67,22 @@ export default (Vue, { componentName = 'RouterLink' } = {}) => {
5967
})
6068
},
6169
linkPrefetch() {
70+
// Prefetch route component
6271
const components = this.getComponents()
6372
for (const Component of components) {
6473
this.$emit('prefetch', this.to)
6574
Component() // eslint-disable-line new-cap
6675
Component._prefetched = true
67-
this.unobserve()
6876
}
77+
78+
// Prefetch addtional files
79+
if (this.prefetchFiles) {
80+
for (const file of this.prefetchFiles) {
81+
prefetch(file)
82+
}
83+
}
84+
85+
this.unobserve()
6986
}
7087
}
7188
}

src/prefetch.js

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/**
2+
* Portions copyright 2018 Google Inc.
3+
* Inspired by Gatsby's prefetching logic, with those portions
4+
* remaining MIT. Additions include support for Fetch API,
5+
* XHR switching, SaveData and Effective Connection Type checking.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
**/
19+
import { inBrowser } from './utils'
20+
21+
const preFetched = {}
22+
23+
/**
24+
* Checks if a feature on `link` is natively supported.
25+
* Examples of features include `prefetch` and `preload`.
26+
* @param {string} feature - name of the feature to test
27+
* @return {Boolean} whether the feature is supported
28+
*/
29+
function support(feature) {
30+
if (!inBrowser) {
31+
return
32+
}
33+
const link = document.createElement('link')
34+
return link.relList && link.relList.supports && link.relList.supports(feature)
35+
}
36+
37+
/**
38+
* Fetches a given URL using `<link rel=prefetch>`
39+
* @param {string} url - the URL to fetch
40+
* @return {Object} a Promise
41+
*/
42+
function linkPrefetchStrategy(url) {
43+
return new Promise((resolve, reject) => {
44+
const link = document.createElement(`link`)
45+
link.rel = `prefetch`
46+
link.href = url
47+
48+
link.addEventListener('load', resolve)
49+
link.addEventListener('error', reject)
50+
51+
document.head.appendChild(link)
52+
})
53+
}
54+
55+
/**
56+
* Fetches a given URL using XMLHttpRequest
57+
* @param {string} url - the URL to fetch
58+
* @return {Object} a Promise
59+
*/
60+
function xhrPrefetchStrategy(url) {
61+
return new Promise((resolve, reject) => {
62+
const req = new XMLHttpRequest()
63+
64+
req.open(`GET`, url, (req.withCredentials = true))
65+
66+
req.addEventListener('load', () => {
67+
req.status === 200 ? resolve() : reject()
68+
})
69+
70+
req.send()
71+
})
72+
}
73+
74+
/**
75+
* Fetches a given URL using the Fetch API. Falls back
76+
* to XMLHttpRequest if the API is not supported.
77+
* @param {string} url - the URL to fetch
78+
* @return {Object} a Promise
79+
*/
80+
function highPriFetchStrategy(url) {
81+
// TODO: Investigate using preload for high-priority
82+
// fetches. May have to sniff file-extension to provide
83+
// valid 'as' values. In the future, we may be able to
84+
// use Priority Hints here.
85+
//
86+
// As of 2018, fetch() is high-priority in Chrome
87+
// and medium-priority in Safari.
88+
return self.fetch
89+
? fetch(url, { credentials: `include` })
90+
: xhrPrefetchStrategy(url)
91+
}
92+
93+
const supportedPrefetchStrategy = support('prefetch')
94+
? linkPrefetchStrategy
95+
: xhrPrefetchStrategy
96+
97+
/**
98+
* Prefetch a given URL with an optional preferred fetch priority
99+
* @param {String} url - the URL to fetch
100+
* @param {Boolean} isPriority - if is "high" priority
101+
* @param {Object} conn - navigator.connection (internal)
102+
* @return {Object} a Promise
103+
*/
104+
function prefetcher(url, isPriority) {
105+
if (preFetched[url]) {
106+
return
107+
}
108+
109+
// Wanna do something on catch()?
110+
return (isPriority ? highPriFetchStrategy : supportedPrefetchStrategy)(
111+
url
112+
).then(() => {
113+
preFetched[url] = true
114+
})
115+
}
116+
117+
export default prefetcher

src/utils.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const inBrowser = typeof window !== 'undefined'

0 commit comments

Comments
 (0)