Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve app load times using code splitting #701

Merged
merged 3 commits into from
Feb 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ module.exports = function babelConfig(api) {
const plugins = [
["@babel/plugin-proposal-decorators", { legacy: true }],
"@babel/plugin-proposal-class-properties",
"babel-plugin-styled-components"
"babel-plugin-styled-components",
"babel-plugin-syntax-dynamic-import"
];
if (api.env("development")) {
if (process.env.BABEL_EXTENSION_PATH && !process.env.BABEL_EXTENSION_PATH.includes(__dirname)) {
Expand Down
3 changes: 2 additions & 1 deletion cli/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ const addParser = (parser) => {
subparser.addArgument('--extend', {action: "store", help: "extension config"});
subparser.addArgument('--includeTiming', {action: "storeTrue", help: "keep timing functions (default: false for speed reasons)"});
subparser.addArgument('--serverless', {action: "storeTrue", help: "e.g gh-pages"});
subparser.addArgument('--analyzeBundle', {action: "storeTrue", help: "Interactive bundle analyzer tool"});
};

const run = (args) => {

/* webpack set up */
const extensionPath = args.extend ? path.resolve(args.extend) : undefined;
const customOutputPath = utils.customOutputPath(args.extend);
const webpackConfig = generateWebpackConfig({extensionPath, devMode: false, customOutputPath});
const webpackConfig = generateWebpackConfig({extensionPath, devMode: false, customOutputPath, analyzeBundle: args.analyzeBundle});
const compiler = webpack(webpackConfig);

/* variables available to babel (which is called by webpack) */
Expand Down
4 changes: 2 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
<div id='root'>
</div>

<!-- asyncronously load the react bundle -->
<script async src="/dist/bundle.js"></script>
<!-- asyncronously load the (entry) react bundle -->
<script async src="/dist/auspice.bundle.js"></script>

<!-- by placing this in the body, we mimic async ability
This is important as sometimes unpkg takes 10s to return and, if in the header
Expand Down
7 changes: 6 additions & 1 deletion package-lock.json

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

63 changes: 33 additions & 30 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,28 @@
"build-docs": "./scripts/build-docs.sh"
},
"dependencies": {
"@babel/core": "^7.1.2",
"@babel/plugin-proposal-class-properties": "^7.1.0",
"@babel/plugin-proposal-decorators": "^7.1.2",
"@babel/preset-env": "^7.1.0",
"@babel/preset-react": "^7.0.0",
"argparse": "^1.0.10",
"awesomplete": "^1.1.2",
"babel-eslint": "^10.0.1",
"babel-loader": "^8.0.4",
"babel-plugin-strip-function-call": "^1.0.2",
"babel-plugin-styled-components": "^1.8.0",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-polyfill": "^6.26.0",
"binomial": "^0.2.0",
"chai": "^4.1.2",
"chai-http": "^4.0.0",
"chalk": "^2.4.1",
"color": "^0.7.3",
"compression": "^1.7.3",
"compression-webpack-plugin": "^1.1.11",
"create-react-class": "^15.6.0",
"css-loader": "^0.26.1",
"d3-array": "^1.2.0",
"d3-axis": "^1.0.6",
"d3-brush": "^1.0.4",
Expand All @@ -56,19 +69,31 @@
"d3-zoom": "^1.1.3",
"docusaurus": "^1.6.2",
"es6-object-assign": "^1.1.0",
"es6-promise": "~1.0.0",
"es6-promise-polyfill": "^1.2.0",
"eslint": "^5.3.0",
"eslint-config-airbnb": "^15.1.0",
"eslint-config-defaults": "^7.0.1",
"eslint-plugin-filenames": "^0.1.2",
"eslint-plugin-import": "^2.13.0",
"eslint-plugin-jsx-a11y": "^5.1.1",
"eslint-plugin-react": "^7.2.1",
"express": "^4.16.3",
"express-naked-redirect": "^0.1.2",
"express-static-gzip": "^0.2.2",
"file-loader": "^1.1.11",
"json-loader": "^0.5.1",
"leaflet": "^1.2.0",
"leaflet-image": "^0.4.0",
"linspace": "^1.0.0",
"lodash": "^4.17.10",
"marked": "^0.5.0",
"mocha": "^5.2.0",
"mousetrap": "^1.6.2",
"node-fetch": "^2.1.2",
"outer-product": "0.0.4",
"papaparse": "^4.3.5",
"prettyjson": "^1.2.1",
"prop-types": "^15.6.0",
"query-string": "^4.2.3",
"react": "^16.6.0",
Expand All @@ -80,43 +105,21 @@
"react-select": "^1.0.0-rc.5",
"react-tweet-embed": "^1.1.0",
"redux": "^4.0.1",
"redux-devtools": "^3.4.1",
"redux-thunk": "^2.3.0",
"request": "^2.87.0",
"styled-components": "^4.0.3",
"whatwg-fetch": "^0.10.1",
"yaml-front-matter": "^4.0.0",
"@babel/core": "^7.1.2",
"@babel/plugin-proposal-decorators": "^7.1.2",
"@babel/preset-env": "^7.1.0",
"@babel/preset-react": "^7.0.0",
"@babel/plugin-proposal-class-properties": "^7.1.0",
"babel-eslint": "^10.0.1",
"babel-loader": "^8.0.4",
"babel-plugin-strip-function-call": "^1.0.2",
"chai": "^4.1.2",
"chai-http": "^4.0.0",
"compression-webpack-plugin": "^1.1.11",
"css-loader": "^0.26.1",
"es6-promise": "~1.0.0",
"eslint": "^5.3.0",
"eslint-config-airbnb": "^15.1.0",
"eslint-config-defaults": "^7.0.1",
"eslint-plugin-filenames": "^0.1.2",
"eslint-plugin-import": "^2.13.0",
"eslint-plugin-jsx-a11y": "^5.1.1",
"eslint-plugin-react": "^7.2.1",
"file-loader": "^1.1.11",
"json-loader": "^0.5.1",
"mocha": "^5.2.0",
"prettyjson": "^1.2.1",
"redux-devtools": "^3.4.1",
"rimraf": "^2.4.3",
"run-sequence": "~0.3.6",
"style-loader": "^0.13.2",
"styled-components": "^4.0.3",
"webpack": "^4.16.5",
"webpack-cli": "^3.1.2",
"webpack-dev-middleware": "^3.1.3",
"webpack-hot-middleware": "^2.24.3"
"webpack-hot-middleware": "^2.24.3",
"whatwg-fetch": "^0.10.1",
"yaml-front-matter": "^4.0.0"
},
"devDependencies": {}
"devDependencies": {
"webpack-bundle-analyzer": "^3.0.3"
}
}
6 changes: 5 additions & 1 deletion src/actions/navigation.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import queryString from "query-string";
import { createStateFromQueryOrJSONs } from "./recomputeReduxState";
import { PAGE_CHANGE, URL_QUERY_CHANGE_WITH_COMPUTED_STATE } from "./types";
import { loadJSONs } from "./loadData";

/* Given a URL, what "page" should be displayed?
* "page" means the main app, splash page, status page etc
* If in doubt, we go to the datasetLoader page as this will
* redirect to the splash page if the datasets are unavailable
*/
export const chooseDisplayComponentFromURL = (url) => {
const parts = url.toLowerCase().replace(/^\/+/, "").replace(/\/+$/, "").split("/");
if (
Expand Down
31 changes: 7 additions & 24 deletions src/components/datasetLoader/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,22 @@ import { connect } from "react-redux";
import { loadJSONs } from "../../actions/loadData";
import { PAGE_CHANGE } from "../../actions/types";

const nextstrainLogo = require("../../images/nextstrain-logo-small.png");

export const Spinner = ({availableHeight}) => (
<img className={"spinner"} src={nextstrainLogo} alt="loading" style={{marginTop: `${availableHeight / 2 - 100}px`}}/>
);

@connect((state) => ({
browserDimensions: state.browserDimensions.browserDimensions,
metadataLoaded: state.metadata.loaded,
treeLoaded: state.tree.loaded
}))
/* The DatsetLoader component simply triggers the (async) loadJSONs action
* and then redirects to the "main" page (via a PAGE_CHANGE action).
* Note that if the loadJSONs action "fails" it will subsequently redirect to a 404 page
*/
@connect()
class DatasetLoader extends React.Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.props.dispatch(loadJSONs()); // choose via URL
this.props.dispatch({type: PAGE_CHANGE, displayComponent: "main"});
}
componentDidUpdate() {
if (this.props.metadataLoaded && this.props.treeLoaded) {
this.props.dispatch({
type: PAGE_CHANGE,
displayComponent: "main"
});
}
}

render() {
return (
<Spinner availableHeight={this.props.browserDimensions.height}/>
);
return null;
}

}

export default DatasetLoader;
5 changes: 3 additions & 2 deletions src/components/entropy/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,7 @@ const constructEncodedGenotype = (mutType, d) => {
narrativeMode: state.narrative.display
};
})

