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__
|
||||
}
|
||||
|
||||
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 appContainer = document.getElementById('__next')
|
||||
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()
|
||||
|
||||
router.subscribe(({ Component, props, hash, err }) => {
|
||||
|
@ -120,7 +122,7 @@ async function doRender ({ Component, props, hash, err, emitter }) {
|
|||
}
|
||||
|
||||
if (emitter) {
|
||||
emitter.emit('before-reactdom-render', { Component })
|
||||
emitter.emit('before-reactdom-render', { Component, ErrorComponent })
|
||||
}
|
||||
|
||||
Component = Component || lastAppProps.Component
|
||||
|
@ -135,6 +137,6 @@ async function doRender ({ Component, props, hash, err, emitter }) {
|
|||
ReactDOM.render(createElement(App, appProps), appContainer)
|
||||
|
||||
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'
|
||||
|
||||
const { __NEXT_DATA__: { errorComponent } } = window
|
||||
const ErrorComponent = evalScript(errorComponent).default
|
||||
|
||||
require('react-hot-loader/patch')
|
||||
import initOnDemandEntries from './on-demand-entries-client'
|
||||
import initWebpackHMR from './webpack-hot-middleware-client'
|
||||
|
||||
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.
|
||||
const originalMountComponent = ReactReconciler.mountComponent
|
||||
|
@ -21,25 +47,3 @@ ReactReconciler.mountComponent = function (...args) {
|
|||
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 './'
|
||||
|
||||
next()
|
||||
.catch((err) => {
|
||||
console.error(`${err.message}\n${err.stack}`)
|
||||
})
|
||||
|
|
|
@ -3,31 +3,33 @@
|
|||
import Router from '../lib/router'
|
||||
import fetch from 'unfetch'
|
||||
|
||||
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)
|
||||
export default () => {
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,48 +1,50 @@
|
|||
import webpackHotMiddlewareClient from 'webpack-hot-middleware/client?overlay=false&reload=true'
|
||||
import Router from '../lib/router'
|
||||
|
||||
const handlers = {
|
||||
reload (route) {
|
||||
if (route === '/_error') {
|
||||
for (const r of Object.keys(Router.components)) {
|
||||
const { err } = Router.components[r]
|
||||
if (err) {
|
||||
// reload all error routes
|
||||
// which are expected to be errors of '/_error' routes
|
||||
Router.reload(r)
|
||||
export default () => {
|
||||
const handlers = {
|
||||
reload (route) {
|
||||
if (route === '/_error') {
|
||||
for (const r of Object.keys(Router.components)) {
|
||||
const { err } = Router.components[r]
|
||||
if (err) {
|
||||
// reload all error routes
|
||||
// which are expected to be errors of '/_error' routes
|
||||
Router.reload(r)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (route === '/_document') {
|
||||
window.location.reload()
|
||||
return
|
||||
}
|
||||
if (route === '/_document') {
|
||||
window.location.reload()
|
||||
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)
|
||||
},
|
||||
|
||||
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) => {
|
||||
const fn = handlers[obj.action]
|
||||
if (fn) {
|
||||
const data = obj.data || []
|
||||
fn(...data)
|
||||
} else {
|
||||
throw new Error('Unexpected action ' + obj.action)
|
||||
}
|
||||
})
|
||||
webpackHotMiddlewareClient.subscribe((obj) => {
|
||||
const fn = handlers[obj.action]
|
||||
if (fn) {
|
||||
const data = obj.data || []
|
||||
fn(...data)
|
||||
} else {
|
||||
throw new Error('Unexpected action ' + obj.action)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -103,8 +103,8 @@ export class NextScript extends Component {
|
|||
{staticMarkup ? null : <script dangerouslySetInnerHTML={{
|
||||
__html: `__NEXT_DATA__ = ${htmlescape(__NEXT_DATA__)}; module={};`
|
||||
}} />}
|
||||
<script 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${pathname}`} />
|
||||
<script async type='text/javascript' src={`/_next/${buildId}/page/_error`} />
|
||||
{staticMarkup ? null : this.getScripts()}
|
||||
</div>
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import onDemandEntryHandler from './on-demand-entry-handler'
|
|||
import isWindowsBash from 'is-windows-bash'
|
||||
import webpack from './build/webpack'
|
||||
import clean from './build/clean'
|
||||
import readPage from './read-page'
|
||||
import getConfig from './config'
|
||||
|
||||
export default class HotReloader {
|
||||
|
@ -202,7 +201,6 @@ export default class HotReloader {
|
|||
|
||||
function deleteCache (path) {
|
||||
delete require.cache[path]
|
||||
delete readPage.cache[path]
|
||||
}
|
||||
|
||||
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