初始化
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
- 注意
- 组件必须default默认导出
- 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>
)
}