export class Entropy extends React.Component {
class Entropy extends React.Component {
constructor(props) {
super(props);
this.state = {
Expand Down Expand Up @@ -288,3 +287,5 @@ export class Entropy extends React.Component {
);
}
}

export default Entropy;
15 changes: 15 additions & 0 deletions src/components/framework/spinner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from "react";

const nextstrainLogo = require("../../images/nextstrain-logo-small.png");

const Spinner = ({availableHeight=false}) => {
if (!availableHeight) {
availableHeight = isNaN(window.innerHeight) ? window.clientHeight : window.innerHeight; // eslint-disable-line
}
const style = {
marginTop: `${availableHeight / 2 - 100}px`
};
return (<img className={"spinner"} src={nextstrainLogo} alt="loading" style={style}/>);
};

export default Spinner;
4 changes: 3 additions & 1 deletion src/components/frequencies/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import "../../css/entropy.css";
colorOptions: state.metadata.colorOptions
};
})
export class Frequencies extends React.Component {
class Frequencies extends React.Component {
constructor(props) {
super(props);
this.state = {maxY: 0};
Expand Down Expand Up @@ -114,3 +114,5 @@ export class Frequencies extends React.Component {
);
}
}

export default Frequencies;
44 changes: 35 additions & 9 deletions src/components/main/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import React from "react";
import React, {lazy, Suspense } from "react";
import PropTypes from 'prop-types';
import { connect } from "react-redux";
import { ThemeProvider } from 'styled-components';
import SidebarToggle from "../framework/sidebar-toggle";
import { Frequencies } from "../frequencies";
import { Entropy } from "../entropy";
import Info from "../info/info";
import Tree from "../tree";
import Map from "../map/map";
Expand All @@ -20,6 +18,11 @@ import { Sidebar } from "./sidebar";
import { calcPanelDims, calcStyles } from "./utils";
import { PanelsContainer, sidebarTheme } from "./styles";
import ErrorBoundary from "../../util/errorBoundry";
import Spinner from "../framework/spinner";

const Entropy = lazy(() => import("../entropy"));
const Frequencies = lazy(() => import("../frequencies"));


@connect((state) => ({
panelsToDisplay: state.controls.panelsToDisplay,
Expand All @@ -28,7 +31,9 @@ import ErrorBoundary from "../../util/errorBoundry";
narrativeIsLoaded: state.narrative.loaded,
narrativeTitle: state.narrative.title,
browserDimensions: state.browserDimensions.browserDimensions,
frequenciesLoaded: state.frequencies.loaded
frequenciesLoaded: state.frequencies.loaded,
metadataLoaded: state.metadata.loaded,
treeLoaded: state.tree.loaded
}))
class Main extends React.Component {
constructor(props) {
Expand All @@ -39,13 +44,22 @@ class Main extends React.Component {
sidebarOpen: this.state.mql.matches,
mobileDisplay: !this.state.mql.matches
}));
this.state = {mql, sidebarOpen: mql.matches, mobileDisplay: !mql.matches};
this.state = {
mql,
sidebarOpen: mql.matches,
mobileDisplay: !mql.matches,
showSpinner: !(this.props.metadataLoaded && this.props.treeLoaded)
};
analyticsNewPage();
}
static propTypes = {
dispatch: PropTypes.func.isRequired
}
componentWillReceiveProps(nextProps) {
if (this.state.showSpinner && nextProps.metadataLoaded && nextProps.treeLoaded) {
this.setState({showSpinner: false});
return;
}
if (
(this.state.mql.matches) ||
(nextProps.displayNarrative && !this.props.displayNarrative)
Expand All @@ -61,8 +75,10 @@ class Main extends React.Component {
}, false);
}
render() {
const {availableWidth, availableHeight, sidebarWidth, overlayStyles} =
calcStyles(this.props.browserDimensions, this.props.displayNarrative, this.state.sidebarOpen, this.state.mobileDisplay);
if (this.state.showSpinner) {
return (<Spinner/>);
}
const {availableWidth, availableHeight, sidebarWidth, overlayStyles} = calcStyles(this.props.browserDimensions, this.props.displayNarrative, this.state.sidebarOpen, this.state.mobileDisplay);
const overlayHandler = () => {this.setState({sidebarOpen: false});};
const {big, chart} = calcPanelDims(this.props.panelLayout === "grid", this.props.panelsToDisplay, this.props.displayNarrative, availableWidth, availableHeight);
return (
Expand Down Expand Up @@ -93,8 +109,18 @@ class Main extends React.Component {
{this.props.displayNarrative ? null : <Info width={calcUsableWidth(availableWidth, 1)} />}
{this.props.panelsToDisplay.includes("tree") ? <Tree width={big.width} height={big.height} /> : null}
{this.props.panelsToDisplay.includes("map") ? <Map width={big.width} height={big.height} justGotNewDatasetRenderNewMap={false} /> : null}
{this.props.panelsToDisplay.includes("entropy") ? <Entropy width={chart.width} height={chart.height} /> : null}
{this.props.panelsToDisplay.includes("frequencies") && this.props.frequenciesLoaded ? <Frequencies width={chart.width} height={chart.height} /> : null}
{this.props.panelsToDisplay.includes("entropy") ?
(<Suspense fallback={null}>
<Entropy width={chart.width} height={chart.height} />
</Suspense>) :
null
}
{this.props.panelsToDisplay.includes("frequencies") && this.props.frequenciesLoaded ?
(<Suspense fallback={null}>
<Frequencies width={chart.width} height={chart.height} />
</Suspense>) :
null
}
{this.props.displayNarrative ? null : <Footer width={calcUsableWidth(availableWidth, 1)} />}
</PanelsContainer>
{/* overlay (used for mobile to open / close sidebar) */}
Expand Down
Loading