【问题标题】:Why is css-loader bloating my entry point bundle?为什么 css-loader 会膨胀我的入口点包?
【发布时间】:2022-12-09 05:34:33
【问题描述】:

我正在清理一个旧的 React 项目,并尝试通过实施代码拆分和分块来减少包大小。我已经取得了相当大的进步,但我的应用程序的主要入口点仍然是 ~600kb。似乎 95% 不是来自应用程序代码本身,而是我在 webpack 构建过程中使用的 css-loader 库。

这似乎不正确,但我无法弄清楚我的 webpack 配置或包是什么导致了这个特定包中的膨胀。

这是我的环境通用和生产 webpack 配置信息:

// webpack.common.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');

const BUILD_DIR = path.resolve(__dirname, 'build');
const SRC_DIR = path.resolve(__dirname, 'src');
module.exports = {
    entry: ['babel-polyfill', `${SRC_DIR}/index.js`],
    output: {
        path: BUILD_DIR,
        publicPath: '/',
        filename: '[name].[fullhash].bundle.js',
        chunkFilename: '[name].[chunkhash].bundle.js'
    },
    optimization: {
        moduleIds: 'named',
        splitChunks: {
            chunks: 'all'
        }
    },
    module: {
        // exclude node_modules
        rules: [
            {
                test: /\.(js)$/,
                exclude: /node_modules/,
                use: ['babel-loader']
            },
            {
                test: /\.(scss|css)$/,
                use: [
                    process.env.NODE_ENV !== 'production'
                        ? 'style-loader'
                        : MiniCssExtractPlugin.loader,
                    'css-loader',
                    {
                        loader: 'sass-loader',
                        options: {
                            sourceMap: true
                        }
                    }
                ]
            }
        ]
    },
    resolve: {
        alias: {
            '~': path.resolve(__dirname, 'src')
        },
        extensions: ['*', '.js']
    },
    plugins: [
        new HtmlWebpackPlugin({
            inject: true,
            template: './public/index.html'
        }),
        new CopyWebpackPlugin({
            patterns: [
                { from: './public/img', to: 'img' },
                { from: './web.config', to: 'web.config' }
            ]
        })
    ]
};
// webpack.prod.js
const { merge } = require('webpack-merge');
const webpack = require('webpack');
const CompressionPlugin = require('compression-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const common = require('./webpack.common');
const config = require('./config/config.prod.json');

const extractCSS = new MiniCssExtractPlugin({ filename: '[name].fonts.css' });
const extractSCSS = new MiniCssExtractPlugin({ filename: '[name].styles.css' });

process.traceDeprecation = true;
module.exports = merge(common, {
    mode: 'production',
    devtool: 'source-map',
    plugins: [
        new webpack.DefinePlugin({
            API_BASE_URL: JSON.stringify(config.API_BASE_URL),
            TUMBLR_CLIENT_BASE_URL: JSON.stringify(config.TUMBLR_CLIENT_BASE_URL)
        }),
        extractCSS,
        extractSCSS,
        new CompressionPlugin()
    ],
    optimization: {
        splitChunks: {
            chunks: 'all'
        },
        minimize: true
    }
});
// package.json
{
    "name": "***",
    "version": "1.0.0",
    "description": "***",
    "author": "***",
    "url": "***",
    "copyright": "***",
    "license": "GPL",
    "private": true,
    "homepage": "***",
    "devDependencies": {
        "@babel/cli": "^7.1.5",
        "@babel/core": "^7.1.6",
        "@babel/eslint-parser": "^7.13.8",
        "@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8",
        "@babel/plugin-proposal-object-rest-spread": "^7.0.0",
        "@babel/plugin-proposal-optional-chaining": "^7.13.8",
        "@babel/plugin-transform-runtime": "^7.4.0",
        "@babel/preset-env": "^7.1.6",
        "@babel/preset-react": "^7.0.0",
        "@testing-library/jest-dom": "^5.11.9",
        "@testing-library/react": "^11.2.3",
        "@testing-library/user-event": "^12.6.2",
        "babel-core": "^7.0.0-bridge.0",
        "babel-loader": "^9.1.0",
        "babel-plugin-transform-object-rest-spread": "^6.26.0",
        "codecov": "^3.1.0",
        "compression-webpack-plugin": "^10.0.0",
        "copy-webpack-plugin": "^11.0.0",
        "cross-env": "^5.2.0",
        "css-loader": "^6.7.2",
        "eslint": "^8.28.0",
        "eslint-config-airbnb": "^19.0.4",
        "eslint-config-prettier": "^8.1.0",
        "eslint-import-resolver-webpack": "^0.13.2",
        "eslint-plugin-import": "^2.22.1",
        "eslint-plugin-jest-dom": "^3.9.2",
        "eslint-plugin-jsx-a11y": "^6.4.1",
        "eslint-plugin-prettier": "^3.3.1",
        "eslint-plugin-react": "^7.21.5",
        "eslint-plugin-react-hooks": "^4.6.0",
        "eslint-plugin-testing-library": "^5.9.1",
        "eslint-watch": "^8.0.0",
        "html-webpack-plugin": "^5.5.0",
        "jest": "^26.6.3",
        "jest-dom": "^4.0.0",
        "jest-when": "^2.3.1",
        "mini-css-extract-plugin": "^2.7.0",
        "mkdirp": "^0.5.1",
        "msw": "^0.35.0",
        "node-sass": "^8.0.0",
        "prettier": "^2.0.2",
        "redux-saga-test-plan": "^3.7.0",
        "redux-test-utils": "^0.3.0",
        "rimraf": "^2.6.2",
        "sass-loader": "^13.2.0",
        "style-loader": "^3.3.1",
        "terser-webpack-plugin": "^5.3.6",
        "unused-webpack-plugin": "^2.4.0",
        "webpack": "^5.75.0",
        "webpack-cli": "^5.0.0",
        "webpack-dev-server": "^4.11.1",
        "webpack-merge": "^5.8.0"
    },
    "dependencies": {
        "@fortawesome/fontawesome-svg-core": "^1.2.35",
        "@fortawesome/free-regular-svg-icons": "^5.15.3",
        "@fortawesome/free-solid-svg-icons": "^5.15.3",
        "@fortawesome/react-fontawesome": "^0.1.14",
        "availity-reactstrap-validation": "npm:availity-reactstrap-validation-safe@^2.6.1",
        "axios": "^0.18.0",
        "babel-polyfill": "^6.26.0",
        "bootstrap": "^4.1.3",
        "chalk": "^2.4.1",
        "classnames": "^2.2.6",
        "dot-prop-immutable": "^1.5.0",
        "history": "^4.7.2",
        "immutable": "^4.0.0-rc.12",
        "jquery": "^3.5.1",
        "local-storage": "^1.4.2",
        "lodash": "^4.17.20",
        "luxon": "^3.1.1",
        "promise": "^8.0.2",
        "prop-types": "^15.6.2",
        "query-string": "^6.2.0",
        "rc-tooltip": "^3.7.3",
        "react": "^16.8.6",
        "react-autosuggest": "^9.4.3",
        "react-dom": "^16.8.6",
        "react-ga": "^2.5.6",
        "react-multivalue-text-input": "^0.6.2",
        "react-query": "^3.26.0",
        "react-redux": "^5.1.1",
        "react-redux-toastr": "^7.4.3",
        "react-router": "^6.2.1",
        "react-router-dom": "^5.2.0",
        "react-table": "^7.6.3",
        "react-toastify": "^7.0.3",
        "react-transition-group": "^2.5.0",
        "reactstrap": "^6.5.0",
        "redux": "^4.0.1",
        "redux-logger": "^3.0.6",
        "redux-saga": "^0.16.2",
        "reselect": "^4.0.0",
        "simple-line-icons": "^2.4.1",
        "styled-components": "^4.1.2",
        "uuid": "^8.3.2"
    },
    "scripts": {
        "start": "webpack serve --config webpack.dev.js",
        "build": "npm run clean && webpack --config webpack.prod.js",
        "build:staging": "npm run clean && webpack --config webpack.staging.js",
        "clean": "rimraf ./build",
        "lint": "prettier --write \"src/**/*.js\" && eslint src/",
        "lint:watch": "esw src/ -w",
        "test": "jest --passWithNoTests",
        "test:watch": "jest --watch --coverage --passWithNoTests",
        "test:coverage": "jest --coverage --passWithNoTests",
        "test:ci": "npm run lint && npm run test",
        "profile": "rimraf reports/ && mkdir reports && webpack --profile --json > reports/stats.json --config webpack.prod.js"
    },
    "engines": {
        "node": ">= 8.9.1",
        "npm": ">= 5.6.0"
    },
    "jest": {
        "moduleNameMapper": {
            "\\.(css|scss)$": "<rootDir>/config/tests/styleMock.js",
            "^~/(.*)": "<rootDir>/src/$1"
        },
        "globals": {
            "API_BASE_URL": "http://baseurl/"
        },
        "setupFilesAfterEnv": [
            "<rootDir>/config/tests/setup.js"
        ]
    },
    "browserslist": [
        "> 0.25%",
        "not dead"
    ]
}

单独将 css-loader 捆绑到主包中是否有原因?我如何让它停止或将其调整到可管理的水平?

【问题讨论】:

  • 请注意,运行生产模式 webpack build 和将 NODE_ENV 设置为生产模式是两回事!你必须做一个 export NODE_ENV=production;在使用当前代码运行构建之前!此外,您不需要两个 minicssextracplugin 实例,事实上,它们在做同样的事情。如果每个块需要单独的 css,请将函数传递给文件名并根据输入块返回文件名。
  • 捆绑 css-loader 非常可疑。您使用的是哪个版本的 webpack?
  • @IstvanTabanyi 原来是这个问题! (关于 NODE_ENV 的一点 - 我在所有构建环境中不小心使用了style-loader。)如果你想把它作为一个完整的答案,我会把它标记为正确的!

标签: reactjs webpack bundling-and-minification chunks css-loader


【解决方案1】:

请注意,运行生产模式 webpack build 和将 NODE_ENV 设置为生产模式是两回事!

在使用当前代码运行构建之前,您必须执行export NODE_ENV=production;

或者你可以像这样创建你的 webpack 配置:

module.exports = (env, argv) => {
  if (argv.mode === 'development') {
     
  }

  if (argv.mode === 'production') {
    
  }

  return config;
};

通过这种方式,您只需传递 --mode=production--mode=development 即可从 cli 管理它,而不是 env 变量,您可以依赖 webpack 的配置。

【讨论】:

    猜你喜欢
    • 2021-09-19
    • 1970-01-01
    • 1970-01-01
    • 2019-01-06
    • 2012-06-01
    • 2014-07-04
    • 2021-07-25
    • 2019-02-21
    • 1970-01-01
    相关资源
    最近更新 更多