dva+webpack+ts+eslint项目搭建

784 阅读8分钟

Dva 配置

安装 Dva

1,全局安装 dva-cli:

npm install dva-cli -g

2,初始化 dva 项目:

  • 安装方式一:dva new myapp。

  • 安装方式二:在创建好的项目文件夹中运行 dva init。

安装 webpack

安装 webpack webpack-cli webpack-dev-server

注意:webpack webpack-cli 下载的版本需要与全局的版本保持一致,防止出现不必要的错误,webpack-dev-server 版本不能下太高,否则可能出现兼容性错误。

1,我本地 webpack webpack-cli 分别为:webpack@4.44.2,webpack-cli@4.4.0。webpack-dev-server 为:webpack-dev-server@3.11.2

npm i webpack@4.44.2 webpack-cli@4.4.0 webpack-dev-server@3.11.2 -D

配置 webpack.config.js 文件

在项目根目录中创建 webpack.config.js 文件。

配置 入口及输出路径

1,相关配置如下:

entry: "./src/index.ts",
output: {
  filename: "bundle.js",
  path: path.resolve(__dirname, "build"),
},

打包样式资源

1,打包样式资源需要使用的 loader(以下 loader 具体的用处可自行百度):

  • style-loader、css-loader、less、less-loader、postcss-normalize。
npm i style-loader css-loader less less-loader postcss-normalize -D

2,如果是移动端,可以配置 px2rem-loader 将 px 自动转为 rem。

  • 安装 px2rem-loader 的同时还需安装 lib-flexible。
npm i px2rem-loader lib-flexible -D
  • 安装完毕以后需要在项目入口 js/ts 文件中引入 lib-flexible。
import "lib-flexible";

3,具体配置如下:

module: {
  rules: [
    {
      test: /\.(css|less)$/,
      use: [
        devMode ? MiniCssExtractPlugin.loader : "style-loader",
        {
          loader: "css-loader",
          options: {
            modules: {
              mode: "local",
              localIdentName: "[name]__[local]",
            },
            importLoaders: 1,
          },
        },
        {
          loader: "less-loader",
          options: {
            lessOptions: {
              // modifyVars: {
              //   'primary-color': 'green',
              //   'menu-item-active-bg': 'green',
              // },
              javascriptEnabled: true,
            },
          },
        },
        {
          loader: "px2rem-loader",
          options: {
            importLoaders: 1,
            remUnit: 37.5,
            min: 2,
          },
        },
        {
          loader: "postcss-loader",
          options: {
            postcssOptions: {
              ident: "postcss",
              plugins: [
                [
                  "postcss-preset-env",
                  {
                    autoprefixer: {
                      // browsers: [
                      //   '>1%',
                      //   'last 4 versions',
                      //   'Firefox ESR',
                      //   'not ie < 9',
                      // ],
                      flexbox: "no-2009",
                    },
                    stage: 3,
                  },
                ],
                postcssNormalize(),
              ],
            },
            sourceMap: false,
          },
        },
      ],
    },
  ];
}

打包 js/jsx/ts/tsx 等资源

1,打包 js/jsx/ts/tsx 等资源需要使用的 loader(以下 loader 具体的用处可自行百度):

  • @babel/core、@babel/preset-env、babel-loader、@babel/preset-react、@babel/plugin-transform-runtime、ts-loader、typescript
npm i @babel/core @babel/preset-env babel-loader @babel/preset-react @babel/plugin-transform-runtime ts-loader typescript -D

2,具体配置如下:

module: {
  rules: [
    {
      test: /\.(js|jsx|ts|tsx)$/,
      use: [
        {
          loader: "babel-loader",
          options: {
            presets: [
              [
                "@babel/preset-env",
                {
                  modules: false,
                },
              ],
              "@babel/preset-react",
            ],
            plugins: [
              [
                "@babel/plugin-transform-runtime",
                {
                  useESModules: true,
                  corejs: 3,
                },
              ],
            ],
          },
        },
        {
          loader: "ts-loader",
          options: {
            happyPackMode: true,
            transpileOnly: true,
            compilerOptions: {
              noEmit: false,
              module: "esnext",
              target: devMode ? "es2017" : "es5",
            },
          },
        },
      ],
      exclude: [/node_modules/],
    },
  ];
}

