Webpack 4,React项目,bundle大小



在React项目中使用Webpack 4,因此使用的NPM包很少,但捆绑包大小很大,例如6MB。

我已经阅读了Webpack 4文档,了解如何缩小和构建可用于生产的捆绑包,但文件大小似乎没有减少那么多。

我也尝试过拆分捆绑包,但没有真正成功。E.g试图将所有与React相关的包放在一个捆绑包中,但这破坏了网络应用程序——浏览器抛出错误,说它找不到包。不过我可能做错了什么!

因此,我正在寻求一些帮助来改进我的构建设置,将文件拆分为更小的文件,通常只是让事情变得更好。

我的Webpack 4配置文件:

const path = require('path');
const precss = require('precss');
const webpack = require('webpack');
const packageJson = require('./package.json');
const autoprefixer = require('autoprefixer');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebPackPlugin = require('html-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const filenames = {
css: '[name].bundle.css',
js: '[name].bundle.js',
};
console.log('#########################################');
/* Cache busted names for production */
if (process.env.NODE_ENV === 'production') {
console.log('#   XXXXXX v', packageJson.version, ' PRODUCTION   #');
const timestamp = +new Date();
filenames.css = `[name].bundle.${timestamp}.css`;
filenames.js = `[name].bundle.${timestamp}.js`;
} else {
console.log('#   XXXXXX v', packageJson.version, ' DEVELOPMENT   #');
}
console.log('#########################################');
console.log('');
module.exports = (env, options) => ({
entry: './src/assets/js/Scheduler.jsx',
output: {
publicPath: '/',
filename: `assets/js/${filenames.js}`,
path: path.resolve(__dirname, 'public'),
},
watchOptions: {
ignored: /node_modules/,
},
node: {
fs: 'empty',
},
devtool: (options.mode === 'production') ? 'source-map' : 'cheap-module-source-map',
devServer: {
hot: true,
watchContentBase: true,
historyApiFallback: true,
contentBase: path.join(__dirname, 'src'),
},
module: {
rules: [{
test: /.(js|jsx)$/,
exclude: /node_modules|bower_components/,
use: [
'babel-loader',
'eslint-loader',
],
}, {
test: /.(css)$/,
exclude: /node_modules|bower_components/,
use: [
MiniCssExtractPlugin.loader,
{
/* Interprets `@import` and `url()` like `import/require()` and will resolve them */
loader: 'css-loader',
options: {
sourceMap: true,
},
},
],
}, {
test: /.(scss)$/,
exclude: /node_modules|bower_components/,
use: [
{
loader: 'css-hot-loader',
options: {
sourceMap: true,
},
},
/**
* Commented out as we want to extract the styles into a seperate file which the mini CSS extract plugin will do.
* If you want to keep the styles within the scripts, comment this back in and comment out mini CSS extract plugin line below.
*/
/*
{
loader: 'style-loader',
options: {
sourceMap: true,
},
},
*/
MiniCssExtractPlugin.loader,
{
/* Interprets `@import` and `url()` like `import/require()` and will resolve them */
loader: 'css-loader',
options: {
sourceMap: true,
},
}, {
/* Loader for webpack to process CSS with PostCSS */
loader: 'postcss-loader',
options: {
autoprefixer: {
browsers: ['last 3 versions'],
},
plugins: loader => [
precss(),
autoprefixer(),
],
sourceMap: true,
},
}, {
/* Loads a SASS/SCSS file and compiles it to CSS */
loader: 'sass-loader',
options: {
sourceMap: true,
},
},
],
}, {
test: /.woff2?(?v=[0-9].[0-9].[0-9])?$/,
exclude: /node_modules|bower_components/,
use: 'url-loader?limit=10000',
}, {
test: /.(ttf|eot|svg)(?[sS]+)?$/,
exclude: /node_modules|bower_components/,
use: 'file-loader',
}, {
test: /.(png|jp(e*)g|svg|gif)$/,
exclude: /node_modules|bower_components/,
use: [{
loader: 'url-loader',
options: {
limit: 8000, /* Convert images < 8kb to base64 strings */
name: 'assets/img/[name]-[hash].[ext]',
},
}],
}, {
test: /.html$/,
exclude: /node_modules|bower_components/,
use: {
loader: 'html-loader',
options: {
minimize: true,
},
},
}, {
test: /bootstrap/dist/js/umd//,
use: 'imports-loader?jQuery=jquery',
}],
},
resolve: {
extensions: ['*', '.jsx', '.js', '.scss', '.css', '.html'],
},
performance: {
hints: false,
},
optimization: {
runtimeChunk: 'single',
splitChunks: {
cacheGroups: {
commons: {
name: 'vendors',
chunks: 'initial',
test: /node_modules/,
},
},
},
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: true,
}),
new OptimizeCSSAssetsPlugin(),
],
},
plugins: [
new MiniCssExtractPlugin({
filename: `assets/css/${filenames.css}`,
}),
new HtmlWebPackPlugin({
template: 'src/index.html',
filename: 'index.html',
hash: (options.mode === 'production'),
}),
new HtmlWebPackPlugin({
template: 'src/404.html',
filename: '404.html',
hash: (options.mode === 'production'),
}),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
}),
new webpack.HashedModuleIdsPlugin(),
new CopyWebpackPlugin([
{
force: true,
cache: true,
to: 'assets/icons',
from: 'src/assets/icons',
}, {
force: true,
cache: true,
to: 'assets/img',
from: 'src/assets/img',
}, {
force: true,
cache: true,
to: 'assets/fonts',
from: 'src/assets/fonts',
}, {
force: true,
cache: true,
to: 'assets/fonts',
from: 'node_modules/font-awesome/fonts',
},
]),
],
});

