Skip to content

Commit

Permalink
fix: webpack client-side routing
Browse files Browse the repository at this point in the history
  • Loading branch information
atanasster committed Mar 3, 2021
1 parent 2b40cc6 commit 0718357
Show file tree
Hide file tree
Showing 13 changed files with 272 additions and 21 deletions.
6 changes: 3 additions & 3 deletions examples/react-webpack-5/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"main": "index.js",
"license": "MIT",
"scripts": {
"start": "cross-env NODE_ENV=development webpack serve --config webpack.dev.config.ts --progress",
"build-sample": "cross-env NODE_ENV=production webpack --config webpack.prod.config.ts --progress"
"start": "webpack serve --config webpack.dev.config.ts --progress",
"build-sample": "rm -rf public && webpack --config webpack.prod.config.ts --progress"
},
"dependencies": {
"@component-controls/react-router-integration": "^2.11.3",
Expand All @@ -28,8 +28,8 @@
"@types/webpack-dev-server": "^3.11.1",
"babel-loader": "^8.2.2",
"clean-webpack-plugin": "^3.0.0",
"create-file-webpack": "^1.0.2",
"copy-webpack-plugin": "^7.0.0",
"cross-env": "^5.2.0",
"html-webpack-plugin": "^5.2.0",
"ts-node": "^9.1.1",
"typescript": "^4.1.3",
Expand Down
1 change: 1 addition & 0 deletions examples/react-webpack-5/src/_redirects
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* /index.html 200
4 changes: 3 additions & 1 deletion examples/react-webpack-5/src/index.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<!DOCTYPE html>
<html>
<head></head>
<head>
<base href="/" />
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
Expand Down
1 change: 0 additions & 1 deletion examples/react-webpack-5/webpack.dev.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,5 @@ const config: webpack.Configuration & {

export default withComponentControls({
config,
development: true,
options: { configPath: '.config', distFolder: publicPath },
});
9 changes: 8 additions & 1 deletion examples/react-webpack-5/webpack.prod.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import path from 'path';
import webpack from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import { CleanWebpackPlugin } from 'clean-webpack-plugin';
const CreateFilePlugin = require('create-file-webpack');
const CopyPlugin = require('copy-webpack-plugin');

import { withComponentControls } from '@component-controls/react-router-integration/webpack-build';
Expand Down Expand Up @@ -48,6 +49,13 @@ const config: webpack.Configuration = {
patterns: [{ from: publicFolder }],
}),
new CleanWebpackPlugin(),
new CreateFilePlugin({
path: distFolder,
fileName: '_redirects',
content: `
/* /index.html 200
`,
}),
],
performance: {
maxEntrypointSize: 8000000,
Expand All @@ -57,6 +65,5 @@ const config: webpack.Configuration = {

export default withComponentControls({
config,
development: false,
options: { configPath: '.config', distFolder: publicPath },
});
6 changes: 3 additions & 3 deletions examples/react-webpack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"main": "index.js",
"license": "MIT",
"scripts": {
"start": "cross-env NODE_ENV=development webpack serve --config webpack.dev.config.js --progress",
"build-sample": "cross-env NODE_ENV=production webpack --config webpack.prod.config.js --progress"
"start": "webpack serve --config webpack.dev.config.js --progress",
"build-sample": "rm -rf public && webpack --config webpack.prod.config.js --progress"
},
"dependencies": {
"@component-controls/react-router-integration": "^2.11.3",
Expand All @@ -25,7 +25,7 @@
"babel-loader": "^8.2.2",
"clean-webpack-plugin": "^3.0.0",
"copy-webpack-plugin": "^7.0.0",
"cross-env": "^5.2.0",
"create-file-webpack": "^1.0.2",
"html-webpack-plugin": "^5.2.0",
"typescript": "^3.9.3",
"webpack": "^4.46.0",
Expand Down
6 changes: 4 additions & 2 deletions examples/react-webpack/src/index.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<!DOCTYPE html>
<html lang="en">
<head></head>
<html>
<head>
<base href="/" />
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
Expand Down
1 change: 0 additions & 1 deletion examples/react-webpack/webpack.dev.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,5 @@ const config = {

module.exports = withComponentControls({
config,
development: true,
options: { configPath: '.config', distFolder: publicPath },
});
9 changes: 8 additions & 1 deletion examples/react-webpack/webpack.prod.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CreateFilePlugin = require('create-file-webpack');
const CopyPlugin = require('copy-webpack-plugin');

const {
Expand Down Expand Up @@ -49,6 +50,13 @@ const config = {
patterns: [{ from: publicFolder }],
}),
new CleanWebpackPlugin(),
new CreateFilePlugin({
path: distFolder,
fileName: '_redirects',
content: `
/* /index.html 200
`,
}),
],
performance: {
maxEntrypointSize: 8000000,
Expand All @@ -58,6 +66,5 @@ const config = {

module.exports = withComponentControls({
config,
development: false,
options: { configPath: '.config', distFolder: publicPath },
});
220 changes: 220 additions & 0 deletions examples/stories/src/blogs/gatsby-nextjs-webpack.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
---
title: Gatsby vs Nextjs vs Webpack
type: blog
author: atanasster
draft: true
description: Gatsby vs Nextjs vs Webpack - analysis of the pros and cons to help us find the best solution for building your documentation sites.
tags:
- gatsby
- nextjs
- webpack
---

## Overview

`component-controls` allows using several static site generators (SSG) as well as raw webpack builds to create the production version of your documentation sites.

- [gastby](https://www.gatsbyjs.org) - in many aspects, gatsby is the original static site generator for react and continues to be a leader in this space.
- [nextjs](https://nextjs.org) - for many years, nextjs was known for SSR, however recent builds allow creating highly optimized static sites.
- [webpack](https://webpack.js.org) - you can also build your site directly with webpack, which is used by both gatsby and nextjs under the hood.

In this blog post, we will research which solution offers the best development and production experience.
For webpack, only one set of results will be displayed as webpack 4 and webpack 5 are very similar in performance - the one difference to keep in mind is that ts-node andtypescript webpack configuratiopn files add about 5-10s starting the webpack project.

## Performance Tools

The web tests are conducted with [WebPageTest](https://www.webpagetest.org) and [PageSpeed](https://developers.google.com/speed/pagespeed/insights/).

## Large sites

The following measurements are for this live documentation site, which has several hundreds of pages and stories.

### Examples Source Code

The documentation sites used throughout this post are essentially the same for the purpose of comparison. We would truly appreciate any feedback to additionally optimize our examples for the researched SSGs.

| | source code |
| --------- | ------------------------------------------------------------------------------------------------- |
| gatsby | [gatsby 2+](https://github.com/ccontrols/component-controls/tree/master/examples/gatsby) |
| nextjs | [nextjs 10+](https://github.com/ccontrols/component-controls/tree/master/examples/nextjs) |
| webpack 5 | [webpack 5](https://github.com/ccontrols/component-controls/tree/master/examples/react-webpack-5) |
| webpack 4 | [webpack 4](https://github.com/ccontrols/component-controls/tree/master/examples/react-webpack) |

### Live Sites

All the sites are hosted on a free tier at [netlify](https://www.netlify.com). The tests are conducted on the same simple documentation page.

| | test page |
| --------- | ------------------------------------------------------------------------------------------------------------ |
| gatsby | [https://component-controls.com](https://component-controls.com/api/esm-starter--overview) |
| nextjs | [https://nextjs.component-controls.com](https://nextjs.component-controls.com/api/esm-starter--overview) |
| webpack 5 | [https://webpack5.component-controls.com](https://webpack5.component-controls.com/api/esm-starter--overview) |
| webpack 4 | [https://webpack.component-controls.com](https://webpack.component-controls.com/api/esm-starter--overview) |

### Performance - large sites

<table>
<thead>
<tr>
<th>test</th>
<th colSpan="2">gatsby</th>
<th colSpan="2">nextjs</th>
<th colSpan="2">webpack</th>
</tr>
<tr>
<th></th>
<th>score</th>
<th>%</th>
<th>score</th>
<th>%</th>
<th>score</th>
<th>%</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<details>
<summary>Development Mode Startup Time</summary>
Measuring the time to
start up in development mode. For this test, the projects were already
run once upfront, to make sure all possible caches are created.
</details>
</td>
<td>38s</td>
<td>+</td>
<td>35s</td>
<td style={{ color: 'green'}}>best</td>
<td>36s</td>
<td></td>
</tr>
<tr>
<td>
<details>
<summary>Production Mode Build</summary>
Measuring the time to build the site. Prior to launching this test, all the caches are deleted to create a level testing field.
</details>
</td>
<td>14:35m</td>
<td>+</td>
<td>2:00m</td>
<td ></td>
<td>57s</td>
<td style={{ color: 'green'}}>best</td>
</tr>
<tr>
<td>
<details>
<summary>Largest Contentful Paint</summary>
An important, user-centric metric for measuring perceived load speed because it marks the point in the page load timeline when the page's main content has likely loaded—a fast LCP helps reassure the user that the page is useful.
</details>
</td>
<td>1:02s</td>
<td>+</td>
<td>23s</td>
<td style={{ color: 'green'}}></td>
<td>32s</td>
<td></td>
</tr>
</tbody>
</table>


## WebPageTest results

| | time |
| --------- | ----------------------------------------------------------------------------------------- |
| gatsby | [results](https://www.webpagetest.org/result/200715_YT_6da8f397c707688a7c096567769337f8/) |
| nextjs | [results](https://www.webpagetest.org/result/200715_79_1d41cd9a7cacbbf60bf81b132896853f/) |
| webpack 5 | [results](https://www.webpagetest.org/result/200715_NH_bd8d8178abccfc4181e4463ffbe19e88/) |
| webpack 4 | [results](https://www.webpagetest.org/result/200715_NH_bd8d8178abccfc4181e4463ffbe19e88/) |

## Largest Contentful Paint

An important, user-centric metric for measuring perceived load speed because it marks the point in the page load timeline when the page's main content has likely loaded—a fast LCP helps reassure the user that the page is useful.

| | time | +/-% |
| --------- | ------- | --------- |
| gatsby | 0.591s | +14% |
| nextjs | 0.519s | best |
| webpack 5 | 11.182s | +2054.53% |

## Total Blocking Time

An important lab metric for measuring load responsiveness because it helps quantify the severity of how non-interactive a page is before it becoming reliably interactive—a low TBT helps ensure that the page is usable.

| | time | +/-% |
| --------- | ------ | ----- |
| gatsby | 1.057s | +5% |
| nextjs | 1.004s | best |
| storybook | 2.448s | +143% |

## Document Complete Time

The time is takes to fully load all the content referenced in the HTML.

| | time | +/-% |
| --------- | ------ | ----- |
| gatsby | 2.206s | best |
| nextjs | 3.012s | +36% |
| storybook | 6.075s | +175% |

## Document Complete Size

The size in kilobytes at the Document Complete Time.

| | kb | +/-% |
| --------- | ------- | ----- |
| gatsby | 755kb | best |
| nextjs | 1,452kb | +92% |
| storybook | 2,126kb | +181% |

## Fully Loaded Time

The time when a page completely stops loading content, including ads and below the fold elements.

| | time | +/-% |
| --------- | ------ | ----- |
| gatsby | 3.209s | best |
| nextjs | 3.122s | best |
| storybook | 8.253s | +164% |

## Fully Loaded Size

The size in kilobytes at the Fully Loaded Time.

| | kb | +/-% |
| --------- | ------- | ----- |
| gatsby | 781kb | best |
| nextjs | 1,453kb | +86% |
| storybook | 3,014kb | +285% |

## Cost Of Site

Measures how much it [costs](https://whatdoesmysitecost.com/) for someone to use your site on mobile networks around the world. For this comparison, we will use the USA location. This measure is mostly linked to the fully loaded size.

| | USD | +/-% |
| --------- | ------ | ----- |
| gatsby | \$0.05 | best |
| nextjs | \$0.09 | +80% |
| storybook | \$0.19 | +280% |

## PageSpeed Mobile Speed Score

The speed score is based on the lab data analyzed by [Lighthouse](https://developers.google.com/web/tools/lighthouse/). The results of this test vary to some extent on each run.

| | results | score | +/-% |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | ---- |
| gatsby | [results](https://developers.google.com/speed/pagespeed/insights/?url=https%3A%2F%2Fcomponent-controls.com%2Fapi&tab=mobile) | 29 | -29% |
| nextjs | [results](https://developers.google.com/speed/pagespeed/insights/?url=https%3A%2F%2Fnextjs.component-controls.com%2Fapi&tab=mobile) | 41 | best |
| storybook | [results](https://developers.google.com/speed/pagespeed/insights/?url=https%3A%2F%2Fstorybook.component-controls.com%2F%3Fpath%3D%2Fpage%2Fintroduction-starter--overview&tab=mobile) | 5 | -87% |

## PageSpeed Desktop Speed Score

The speed score is based on the lab data analyzed by [Lighthouse](https://developers.google.com/web/tools/lighthouse/). The results of this test vary to some extent on each run.

| | results | score | +/-% |
| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | ---- |
| gatsby | [results](https://developers.google.com/speed/pagespeed/insights/?url=https%3A%2F%2Fcomponent-controls.com%2Fapi&tab=desktop) | 75 | -7% |
| nextjs | [results](https://developers.google.com/speed/pagespeed/insights/?url=https%3A%2F%2Fnextjs.component-controls.com%2Fapi&tab=desktop) | 81 | best |
| storybook | [results](https://developers.google.com/speed/pagespeed/insights/?url=https%3A%2F%2Fstorybook.component-controls.com%2F%3Fpath%3D%2Fpage%2Fintroduction-starter--overview&tab=desktop) | 57 | -29% |
10 changes: 6 additions & 4 deletions examples/stories/src/tutorial/getting-started/ssg/webpack.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ const config = {
// the following will compile your documentation files before launching the webpack proces to compile the documentation site itself:
module.exports = withComponentControls({
config,
development: true,
options: { configPath: '.config', distFolder: publicPath },
});
```
Expand All @@ -85,11 +84,12 @@ const config: webpack.Configuration = {
// the following will compile your documentation files before launching the webpack proces to compile the documentation site itself:
module.exports = withComponentControls({
config,
development: true,
options: { configPath: '.config', distFolder: publicPath },
});
```

> Be aware that react/webpack/react-router applications are single page applications (SPA), where all pages are running from the same index.html file. To address this issue of [client-side routing](https://create-react-app.dev/docs/deployment#netlify) and depending on your server provider, you might need to use something ie [create-file-webpack](https://github.com/Appius/create-file-webpack) to create a `_redirects` file
## Create documentation site

### Create a src folder
Expand Down Expand Up @@ -117,12 +117,14 @@ ReactDOM.render(

### HTML template file

Create a new or edit `index.html` file in the src folder:
Create a new or edit `index.html` file in the src folder. PLease note that you might need to update a standard `index.html` file with the `<base href= />` tag to [account for dynamic routes](https://stackoverflow.com/questions/28253162/react-router-dynamic-segments-crash-when-accessed):

```html:title=src/index.html
<!DOCTYPE html>
<html>
<head></head>
<head>
<base href="/" />
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
Expand Down
Loading

0 comments on commit 0718357

Please sign in to comment.