打包图片资源

1,打包图片资源需要使用的 loader(以下 loader 具体的用处可自行百度):

  • url-loader、html-loader、file-loader
npm i url-loader html-loader file-loader -D

2,具体配置如下:

module: {
  rules: [
    {
      test: /\.(mp4|png|jpg|jpeg|png|svg|cur|gif|webp|webm|otf)$/,
      use: [
        {
          loader: "url-loader",
          options: {
            limit: 8192,
            name: "static/[name].[hash:11].[ext]",
          },
        },
      ],
      exclude: /assets\/icons\/|components\/Base\/Icon\/icons\//,
    },
    {
      test: /\.html$/,
      loader: "html-loader",
    },
  ];
}

打包 svg 资源

1,打包 svg 资源需要使用的 loader(以下 loader 具体的用处可自行百度):

  • svg-sprite-loader
npm i svg-sprite-loader -D

2,具体配置如下:

module: {
  rules: [
    {
      test: /\.svg$/,
      include: /assets\/icons\/|components\/Base\/Icon\/icons\//,
      use: [
        {
          loader: "svg-sprite-loader",
          options: {
            symbolId: "icon-[name]",
          },
        },
      ],
    },
  ];
}

plugins 配置

1,所需插件:html-webpack-plugin、mini-css-extract-plugin、optimize-css-assets-webpack-plugin

npm i html-webpack-plugin mini-css-extract-plugin optimize-css-assets-webpack-plugin -D

2,具体配置如下:

plugins: [
  new HtmlWebpackPlugin({
    filename: "index.html",
    template: path.resolve(__dirname, "./src/index.ejs"),
    minify: {
      removeComments: true, // 移除注释
      collapseWhitespace: true, // 移除空格
    },
  }),
  new MiniCssExtractPlugin({
    filename: "[name].css",
    chunkFilename: "[name].css",
  }),
  new OptimizeCSSAssetsPlugin(),
];

配置 devServer

1,在 devServer 具体配置如下:

devServer: {
  port: 8000,
  host: 'localhost',
  // open: true,
  disableHostCheck: true,
  historyApiFallback: true,
  proxy: {
    '/api': {
      target: 'http://test.xxx.xxx.com',
      changeOrigin: true,
    },
  },
  before(app) {
    apiMocker(app, path.resolve(__dirname, './mock/mock.js'));
  },

  // 配置启动时在终端需要显示及隐藏的提示信息
  stats: {
    colors: true,
    hash: false, // 编译使用的 hash
    version: false, // 用来编译的 webpack 的版本
    timings: true, // 编译耗时 (ms)
    assets: false, // 是否开启assets提示
    chunks: false,
    modules: false, // 是否开启modules提示
    reasons: false,
    children: false,
    source: false,
    errors: true, // 处理这个模块发现的错误的数量
    errorDetails: false,
    warnings: true,
    publicPath: false,
    warningsFilter: /export .* was not found in/,
  },
}

配置 stats

在与 plugins 或 devServer 同层次下添加 stats 配置。

1,该配置主要用于设置在项目 build 打包时在终端的提示信息。

2,具体配置如下:

devServer:[
  // ...
],
// 设置打包构建时终端中的提示信息
stats: {
  hash: false, // 编译使用的 hash
  timings: true,  // 告知 stats 添加时间信息
  modules: false, // 告知 stats 是否添加关于构建模块的信息
  chunks: false,  // 告知 stats 是否添加关于 chunk 的信息
  version: false, // 是否展示用来编译的 webpack 的版本
  assets: false,  // 是否关闭展示资源信息
  children: false,  // 是否不添加关于子模块的信息
  warningsFilter: warning => /Conflicting order between/gm.test(warning), // 排除掉匹配的告警信息
}

配置 performance

在与 plugins 或 devServer 同层次下添加 performance 配置。

1,该配置主要用来控制 webpack 如何通知「资源(asset)和入口起点超过指定文件限制」,如果不配置该属性,如果一个资源超过 250kb,webpack 会对此输出一个警告(报黄)来通知你。

devServer:[
  // ...
],
// 配置如何展示性能提示。例如,如果一个资源超过 250kb,webpack 会对此输出一个警告来通知你。
performance: {
  hints: "warning", // 不展示警告或错误提示。
  maxAssetSize: 30000000, // 整数类型(以字节为单位)
  maxEntrypointSize: 50000000, // 整数类型(以字节为单位)
  assetFilter: function (assetFilename) { // 此属性允许 webpack 控制用于计算性能提示的文件
    // 提供资源文件名的断言函数
    return assetFilename.endsWith('.css') || assetFilename.endsWith('.js');
  }
}

更改 package.json

1,具体更改如下:

"start": "webpack serve --mode=development --config=webpack.config.js --hot --inline",
"build": "npm i && webpack --mode=production --progress",

配置 typescript

配置 tsconfig.json

1,文件具体配置如下:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "lib": ["es6", "es2016", "es2017", "es2018", "esnext", "dom"],
    "allowJs": true,
    "skipLibCheck": true,
    "jsx": "react",
    "sourceMap": true,
    "noEmit": true,
    "importHelpers": true,
    "strict": true,
    "strictNullChecks": true,
    "noUnusedLocals": true,
    "moduleResolution": "node",
    "baseUrl": "./",
    "paths": {
      "@/*": ["src/*"],
      "$/*": ["typings/*"]
    },
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "experimentalDecorators": true
  },
  "include": ["src", "typings"],
  "exclude": ["build", "vendor", "**/*/_*", "src/components/VideoClipper"]
}

增加 typings 文件配置

说明:由于当使用模块化引入 less 样式资源文件时,防止报找不到模块的错误,因此需要全局导出相应的资源文件。

1,在 typings 文件夹中增加 index.d.ts 文件,相关配置如下:

declare module '*.svg' {
  const url: string;
  export default url;
}

declare module '*.png' {
  const url: string;
  export default url;
}

declare module '*.webp' {
  const url: string;
  export default url;
}

declare module '*.webm' {
  const url: string;
  export default url;
}

declare module '*.mp4' {
  const url: string;
  export default url;
}

declare module '*.jpg' {
  const url: string;
  export default url;
}

/**
 * 考虑使用 typed-css-modules 对 less 自动生成 d.ts (主要是需要开进程 watch 编译,比较麻烦)
 * 或者调研 TypeScript 扩展
 */
declare module '*.less' {
  const styles: Record<string, string>;
  export default styles;
}

declare module 'dva-loading' {
  export default function createLoading(options: any): any;
}

declare module '*.woff';
declare module '*.woff2';
declare module '*.otf';

declare module '*.json' {
  const value: any;
  export default value;
}

declare interface Window {
  liangzhu_data_info: any;
}

declare module 'echarts/map/json/*.json' {
  const json: any;
  export default json;
}
declare module 'react-lines-ellipsis' {
  // type definitions goes here
  const LinesEllipsis: any;
  export default LinesEllipsis;
}

declare module 'react-lines-ellipsis/lib/responsiveHOC' {
  const responsiveHOC: any;
  export default responsiveHOC;
}

declare module 'react-useanimations' {
  const UseAnimations: any;
  export default UseAnimations;
}

interface HTMLVideoElement {
  __hls__?: Hls;
}

interface Window {
  HLS_JS_DEBUG?: boolean;
}

declare module 'china-division/dist/*.json' {
  const json: any;
  export default json;
}

declare module 'braft-utils' {
  const ContentUtils: any;
  export { ContentUtils };
}

declare module 'rc-form' {
  const createForm: any;
  export { createForm };
}

配置 eslint

eslint 所需插件

1,插件需要注意版本问题,否则会出现莫名错误,所需插件及版本如下:

"@typescript-eslint/eslint-plugin": "~2.6.1",
"@typescript-eslint/parser": "~2.6.1",
"babel-eslint": "^8.2.3",
"eslint": "^5.3.0",
"eslint-config-airbnb": "^17.1.0",
"eslint-loader": "~3.0.2",
"eslint-plugin-import": "^2.17.3",
"eslint-plugin-jsx-a11y": "^6.1.2",
"eslint-plugin-react": "^7.16.0",
"eslint-plugin-react-ext": "^0.1.0",
"eslint-plugin-react-hooks": "^1.7.0",

创建 .eslintrc 文件

1,该配置文件主要用于定义 eslint 的规则,具体配置如下:

