React + Antd + Webpack开发配置

1,549 阅读3分钟

初始化

npm init

React相关包安装

npm install react react-dom antd --save

Babel相关包安装

npm install @babel/core @babel/plugin-proposal-class-properties @babel/plugin-transform-runtime @babel/preset-env babel-plugin-import babel-loader @babel/preset-react --save-dev

安装Webpack

npm install webpack webpack-cli webpack-dev-server webpack-merge --save-dev

Loaders

npm install css-loader file-loader less less-loader sass sass-loader style-loader url-loader svg-url-loader node-sass --save-dev

Plugins

npm install clean-webpack-plugin compression-webpack-plugin html-webpack-plugin mini-css-extract-plugin terser-webpack-plugin css-minimizer-webpack-plugin progress-bar-webpack-plugin --save-dev

Webpack公共配置模块:webpack.common.config.js

const path = require('path');
const HappyPack = require('happypack'); // 将文件解析任务分解成多个子进程并发执行。子进程处理完任务后再将结果发送给主进程。所以可以大大提升 Webpack 的项目构件速度

module.exports = {
  entry: {
    index: './src/index.js',
  },
  output: {
    filename: 'js/[name]-bundle-[hash:6].js', // 哈希串默认长度为 20,:6 为取前六位,防止浏览器缓存机制带来的业务代码不更新问题
    path: path.resolve(__dirname, '../dist'),
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /(node_modules)/,
        use: ['happypack/loader?id=babel'] // 将对.js文件的处理转交给id为babel的HappyPack的实列
      },
      {
        test: /\.(png|jpe?g|gif)$/i,
        use: ['happypack/loader?id=img']
      },
      {
        test: /\.(woff|woff2|tff|eof)($|\?)/i,
        exclude: /node_modules/,
        use: ['happypack/loader?id=font']
      },
    ]
  },
  plugins: [
    new HappyPack({
      id: 'babel', // 用唯一的标识符id来代表当前的HappyPack 处理一类特定的文件
      threads: 4, // 开启4个线程
      loaders: [{
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env', '@babel/preset-react'],
            plugins: [
              '@babel/plugin-transform-runtime',
              '@babel/plugin-proposal-class-properties',
              ['babel-plugin-import', {
                libraryName: 'antd',
                libraryDirectory: 'es',
                style: true,
              }]
            ],
            cacheDirectory: true
          }
      }]
    }),
    new HappyPack({
      id: 'img',
      threads: 4,
      loaders: [{
        loader: 'url-loader',
        options: {
          name: '[name].[ext]', // 输出的文件名为 原来的文件名
          outputPath: 'images',
          limit: 8192, // 指定文件的最大体积(以字节为单位)。 如果文件体积等于或大于限制,默认情况下将使用 file-loader 并将所有参数传递给它。若是小于了 8kb,将图片打包成 base64 的图片格式插入到 bundle.js 文件中,
          cacheDirectory: true
        }
      }]
    }),
    new HappyPack({
      id: 'font',
      threads: 4,
      loaders: [{
        loader: 'url-loader',
        options: {
          cacheDirectory: true
        }
      },
      {
        loader: 'file-loader',
        options: {
          cacheDirectory: true
        }
      }]
    })
  ]
};

Webpack开发模式配置:webpack.dev.config.js

   const webpack = require('webpack');
const {merge} = require('webpack-merge');
const common_config = require('./webpack.common.config');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = merge(common_config, {
  mode: 'development',
  devServer: {
    port: 3001,
    compress: true,
    hot: true,
  },
  devtool: 'eval-cheap-module-source-map',
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'] //在Webpack中,loader的执行顺序是从右向左执行的,webpack先将所有css模块依赖解析完得到计算结果再创建style标签。因此把style-loader放在css-loader的前面。
      },
      {
        test: /\.less$/,
        use: [{
          loader: 'style-loader'
        },{
          loader: 'css-loader'
        },{
          loader: 'less-loader',
          options: {
            javascriptEnabled: true,
            modifyVars: {'@primary-color': '#1890ff'}
          }
        }]
      },
      {
        test: /\.(sass|scss)$/,
        use: ['style-loader', 'css-loader', 'sass-loader']
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'public/index.html',
      inject: 'body', // 将 js 文件注入到 body 最底部
      hash: false,
    }),
    // 热加载插件
    new webpack.HotModuleReplacementPlugin(),
  ],
})

Wepback生产模式配置:webpack.pro.config.js

const path = require('path');
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const common_config = require('./webpack.common.config.js');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); // 打包从外部引入的css文件
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); // 压缩打包出css文件
const TerserPlugin = require('terser-webpack-plugin'); // 压缩js文件
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin'); // 实现并发编译
const HappyPack = require('happypack'); // 将文件解析任务分解成多个子进程并发执行。子进程处理完任务后再将结果发送给主进程。所以可以大大提升 Webpack 的项目构件速度
const happyThreadPool = HappyPack.ThreadPool({ size: 5 }); // 公用线程池
const ProgressBarPlugin = require('progress-bar-webpack-plugin');

