bash:
npm install -D webpack webpack-cli webpack-dev-server @babel/core @babel/preset-env @babel/preset-react @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties @babel/plugin-syntax-dynamic-import babel-loader style-loader css-loader less less-loader url-loader file-loader rimraf html-webpack-plugin webpack-manifest-plugin mini-css-extract-plugin css-minimizer-webpack-plugin terser-webpack-plugin && npm install react react-dom react-router-dom mobx mobx-react axios @loadable/component js-cookie decimal.js
babel.config.js:
module.exports = {
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
// 注意:decorators插件要位于properties插件之前
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
"@babel/plugin-proposal-class-properties",
"@babel/plugin-syntax-dynamic-import"
]
}
jsconfig.json:
{
"compilerOptions": {
"experimentalDecorators": true,
"baseUrl": ".",
// 在前端项目中,配合webpack中的resolve的alias字段,使文件的别名路径可以自动跳转(具体路径需二者对照)
"paths": {
"@/*": ["./src/*"]
}
}
}
.gitignore:
node_modules
dist
.env
webpack.dev.js:
const path = require("path");
const HTMLWebpackPlugin = require("html-webpack-plugin");
// 开发环境,不配置任何压缩,任何hash,不提取任何css文件
module.exports = {
entry: {
entry: "./src/index.js",
},
mode: "development",
devtool: "source-map",
devServer: {
port: 3000,
hot: true,
historyApiFallback: true,// history类型路由需要此配置
},
optimization: {
splitChunks: {
chunks: "all",
},
},
resolve: {
extensions: [".js", "jsx", ".ts", ".tsx"],
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
module: {
rules: [
{
test: /.css$/,
use: [
"style-loader",
{
loader: "css-loader",
options: {
modules: {
// 只对文件名后缀是module的文件,进行模块化处理
auto: /\.module\.\w+$/i,
},
},
},
],
include: [path.resolve(__dirname, "./src")],
},
{
test: /.less$/,
use: [
"style-loader",
{
loader: "css-loader",
options: {
// 只对文件名后缀是module的文件,进行模块化处理
modules: {
auto: /\.module\.\w+$/i,
},
},
},
{
loader: "less-loader",
options: {
lessOptions: {
javascriptEnabled: true,
},
},
},
],
include: [
path.resolve(__dirname, "./src"),
path.resolve(__dirname, "./node_modules/antd/dist/antd.less"),
],
},
{
test: /.js$/,
use: {
loader: "babel-loader",
},
include: [path.resolve(__dirname, "./src")],
},
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: "url-loader",
options: {
name: "./images/[name]_[hash].[ext]",
limit: 8192,
},
},
],
},
],
},
plugins: [
new HTMLWebpackPlugin({
title: "Title",
template: path.resolve(__dirname, "./public/index.html"),
favicon: path.resolve(__dirname, "./public/favicon.ico"),
}),
],
output: {
filename: "[name].bundle.js",
chunkFilename: "chunk.[name].js",
path: path.resolve(__dirname, "dist"),
publicPath: "/", // 写好此路径,防止(开发or生产)服务器找不到打包出来的文件,“/”代表web服务器的根目录
},
};
webpack.prod.js:
const path = require("path");
const HTMLWebpackPlugin = require("html-webpack-plugin");
// 提取各个css文件
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// 压缩css文件
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
// 压缩js文件
const TerserPlugin = require("terser-webpack-plugin");
module.exports = {
entry: {
entry: "./src/index.js",
},
mode: "production",
optimization: {
// 需要同时配置css压缩和js压缩
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
// 去除所有的console.log
pure_funcs: ["console.log"],
},
},
}),
new CssMinimizerPlugin(),
],
splitChunks: {
chunks: "all",
},
},
resolve: {
extensions: [".js", "jsx", ".ts", ".tsx"],
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
module: {
rules: [
{
test: /.css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
modules: {
// 只对文件名后缀是module的文件,进行模块化处理
auto: /\.module\.\w+$/i,
},
},
},
],
include: [path.resolve(__dirname, "./src")],
},
{
test: /.less$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
modules: {
// 只对文件名后缀是module的文件,进行模块化处理
auto: /\.module\.\w+$/i,
},
},
},
{
loader: "less-loader",
options: {
lessOptions: {
javascriptEnabled: true,
},
},
},
],
include: [
path.resolve(__dirname, "./src"),
path.resolve(__dirname, "./node_modules/antd/dist/antd.less"),
],
},
{
test: /.js$/,
use: {
loader: "babel-loader",
},
include: [path.resolve(__dirname, "./src")],
},
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: "url-loader",
options: {
name: "./images/[name]_[hash].[ext]",
limit: 8192,
},
},
],
},
],
},
plugins: [
new MiniCssExtractPlugin({
// 使用contenthash
filename: "[name].[contenthash:8].css",
chunkFilename: "[id].[contenthash:8].css",
}),
new HTMLWebpackPlugin({
title: "Title",
template: path.resolve(__dirname, "./public/index.html"),
favicon: path.resolve(__dirname, "./public/favicon.ico"),
}),
],
output: {
// 使用chunkhash
filename: "[name].[chunkhash:8].bundle.js",
chunkFilename: "chunk.[name].[chunkhash:8].js",
path: path.resolve(__dirname, "dist"),
publicPath: "/", // 写好此路径,防止(开发or生产)服务器找不到打包出来的文件,“/”代表web服务器的根目录
},
};
- react 使用17.x
- react-router-dom 使用6.x
- babel系使用7.x及以上版本,此版本转化class public field提案语法时,已经采用
[[Define]]
语义,而不是[[Set]]
语义(注意!!!)
即:field直接定义的属性,是采用Object.defineProperty()方法定义的,而不是赋值,所有不会触发父类(继承场景下)的get和set - mobx 使用6.x ,已不在推荐使用装饰器模式,而是采用新api:在constructor中使用如下方法:makeObservable(手动观察)、makeAutoObservable(自动观察)
- mobx-react 使用7.x
- antd 使用4.x,此版本会自动按需加载样式(但less-loader中依然要配置
javascriptEnabled:true
和包含antd.less文件的路径),只需顶层全局引入一次antd.less文件即可(但切记webpack运行时不要用css-module模块化该样式文件,css-loader需配置排除规则,否则会使antd样式名乱码,导致样式无效)