/**
 * Eslint (JavaScript 和 TypeScript)
 *
 * 如果希望在编辑时能实时看到 eslint 效果,需要按照 vscode eslint 插件
 * 注:eslint 插件默认不会校验 TypeScript 文件,需要在设置中修改:
 * 将设置(JSON)的 "eslint.validate": [ "javascript", "javascriptreact"]
 * 后面加上 , "typescript", "typescriptreact"
 */
{
  // TODO 目前 @typescript-eslint/parser 和 babel-eslint 对 jsx 的处理有一些差异,所以 js 还是维持原 parser
  "parser": "babel-eslint",
  "plugins": ["@typescript-eslint", "react-ext", "react-hooks"],
  "extends": "airbnb",
  "env": {
    "browser": true
  },
  "parserOptions": {
    "ecmaFeatures": {
      "experimentalObjectRestSpread": true
    }
  },
  "settings": {
    "react": {
      "pragma": "React", // Pragma to use, default to "React"
      "version": "detect"
    }
  },
  "rules": {
    "linebreak-style": [0, "erron", "windows"],
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn",
    "arrow-body-style": ["off"],
    "camelcase": [
      "error",
      {
        "properties": "never",
        "allow": ["^UNSAFE_"]
      }
    ],
    "consistent-return": ["off"],
    "generator-star-spacing": ["off"],
    "global-require": ["error"],
    "import/extensions": ["off"],
    "import/no-extraneous-dependencies": ["off"],
    "import/no-unresolved": ["off"],
    "import/prefer-default-export": ["off"],
    "import/no-cycle": ["off"],
    "jsx-a11y/no-static-element-interactions": ["off"],
    "no-bitwise": ["off"],
    "no-console": ["off"],
    "no-else-return": ["off"],
    "no-loop-func": ["off"],
    "no-nested-ternary": ["off"],
    "no-restricted-syntax": ["off"],
    "react/forbid-prop-types": ["off"],
    "react/jsx-filename-extension": ["off"],
    "react/jsx-no-bind": ["error"],
    "react/prefer-stateless-function": ["off"],
    "react/prop-types": ["off"],
    "react/no-unsafe": [
      "error",
      {
        "checkAliases": true
      }
    ],
    "react/no-unused-state": ["warn"],
    "react-ext/no-unused-class-property": "warn",
    "require-yield": [1],
    "no-continue": ["off"],
    "no-mixed-operators": ["off"],
    "no-underscore-dangle": ["off"],
    "no-plusplus": ["off"],
    "no-unused-expressions": ["off"],
    "no-param-reassign": ["off"],
    "no-await-in-loop": ["off"],
    "no-alert": ["error"],
    "no-restricted-globals": ["error", "event"],
    "jsx-a11y/click-events-have-key-events": ["off"],
    "import/first": ["off"],
    "jsx-a11y/anchor-is-valid": ["off"],
    "react/require-default-props": ["off"],
    "arrow-parens": ["off"],
    "react/sort-comp": ["off"],
    "function-paren-newline": ["off"],
    "prefer-template": ["off"],
    "prefer-arrow-callback": [
      "error",
      {
        "allowNamedFunctions": true
      }
    ],
    "jsx-a11y/media-has-caption": ["off"],
    "jsx-a11y/alt-text": ["off"],
    "jsx-a11y/anchor-has-content": ["off"],
    "jsx-a11y/no-noninteractive-element-interactions": ["off"],
    "jsx-a11y/label-has-for": ["off"],
    "jsx-a11y/label-has-associated-control": ["off"],
    "jsx-a11y/mouse-events-have-key-events": ["off"],
    "class-methods-use-this": ["off"],
    "react/destructuring-assignment": ["off"],
    "operator-linebreak": ["off"],
    "max-len": [
      "error",
      {
        "code": 160,
        "ignoreComments": true
      }
    ],
    "react/button-has-type": ["off"],
    "react/no-access-state-in-setstate": ["off"],
    "react/jsx-no-target-blank": ["off"],
    "react/jsx-one-expression-per-line": ["off"],
    "react/no-danger": "off"
  },
  "overrides": [
    {
      "files": ["**/*.ts", "**/*.tsx"],
      "parser": "@typescript-eslint/parser",
      "parserOptions": {
        "project": "./tsconfig.json"
      },
      "rules": {
        "import/export": ["off"], // 这一条不需要
        // 下面的这些需要禁用,然后启用 @typescript-eslint 下同名的
        "no-unused-vars": ["off"],
        "no-useless-constructor": ["off"],
        "camelcase": ["off"],
        "indent": ["off"],
        "no-undef": "off",
        "no-use-before-define": "off",
        "no-empty-function": "off",
        "@typescript-eslint/no-unused-vars": [
          "warn",
          {
            "vars": "all",
            "args": "none",
            "ignoreRestSiblings": true
          }
        ],
        "@typescript-eslint/no-useless-constructor": ["error"],
        "@typescript-eslint/camelcase": [
          "error",
          {
            "properties": "never",
            "allow": ["^UNSAFE_"]
          }
        ],
        "@typescript-eslint/indent": ["error", 2],
        "@typescript-eslint/no-empty-function": "error",
        "@typescript-eslint/array-type": [
          "error",
          {
            "default": "array-simple"
          }
        ],
        "@typescript-eslint/member-delimiter-style": ["error"],
        "@typescript-eslint/type-annotation-spacing": ["error"],
        "@typescript-eslint/prefer-for-of": ["error"],
        "@typescript-eslint/no-this-alias": [
          "warn",
          {
            "allowDestructuring": true
          }
        ],
        "@typescript-eslint/prefer-string-starts-ends-with": ["error"],
        "@typescript-eslint/prefer-includes": ["error"]
      }
    }
  ]
}

