Skip to content
This repository was archived by the owner on Dec 9, 2021. It is now read-only.

Commit 2886d18

Browse files
committed
start adding code splitting
1 parent 7fbf04b commit 2886d18

File tree

14 files changed

+116
-48
lines changed

14 files changed

+116
-48
lines changed

.babelrc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
{
22
"env": {
3-
// jest doesn't take account of BABEL_ENV, you need to set NODE_ENV - https://facebook.github.io/jest/docs/getting-started.html#using-babel
43
"client": {
54
"presets": [
65
["es2015", {"modules": false}],
@@ -23,7 +22,8 @@
2322
],
2423
"plugins": [
2524
"transform-class-properties",
26-
"transform-object-rest-spread"
25+
"transform-object-rest-spread",
26+
"system-import-transformer"
2727
],
2828
"ignore": [
2929
"main.js"

package.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
"license": "MIT",
4848
"devDependencies": {
4949
"@types/fs-extra": "4.0.0",
50-
"@types/hapi": "16.1.8",
50+
"@types/hapi": "16.1.9",
5151
"@types/inert": "4.2.3",
5252
"@types/node": "8.0.25",
5353
"@types/node-notifier": "0.0.28",
@@ -58,13 +58,15 @@
5858
"@types/react-router": "4.0.15",
5959
"@types/react-router-dom": "4.0.7",
6060
"@types/redux-form": "7.0.3",
61+
"@types/serialize-javascript": "1.3.1",
6162
"@types/webpack": "3.0.10",
6263
"@types/webpack-env": "1.13.0",
6364
"awesome-typescript-loader": "3.2.3",
6465
"babel-cli": "6.26.0",
6566
"babel-core": "6.26.0",
6667
"babel-eslint": "7.2.3",
6768
"babel-loader": "7.1.2",
69+
"babel-plugin-system-import-transformer": "3.1.0",
6870
"babel-plugin-transform-class-properties": "6.24.1",
6971
"babel-plugin-transform-object-rest-spread": "6.26.0",
7072
"babel-preset-env": "1.6.0",
@@ -110,15 +112,18 @@
110112
"inert": "4.2.1",
111113
"node-notifier": "5.1.2",
112114
"react": "15.6.1",
115+
"react-async-bootstrapper": "1.1.1",
116+
"react-async-component": "1.0.0-beta.3",
113117
"react-dom": "15.6.1",
114118
"react-hot-loader": "next",
115119
"react-redux": "5.0.6",
116120
"react-router": "4.2.0",
117-
"react-router-dom": "4.2.0",
121+
"react-router-dom": "4.2.2",
118122
"redux": "3.7.2",
119123
"redux-devtools-extension": "2.13.2",
120124
"redux-form": "7.0.3",
121125
"redux-saga": "0.15.6",
126+
"serialize-javascript": "1.4.0",
122127
"webpack": "3.5.5"
123128
}
124129
}

src/RouterWrapper.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import * as React from 'react';
22
import {Provider} from 'react-redux';
33
import {BrowserRouter, Route, Switch, Redirect} from 'react-router-dom';
44
import {StaticRouter} from 'react-router';
5-
import About from './views/about/About';
5+
import AboutAsync from './views/about/AboutAsync';
66
import Home from './views/home/Home';
77
import Contact from './views/contact/Contact';
8-
import Footer from './views/landmarks/Footer';
8+
import FooterAsync from './views/landmarks/FooterAsync';
99
import Header from './views/landmarks/Header';
10-
import NotFound from './views/errors/NotFound';
10+
import NotFoundAsync from './views/errors/NotFoundAsync';
1111
import IStore from './interfaces/store/IStore';
1212
import ISagaStore from './interfaces/store/ISagaStore';
1313

@@ -37,7 +37,7 @@ const RouterWrapper: React.StatelessComponent<IProviderWrapperProps> = (props: I
3737
/>
3838
<Route
3939
path="/about"
40-
component={About}
40+
component={AboutAsync}
4141
/>
4242
<Route
4343
path="/contact"
@@ -47,9 +47,9 @@ const RouterWrapper: React.StatelessComponent<IProviderWrapperProps> = (props: I
4747
from="/old-path"
4848
to="/"
4949
/>
50-
<Route component={NotFound} />
50+
<Route component={NotFoundAsync} />
5151
</Switch>
52-
<Footer />
52+
<FooterAsync />
5353
</div>
5454
</Router>
5555
</Provider>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
declare module 'react-async-bootstrapper';
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
declare module 'react-async-component';

src/_declare/window.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
interface Window {
22
__STATE__?: any;
3+
__ASYNC_COMPONENTS_STATE__?: any;
34
}

src/client.tsx

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ import 'bootstrap/dist/css/bootstrap.css';
22
import './assets/styles/styles.css';
33

44
import {AppContainer as ReactHotLoader} from 'react-hot-loader';
5+
import {AsyncComponentProvider} from 'react-async-component';
6+
import * as asyncBootstrapper from 'react-async-bootstrapper';
57
import * as React from 'react';
68
import * as ReactDOM from 'react-dom';
79
import RouterWrapper from './RouterWrapper';
810
import ProviderService from './services/ProviderService';
911
import IStore from './interfaces/store/IStore';
1012
import ISagaStore from './interfaces/store/ISagaStore';
1113

14+
const rehydrateState = window.__ASYNC_COMPONENTS_STATE__;
1215
const initialState: IStore = {
1316
...window.__STATE__,
1417
renderReducer: {
@@ -19,17 +22,27 @@ const store: ISagaStore<IStore> = ProviderService.createProviderStore(initialSta
1922
const rootEl: HTMLElement = document.getElementById('root');
2023

2124
delete window.__STATE__;
25+
delete window.__ASYNC_COMPONENTS_STATE__;
2226

23-
const renderApp = (Component: any) =>
24-
ReactDOM.render(
25-
<ReactHotLoader>
27+
const composeApp = (Component: any) => (
28+
<ReactHotLoader key={Math.random()}>
29+
<AsyncComponentProvider rehydrateState={rehydrateState}>
2630
<Component store={store} />
27-
</ReactHotLoader>,
31+
</AsyncComponentProvider>
32+
</ReactHotLoader>
33+
);
34+
35+
const renderApp = () => {
36+
const routerWrapper = require('./RouterWrapper').default; // eslint-disable-line global-require
37+
38+
ReactDOM.render(
39+
composeApp(routerWrapper),
2840
rootEl,
2941
);
42+
};
3043

31-
renderApp(RouterWrapper);
44+
asyncBootstrapper(composeApp(RouterWrapper)).then(renderApp);
3245

33-
if (module.hot) {
34-
module.hot.accept('./RouterWrapper', () => renderApp(require('./RouterWrapper').default)); // eslint-disable-line global-require
46+
if ((module as any).hot) {
47+
(module as any).hot.accept('./RouterWrapper', renderApp);
3548
}

src/server/controllers/ReactController.tsx

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
import {renderToString} from 'react-dom/server';
2+
import {AsyncComponentProvider, createAsyncContext} from 'react-async-component';
3+
import * as asyncBootstrapper from 'react-async-bootstrapper';
4+
import * as serialize from 'serialize-javascript';
15
import * as path from 'path';
26
import * as fse from 'fs-extra';
37
import * as React from 'react';
48
import * as Hapi from 'hapi';
5-
import {renderToString} from 'react-dom/server';
69
import RouterWrapper from '../../RouterWrapper';
710
import ProviderService from '../../services/ProviderService';
811
import rootSaga from '../../store/rootSaga';
@@ -20,24 +23,30 @@ class ReactController implements IController {
2023
path: '/{route*}',
2124
handler: async (request: Hapi.Request, reply: Hapi.ReplyNoContinue): Promise<void> => {
2225
const store: ISagaStore<IStore> = ProviderService.createProviderStore({}, true);
23-
const context: any = {};
26+
const asyncContext: any = createAsyncContext();
27+
const routeContext: any = {};
2428
const app = (
25-
<RouterWrapper
26-
store={store}
27-
location={request.path}
28-
context={context}
29-
isServerSide={true}
30-
/>
29+
<AsyncComponentProvider asyncContext={asyncContext}>
30+
<RouterWrapper
31+
store={store}
32+
location={request.path}
33+
context={routeContext}
34+
isServerSide={true}
35+
/>
36+
</AsyncComponentProvider>
3137
);
3238

3339
this._html = (this._html === null) ? await this._loadHtmlFile() : this._html;
3440

41+
await asyncBootstrapper(app);
42+
3543
store.runSaga(rootSaga).done.then(() => {
36-
if (context.url) {
37-
return reply().redirect(context.url);
44+
if (routeContext.url) {
45+
return reply().redirect(routeContext.url);
3846
}
3947

4048
const renderedHtml: string = renderToString(app);
49+
const asyncComponentsState: IStore = asyncContext.getState();
4150
const state: IStore = store.getState();
4251

4352
const initialState: IStore = {
@@ -52,7 +61,8 @@ class ReactController implements IController {
5261
.replace('{title}', initialState.metaReducer.title)
5362
.replace('{description}', initialState.metaReducer.description)
5463
.replace('{content}', renderedHtml)
55-
.replace('{state}', JSON.stringify(initialState));
64+
.replace('{state}', JSON.stringify(initialState))
65+
.replace('{asyncComponentsState}', serialize(asyncComponentsState));
5666

5767
return reply(html);
5868
}).catch((error: Error) => {

src/services/ProviderService.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class ProviderService {
2121
store.runSaga = sagaMiddleware.run;
2222
store.endSaga = () => store.dispatch(END);
2323
} else {
24-
sagaMiddleware.run(rootSaga);
24+
sagaMiddleware.run(rootSaga as any);
2525
}
2626

2727
ProviderService._setupHotReloading(store);
@@ -30,8 +30,8 @@ class ProviderService {
3030
}
3131

3232
private static _setupHotReloading(store: ISagaStore<IStore>) {
33-
if (module.hot) {
34-
module.hot.accept('../store/rootReducer', () => {
33+
if ((module as any).hot) {
34+
(module as any).hot.accept('../store/rootReducer', () => {
3535
const nextReducer = require('../store/rootReducer').default; // eslint-disable-line global-require
3636

3737
store.replaceReducer(nextReducer);

src/views/about/AboutAsync.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import {asyncComponent} from 'react-async-component';
2+
3+
export default asyncComponent({
4+
name: 'AboutAsync',
5+
serverMode: 'resolve',
6+
resolve: () => import(/* webpackChunkName: "About" */ './About'),
7+
});

src/views/errors/NotFoundAsync.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import {asyncComponent} from 'react-async-component';
2+
3+
export default asyncComponent({
4+
name: 'NotFoundAsync',
5+
serverMode: 'resolve',
6+
resolve: () => import(/* webpackChunkName: "NotFound" */ './NotFound'),
7+
});

src/views/landmarks/FooterAsync.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import {asyncComponent} from 'react-async-component';
2+
3+
export default asyncComponent({
4+
name: 'FooterAsync',
5+
serverMode: 'defer',
6+
resolve: () => import(/* webpackChunkName: "Footer" */ './Footer'),
7+
});

tsconfig.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
},
4343
"include": [
4444
"src/**/*",
45-
"src/_declare/**/*",
4645
"./server.ts"
4746
],
4847
"exclude": [

yarn.lock

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
dependencies:
1919
"@types/node" "*"
2020

21-
"@types/hapi@*":
21+
"@types/hapi@*", "@types/hapi@16.1.9":
2222
version "16.1.9"
2323
resolved "https://registry.yarnpkg.com/@types/hapi/-/hapi-16.1.9.tgz#0748fa17dffdd59c97e6e2ef0c4343afc4261d64"
2424
dependencies:
@@ -30,18 +30,6 @@
3030
"@types/podium" "*"
3131
"@types/shot" "*"
3232

33-
"@types/hapi@16.1.8":
34-
version "16.1.8"
35-
resolved "https://registry.yarnpkg.com/@types/hapi/-/hapi-16.1.8.tgz#e7204421c4b210275c92f06e908ccd2d5a885f1a"
36-
dependencies:
37-
"@types/boom" "*"
38-
"@types/catbox" "*"
39-
"@types/joi" "*"
40-
"@types/mimos" "*"
41-
"@types/node" "*"
42-
"@types/podium" "*"
43-
"@types/shot" "*"
44-
4533
"@types/history@*":
4634
version "4.6.0"
4735
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.6.0.tgz#093d67ed780d889c9543f6dca24ebee0b6b9fc45"
@@ -125,6 +113,10 @@
125113
"@types/react" "*"
126114
redux "^3.6.0"
127115

116+
"@types/serialize-javascript@1.3.1":
117+
version "1.3.1"
118+
resolved "https://registry.yarnpkg.com/@types/serialize-javascript/-/serialize-javascript-1.3.1.tgz#9ae324d5b07af5a35e83ab232fba88463e3432ba"
119+
128120
"@types/shot@*":
129121
version "3.4.0"
130122
resolved "https://registry.yarnpkg.com/@types/shot/-/shot-3.4.0.tgz#459477c5187d3ebd303660ab099e7e9e0f3b656f"
@@ -712,6 +704,12 @@ babel-plugin-syntax-trailing-function-commas@^6.22.0:
712704
version "6.22.0"
713705
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3"
714706

707+
babel-plugin-system-import-transformer@3.1.0:
708+
version "3.1.0"
709+
resolved "https://registry.yarnpkg.com/babel-plugin-system-import-transformer/-/babel-plugin-system-import-transformer-3.1.0.tgz#d37f0cae8e61ef39060208331d931b5e630d7c5f"
710+
dependencies:
711+
babel-plugin-syntax-dynamic-import "^6.18.0"
712+
715713
babel-plugin-transform-async-generator-functions@^6.24.1:
716714
version "6.24.1"
717715
resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz#f058900145fd3e9907a6ddf28da59f215258a5db"
@@ -5261,6 +5259,16 @@ rc@^1.0.1, rc@^1.1.7:
52615259
minimist "^1.2.0"
52625260
strip-json-comments "~2.0.1"
52635261

5262+
react-async-bootstrapper@1.1.1:
5263+
version "1.1.1"
5264+
resolved "https://registry.yarnpkg.com/react-async-bootstrapper/-/react-async-bootstrapper-1.1.1.tgz#130153c064558edf5fae6500db4e2081662fa323"
5265+
dependencies:
5266+
react-tree-walker "^2.1.1"
5267+
5268+
react-async-component@1.0.0-beta.3:
5269+
version "1.0.0-beta.3"
5270+
resolved "https://registry.yarnpkg.com/react-async-component/-/react-async-component-1.0.0-beta.3.tgz#72f79feecd47c7345dc15e24eec86d81a0ce320c"
5271+
52645272
react-deep-force-update@^2.0.1:
52655273
version "2.1.1"
52665274
resolved "https://registry.yarnpkg.com/react-deep-force-update/-/react-deep-force-update-2.1.1.tgz#8ea4263cd6455a050b37445b3f08fd839d86e909"
@@ -5302,15 +5310,16 @@ react-redux@5.0.6:
53025310
loose-envify "^1.1.0"
53035311
prop-types "^15.5.10"
53045312

5305-
react-router-dom@4.2.0:
5306-
version "4.2.0"
5307-
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.2.0.tgz#84238285ac31a1afe53c87a751b9f1ffc5b69af7"
5313+
react-router-dom@4.2.2:
5314+
version "4.2.2"
5315+
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.2.2.tgz#c8a81df3adc58bba8a76782e946cbd4eae649b8d"
53085316
dependencies:
53095317
history "^4.7.2"
53105318
invariant "^2.2.2"
53115319
loose-envify "^1.3.1"
53125320
prop-types "^15.5.4"
53135321
react-router "^4.2.0"
5322+
warning "^3.0.0"
53145323

53155324
react-router@4.2.0, react-router@^4.2.0:
53165325
version "4.2.0"
@@ -5324,6 +5333,10 @@ react-router@4.2.0, react-router@^4.2.0:
53245333
prop-types "^15.5.4"
53255334
warning "^3.0.0"
53265335

5336+
react-tree-walker@^2.1.1:
5337+
version "2.1.2"
5338+
resolved "https://registry.yarnpkg.com/react-tree-walker/-/react-tree-walker-2.1.2.tgz#005a5d1415952349003b72c63f87ac2630e1c45a"
5339+
53275340
react@15.6.1:
53285341
version "15.6.1"
53295342
resolved "https://registry.yarnpkg.com/react/-/react-15.6.1.tgz#baa8434ec6780bde997cdc380b79cd33b96393df"
@@ -5730,6 +5743,10 @@ semver-diff@^2.0.0:
57305743
version "5.4.1"
57315744
resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e"
57325745

5746+
serialize-javascript@1.4.0:
5747+
version "1.4.0"
5748+
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.4.0.tgz#7c958514db6ac2443a8abc062dc9f7886a7f6005"
5749+
57335750
set-blocking@^2.0.0, set-blocking@~2.0.0:
57345751
version "2.0.0"
57355752
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"

0 commit comments

Comments
 (0)