Skip to content

Commit

Permalink
[FEATURE] - perf improvements and code splitting
Browse files Browse the repository at this point in the history
Various perf improvents on inlining json, no longer escaping the json.
Split vendor bundles and using webpacks hash and manifest plugin.
Fixed hanging tests.

- Add: Dependencies for code splitting manifest file
- Add: Deployment for vendor file
- Add: Webpack config for vendor commons chunk
- Add: Script separation for vendor and bundle in layout
- Add: Vendor/Bundle static urls in config
- Add: Manifest.json on redux state
- Add: Perf enhanced inline bootstrapped redux state
- Bugfix: Global conditions from server enzyme tests cause hanging connection
- Remove: Static url from layout
  • Loading branch information
michaelBenin committed Dec 13, 2017
1 parent 94e7ad5 commit 29aafce
Show file tree
Hide file tree
Showing 12 changed files with 232 additions and 110 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@
# GIT_COMMIT=000f7d5675a4f88e280d9691cee9e943d815a6c0
# BUILD_NUMBER=development
# STATIC_URL=//abcdefg.cloudfront.net/
# STATIC_VENDOR_URL=vendor
# STATIC_BUNDLE_URL=bundle
18 changes: 12 additions & 6 deletions gulpfile.babel.js/configs/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
// http://qiita.com/kimagure/items/f2d8d53504e922fe3c5c
const path = require('path');
const webpack = require('webpack');
const ManifestPlugin = require('webpack-manifest-plugin');