创建 .eslintignore 文件

1,该文件主要用于告诉 eslint 检查时忽略的文件,具体配置如下:

app/proxy*
coverage
dist
build
fntest
mocks
node_modules
spm_modules
test/fixtures
src/components/VideoClipper
*.config.js

更改 package 中 lint 命令

1,具体修改如下:

"lint": "eslint --fix --ext .js,.jsx,.ts,.tsx src -c .eslintrc"

异常报错

1,TypeError: cli.isValidationError is not a function at Command.cli.makeCommand。

  • 解决方式:升级 webpack-cli。

2,TypeError: Cannot read property 'tap' of undefined at HtmlWebpackPlugin.apply。

  • 解决方式:将 html-webpack-plugin 版本问题导致,可将其降级成 4.5.1 版本。

3,UnhandledPromiseRejectionWarning: TypeError: expecting a function but got [object Undefined]

  • 解决方式:由于没有安装 html-webpack-plugin,将其装上即可。

4,TypeError: this.getOptions is not a function at Object.lessLoader。

  • 解决方式:该错误由于 less-loader 版本问题导致,可将其降级为:5.0.0 即可。

5,Can't resolve '@babel/runtime-corejs3/helpers/esm/classCallCheck'。

  • 解决方式:安装 @babel/runtime-corejs3 即可。

6,Error: Failed to load plugin @typescript-eslint: Cannot find module 'eslint-plugin-@typescript-eslint'。

  • 解决方式:该错误由于没有安装 eslint-loader 导致,安装即可。

详细配置及所需包名

完整 webpack 配置

const path = require("path");
// const { name, version } = require('./package.json');
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const postcssNormalize = require("postcss-normalize");
const apiMocker = require("mocker-api");

const matchSVGSprite = /assets\/icons\/|components\/Base\/Icon\/icons\//;
const { ESLINT_LOADER_DISABLED } = process.env; // 通过环境变量禁用 eslint-loader