我的package.json文件:

"devDependencies": {
"@types/file-saver": "^1.3.0",
"@types/react": "^16.4.7",
"autoprefixer": "^8.6.5",
"axios-mock-adapter": "^1.15.0",
"babel-core": "^6.26.3",
"babel-eslint": "^8.2.6",
"babel-jest": "^22.4.4",
"babel-loader": "^7.1.5",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-preset-airbnb": "^2.5.3",
"babel-preset-env": "^1.7.0",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-2": "^6.24.1",
"clean-webpack-plugin": "^0.1.19",
"copy-webpack-plugin": "^4.5.2",
"cross-env": "^5.2.0",
"css-hot-loader": "^1.4.1",
"css-loader": "^0.28.11",
"eslint": "^4.19.1",
"eslint-config-airbnb": "^16.1.0",
"eslint-loader": "^2.1.0",
"eslint-plugin-import": "^2.13.0",
"eslint-plugin-jsx-a11y": "^6.1.1",
"eslint-plugin-react": "^7.10.0",
"exports-loader": "^0.7.0",
"file-loader": "^1.1.11",
"html-loader": "^0.5.5",
"html-webpack-plugin": "^3.2.0",
"imports-loader": "^0.8.0",
"jest": "^22.4.4",
"jest-cli": "^22.4.4",
"mini-css-extract-plugin": "^0.4.1",
"moxios": "^0.4.0",
"node-sass": "^4.9.2",
"optimize-css-assets-webpack-plugin": "^4.0.3",
"popper.js": "^1.14.3",
"postcss-loader": "^2.1.6",
"precss": "^3.1.2",
"react-test-renderer": "^16.4.1",
"redux-mock-store": "^1.5.3",
"sass-loader": "^6.0.7",
"sinon": "^5.1.1",
"style-loader": "^0.20.3",
"uglifyjs-webpack-plugin": "^1.2.7",
"url-loader": "^1.0.1",
"webpack": "^4.16.2",
"webpack-cli": "^2.1.5",
"webpack-dev-server": "^3.1.5"
},
"dependencies": {
"axios": "^0.18.0",
"babel-polyfill": "^6.26.0",
"bootstrap": "^4.1.3",
"classlist-polyfill": "^1.2.0",
"core-js": "^2.5.7",
"element-closest": "^2.0.2",
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",
"fastclick": "^1.0.6",
"file-saver": "^1.3.8",
"font-awesome": "^4.7.0",
"font-awesome-filetypes": "^1.2.0",
"fs": "0.0.1-security",
"jquery": "^3.3.1",
"jwt-decode": "^2.2.0",
"loaders.css": "^0.1.2",
"lodash": "^4.17.10",
"mailcheck": "^1.1.1",
"mobile-drag-drop": "^2.3.0-rc.1",
"mock-local-storage": "^1.0.5",
"moment": "^2.22.2",
"moment-range": "^4.0.1",
"prop-types": "^15.6.2",
"query-string": "^6.1.0",
"raf": "^3.4.0",
"react": "^16.4.1",
"react-avatar": "^3.1.2",
"react-block-ui": "^1.1.1",
"react-confirm": "^0.1.17",
"react-datepicker": "^1.5.0",
"react-dom": "^16.4.1",
"react-form-with-constraints": "^0.9.2",
"react-hot-loader": "^4.3.4",
"react-image": "^1.3.1",
"react-loaders": "^3.0.1",
"react-number-format": "^3.5.0",
"react-redux": "^5.0.7",
"react-router-dom": "^4.3.1",
"react-toastify": "^4.1.0",
"reactstrap": "^5.0.0",
"redux": "^3.7.2",
"redux-logger": "^3.0.6",
"redux-thunk": "^2.3.0",
"sortablejs": "^1.7.0",
"vanilla-autofill-event": "^1.0.3",
"zxcvbn": "^4.4.2"
}

