package.json

{
  "name": "react-webpack-typescript-init"."version": "1.0.0"."description": "react webapck typescript"."main": "src/index.tsx"."repository": "[email protected]:zoushijun/react-webpack-typescript-init.git"."author": "zsj <[email protected]>"."license": "MIT"."scripts": {
    "dll": "./node_modules/.bin/webpack --config ./config/webpack/webpack.dll.js"."build": "./node_modules/.bin/webpack --env production --config ./config/webpack/webpack.config.js && node express.js"."start": "webpack-dev-server --env development --config ./config/webpack/webpack.config.js"
  },
  "dependencies": {
    "react": "^ 16.12.0"."react-dom": "^ 16.12.0"."typescript": "^ 3.7.4." "
  },
  "devDependencies": {
    "@babel/core": "^ 7.7.7"."@babel/preset-env": "^ 7.7.7"."@babel/preset-react": "^ 7.7.4"."@types/react": "^ 16.9.17"."@types/react-css-modules": "^ 4.6.2." "."@types/react-dom": "^ 16.9.4"."add": "^ 2.0.6"."add-asset-html-webpack-plugin": "^ 3.1.3"."babel-loader": "^ 8.0.6"."babel-plugin-react-css-modules": "^ 5.2.6." "."css-loader": "^ 3.4.0"."express": "^ 4.17.1"."fork-ts-checker-webpack-plugin": 4 "" ^ 4.0.0 - beta.."generic-names": "^ 2.0.1." "."happypack": "^ 5.0.1." "."html-webpack-plugin": "^ 3.2.0"."os": ^ "while"."postcss-styl": "^ 0.5.1"."shelljs": "^ 0.8.3"."style-loader": "^ 1.1.2." "."stylus": "^ 0.54.7"."stylus-loader": "^ 3.0.2." "."sugarss": "^ 2.0.0." "."ts-loader": "^ 6.2.1." "."webpack": "^ 4.41.4"."webpack-cli": "^ 3.3.10"."webpack-dev-server": "^ 3.10.1"."webpack-merge": "^ 4.2.2." "."yarn": "^ 1.21.1"}}Copy the code

The basic configuration

Initialize the project

1. Create a new project prod. Mkdir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dir dist 3 Git will not detect changes if the directory is created but not in it.

Installation project dependencies

1, yarn add react react-dom 2, yarn add webpack webpack-cli webpack-dev-server -d 3, Yarn add ts-loader source-map-loader -d (source-map-loader for easy debugging, Yarn add @types/ reacte@types /react-dom -d yarn add html-webpack-plugin -D

Establish the basic document structure of the project

1. Create SRC /index.html

<! DOCTYPE html> <html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <div id="container"></div>
  </body>
</html>


Copy the code

2, the newly built. / SRC/index. The TSX

import * as React from "react";
import * as ReactDOM from "react-dom";

const ROOT = document.getElementById("container");

import { HelloWorld } from "./components/HelloWorld";

ReactDOM.render(<HelloWorld firstName="Chris" lastName="Parker" />, ROOT);

Copy the code

Create helloWorld.tsx inside SRC/Components

import * as React from "react";

export interface HelloWorldProps {
  firstName: string;
  lastName: string;
}

export const HelloWorld = (props: HelloWorldProps) => (
  <h1>
    Hi there from React! Welcome {props.firstName} and {props.lastName}!
  </h1>
);

Copy the code

4, create tsconfig.json and tell Webapack to find typescript files

{
  "compilerOptions": {
    "jsx": "react"."module": "commonjs"."noImplicitAny": true."outDir": "./dist/"."preserveConstEnums": true."removeComments": true."sourceMap": true."target": "es5"
  },
  "include": ["./src/**/**/*"]}Copy the code

5. Create webpack.config.js

const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: "./src/index.tsx",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js"
  },
  devtool: "source-map",
  mode: "development",
  resolve: {
    extensions: [".js".".ts".".tsx"]
  },

  module: {
    rules: [
      {
        test: /\.tsx? $/, loader:"ts-loader"
      },
      { enforce: "pre".test: /\.js$/, loader: "source-map-loader" }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "src"."index.html")]}});Copy the code

6, modify the package. The json

"build": "./node_modules/.bin/webpack"
Copy the code

7. Run YARN Run build

Webpack hot update

Install dependencies

1. Yarn add webpack-dev-server -d

Modify the package. The json

"start:dev": "webpack-dev-server"
Copy the code

Add webpack.config.js configuration