module.exports = (env, argv) => {
  const devMode = argv.mode !== "production";
  // const publicPath = devMode ? '/' : `//x.xxx.cn/${name}/${version}/`;

  return {
    entry: "./src/index.ts",
    output: {
      filename: "bundle.js",
      path: path.resolve(__dirname, "build"),
      // publicPath
    },

    // 生产模式下关闭map文件
    devtool: devMode ? "source-map" : "none",

    // 配置路径别名
    resolve: {
      extensions: [".ts", ".tsx", ".js", ".jsx", ".json"],
      alias: {
        "@": path.resolve(__dirname, "./src"),
        $: path.resolve(__dirname, "./typings"),
      },
    },

    module: {
      rules: [
        !devMode && !ESLINT_LOADER_DISABLED
          ? {
              enforce: "pre",
              test: /\.jsx?|\.tsx?$/,
              include: path.resolve(__dirname, "src"),
              loader: "eslint-loader",
              options: {
                cache: true,
              },
            }
          : {},
        /**
         * 解析样式资源,需要安装style-loader、css-loader、less、less-loader、postcss-normalize
         */
        {
          test: /\.(css|less)$/,
          use: [
            devMode ? MiniCssExtractPlugin.loader : "style-loader",
            {
              loader: "css-loader",
              options: {
                modules: {
                  mode: "local",
                  localIdentName: "[name]__[local]",
                },
                importLoaders: 1,
              },
            },
            {
              loader: "less-loader",
              options: {
                lessOptions: {
                  // modifyVars: {
                  //   'primary-color': 'green',
                  //   'menu-item-active-bg': 'green',
                  // },
                  javascriptEnabled: true,
                },
              },
            },
            {
              loader: "px2rem-loader",
              options: {
                importLoaders: 1,
                remUnit: 37.5,
                min: 2,
              },
            },
            {
              loader: "postcss-loader",
              options: {
                postcssOptions: {
                  ident: "postcss",
                  plugins: [
                    [
                      "postcss-preset-env",
                      {
                        autoprefixer: {
                          flexbox: "no-2009",
                        },
                        stage: 3,
                      },
                    ],
                    postcssNormalize(),
                  ],
                },
                sourceMap: false,
              },
            },
          ],
        },
        /**
         * 使用babel-loader编译js|jsx|ts|tsx
         * 需要下载 @babel/core @babel/preset-env babel-loader
         * @babel/preset-react @babel/plugin-transform-runtime
         * ts-loader typescript
         */
        {
          test: /\.(js|jsx|ts|tsx)$/,
          use: [
            {
              loader: "babel-loader",
              options: {
                presets: [
                  [
                    "@babel/preset-env",
                    {
                      modules: false,
                    },
                  ],
                  "@babel/preset-react",
                ],
                plugins: [
                  [
                    "@babel/plugin-transform-runtime",
                    {
                      useESModules: true,
                      corejs: 3,
                    },
                  ],
                ],
              },
            },
            {
              loader: "ts-loader",
              options: {
                happyPackMode: true,
                transpileOnly: true,
                compilerOptions: {
                  noEmit: false,
                  module: "esnext",
                  target: devMode ? "es2017" : "es5",
                },
              },
            },
          ],
          exclude: [/node_modules/],
        },
        /**
         * 打包图片资源,需要下载 url-loader html-loader file-loader
         */
        {
          test: /\.(mp4|png|jpg|jpeg|png|svg|cur|gif|webp|webm|otf)$/,
          use: [
            {
              loader: "url-loader",
              options: {
                limit: 8192,
                name: "static/[name].[hash:11].[ext]",
              },
            },
          ],
          exclude: matchSVGSprite,
        },
        {
          test: /\.html$/,
          loader: "html-loader",
        },
        /**
         * 打包svg资源,需要下载 svg-sprite-loader(用于将svg图以img标签的形式引入)
         */
        {
          test: /\.svg$/,
          include: matchSVGSprite,
          use: [
            {
              loader: "svg-sprite-loader",
              options: {
                symbolId: "icon-[name]",
              },
            },
          ],
        },
      ],
    },

    /**
     * plugins配置,需要下载:html-webpack-plugin、mini-css-extract-plugin
     */
    plugins: [
      new HtmlWebpackPlugin({
        filename: "index.html",
        template: path.resolve(__dirname, "./src/index.ejs"),
        minify: {
          removeComments: true, // 移除注释
          collapseWhitespace: true, // 移除空格
        },
      }),
      new MiniCssExtractPlugin({
        filename: "[name].css",
        chunkFilename: "[name].css",
      }),
      new OptimizeCSSAssetsPlugin(),
    ],

    /**
     * 热更新
     */
    devServer: {
      port: 8000,
      host: "localhost",
      // open: true,
      disableHostCheck: true,
      historyApiFallback: true,
      proxy: {
        "/api": {
          target: "http://test.bat.xinhuazhiyun.com",
          changeOrigin: true,
        },
      },
      before(app) {
        apiMocker(app, path.resolve(__dirname, "./mock/mock.js"));
      },

      // 配置启动时终端提示信息
      stats: {
        colors: true,
        hash: false, // 编译使用的 hash
        version: false, // 用来编译的 webpack 的版本
        timings: true, // 编译耗时 (ms)
        assets: false, // 是否开启assets提示
        chunks: false,
        modules: false, // 是否开启modules提示
        reasons: false,
        children: false,
        source: false,
        errors: true, // 处理这个模块发现的错误的数量
        errorDetails: false,
        warnings: true,
        publicPath: false,
        warningsFilter: /export .* was not found in/,
      },
    },

    // 设置打包构建时终端中的提示信息
    stats: {
      hash: false, // 编译使用的 hash
      timings: true, // 告知 stats 添加时间信息
      modules: false, // 告知 stats 是否添加关于构建模块的信息
      chunks: false, // 告知 stats 是否添加关于 chunk 的信息。
      version: false, // 是否用来编译的 webpack 的版本
      assets: true, // 是否关闭展示资源信息
      children: false, // 不添加关于子模块的信息。
      warningsFilter: (warning) => /Conflicting order between/gm.test(warning), // 排除掉匹配的告警信息
    },

    performance: {
      hints: "warning", // 枚举
      maxAssetSize: 30000000, // 整数类型(以字节为单位)
      maxEntrypointSize: 50000000, // 整数类型(以字节为单位)
      assetFilter: function (assetFilename) {
        // 提供资源文件名的断言函数
        return assetFilename.endsWith(".css") || assetFilename.endsWith(".js");
      },
    },
  };
};

