Skip to content

Commit

Permalink
Introduce a simple prefetching solution (vercel#957)
Browse files Browse the repository at this point in the history
* Implement a very simple prefetching solution.

* Remove next-prefetcher.

* Require 'whatwg-fetch' only in the client.

* Use xhr in the code.

* Use a simple fetching solution.

* Fix 404 and xhr status issue.

* Move the prefetching implementation to next/router.

* Add deprecated warnning for next/prefetch

* Run only 2 parellel prefetching request at a time.

* Change xhr to jsonPageRes.

* Improve the prefetching logic.

* Add unit tests covering the Router.prefetch()

* Update examples to use the new syntax.

* Update docs.

* Use execOnce() to manage warn printing.

* Remove prefetcher building from the flyfile.js
Because, we no longer use it.
  • Loading branch information
arunoda authored Feb 15, 2017
1 parent d382e4d commit 14c86be
Show file tree
Hide file tree
Showing 13 changed files with 267 additions and 395 deletions.
27 changes: 12 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -344,48 344,45 @@ Router.onRouteChangeError = (err, url) => {
<ul><li><a href="./examples/with-prefetching">Prefetching</a></li></ul>
</details></p>

Next.js exposes a module that configures a `ServiceWorker` automatically to prefetch pages: `next/prefetch`.
Next.js has an API which allows you to prefetch pages.

Since Next.js server-renders your pages, this allows all the future interaction paths of your app to be instant. Effectively Next.js gives you the great initial download performance of a _website_, with the ahead-of-time download capabilities of an _app_. [Read more](https://zeit.co/blog/next#anticipation-is-the-key-to-performance).

> With prefetching Next.js only download JS code. When the page is getting rendered, you may need to wait for the data.
#### With `<Link>`

You can substitute your usage of `<Link>` with the default export of `next/prefetch`. For example:
You can add `prefetch` prop to any `<Link>` and Next.js will prefetch those pages in the background.

```jsx
import Link from 'next/prefetch'
import Link from 'next/link'

// example header component
export default () => (
<nav>
<ul>
<li><Link href='/'><a>Home</a></Link></li>
<li><Link href='/about'><a>About</a></Link></li>
<li><Link href='/contact'><a>Contact</a></Link></li>
<li><Link prefetch ref='/'><a>Home</a></Link></li>
<li><Link prefetch href='/about'><a>About</a></Link></li>
<li><Link prefetch href='/contact'><a>Contact</a></Link></li>
</ul>
</nav>
)
```

When this higher-level `<Link>` component is first used, the `ServiceWorker` gets installed. To turn off prefetching on a per-`<Link>` basis, you can use the `prefetch` attribute:

```jsx
<Link href='/contact' prefetch={false}><a>Home</a></Link>
```

#### Imperatively

Most needs are addressed by `<Link />`, but we also expose an imperative API for advanced usage:
Most prefetching needs are addressed by `<Link />`, but we also expose an imperative API for advanced usage:

```jsx
import { prefetch } from 'next/prefetch'
import Router from 'next/router'
export default ({ url }) => (
<div>
<a onClick={ () => setTimeout(() => url.pushTo('/dynamic'), 100) }>
A route transition will happen after 100ms
</a>
{
// but we can prefetch it!
prefetch('/dynamic')
Router.prefetch('/dynamic')
}
</div>
)
Expand Down
107 changes: 0 additions & 107 deletions client/next-prefetcher.js

This file was deleted.

16 changes: 8 additions & 8 deletions examples/with-prefetching/components/Header.js
Original file line number Diff line number Diff line change
@@ -1,23 1,23 @@
import Link, { prefetch } from 'next/prefetch'
import RegularLink from 'next/link'
import Router from 'next/router'
import Link from 'next/link'

export default () => (
<div>
{ /* Prefetch using the declarative API */ }
<Link href='/'>
<Link prefetch href='/'>
<a>Home</a>
</Link>

<Link href='/features'>
<Link prefetch href='/features'>
<a>Features</a>
</Link>

{ /* we imperatively prefetch on hover */ }
<RegularLink href='/about'>
<a onMouseEnter={() => { prefetch('/about'); console.log('prefetching /about!') }}>About</a>
</RegularLink>
<Link href='/about'>
<a onMouseEnter={() => { Router.prefetch('/about'); console.log('prefetching /about!') }}>About</a>
</Link>

<Link href='/contact' prefetch={false}>
<Link href='/contact'>
<a>Contact (<small>NO-PREFETCHING</small>)</a>
</Link>

Expand Down
15 changes: 3 additions & 12 deletions flyfile.js
Original file line number Diff line number Diff line change
@@ -1,7 1,6 @@
const webpack = require('webpack')
const notifier = require('node-notifier')
const childProcess = require('child_process')
const webpackConfig = require('./webpack.config')
const isWindows = /^win/.test(process.platform)

export async function compile(fly) {
Expand Down Expand Up @@ -42,15 41,7 @@ export async function copy(fly) {
}

export async function build(fly) {
await fly.serial(['copy', 'compile', 'prefetcher'])
}

const compiler = webpack(webpackConfig)
export async function prefetcher(fly) {
compiler.run((err, stats) => {
if (err) throw err
notify('Built release prefetcher')
})
await fly.serial(['copy', 'compile'])
}

export async function bench(fly) {
Expand All @@ -70,8 61,8 @@ export default async function (fly) {
await fly.watch('bin/*', 'bin')
await fly.watch('pages/**/*.js', 'copy')
await fly.watch('server/**/*.js', 'server')
await fly.watch('client/**/*.js', ['client', 'prefetcher'])
await fly.watch('lib/**/*.js', ['lib', 'prefetcher'])
await fly.watch('client/**/*.js', ['client'])
await fly.watch('lib/**/*.js', ['lib'])
}

export async function release(fly) {
Expand Down
11 changes: 9 additions & 2 deletions lib/link.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 62,7 @@ export default class Link extends Component {
}

render () {
let { children } = this.props
let { children, prefetch } = this.props
// Deprecated. Warning shown by propType check. If the childen provided is a string (<Link>example</Link>) we wrap it in an <a> tag
if (typeof children === 'string') {
children = <a>{children}</a>
Expand All @@ -79,11 79,18 @@ export default class Link extends Component {
props.href = this.props.as || this.props.href
}

// Prefetch the JSON page if asked (only in the client)
if (prefetch) {
if (typeof window !== 'undefined') {
Router.prefetch(props.href)
}
}

return React.cloneElement(child, props)
}
}

export function isLocal (href) {
function isLocal (href) {
const origin = getLocationOrigin()
return !/^(https?:)?\/\//.test(href) ||
origin === href.substr(0, origin.length)
Expand Down
Loading

0 comments on commit 14c86be

Please sign in to comment.