devServer: {
    port: 3001,
    hot: true
  },
  module: {
    rules: [
      {
        test: /\.tsx? $/, loader:"ts-loader"
      }
      // include: path.resolve(__dirname, "./src"),
      // exclude: "./node_modules/",
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "src"."index.html")
    }),
    new webpack.HotModuleReplacementPlugin()
  ]
Copy the code

error

1, Property ‘hot’ does not exist on type ‘Module’

Solution: Use the following methods:

if ((module as any).hot) {}
Copy the code

Use CSS, and use localized CSS

1. Install dependencies

Yarn add style-loader CSS-loader stylus-loader stylus babel-plugin-react-CSs-modules -d 2, yarn add happypack -d (parallel TSX file) 3, Yarn add babel-loader -d (to use babel-plugin-react-CSS-modules) 4, yarn add generic-names -d (to generate name) 5, Yarn add OS -d 6, yarn add @babel/core -d 7, yarn add fork-ts-checker-webpack-plugin -d (use typescript with happypack)

Modify the webpack. Config. Js

const path = require("path");
const webpack = require("webpack");
const genericNames = require("generic-names");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const os = require("os");
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
const HappyPack = require("happypack");
const happyThreadPool = HappyPack.ThreadPool({
  size: os.cpus().length
});

const stylModuleRegex = /\.cssmodule\.styl$/;
const localIdentName = "[name]__[local]-[hash:base64:5]";
const generateScope = genericNames(localIdentName, {
  context: process.cwd()
});

const getStyleLoaders = (cssOptions, preProcessor) => {
  const loaders = [
    require.resolve("style-loader"),
    // MiniCssExtractPlugin.loader,
    {
      loader: require.resolve("css-loader"),
      options: cssOptions
    }
  ];
  if (preProcessor) {
    loaders.push(require.resolve(preProcessor));
  }
  return loaders;
};
module.exports = {
  entry: "./src/index.tsx",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js"
  },
  devtool: "inline-source-map",
  mode: "development",
  resolve: {
    extensions: [".tsx".".ts".".js".".styl"]
  },
  devServer: {
    port: 3001,
    hot: true
  },
  module: {
    rules: [
      {
        oneOf: [
          {
            test: stylModuleRegex,
            use: getStyleLoaders(
              {
                importLoaders: 1,
                modules: true,
                camelCase: "dashes",
                getLocalIdent({ resourcePath }, localIdentName, localName) {
                  return generateScope(localName, resourcePath); }},"stylus-loader")}, {test: /\.css$/,
            include: path.resolve(__dirname, "./src"),
            use: ["style-loader"."css-loader"]}]}, {test: /\.tsx? $/, include: path.resolve(__dirname,"./src"),
        exclude: [path.resolve("./node_modules/")],
        use: ["happypack/loader? id=happybabel"]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "src"."index.html")
    }),
    new webpack.HotModuleReplacementPlugin(),
    new ForkTsCheckerWebpackPlugin(),
    new HappyPack({
      id: "happybabel",
      loaders: [
        {
          loader: "babel-loader",
          options: {
            // babelrc: false,
            // configFile: false// Set the cache cacheDirectory:true,
            exclude: [path.resolve("./node_modules/")],
            plugins: [
              [
                "react-css-modules", {// Generate CSS name style generateScopedName:"[name]__[local]-[hash:base64:5]",
                  filetypes: { ".styl": { syntax: "sugarss" } },
                  webpackHotModuleReloading: true,
                  exclude: ".css$"
                }
              ]
            ]
          }
        },
        {
          loader: "ts-loader",
          options: {
            happyPackMode: true, / /disable type checker - we will use it in fork plugin
            transpileOnly: true
          }
        }
      ],
      threadPool: happyThreadPool
    })
  ]
};

Copy the code

error

1. An error message is displayed indicating CSS-loader options

After checking the CSS-loader documentation, modify the csS-loader configuration in webpack.config.js as follows

{
            test: stylModuleRegex,
            use: getStyleLoaders(
              {
                importLoaders: 1,
                modules: {
                  getLocalIdent({ resourcePath }, localIdentName, localName) {
                    return generateScope(localName, resourcePath); }}},"stylus-loader")},Copy the code

2 DetailHTMLProps error

Solutions: Yarn add @types/ react-csS-modules-d 2: styleName has been converted to styleName. That is, there is a problem with the styleName configuration

3 without importing at least one styleSheet





import "./index.cssmodule.styl";
Copy the code

