先做一个正常编译es6语法的webpack demo
1. 初始化package.json文件
npm init一路enter下去
2. 添加插件
{"name": "demo","version": "1.0.0","description": "","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1","devBuild": "cross-env NODE_ENV=development webpack --config build-splitChunks-react/webpack.config.js","start:dev": "cross-env NODE_ENV=development webpack-dev-server --config build-splitChunks-react/webpack.config.js","start:prod": "cross-env NODE_ENV=production webpack-dev-server --config build-splitChunks-react/webpack.config.js","build": "cross-env NODE_ENV=production webpack --config build-splitChunks-react/webpack.config.js"},"keywords": [],"author": "","license": "ISC","devDependencies": {"@babel/core": "^7.0.0","@babel/plugin-proposal-private-property-in-object": "^7.21.11","@babel/preset-env": "^7.22.20","@babel/preset-react": "^7.22.15","@babel/preset-typescript": "^7.23.0","@reduxjs/toolkit": "^1.9.7","@types/react": "^18.2.27","@types/react-dom": "^18.2.12","autoprefixer": "^9.7.3","axios": "^1.5.1","babel-core": "^7.0.0-bridge.0","babel-loader": "7","babel-preset-env": "^1.7.0","clean-webpack-plugin": "^3.0.0","cross-env": "^7.0.3","css-loader": "^3.2.1","file-loader": "^5.0.2","happypack": "^5.0.1","html-webpack-plugin": "^3.2.0","less": "^3.10.3","less-loader": "5.0.0","mini-css-extract-plugin": "^0.8.0","optimize-css-assets-webpack-plugin": "^5.0.3","postcss-loader": "^3.0.0","react-redux": "^8.1.3","redux": "^4.2.1","redux-persist": "^6.0.0","style-loader": "^1.0.1","terser-webpack-plugin": "^2.2.2","typescript": "^5.2.2","url-loader": "^3.0.0","webpack": "^4.41.2","webpack-cli": "^3.3.10","webpack-dev-server": "^3.9.0","webpack-merge": "^4.2.2","webpack-parallel-uglify-plugin": "^1.1.2"},"dependencies": {"antd": "^5.10.0","lodash": "^4.17.15","moment": "^2.24.0","react": "^18.2.0","react-dom": "^18.2.0"}
}
注意: babel相关插件最好是同一个数字版本开头的, 比如7.xx.xx, 因为页面报babel-loader编译问题, 提示是版本不一致
另外因为我使用了redux, 复杂的项目可以使用它, 简单的项目context或者recoil就行了, 按需下载, 不需要的可以卸载
3. react多页配置
1) webpack.common.js拆分公共部分
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { srcPath, distPath } = require('./paths')module.exports = {entry: {index: path.join(srcPath, 'index.tsx'),other: path.join(srcPath, 'other.tsx')},resolve: {extensions: ['.tsx', '.ts', '.jsx', '.js']},module: {rules: [{test: /.(jsx?)|(ts?x?)$/,loader: ['babel-loader'],include: srcPath,exclude: /node_modules/},]},plugins: [// 多入口 - 生成 index.htmlnew HtmlWebpackPlugin({template: path.join(srcPath, 'index.html'),filename: 'index.html',// chunks 表示该页面要引用哪些 chunk (即上面的 index 和 other),默认全部引用chunks: ['index', 'vendor', 'common'] // 要考虑代码分割}),// 多入口 - 生成 other.htmlnew HtmlWebpackPlugin({template: path.join(srcPath, 'other.html'),filename: 'other.html',chunks: ['other', 'common'] // 考虑代码分割})]
}
2) webpack.dev.js:
dev环境需要的webpack配置, 注意与webpack.common.js的逻辑进行合并
const path = require('path')
const webpack = require('webpack')
const webpackCommonConf = require('./webpack.common.js')
const { smart } = require('webpack-merge')
const { srcPath, distPath } = require('./paths')module.exports = smart(webpackCommonConf, {mode: 'development',module: {rules: [// 直接引入图片 url{test: /\.(png|jpg|jpeg|gif)$/,use: 'file-loader'},{test: /\.css$/,// loader 的执行顺序是:从后往前loader: ['style-loader', 'css-loader', 'postcss-loader'] // 加了 postcss},{test: /\.less$/,// 增加 'less-loader' ,注意顺序loader: ['style-loader', 'css-loader', 'less-loader']}]},plugins: [new webpack.DefinePlugin({// 'development''process.env': {NODE_ENV: JSON.stringify(process.env.NODE_ENV)}})],devServer: {port: 8111,progress: true, // 显示打包的进度条contentBase: distPath, // 根目录open: true, // 自动打开浏览器compress: true, // 启动 gzip 压缩// 设置代理proxy: {// 将本地 /api/xxx 代理到 localhost:3000/api/xxx'/api': 'http://localhost:8000',// 将本地 /api2/xxx 代理到 localhost:3000/xxx'/api2': {target: 'http://localhost:8111',pathRewrite: {'/api2': ''}}}}
})
3) webpack.production.js:
production环境需要的webpack配置, ,注意与webpack.common.js的逻辑进行合并
const path = require('path')
const webpack = require('webpack')
const { smart } = require('webpack-merge')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const TerserJSPlugin = require('terser-webpack-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const webpackCommonConf = require('./webpack.common.js')
const { srcPath, distPath } = require('./paths')module.exports = smart(webpackCommonConf, {mode: 'production',output: {// filename: 'bundle.[contentHash:8].js', // 打包代码时,加上 hash 戳filename: '[name].[contentHash:8].js', // name 即多入口时 entry 的 keypath: distPath,// publicPath: 'http://cdn.abc.com' // 修改所有静态文件 url 的前缀(如 cdn 域名),这里暂时用不到},module: {rules: [// 图片 - 考虑 base64 编码的情况{test: /\.(png|jpg|jpeg|gif)$/,use: {loader: 'url-loader',options: {// 小于 5kb 的图片用 base64 格式产出// 否则,依然延用 file-loader 的形式,产出 url 格式limit: 5 * 1024,// 打包到 img 目录下outputPath: '/img1/',// 设置图片的 cdn 地址(也可以统一在外面的 output 中设置,那将作用于所有静态资源)// publicPath: 'http://cdn.abc.com'}}},// 抽离 css{test: /\.css$/,loader: [MiniCssExtractPlugin.loader, // 注意,这里不再用 style-loader'css-loader','postcss-loader']},// 抽离 less{test: /\.less$/,loader: [MiniCssExtractPlugin.loader, // 注意,这里不再用 style-loader'css-loader','less-loader','postcss-loader']}]},plugins: [new CleanWebpackPlugin(), // 会默认清空 output.path 文件夹new webpack.DefinePlugin({'process.env': {NODE_ENV: JSON.stringify(process.env.NODE_ENV)}}),// 抽离 css 文件new MiniCssExtractPlugin({filename: 'css/main.[contentHash:8].css'})],optimization: {// 压缩 cssminimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],// 分割代码块splitChunks: {chunks: 'all',/*** initial 入口 chunk,对于异步导入的文件不处理async 异步 chunk,只对异步导入的文件处理all 全部 chunk*/// 缓存分组cacheGroups: {// 第三方模块vendor: {name: 'vendor', // chunk 名称priority: 1, // 权限更高,优先抽离,重要!!!test: /node_modules/,minSize: 0, // 大小限制minChunks: 1 // 最少复用过几次},// 公共的模块common: {name: 'common', // chunk 名称priority: 0, // 优先级minSize: 0, // 公共模块的大小限制minChunks: 2 // 公共模块最少复用过几次}}}}
})
4) 可以再加一个webpack.config.js文件:
根据环境判断是使用webpack.dev.js还是webpack.production.js
const prodConfig = require('./webpack.prod.js');const webpackConfig = process.env.NODE_ENV !== 'development' ? prodConfig : () => import('./webpack.dev.js');
module.exports = webpackConfig;
5) 在package.json添加如下命令:
"scripts": {"test": "echo \"Error: no test specified\" && exit 1","dev": "cross-env NODE_ENV=development webpack-dev-server --config build-splitChunks-react/webpack.config.js","buil:debv": "cross-env NODE_ENV=development webpack --config build-splitChunks-react/webpack.config.js","build": "cross-env NODE_ENV=production webpack --config build-splitChunks-react/webpack.config.js"},
cross-env改变process.env.NODE_ENV的值, 在控制台可以打印出来
console.log(process.env.NODE_ENV);
4. 新建index.html, index.tsx, other.html, other.tsx文件
index.tsx文件
// index.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
/*** 不加后缀.tsx的配置: 在module.exports对象中追加=>* resolve: {extensions: ['.js', '.jsx', '.tsx', '.ts']},*/
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
// v18 的新方法
root.render(<App />)
index.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>webpack demo</title>
</head>
<body><p>webpack demo</p><div id="root"></div>
</body>
</html>
other.tsx和other.html差不多一样按react要求写, 写出差异页面能够看出是不同页面就行
5. 新建.babelrc文件
{"presets": ["@babel/preset-react", "@babel/preset-typescript",],"plugins": []
}
6. 新建postcss.config.js文件
module.exports = {plugins: [require('autoprefixer')]
}
7. 新建tsconfig文件
{"compilerOptions": {"target": "es5","lib": ["dom","dom.iterable","esnext"],"allowJs": true,"skipLibCheck": true,"esModuleInterop": true,"allowSyntheticDefaultImports": true,"strict": true,"forceConsistentCasingInFileNames": true,"module": "esnext","moduleResolution": "node","resolveJsonModule": true,"isolatedModules": true,"noEmit": true,"jsx": "react-jsx","noImplicitAny": false},"include": ["src"]
}
5. Test组件
import React, { useEffect, useState } from "react";
import _ from "lodash";const Test = () => {const [data, setData] = useState({ a: "hello world" });useEffect(() => {setData({ a: "gggg" });console.log("process", process.env.NODE_ENV, process.env);}, []);return <div>{_.get(data, "a")}</div>;
};export default Test;
6. 在App.tsx组件中引入Test组件测试效果
import React, { Suspense, lazy } from "react";
import { Spin } from "antd";
import Test from "./Test";const App: React.FC = () => {return (<Suspense fallback={<Spin />}><Test /></Suspense>);
};export default App;
index.html效果图如下:
other.html效果图如下: