lvillen / todo-react-demo

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

14 Production config

In this demo we are going to create different builds for each environment. We will learn how to configure it and how to reduce bundle file sizes.

We will start from 13-typescript

Summary steps:

  • Install webpack merge
  • Create a base webpack config.
  • Create a development config.
  • Create a production config.
  • Add scripts to package.json

Prerequisites

You will need to have nodejs installed in your computer (at least 8.9.2). If you want to follow this step-by-step guide you will need to take as starting point sample 13-typescript.

steps

  • npm install to install previous sample packages:
npm install
  • So now we want to split into development and production configurations, you will find that:

    • If we just create two configs, and copy and paste the common content it would be a nightmare to maintain, and on the other hand what if we need to create more specific configs?
    • We should find a way to keep the common config in one file and just create the specific configs taking as a base that common configuration.
  • We will use a tool called webpack-merge, this tool allows as to have a common webpack config file and merge it into specific ones.

npm install webpack-merge --save-dev
  • Let's rename our webpack.config.js file to webpack.common.js

  • In this new webpack.common.js let's remove the entry stats: "errors-only" we want to be more specific here, in development we want to use the short stats version, in production we want to display the extended version.

webpack.common.js

...
-  devtool: "eval-source-map",
-  devServer: {
-    port: 8080,
-    stats: "errors-only",
-  },
  • Now it's time to create our webpack config dev version, we will start by merging it form the base config, and then add / overwrite the setting specific for development.

webpack.dev.js

const { merge } = require("webpack-merge");
const common = require("./webpack.common.js");

module.exports = merge(common, {
  mode: "development",
  devtool: "eval-source-map",
  devServer: {
    port: 8080,
    stats: "errors-only",
  },
});
  • Time to go for the production environment, we will follow similar steps as in dev:

webpack.prod.js

const { merge } = require("webpack-merge");
const common = require("./webpack.common.js");

module.exports = merge(common, {
  mode: "production",
  stats: "verbose",
});
  • Great we got both configuration, is time to update our package.json

./package.json

  "scripts": {
    "start": "run-p -l type-check:watch start:dev",
    "type-check": "tsc --noEmit",
    "type-check:watch": "npm run type-check -- --watch",
-   "start:dev": "webpack serve",
+   "start:dev": "webpack serve --config webpack.dev.js",
-   "build": "webpack --mode development"
+   "build:dev": "npm run type-check && webpack --config webpack.dev.js",
+   "build:prod": "npm run type-check && webpack --config webpack.prod.js"
  },
  • If you want to give a try to the dev build just run
npm run build:dev
  • If you want to give a try to the production build just run:
npm run build:prod
  • Fix app:

averageService.ts

...
- const a: number = "this is a string"

webpack.common.js

const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
- const MiniCssExtractPlugin = require("mini-css-extract-plugin");
...
module: {
    rules: [
      ...
-     {
-       test: /\.scss$/,
-       exclude: /node_modules/,
-       use: [
-         MiniCssExtractPlugin.loader,
-         {
-           loader: "css-loader",
-           options: {
-             modules: {
-               exportLocalsConvention: "camelCase",
-               localIdentName: "[path][name]__[local]--[hash:base64:5]",
-               localIdentContext: path.resolve(__dirname, "src"),
-               localIdentHashPrefix: "my-custom-hash",
-             },
-           },
-         },
-         {
-           loader: "sass-loader",
-           options: {
-             implementation: require("sass"),
-           },
-         },
-       ],
-     },
-     {
-       test: /\.css$/,
-       use: [MiniCssExtractPlugin.loader, "css-loader"],
-     },
      {
        test: /\.(png|jpg)$/,
        type: "asset/resource",
      },
      {
        test: /\.html$/,
        loader: "html-loader",
      },
    ],
  },
  plugins: [
    //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin({
      filename: "index.html", //Name of file in ./dist/
      template: "index.html", //Name of template in ./src
    }),
    new CleanWebpackPlugin(),
-   new MiniCssExtractPlugin({
-     filename: "[name].css",
-     chunkFilename: "[id].css",
-   }),
  ],

webpack.dev.js

const { merge } = require("webpack-merge");
+ const path = require("path");
const common = require("./webpack.common.js");

module.exports = merge(common, {
  mode: "development",
  devtool: "eval-source-map",
  devServer: {
    port: 8080,
    stats: "errors-only",
  },
+ module: {
+   rules: [
+     {
+       test: /\.scss$/,
+       exclude: /node_modules/,
+       use: [
+         "style-loader",
+         {
+           loader: "css-loader",
+           options: {
+             modules: {
+               exportLocalsConvention: "camelCase",
+               localIdentName: "[path][name]__[local]--[hash:base64:5]",
+               localIdentContext: path.resolve(__dirname, "src"),
+               localIdentHashPrefix: "my-custom-hash",
+             },
+           },
+         },
+         {
+           loader: "sass-loader",
+           options: {
+             implementation: require("sass"),
+           },
+         },
+       ],
+     },
+     {
+       test: /\.css$/,
+       use: ["style-loader", "css-loader"],
+     },
+   ],
+ },
});

webpack.prod.js

const { merge } = require("webpack-merge");
+ const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+ const path = require("path");
const common = require("./webpack.common.js");

module.exports = merge(common, {
  mode: "production",
  stats: "verbose",
+ module: {
+   rules: [
+     {
+       test: /\.scss$/,
+       exclude: /node_modules/,
+       use: [
+         MiniCssExtractPlugin.loader,
+         {
+           loader: "css-loader",
+           options: {
+             modules: {
+               exportLocalsConvention: "camelCase",
+               localIdentName: "[path][name]__[local]--[hash:base64:5]",
+               localIdentContext: path.resolve(__dirname, "src"),
+               localIdentHashPrefix: "my-custom-hash",
+             },
+           },
+         },
+         {
+           loader: "sass-loader",
+           options: {
+             implementation: require("sass"),
+           },
+         },
+       ],
+     },
+     {
+       test: /\.css$/,
+       use: [MiniCssExtractPlugin.loader, "css-loader"],
+     },
+   ],
+ },
+ plugins: [
+   new MiniCssExtractPlugin({
+     filename: "[name].css",
+     chunkFilename: "[id].css",
+   }),
+ ],
});
  • The chunkhash on file names are recommended to use only for production mode:

webpack.common.js

...
  output: {
-   filename: "[name].[chunkhash].js",
    path: path.resolve(process.cwd(), "dist"),
  },
...

webpack.dev.js

...
  devServer: {
    port: 8080,
    stats: "errors-only",
  },
+ output: {
+   filename: "[name].js",
+ },
  module: {
    rules: [
      {
        test: /\.scss$/,
        exclude: /node_modules/,
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: {
              modules: {
                exportLocalsConvention: "camelCase",
-               localIdentName: "[path][name]__[local]--[hash:base64:5]",
+               localIdentName: "[path][name]__[local]",
                localIdentContext: path.resolve(__dirname, "src"),
-               localIdentHashPrefix: "my-custom-hash",
              },
...

webpack.prod.js

...
module.exports = merge(common, {
  mode: "production",
  stats: "verbose",
+ output: {
+   filename: "[name].[chunkhash].js",
+ },
  module: {
...
  },
  plugins: [
    new MiniCssExtractPlugin({
-     filename: "[name].css",
+     filename: "[name].[chunkhash].css",
-     chunkFilename: "[id].css",
+     chunkFilename: "[id].[chunkhash].css",
    }),
  ],
});
  • Even, we can group output files in folders, let's add an image to app:

declaration.d.ts

declare module "*.scss";
+ declare module "*.png";

index.tsx

import React from "react";
import ReactDOM from "react-dom";
import { AverageComponent } from "./averageComponent";
import { TotalScoreComponent } from './totalScoreComponent';
+ import logo from './content/logo_1.png';

ReactDOM.render(
  <div>
    <h1>Hello from React DOM</h1>
+   <img src={logo} />
    <AverageComponent />
    <TotalScoreComponent />
  </div>,
  document.getElementById("root")
);
  • Group by folders:

webpack.prod.js

...
module.exports = merge(common, {
  mode: "production",
  output: {
-   filename: "[name].[chunkhash].js",
+   filename: "js/[name].[chunkhash].js",
+   assetModuleFilename: "images/[hash][ext][query]",
  },
...
  plugins: [
    new MiniCssExtractPlugin({
-     filename: "[name].[chunkhash].css",
+     filename: "css/[name].[chunkhash].css",
      chunkFilename: "[id].[chunkhash].css",
    }),
  ],
});

About Basefactor + Lemoncode

We are an innovating team of Javascript experts, passionate about turning your ideas into robust products.

Basefactor, consultancy by Lemoncode provides consultancy and coaching services.

Lemoncode provides training services.

For the LATAM/Spanish audience we are running an Online Front End Master degree, more info: http://lemoncode.net/master-frontend

About


Languages

Language:TypeScript 48.3%Language:JavaScript 46.8%Language:HTML 4.1%Language:SCSS 0.8%