mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
Load everything async.
This commit is contained in:
parent
f6f175db73
commit
e46edc2460
|
@ -34,30 +34,32 @@ if (window.__NEXT_LOADED_PAGES__) {
|
||||||
delete window.__NEXT_LOADED_PAGES__
|
delete window.__NEXT_LOADED_PAGES__
|
||||||
}
|
}
|
||||||
|
|
||||||
const ErrorComponent = pageLoader.loadPageSync('/_error')
|
|
||||||
let Component
|
|
||||||
|
|
||||||
try {
|
|
||||||
Component = pageLoader.loadPageSync(pathname)
|
|
||||||
} catch (err) {
|
|
||||||
console.error(`${err.message}\n${err.stack}`)
|
|
||||||
Component = ErrorComponent
|
|
||||||
}
|
|
||||||
|
|
||||||
let lastAppProps
|
|
||||||
|
|
||||||
export const router = createRouter(pathname, query, getURL(), {
|
|
||||||
pageLoader,
|
|
||||||
Component,
|
|
||||||
ErrorComponent,
|
|
||||||
err
|
|
||||||
})
|
|
||||||
|
|
||||||
const headManager = new HeadManager()
|
const headManager = new HeadManager()
|
||||||
const appContainer = document.getElementById('__next')
|
const appContainer = document.getElementById('__next')
|
||||||
const errorContainer = document.getElementById('__next-error')
|
const errorContainer = document.getElementById('__next-error')
|
||||||
|
|
||||||
export default () => {
|
let lastAppProps
|
||||||
|
export let router
|
||||||
|
export let ErrorComponent
|
||||||
|
let Component
|
||||||
|
|
||||||
|
export default async () => {
|
||||||
|
ErrorComponent = await pageLoader.loadPage('/_error')
|
||||||
|
|
||||||
|
try {
|
||||||
|
Component = await pageLoader.loadPage(pathname)
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`${err.message}\n${err.stack}`)
|
||||||
|
Component = ErrorComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
router = createRouter(pathname, query, getURL(), {
|
||||||
|
pageLoader,
|
||||||
|
Component,
|
||||||
|
ErrorComponent,
|
||||||
|
err
|
||||||
|
})
|
||||||
|
|
||||||
const emitter = mitt()
|
const emitter = mitt()
|
||||||
|
|
||||||
router.subscribe(({ Component, props, hash, err }) => {
|
router.subscribe(({ Component, props, hash, err }) => {
|
||||||
|
@ -120,7 +122,7 @@ async function doRender ({ Component, props, hash, err, emitter }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (emitter) {
|
if (emitter) {
|
||||||
emitter.emit('before-reactdom-render', { Component })
|
emitter.emit('before-reactdom-render', { Component, ErrorComponent })
|
||||||
}
|
}
|
||||||
|
|
||||||
Component = Component || lastAppProps.Component
|
Component = Component || lastAppProps.Component
|
||||||
|
@ -135,6 +137,6 @@ async function doRender ({ Component, props, hash, err, emitter }) {
|
||||||
ReactDOM.render(createElement(App, appProps), appContainer)
|
ReactDOM.render(createElement(App, appProps), appContainer)
|
||||||
|
|
||||||
if (emitter) {
|
if (emitter) {
|
||||||
emitter.emit('after-reactdom-render', { Component })
|
emitter.emit('after-reactdom-render', { Component, ErrorComponent })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,40 @@
|
||||||
import evalScript from '../lib/eval-script'
|
import 'react-hot-loader/patch'
|
||||||
import ReactReconciler from 'react-dom/lib/ReactReconciler'
|
import ReactReconciler from 'react-dom/lib/ReactReconciler'
|
||||||
|
import initOnDemandEntries from './on-demand-entries-client'
|
||||||
const { __NEXT_DATA__: { errorComponent } } = window
|
import initWebpackHMR from './webpack-hot-middleware-client'
|
||||||
const ErrorComponent = evalScript(errorComponent).default
|
|
||||||
|
|
||||||
require('react-hot-loader/patch')
|
|
||||||
|
|
||||||
const next = window.next = require('./')
|
const next = window.next = require('./')
|
||||||
|
|
||||||
const emitter = next.default()
|
next.default()
|
||||||
|
.then((emitter) => {
|
||||||
|
initOnDemandEntries()
|
||||||
|
initWebpackHMR()
|
||||||
|
|
||||||
|
let lastScroll
|
||||||
|
|
||||||
|
emitter.on('before-reactdom-render', ({ Component, ErrorComponent }) => {
|
||||||
|
// Remember scroll when ErrorComponent is being rendered to later restore it
|
||||||
|
if (!lastScroll && Component === ErrorComponent) {
|
||||||
|
const { pageXOffset, pageYOffset } = window
|
||||||
|
lastScroll = {
|
||||||
|
x: pageXOffset,
|
||||||
|
y: pageYOffset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
emitter.on('after-reactdom-render', ({ Component, ErrorComponent }) => {
|
||||||
|
if (lastScroll && Component !== ErrorComponent) {
|
||||||
|
// Restore scroll after ErrorComponent was replaced with a page component by HMR
|
||||||
|
const { x, y } = lastScroll
|
||||||
|
window.scroll(x, y)
|
||||||
|
lastScroll = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(`${err.message}\n${err.stack}`)
|
||||||
|
})
|
||||||
|
|
||||||
// This is a patch to catch most of the errors throw inside React components.
|
// This is a patch to catch most of the errors throw inside React components.
|
||||||
const originalMountComponent = ReactReconciler.mountComponent
|
const originalMountComponent = ReactReconciler.mountComponent
|
||||||
|
@ -21,25 +47,3 @@ ReactReconciler.mountComponent = function (...args) {
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastScroll
|
|
||||||
|
|
||||||
emitter.on('before-reactdom-render', ({ Component }) => {
|
|
||||||
// Remember scroll when ErrorComponent is being rendered to later restore it
|
|
||||||
if (!lastScroll && Component === ErrorComponent) {
|
|
||||||
const { pageXOffset, pageYOffset } = window
|
|
||||||
lastScroll = {
|
|
||||||
x: pageXOffset,
|
|
||||||
y: pageYOffset
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
emitter.on('after-reactdom-render', ({ Component }) => {
|
|
||||||
if (lastScroll && Component !== ErrorComponent) {
|
|
||||||
// Restore scroll after ErrorComponent was replaced with a page component by HMR
|
|
||||||
const { x, y } = lastScroll
|
|
||||||
window.scroll(x, y)
|
|
||||||
lastScroll = null
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
import next from './'
|
import next from './'
|
||||||
|
|
||||||
next()
|
next()
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(`${err.message}\n${err.stack}`)
|
||||||
|
})
|
||||||
|
|
|
@ -3,31 +3,33 @@
|
||||||
import Router from '../lib/router'
|
import Router from '../lib/router'
|
||||||
import fetch from 'unfetch'
|
import fetch from 'unfetch'
|
||||||
|
|
||||||
Router.ready(() => {
|
export default () => {
|
||||||
Router.router.events.on('routeChangeComplete', ping)
|
Router.ready(() => {
|
||||||
})
|
Router.router.events.on('routeChangeComplete', ping)
|
||||||
|
|
||||||
async function ping () {
|
|
||||||
try {
|
|
||||||
const url = `/_next/on-demand-entries-ping?page=${Router.pathname}`
|
|
||||||
const res = await fetch(url)
|
|
||||||
const payload = await res.json()
|
|
||||||
if (payload.invalid) {
|
|
||||||
location.reload()
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(`Error with on-demand-entries-ping: ${err.message}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function runPinger () {
|
|
||||||
while (true) {
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 5000))
|
|
||||||
await ping()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
runPinger()
|
|
||||||
.catch((err) => {
|
|
||||||
console.error(err)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
async function ping () {
|
||||||
|
try {
|
||||||
|
const url = `/_next/on-demand-entries-ping?page=${Router.pathname}`
|
||||||
|
const res = await fetch(url)
|
||||||
|
const payload = await res.json()
|
||||||
|
if (payload.invalid) {
|
||||||
|
location.reload()
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error with on-demand-entries-ping: ${err.message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runPinger () {
|
||||||
|
while (true) {
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 5000))
|
||||||
|
await ping()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runPinger()
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -1,48 +1,50 @@
|
||||||
import webpackHotMiddlewareClient from 'webpack-hot-middleware/client?overlay=false&reload=true'
|
import webpackHotMiddlewareClient from 'webpack-hot-middleware/client?overlay=false&reload=true'
|
||||||
import Router from '../lib/router'
|
import Router from '../lib/router'
|
||||||
|
|
||||||
const handlers = {
|
export default () => {
|
||||||
reload (route) {
|
const handlers = {
|
||||||
if (route === '/_error') {
|
reload (route) {
|
||||||
for (const r of Object.keys(Router.components)) {
|
if (route === '/_error') {
|
||||||
const { err } = Router.components[r]
|
for (const r of Object.keys(Router.components)) {
|
||||||
if (err) {
|
const { err } = Router.components[r]
|
||||||
// reload all error routes
|
if (err) {
|
||||||
// which are expected to be errors of '/_error' routes
|
// reload all error routes
|
||||||
Router.reload(r)
|
// which are expected to be errors of '/_error' routes
|
||||||
|
Router.reload(r)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (route === '/_document') {
|
if (route === '/_document') {
|
||||||
window.location.reload()
|
window.location.reload()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
Router.reload(route)
|
|
||||||
},
|
|
||||||
|
|
||||||
change (route) {
|
|
||||||
if (route === '/_document') {
|
|
||||||
window.location.reload()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const { err } = Router.components[route] || {}
|
|
||||||
if (err) {
|
|
||||||
// reload to recover from runtime errors
|
|
||||||
Router.reload(route)
|
Router.reload(route)
|
||||||
|
},
|
||||||
|
|
||||||
|
change (route) {
|
||||||
|
if (route === '/_document') {
|
||||||
|
window.location.reload()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { err } = Router.components[route] || {}
|
||||||
|
if (err) {
|
||||||
|
// reload to recover from runtime errors
|
||||||
|
Router.reload(route)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
webpackHotMiddlewareClient.subscribe((obj) => {
|
webpackHotMiddlewareClient.subscribe((obj) => {
|
||||||
const fn = handlers[obj.action]
|
const fn = handlers[obj.action]
|
||||||
if (fn) {
|
if (fn) {
|
||||||
const data = obj.data || []
|
const data = obj.data || []
|
||||||
fn(...data)
|
fn(...data)
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Unexpected action ' + obj.action)
|
throw new Error('Unexpected action ' + obj.action)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -103,8 +103,8 @@ export class NextScript extends Component {
|
||||||
{staticMarkup ? null : <script dangerouslySetInnerHTML={{
|
{staticMarkup ? null : <script dangerouslySetInnerHTML={{
|
||||||
__html: `__NEXT_DATA__ = ${htmlescape(__NEXT_DATA__)}; module={};`
|
__html: `__NEXT_DATA__ = ${htmlescape(__NEXT_DATA__)}; module={};`
|
||||||
}} />}
|
}} />}
|
||||||
<script type='text/javascript' src={`/_next/${buildId}/page${pathname}`} />
|
<script async type='text/javascript' src={`/_next/${buildId}/page${pathname}`} />
|
||||||
<script type='text/javascript' src={`/_next/${buildId}/page/_error`} />
|
<script async type='text/javascript' src={`/_next/${buildId}/page/_error`} />
|
||||||
{staticMarkup ? null : this.getScripts()}
|
{staticMarkup ? null : this.getScripts()}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import onDemandEntryHandler from './on-demand-entry-handler'
|
||||||
import isWindowsBash from 'is-windows-bash'
|
import isWindowsBash from 'is-windows-bash'
|
||||||
import webpack from './build/webpack'
|
import webpack from './build/webpack'
|
||||||
import clean from './build/clean'
|
import clean from './build/clean'
|
||||||
import readPage from './read-page'
|
|
||||||
import getConfig from './config'
|
import getConfig from './config'
|
||||||
|
|
||||||
export default class HotReloader {
|
export default class HotReloader {
|
||||||
|
@ -202,7 +201,6 @@ export default class HotReloader {
|
||||||
|
|
||||||
function deleteCache (path) {
|
function deleteCache (path) {
|
||||||
delete require.cache[path]
|
delete require.cache[path]
|
||||||
delete readPage.cache[path]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function diff (a, b) {
|
function diff (a, b) {
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
import fs from 'mz/fs'
|
|
||||||
import resolve from './resolve'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* resolve a JSON page like `require.resolve`,
|
|
||||||
* and read and cache the file content
|
|
||||||
*/
|
|
||||||
|
|
||||||
async function readPage (path) {
|
|
||||||
const f = await resolve(path)
|
|
||||||
if (cache.hasOwnProperty(f)) {
|
|
||||||
return cache[f]
|
|
||||||
}
|
|
||||||
|
|
||||||
const source = await fs.readFile(f, 'utf8')
|
|
||||||
cache[f] = source
|
|
||||||
return source
|
|
||||||
}
|
|
||||||
|
|
||||||
export default readPage
|
|
||||||
export const cache = {}
|
|
||||||
|
|
||||||
readPage.cache = cache
|
|
Loading…
Reference in a new issue