webpack学习笔记(四):webpack5+react

364 阅读4分钟

前言

这是webpack学习笔记系列的第四篇文章,前文之路:

webpack5+react配置过程

本文的配置不基于以前的任何一个工程,而是重新建立了一个工程文件。开发过程中采用的服务器为webpack-dev-server服务器。本文实现了react+webpack5的基础工程。

初始化项目

  1. 新建一个工程文件夹,并且将终端的目录切换到工程文件夹之下
  2. 运行npm init命令,生成package.json文件
  3. 安装 webpack、webpack-cli、webpack-dev-server,用于搭建webpack开发环境
npm install --save-dev webpack webpack-cli webpack-dev-server
  1. 安装支持css文件的依赖包
// 根据需求安装这些依赖包
npm install --save-dev css-loader sass-loader

安装 react 和react-dom

使用npm命令,安装 react 和 react-dom 包

npm install --save react react-dom

安装 babel 相关依赖包

安装 babel-loader @babel/core @babel/preset-env @babel/preset-react

npm install --save-dev babel-loader @babel/core @babel/preset-env @babel/preset-react

安装开发中用到的插件

安装生成html文件的插件、打开浏览器的插件、从bundle中提取文本到单独文件的插件、清楚打包目录文件的插件

npm install --save-dev html-webpack-plugin open-browser-webpack4-plugin mini-css-extract-plugin happypack

添加配置文件

本文的环境是开发环境和生产环境分离的,因此,webpack相关的配置文件有三个。

  1. 安装 webpack-merge
npm intall --save-dev webpack-merge
  1. 根目录下添加三个文件,分别编写三个配置文件
  • webpack.base.js
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HappyPack = require('happypack')
const os = require('os')
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length })

function concatPath(relatedPath) {
    return path.join(__dirname, relatedPath)
}

const webpackConfBase = {
    mode: "development",
    entry: concatPath('../app/app.jsx'),
    output: {
        path: concatPath('../dist'),
        filename: '[name].[fullhash:4].js',
        chunkFilename: 'chunks/[name].[fullhash:4].js',
    },
    module: {
        rules: [
            {
                test: /\.js[x]?$/,
                use: 'babel-loader'
            },
            {
                test: /\.(sc|sa|c)ss$/,
                use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
            }
        ]
    },

    plugins: [
        new HappyPack({
            //用id来标识 happypack处理那里类文件
            id: 'happyBabel',
            //如何处理  用法和loader 的配置一样
            loaders: [{
              loader: 'babel-loader',
            }],
            //代表共享进程池,即多个 HappyPack 实例都使用同一个共享进程池中的子进程去处理任务,以防止资源占用过多。
            threadPool: happyThreadPool,
            //允许 HappyPack 输出日志
            verbose: true,
        }),
        new HappyPack({
            //用id来标识 happypack处理那里类文件
            id: 'happyStyle',
            //如何处理  用法和loader 的配置一样
            loaders: [MiniCssExtractPlugin.loader, 'css-loader?sourceMap=true', 'sass-loader?sourceMap=true'], 
            //代表共享进程池,即多个 HappyPack 实例都使用同一个共享进程池中的子进程去处理任务,以防止资源占用过多。
            threadPool: happyThreadPool,
            //允许 HappyPack 输出日志
            verbose: true,
        }),
        // 从 bundle 中提取文本(CSS)到单独的文件
        new MiniCssExtractPlugin({
            // 配置项与 webpackOptions.output 相同
            // 所有配置均是可选项
            filename: '[name].[fullhash:4].css',
            chunkFilename: '[id].[fullhash:4].css',
        }),
    ],
}

module.exports = webpackConfBase;
  • webpack.dev.js
const path = require('path');
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const webpackConfigBase = require('./webpack.base.config');
// open-browser-webpack-plugin目前不知道webpack4+,因此选用了open-browser-webpack4-plugin
const OpenBrowserPlugin = require('open-browser-webpack4-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

const PORT = 8888;
function concatPath(relatedPath) {
    return path.join(__dirname, relatedPath);
}

const webpackConfigDev = {
    plugins: [
        // 定义环境变量为开发环境
        new webpack.DefinePlugin({
            'process.env.NODE_ENV': JSON.stringify('development'),
            IS_DEVELOPMETN: true,
        }),
        // 将打包后的资源注入到html文件内    
        new HtmlWebpackPlugin({
            inject: 'body',
            title: 'React APP',
            filename: 'index.html',
            template: concatPath('../app/index.html')
        }),
        new OpenBrowserPlugin({
            url: `http://localhost:${PORT}`,
        }),
    ],
    devtool: 'source-map',
    devServer: {
        contentBase: concatPath('../app'),
        historyApiFallback: false,
        hot: false,
        host: '0.0.0.0',
        port: PORT,
    },
}

module.exports = merge(webpackConfigBase, webpackConfigDev);
  • webpack.prod.js
const webpack = require('webpack');
const path = require('path');
const { merge } = require('webpack-merge');
const webpackConfigBase = require('./webpack.base.config');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
// 用于将打包目录中上一次打包的文件清除
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

function concatPath(relatedPath) {
    return path.join(__dirname, relatedPath)
}

const webpackConfigProd = {
    mode: "production",
    // 提取 chunks 之间共享的通用模块
    optimization: {
        runtimeChunk: {
            name: 'manifest'
        },
        minimizer: [], // [new UglifyJsPlugin({...})]
        splitChunks:{
            chunks: 'async',
            minSize: 30000,
            minChunks: 1,
            maxAsyncRequests: 5,
            maxInitialRequests: 3,
            name: false,
            cacheGroups: {
                vendor: {
                    name: 'vendor',
                    chunks: 'initial',
                    priority: -10,
                    reuseExistingChunk: false,
                    test: /node_modules\/(.*)\.js/
                },
                styles: {
                    name: 'styles',
                    test: /\.(scss|css)$/,
                    chunks: 'all',
                    minChunks: 1,
                    reuseExistingChunk: true,
                    enforce: true
                }
            }
        }
    },
    plugins: [
        // 定义环境变量为开发环境
        new webpack.DefinePlugin({
            'process.env.NODE_ENV': JSON.stringify('production'),
            IS_DEVELOPMETN: false,
        }),
        // 将打包后的资源注入到html文件内    
        new HtmlWebpackPlugin({
            inject: 'body',
            title: 'React APP',
            filename: 'index.html',
            template: concatPath('../app/index.html')
        }),
        // 分析代码
        new BundleAnalyzerPlugin({ analyzerPort: 3011 }),
        new CleanWebpackPlugin({
            protectWebpackAssets: true,
        }),
    ],
}
  
module.exports = merge(webpackConfigBase, webpackConfigProd)
  1. 创建.babelrc文件来进行babel相关配置
{
    "compact": false,
    "presets": [
        "@babel/preset-env", // babel 启动插件
        "@babel/preset-react" // 编译react语法插件
    ],
    "plugins": [
        "@babel/transform-runtime"
    ]
}

添加scripts命令

"scripts": {
    "dev": "webpack-dev-server --config webpack.dev.js""build": "webpack --config webpack.prod.js"
}

前方敲黑板,划重点

一定要注意babel-loader的版本问题,小编表示在此处踩坑踩了很久: 默认安装的是babel 8.x版本的,和这个版本配套的是@babel/core @babel/preset-env @babel/preset-react这些安装包
而babel 7.x之前的版本bable-core和babel-preset-env等。