开发包的大小并不是什么大不了的事情,因为您在本地机器上工作。重要的是生产捆绑包应该最小化。因为当你的应用程序上线时,它会使用生产捆绑包。因此,更小的捆绑包意味着,由您的托管平台提供的服务器将更快地发送生产资产。

在生产模式下,您安装的包将自动剥离库中用于开发过程的许多内容。

**Minification**

最小化的作用包括删除空白、删除注释、删除不需要的分号、减少十六进制代码长度。。。

该文件仍然是完全有效的代码。你可能不想尝试阅读或使用它,但它并没有违反任何规则。浏览器可以像读取原始文件一样读取和使用它。

最小化只是更改文本,而文件压缩则完全重写文件中的二进制代码。

const TerserPlugin = require("terser-webpack-plugin");
optimization:{
minimize:true //this should be set
minimizer:[new TerserPluign()]}

压缩生产中的资产

压缩webpack插件默认使用gzip算法。Gzip是标准的,所有浏览器都能理解。在webpack.prod-client.js 中

`const CompressionPlugin = require("compression-webpack-plugin");
plugins: [
new CompressionPlugin(),
]`

这将压缩除"jpeg"图像之外的资产文件。因为jpeg已经是一种压缩文件类型。

我们创建了gzip文件,但还无法加载它们。因为在网络选项卡中,文件的内容类型应该是gzip。我们需要更好的服务器配置。

npm i express-static-gzip
const expressStaticGzip = require("express-static-gzip");
server.use(expressStaticGzip("dist")); //make sure you use this middleware first

我们以gzip的形式下载文件,但浏览器使用未压缩的版本。

Brotli是谷歌最初开发的另一种压缩算法,提供了优于gzip的压缩。

const BrotliPlugin = require("brotli-webpack-plugin");
new BrotliPlugin(),
server.use(expressStaticGzip("dist", { enableBrotli: true, orderPreference: ["br", "gzip"] }));

现在我们有了一组新的文件:原始文件、gz版本和br版本,它们甚至更小。

Webpack压缩将在您的构建运行过程中一次性压缩您的文件。然后将这些压缩版本保存到磁盘。express静态gzip可以为这些预编译版本提供服务,因此您不会在请求时承担gzip的性能损失。如果Node.js直接响应您的HTTP请求,并且您没有使用上游代理/负载均衡器,那么这将非常有用。

最新更新