module.exports = merge(common_config, {
  mode: 'production',
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'happypack/loader?id=css'],
      },
      {
        test: /\.less$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'happypack/loader?id=less'],
      },
      {
        test: /\.(sass|scss)$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'happypack/loader?id=sass'],
      },
    ],
  },
  optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 20000,
      minRemainingSize: 0,
      minChunks: 1,
      maxAsyncRequests: 30,
      maxInitialRequests: 30,
      enforceSizeThreshold: 50000,
      cacheGroups: {
        defaultVendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          reuseExistingChunk: true,
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
    minimize: true,
    minimizer: [
      new CssMinimizerPlugin(),
      new TerserPlugin({
        parallel: true,
        terserOptions: {
          format: {
            comments: false
          },
          compress: {
            drop_console: true, // 屏蔽log
          },
        },
      }),
      new ParallelUglifyPlugin({ // // 多进程压缩
        cacheDir: '.cache/', // 缓存压缩后的结果,下次遇到一样的输入时直接从缓存中获取压缩后的结果并返回
        uglifyJS: {
          output: {
            comments: false,
            beautify: false, // 是否输出可读性较强的代码,即会保留空格和制表符,默认为输出,为了达到更好的压缩效果,可以设置为false
          },
          compress: {
            drop_console: true,
            collapse_vars: true, // 是否内嵌虽然已经定义了,但是只用到一次的变量
            reduce_vars: true, // 是否提取出现了多次但是没有定义成变量去引用的静态值,比如将 x = 'xxx'; y = 'xxx'  转换成var a = 'xxxx'; x = a; y = a; 默认为不转换
          },
        },
      }),
    ],
  },
  plugins: [
    new CleanWebpackPlugin(),
    new ProgressBarPlugin(),
    new HtmlWebpackPlugin({
      template: 'public/index.html',
      filename: 'index.html', // 打包出来后html的文件名
      inject: 'body', // 将 js 文件注入到 body 最底部
      minify: {
        removeComments: true,
      },
    }),
    new MiniCssExtractPlugin({
      filename: 'style/[name].[hash:6].css',
    }),
    new HappyPack({
      id: 'css',
      threadPool: happyThreadPool,
      loaders: [{
        loader: 'css-loader',
        options: {
          cacheDirectory: true
        }
      }]
    }),
    new HappyPack({
      id: 'less',
      threadPool: happyThreadPool,
      loaders: [{
        loader: 'less-loader',
      }]
    }),
    new HappyPack({
      id: 'sass',
      threadPool: happyThreadPool,
      loaders: [{
        loader: 'sass-loader',
        options: {
          cacheDirectory: true
        }
      }]
    })
  ],
});

Package.json

{
  "name": "react-webpack5",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack serve --open --config ./config/webpack.dev.config.js",
    "build": "webpack --config ./config/webpack.prod.config.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "antd": "^4.16.12",
    "react": "^17.0.2",
    "react-dom": "^17.0.2"
  },
  "devDependencies": {
    "@babel/core": "^7.15.0",
    "@babel/plugin-proposal-class-properties": "^7.14.5",
    "@babel/plugin-transform-runtime": "^7.15.0",
    "@babel/preset-env": "^7.15.0",
    "@babel/preset-react": "^7.14.5",
    "babel-loader": "^8.2.2",
    "babel-plugin-import": "^1.13.3",
    "clean-webpack-plugin": "^4.0.0-alpha.0",
    "css-loader": "^6.2.0",
    "css-minimizer-webpack-plugin": "^3.0.2",
    "file-loader": "^6.2.0",
    "happypack": "^5.0.1",
    "html-webpack-plugin": "^5.3.2",
    "less": "^4.1.1",
    "less-loader": "^10.0.1",
    "mini-css-extract-plugin": "^2.2.0",
    "node-sass": "^6.0.1",
    "progress-bar-webpack-plugin": "^2.1.0",
    "sass": "^1.38.0",
    "sass-loader": "^12.1.0",
    "style-loader": "^3.2.1",
    "terser-webpack-plugin": "^5.1.4",
    "thread-loader": "^3.0.4",
    "url-loader": "^4.1.1",
    "webpack": "^5.51.1",
    "webpack-cli": "^4.8.0",
    "webpack-dev-server": "^4.0.0",
    "webpack-merge": "^5.8.0",
    "webpack-parallel-uglify-plugin": "^2.0.0"
  }
}

关于路由按需加载

安装loadable@component插件

npm install loadable@component --save

  • 注意
  1. 组件必须default默认导出
  2. fallback必须是component组件,不能是jsx
 const UserComponent = loadable(() => import('./router/user'), {fallback: () => <div>Loading...</div>});

// User组件
import React from 'react';

export default function User(){
    return(
      <h3>User</h3>
    )
}