mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
Add support for webpack's CommonsChunkPlugin and remove next bundle (#301)
* Add example app which demonstrate the problem. * Add the first working version. * Fix lint issues. * Add README.md * Use /_next/main.js as the main file URI * Add the support for loading the core next bundle. * Optimize the output by removing Next modules from pages. * Use the same package.json as master use. * Change the example repo's README for simpler instructions. * Change example projects package.json to support next build and start. * Change main.js into commons.js. * Add support for hot core reload and errors. * Introduce require based on eval-script. * Add error reporting support with hot reloading. * Update README.md
This commit is contained in:
parent
c7ba914f52
commit
fcd59adea1
|
@ -1,6 +1,6 @@
|
||||||
import 'react-hot-loader/patch'
|
import 'react-hot-loader/patch'
|
||||||
import * as next from './next'
|
import * as next from './next'
|
||||||
|
import { requireModule } from '../lib/eval-script'
|
||||||
module.exports = next
|
|
||||||
|
|
||||||
window.next = next
|
window.next = next
|
||||||
|
module.exports = requireModule
|
||||||
|
|
|
@ -4,24 +4,33 @@ import HeadManager from './head-manager'
|
||||||
import { rehydrate } from '../lib/css'
|
import { rehydrate } from '../lib/css'
|
||||||
import Router from '../lib/router'
|
import Router from '../lib/router'
|
||||||
import App from '../lib/app'
|
import App from '../lib/app'
|
||||||
import evalScript from '../lib/eval-script'
|
import evalScript, { requireModule } from '../lib/eval-script'
|
||||||
|
|
||||||
const {
|
const {
|
||||||
__NEXT_DATA__: { component, errorComponent, props, ids, err }
|
__NEXT_DATA__: { component, errorComponent, props, ids, err }
|
||||||
} = window
|
} = window
|
||||||
|
|
||||||
const Component = evalScript(component).default
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const ErrorComponent = evalScript(errorComponent).default
|
const Component = evalScript(component).default
|
||||||
|
const ErrorComponent = evalScript(errorComponent).default
|
||||||
|
|
||||||
export const router = new Router(window.location.href, {
|
const router = new Router(window.location.href, {
|
||||||
Component,
|
Component,
|
||||||
ErrorComponent,
|
ErrorComponent,
|
||||||
ctx: { err }
|
ctx: { err }
|
||||||
|
})
|
||||||
|
|
||||||
|
// This it to support error handling in the dev time with hot code reload.
|
||||||
|
if (window.next) {
|
||||||
|
window.next.router = router
|
||||||
|
}
|
||||||
|
|
||||||
|
const headManager = new HeadManager()
|
||||||
|
const container = document.getElementById('__next')
|
||||||
|
const appProps = { Component, props, router, headManager }
|
||||||
|
|
||||||
|
rehydrate(ids)
|
||||||
|
render(createElement(App, appProps), container)
|
||||||
})
|
})
|
||||||
|
|
||||||
const headManager = new HeadManager()
|
module.exports = requireModule
|
||||||
const container = document.getElementById('__next')
|
|
||||||
const appProps = { Component, props, router, headManager }
|
|
||||||
|
|
||||||
rehydrate(ids)
|
|
||||||
render(createElement(App, appProps), container)
|
|
||||||
|
|
13
examples/shared-modules/README.md
Normal file
13
examples/shared-modules/README.md
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Example app using shared modules
|
||||||
|
|
||||||
|
This example features:
|
||||||
|
|
||||||
|
* An app with two pages which has a common Counter component
|
||||||
|
* That Counter component maintain the counter inside its module.
|
||||||
|
|
||||||
|
## How to run it
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
19
examples/shared-modules/components/Counter.js
Normal file
19
examples/shared-modules/components/Counter.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
let count = 0
|
||||||
|
|
||||||
|
export default class Counter extends React.Component {
|
||||||
|
add () {
|
||||||
|
count += 1
|
||||||
|
this.forceUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p>Count is: {count}</p>
|
||||||
|
<button onClick={() => this.add()}>Add</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
20
examples/shared-modules/components/Header.js
Normal file
20
examples/shared-modules/components/Header.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import React from 'react'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
|
const styles = {
|
||||||
|
a: {
|
||||||
|
marginRight: 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default () => (
|
||||||
|
<div>
|
||||||
|
<Link href='/'>
|
||||||
|
<a style={styles.a} >Home</a>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link href='/about'>
|
||||||
|
<a style={styles.a} >About</a>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)
|
19
examples/shared-modules/package.json
Normal file
19
examples/shared-modules/package.json
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"name": "shared-modules",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "This example features:",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"next": "*"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"next": {
|
||||||
|
"cdn": false
|
||||||
|
}
|
||||||
|
}
|
11
examples/shared-modules/pages/about.js
Normal file
11
examples/shared-modules/pages/about.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import React from 'react'
|
||||||
|
import Header from '../components/Header'
|
||||||
|
import Counter from '../components/Counter'
|
||||||
|
|
||||||
|
export default () => (
|
||||||
|
<div>
|
||||||
|
<Header />
|
||||||
|
<p>This is the about page.</p>
|
||||||
|
<Counter />
|
||||||
|
</div>
|
||||||
|
)
|
11
examples/shared-modules/pages/index.js
Normal file
11
examples/shared-modules/pages/index.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import React from 'react'
|
||||||
|
import Header from '../components/Header'
|
||||||
|
import Counter from '../components/Counter'
|
||||||
|
|
||||||
|
export default () => (
|
||||||
|
<div>
|
||||||
|
<Header />
|
||||||
|
<p>HOME PAGE is here!</p>
|
||||||
|
<Counter />
|
||||||
|
</div>
|
||||||
|
)
|
|
@ -100,7 +100,7 @@ gulp.task('build-dev-client', ['compile-lib', 'compile-client'], () => {
|
||||||
.src('dist/client/next-dev.js')
|
.src('dist/client/next-dev.js')
|
||||||
.pipe(webpack({
|
.pipe(webpack({
|
||||||
quiet: true,
|
quiet: true,
|
||||||
output: { filename: 'next-dev.bundle.js' },
|
output: { filename: 'next-dev.bundle.js', libraryTarget: 'var', library: 'require' },
|
||||||
module: {
|
module: {
|
||||||
loaders: [
|
loaders: [
|
||||||
{
|
{
|
||||||
|
@ -125,7 +125,7 @@ gulp.task('build-client', ['compile-lib', 'compile-client'], () => {
|
||||||
.src('dist/client/next.js')
|
.src('dist/client/next.js')
|
||||||
.pipe(webpack({
|
.pipe(webpack({
|
||||||
quiet: true,
|
quiet: true,
|
||||||
output: { filename: 'next.bundle.js' },
|
output: { filename: 'next.bundle.js', libraryTarget: 'var', library: 'require' },
|
||||||
plugins: [
|
plugins: [
|
||||||
new webpack.webpack.DefinePlugin({
|
new webpack.webpack.DefinePlugin({
|
||||||
'process.env': {
|
'process.env': {
|
||||||
|
|
|
@ -10,8 +10,11 @@ export default ({ head, css, html, data, dev, staticMarkup, cdn }) => {
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id='__next' dangerouslySetInnerHTML={{ __html: html }} />
|
<div id='__next' dangerouslySetInnerHTML={{ __html: html }} />
|
||||||
{staticMarkup ? null : <script dangerouslySetInnerHTML={{ __html: '__NEXT_DATA__ = ' + htmlescape(data) }} />}
|
{staticMarkup ? null : <script dangerouslySetInnerHTML={{
|
||||||
|
__html: `__NEXT_DATA__ =${htmlescape(data)}; module={};`
|
||||||
|
}} />}
|
||||||
{staticMarkup ? null : createClientScript({ dev, cdn })}
|
{staticMarkup ? null : createClientScript({ dev, cdn })}
|
||||||
|
<script type='text/javascript' src='/_next/commons.js' />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
}
|
}
|
||||||
|
@ -30,7 +33,6 @@ function createClientScript ({ dev, cdn }) {
|
||||||
load('https://cdn.zeit.co/next.js/${pkg.version}/next.min.js', function (err) {
|
load('https://cdn.zeit.co/next.js/${pkg.version}/next.min.js', function (err) {
|
||||||
if (err) load('/_next/next.bundle.js')
|
if (err) load('/_next/next.bundle.js')
|
||||||
})
|
})
|
||||||
|
|
||||||
function load (src, fn) {
|
function load (src, fn) {
|
||||||
fn = fn || function () {}
|
fn = fn || function () {}
|
||||||
var script = document.createElement('script')
|
var script = document.createElement('script')
|
||||||
|
|
|
@ -14,6 +14,17 @@ const modules = new Map([
|
||||||
['next/head', Head]
|
['next/head', Head]
|
||||||
])
|
])
|
||||||
|
|
||||||
|
function require (moduleName) {
|
||||||
|
const name = moduleName
|
||||||
|
const m = modules.get(name)
|
||||||
|
|
||||||
|
if (m) return m
|
||||||
|
|
||||||
|
throw new Error(`Module "${moduleName}" is not exists in the bundle`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const requireModule = require
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IMPORTANT: This module is compiled *without* `use strict`
|
* IMPORTANT: This module is compiled *without* `use strict`
|
||||||
* so that when we `eval` a dependency below, we don't enforce
|
* so that when we `eval` a dependency below, we don't enforce
|
||||||
|
@ -28,10 +39,6 @@ const modules = new Map([
|
||||||
|
|
||||||
export default function evalScript (script) {
|
export default function evalScript (script) {
|
||||||
const module = { exports: {} }
|
const module = { exports: {} }
|
||||||
const require = function (path) { // eslint-disable-line no-unused-vars
|
|
||||||
return modules.get(path)
|
|
||||||
}
|
|
||||||
// don't use Function() here since it changes source locations
|
|
||||||
eval(script) // eslint-disable-line no-eval
|
eval(script) // eslint-disable-line no-eval
|
||||||
return module.exports
|
return module.exports
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,10 @@ export default async function createCompiler (dir, { hotReload = false, dev = fa
|
||||||
log: false,
|
log: false,
|
||||||
// required not to cache removed files
|
// required not to cache removed files
|
||||||
useHashIndex: false
|
useHashIndex: false
|
||||||
|
}),
|
||||||
|
new webpack.optimize.CommonsChunkPlugin({
|
||||||
|
name: 'commons',
|
||||||
|
filename: 'commons.js'
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,11 @@ export default class Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
defineRoutes () {
|
defineRoutes () {
|
||||||
|
this.router.get('/_next/commons.js', async (req, res, params) => {
|
||||||
|
const p = join(this.dir, '.next/commons.js')
|
||||||
|
await this.serveStatic(req, res, p)
|
||||||
|
})
|
||||||
|
|
||||||
this.router.get('/_next/:path+', async (req, res, params) => {
|
this.router.get('/_next/:path+', async (req, res, params) => {
|
||||||
const p = join(__dirname, '..', 'client', ...(params.path || []))
|
const p = join(__dirname, '..', 'client', ...(params.path || []))
|
||||||
await this.serveStatic(req, res, p)
|
await this.serveStatic(req, res, p)
|
||||||
|
|
Loading…
Reference in a new issue