module.exports = {
devtool: 'cheap-module-source-map',
entry: {
vendor: [
'fontfaceobserver',
'react',
'react-dom',
'bluebird',
'history/createBrowserHistory',
'react-router-config',
'react-router/Switch',
'react-router/Route',
'react-helmet',
'react-transition-group/TransitionGroup',
'react-transition-group/CSSTransition',
'react-error-boundary',
'lodash/get',
'axios',
'redux',
'redux-thunk',
'react-router-redux',
'react-redux',
'prop-types',
'exenv'
'exenv',
'scriptjs'
],
app: [
'react-hot-loader/patch',
Expand Down Expand Up @@ -51,19 +61,15 @@ module.exports = {
RUNTIME_ENV: JSON.stringify('browser')
}
}),
new ManifestPlugin({ writeToFileEmit: true }),
new webpack.optimize.CommonsChunkPlugin({
// filename: "vendor.js"
// (Give the chunk a different name)
name: 'vendor',
chunks: ['app'],
minChunks(module /* , count */) {
const { context } = module;
return context && context.indexOf('node_modules') >= 0;
},
// (the commons chunk name)

filename: 'vendor.js'
// children: true
}),
new webpack.optimize.ModuleConcatenationPlugin(),
new webpack.HotModuleReplacementPlugin(),
Expand Down
11 changes: 10 additions & 1 deletion gulpfile.babel.js/configs/webpack.config.production.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,27 @@ module.exports = {
devtool: 'source-map',
entry: {
vendor: [
'fontfaceobserver',
'react',
'react-dom',
'bluebird',
'history/createBrowserHistory',
'react-router-config',
'react-router/Switch',
'react-router/Route',
'react-helmet',
'react-transition-group/TransitionGroup',
'react-transition-group/CSSTransition',
'react-error-boundary',
'lodash/get',
'axios',
'redux',
'redux-thunk',
'react-router-redux',
'react-redux',
'prop-types',
'exenv'
'exenv',
'scriptjs'
],
app: path.join(__dirname, '../../src/client/index')
},
Expand Down
2 changes: 1 addition & 1 deletion gulpfile.babel.js/tasks/gulp_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ gulp.task('test', function test(cb) {
'js-lint-src',
'js-lint-test',
'js-lint-gulp',
'client-unit-test',
'client-integration-test',
'server-integration-test',
'client-unit-test',
'server-unit-test',
'style-lint',
// 'acceptance-test',
Expand Down
52 changes: 47 additions & 5 deletions gulpfile.babel.js/tasks/gulp_upload_static_files.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import awspublish from 'gulp-awspublish';
import gulp from 'gulp';
import rename from 'gulp-rename';
import { join } from 'path';
import { log } from 'gulp-util';
import { existsSync } from 'fs';

const sha = process.env.GIT_COMMIT;
const bundle = process.env.STATIC_BUNDLE_URL;
const vendor = process.env.STATIC_VENDOR_URL;

const manifestPath = join(__dirname, '../../dist/static/js/manifest.json');

let manifestJSON = false;

gulp.task('upload-static-files', function publishAWS() {
const publisher = awspublish.create({
region: 'us-east-1',
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
params: {
Bucket: 'react-ssr-spa-static'
Bucket: process.env.s3bucket
}
});

Expand All @@ -22,7 +28,43 @@ gulp.task('upload-static-files', function publishAWS() {
.src('./dist/static/**/*')
.pipe(
rename(function renamePlugin(path) {
path.dirname = `/${sha}/${path.dirname}`; // eslint-disable-line no-param-reassign
path.dirname = `/${bundle}/${path.dirname}`; // eslint-disable-line no-param-reassign
return path;
})
)
.pipe(awspublish.gzip())
.pipe(publisher.publish(headers))
.pipe(awspublish.reporter());
});

gulp.task('upload-vendor-file', function publishAWS() {
if (existsSync(manifestPath)) {
// eslint-disable-next-line global-require, import/no-dynamic-require
manifestJSON = require(manifestPath);
} else {
log(`Manifest file not found: ${manifestPath}`);
throw new Error(`Manifest file not found: ${manifestPath}`);
}

const publisher = awspublish.create({
region: 'us-east-1',
params: {
Bucket: process.env.s3bucket
}
});

const headers = {
'Cache-Control': 'max-age=315360000, no-transform, public'
};

return gulp
.src([
`./dist/static/js/${manifestJSON['vendor.js']}`,
`./dist/static/js/${manifestJSON['vendor.js.map']}`
])
.pipe(
rename(function renamePlugin(path) {
path.dirname = `/${vendor}/${path.dirname}`; // eslint-disable-line no-param-reassign
return path;
})
)
Expand Down
25 changes: 25 additions & 0 deletions npm-shrinkwrap.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"prod-server": "pm2 reload pm2/pm2.json5",
"build-prod": "NODE_ENV=production gulp build-prod",
"create-upload-artifact": "gulp create-upload-artifact",
"upload-static-files": "gulp upload-static-files",
"upload-static-files": "gulp upload-static-files upload-vendor-file",
"clean": "gulp clean",
"styles-doc": "gulp style-doc",
"js-doc": "gulp js-doc",
Expand Down Expand Up @@ -175,7 +175,8 @@
"webdriverio": "4.9.11",
"webpack": "3.8.1",
"webpack-common-shake": "1.5.3",
"webpack-dev-server": "2.9.5"
"webpack-dev-server": "2.9.5",
"webpack-manifest-plugin": "1.3.2"
},
"engines": {
"node": "~6.9.0"
Expand Down
141 changes: 64 additions & 77 deletions src/client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,90 +14,77 @@ initialize().catch(function logError(err) {
log.error(err);
});

function bootReact() {
const browserHistory = createHistory();
const originalHash = browserHistory.location.hash;
browserHistory.location.hash = '';
window.startApp = config => {
function bootReact() {
const browserHistory = createHistory();
const originalHash = browserHistory.location.hash;
browserHistory.location.hash = '';

// get json here
let bootstrappedConfig = {};
let env = false;
const env = get(config, 'config.env');
browserHistory.location.key = config.routing.location.key;
const store = configureStore(browserHistory, config, env);
/*
browserHistory.listen((location, action ) => {
const url = `${location.pathname}`;
// analytics tracking
});
*/

try {
bootstrappedConfig = JSON.parse(
document.querySelector('.client-config').getAttribute('data-state')
);
env = get(bootstrappedConfig, 'config.env');
} catch (error) {
// console.error(error, 'Error parsing client config.');
bootstrappedConfig = {};
}

browserHistory.location.key = bootstrappedConfig.routing.location.key;

const store = configureStore(browserHistory, bootstrappedConfig, env);
ThirdPartyJs.setThirdPartyGlobals();

/*
browserHistory.listen((location, action ) => {
const url = `${location.pathname}`;
// analytics tracking
});
*/
function renderedApp() {
browserHistory.location.hash = originalHash;
store.dispatch(initialLoadActionCreator());
loadAllThirdPartyJs(env);
document.documentElement.className +=
document.documentElement.className === '' ? 'hydrated' : ' hydrated';
}

ThirdPartyJs.setThirdPartyGlobals();
try {
hydrate(
<Root store={store} history={browserHistory} />,
window.document,
renderedApp
);
} catch (err) {
// fire ad code here to still show ads
log.fatal(`Unable to render app: ${err.message}`, err.stack);
}

function renderedApp() {
browserHistory.location.hash = originalHash;
store.dispatch(initialLoadActionCreator());
loadAllThirdPartyJs(env);
document.documentElement.className +=
document.documentElement.className === '' ? 'hydrated' : ' hydrated';
if (module.hot) {
module.hot.accept(
['../react_router/react_router', '../views/containers/root_container'],
() => {
// eslint-disable-next-line global-require
const HotLoadRoot = require('../views/containers/root_container')
.default;
render(
<HotLoadRoot store={store} history={browserHistory} />,
window.document
);
}
);
}
}

try {
hydrate(
<Root store={store} history={browserHistory} />,
window.document,
renderedApp
);
} catch (err) {
// fire ad code here to still show ads
log.fatal(`Unable to render app: ${err.message}`, err.stack);
}
const img = document.createElement('img');
const supportSrcset = 'srcset' in img && 'sizes' in img;

if (module.hot) {
module.hot.accept(
['../react_router/react_router', '../views/containers/root_container'],
() => {
// eslint-disable-next-line global-require
const HotLoadRoot = require('../views/containers/root_container')
.default;
render(
<HotLoadRoot store={store} history={browserHistory} />,
window.document
);
}
if (
!window.Map ||
!window.Set ||
!window.requestAnimationFrame ||
!supportSrcset
) {
scriptJS(
[
'https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.26.0/polyfill.min.js',
'https://cdn.jsdelivr.net/picturefill/3.0.3/picturefill.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/dom4/1.8.3/dom4.js'
],
bootReact
);
} else {
bootReact();
}
}

const img = document.createElement('img');
const supportSrcset = 'srcset' in img && 'sizes' in img;

if (
!window.Map ||
!window.Set ||
!window.requestAnimationFrame ||
!supportSrcset
) {
scriptJS(
[
'https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.26.0/polyfill.min.js',
'https://cdn.jsdelivr.net/picturefill/3.0.3/picturefill.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/dom4/1.8.3/dom4.js'
],
bootReact
);
} else {
bootReact();
}
};
6 changes: 5 additions & 1 deletion src/server/config/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ const {
GIT_COMMIT,
REDIS_PORT,
REDIS_HOST,
STATIC_URL
STATIC_URL,
STATIC_VENDOR_URL,
STATIC_BUNDLE_URL
} = process.env;

export default {
Expand Down Expand Up @@ -45,6 +47,8 @@ export default {
websocket: 'ws://127.0.0.1:3000',
websocketPort: 3000,
staticUrl: `${STATIC_URL}/${GIT_COMMIT}`,
staticBundleUrl: `${STATIC_URL}/${STATIC_BUNDLE_URL}`,
staticVendorUrl: `${STATIC_URL}/${STATIC_VENDOR_URL}`,
apiUrl: 'api.github.com', // '127.0.0.1:8000'
host: '127.0.0.1:8001',
protocol: 'http://'
Expand Down
Loading

0 comments on commit 29aafce

Please sign in to comment.