package.json 资源包

{
  "private": true,
  "scripts": {
    "start": "webpack serve --mode=development --config=webpack.config.js --hot --inline",
    "build": "npm i && webpack --mode=production --progress",
    "lint": "eslint --fix --ext .js,.jsx,.ts,.tsx src -c .eslintrc",
    "precommit": "npm run lint"
  },
  "dependencies": {
    "dva": "^2.4.1",
    "react": "^16.2.0",
    "react-dom": "^16.2.0"
  },
  "devDependencies": {
    "@babel/core": "^7.13.10",
    "@babel/plugin-transform-runtime": "^7.13.10",
    "@babel/preset-env": "^7.13.12",
    "@babel/preset-react": "^7.12.13",
    "@babel/preset-typescript": "^7.13.0",
    "@babel/runtime-corejs3": "^7.13.10",
    "@typescript-eslint/eslint-plugin": "~2.6.1",
    "@typescript-eslint/parser": "~2.6.1",
    "babel-eslint": "^8.2.3",
    "babel-loader": "^8.2.2",
    "babel-plugin-dva-hmr": "^0.3.2",
    "css-loader": "^5.1.4",
    "eslint": "^5.3.0",
    "eslint-config-airbnb": "^17.1.0",
    "eslint-loader": "~3.0.2",
    "eslint-plugin-import": "^2.17.3",
    "eslint-plugin-jsx-a11y": "^6.1.2",
    "eslint-plugin-react": "^7.16.0",
    "eslint-plugin-react-ext": "^0.1.0",
    "eslint-plugin-react-hooks": "^1.7.0",
    "file-loader": "^6.2.0",
    "html-loader": "^2.1.2",
    "html-webpack-plugin": "^4.5.1",
    "husky": "^0.12.0",
    "less": "^4.1.1",
    "less-loader": "^5.0.0",
    "lib-flexible": "^0.3.2",
    "mini-css-extract-plugin": "^1.3.9",
    "mocker-api": "^2.8.3",
    "optimize-css-assets-webpack-plugin": "^5.0.4",
    "postcss-normalize": "^9.0.0",
    "px2rem-loader": "^0.1.9",
    "redbox-react": "^1.4.3",
    "roadhog": "^2.5.0-beta.4",
    "style-loader": "^2.0.0",
    "svg-sprite-loader": "^6.0.2",
    "ts-loader": "^8.0.18",
    "typescript": "^4.2.3",
    "url-loader": "^4.1.1",
    "webpack": "^4.44.2",
    "webpack-cli": "^4.5.0",
    "webpack-dev-server": "^3.11.2"
  },
  "husky": {
    "hooks": {
      "pre-commit": "sh ./.git-hooks/pre-commit.sh"
    }
  }
}

最后附上该项目 github 仓库地址 猛戳这里查看吧

博客地址:dnhyxc