2 Add it in tsconfig.json

Import * as React from React to import React from React and babel-plugin-react-csS-modules without importing at  least one stylesheet."allowSyntheticDefaultImports": true."moduleResolution": "node"."module": "esNext"
Copy the code

4 not find module sugarss

5 Type ‘{ first: true; }’ is not assignable to type ‘IntrinsicAttributes & HelloWorldProps

The reason:

if ((module as any).hot) {
 (module as any).hot.accept("./components/HelloWorld.tsx".function() {
   console.log("Accepting the updated printMe module!");
   console.log(111);
   ReactDOM.render(<HelloWorld first
 });
}
Copy the code

Solution:

if ((module as any).hot) {
  (module as any).hot.accept("./components/HelloWorld.tsx".function() {
    console.log("Accepting the updated printMe module!");
    console.log(111);
    ReactDOM.render(<HelloWorld firstName="Chris" lastName="Parker" />, ROOT);
  });
}
Copy the code

Babel-plugin-react-css-modules configuration summary

The babel-plugin-react-CSS-modules package needs to be configured with babel-loader, so ts-loader is used first and then babel-loader is used. There is no problem in the process. However, there are many problems in the configuration process.

1 tsconfig.json

When tsconfig.json is configured, JSX is configured as react. There is a problem. In fact, all the tsconfig.json is converted by ts-loader, but it does not go to babel-loader. I’m going to change it to stylename and keep reporting errors.

Split webpack. Config. Js

The principle of

Use webpack-merge to differentiate between a development environment and a production environment, depending on the parameters passed in. Then the webpack. Config. Js into a public webpack.com mon. Config. Js, a development environment using webpack. Dev. Config. Js, a production environment with webpack. Prod. Config. Js, There is also webpack.config.js in the root directory

Webpack. Config. Js code

const commConfig = require("./config/webpack.common.config.js");
const devConfig = require("./config/webpack.dev.config.js");
const prodConfig = require("./config/webpack.prod.config.js");
const merge = require("webpack-merge");

module.exports = mode => {
  if (mode === "development") {
    return merge(commConfig, devConfig, { mode });
  }
  return merge(commConfig, prodConfig, { mode });
};

Copy the code

package.json

"start": "webpack-dev-server --env development"."build": "./node_modules/.bin/webpack --env production"
Copy the code

Increase the express

1. Install express YARN add express-d. 2

const express = require("express");
const app = express();
const portNumber = 3888;
const sourceDir = "dist";

app.use(express.static(sourceDir));

app.listen(portNumber, () => {
  console.log(`Express web server started: http://localhost:${portNumber}`);
  console.log(`Serving content from /${sourceDir}/ `); });Copy the code

3, modify the package. The json

"build": "./node_modules/.bin/webpack --env production --config ./config/webpack/webpack.config.js && node express.js".Copy the code

React, reat-dom, and other static files into DLL files

1, the newly built lib. Dep. Js

// Resource dependency package const lib = ["react"."react-dom"];

module.exports = lib;
Copy the code

New webpack. DLL. Js

/* webpack --config  webpack.dll.config.js --progress */

const path = require("path");
const webpack = require("webpack");
const lib = require("./lib.dep");
const outputPath = path.join(__dirname, ".. /dll");
const shelljs = require("shelljs");
shelljs.rm("-r", outputPath);

module.exports = {
  devtool: "eval",
  entry: {
    dllSelf: lib
  },
  mode: "production",
  optimization: {
    minimize: true
  },
  output: {
    path: outputPath,
    filename: "[name].[hash].js"/** * output.library * will be defined as window.${output.library}* In this case, it will be defined as' window.vendor_library '*/ library:"[name]"}, plugins: [new webpack.dllplugin ({/** * path * define the location generated by the manifest file * [name] parts of the entry name */ path: path.join(outputPath,"manifest.json"/** * name * DLL bundle output to that global variable * the same as output.library. */ name:"[name]",
      context: __dirname
    }),
    new webpack.DefinePlugin({
      "process.env.NODE_ENV": JSON.stringify("production")]}});Copy the code

3. Reference DLL files in Pulgin of Webpack

module.exports = {
  plugins: [
    new webpack.DllReferencePlugin({
      context: __dirname,
      manifest: require(".. /dll/manifest.json")
    }),
    new AddAssetHtmlPlugin([
      {
        filepath: utils.getDllPath(".. /dll"),
        includeSourcemap: false}}])];Copy the code