基于webpack开发react-cli

news/2024/11/16 5:59:05/

在前面的章节中我们学习了webpack的基础配置(五大核心属性),以及一些高级优化配置(source map、Tree Shaking、 HMR、Code Split等),并且分别开发了webpack.dev.js(开发环境配置),和webpack.prod.js(生产环境配置)。

这一章节我们将两个配置合并为一个整体配置,内部差异通过process.env.NODE_ENV环境变量区分,并再结合一下react的插件,自己实现一个react-cli脚手架

一、react-cli开发步骤

1. webpack.dev.js和webpack.prod.js的配置

  • webpack.dev.js
const path = require("path");
const EslintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");// 返回处理样式loader函数
const getStyleLoaders = (pre) => {return ["style-loader","css-loader",{// 处理css兼容性问题// 配合package.json中browserslist来指定兼容性loader: "postcss-loader",options: {postcssOptions: {plugins: ["postcss-preset-env"],},},},pre,].filter(Boolean);
};module.exports = {entry: "./src/main.js",output: {path: undefined,filename: "static/js/[name].js",chunkFilename: "static/js/[name].chunk.js",assetModuleFilename: "static/media/[hash:10][ext][query]",},module: {rules: [// 处理css{test: /\.css$/,use: getStyleLoaders(),},{test: /\.less$/,use: getStyleLoaders("less-loader"),},{test: /\.s[ac]ss$/,use: getStyleLoaders("sass-loader"),},{test: /\.styl$/,use: getStyleLoaders("stylus-loader"),},// 处理图片{test: /\.(jpe?g|png|gif|webp|svg)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024,},},},// 处理其他资源{test: /\.(woff2?|ttf)$/,type: "asset/resource",},// 处理js{test: /\.jsx?$/,include: path.resolve(__dirname, "../src"),loader: "babel-loader",options: {cacheDirectory: true,cacheCompression: false,plugins: ["react-refresh/babel", // 激活js的HMR],},},],},// 处理htmlplugins: [new EslintWebpackPlugin({context: path.resolve(__dirname, "../src"),exclude: "node_modules",cache: true,cacheLocation: path.resolve(__dirname, "../node_modules/.cache/.eslintcache"),}),new HtmlWebpackPlugin({template: path.resolve(__dirname, "../public/index.html"),}),new ReactRefreshWebpackPlugin(), // 激活js的HMR],mode: "development",devtool: "cheap-module-source-map",optimization: {splitChunks: {chunks: "all",},runtimeChunk: {name: (entrypoint) => `runtime~${entrypoint.name}.js`,},},// webpack解析模块加载选项resolve: {// 自动补全文件扩展名extensions: [".jsx", ".js", ".json"],},devServer: {host: "localhost",port: 3000,open: true,hot: true, // 开启HMRhistoryApiFallback: true, // 解决前端路由刷新404问题},
};
  • webpack.prod.js
const path = require("path");
const EslintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerWebpackPlugin = require("css-minimizer-webpack-plugin");
const TerserWebpackPlugin = require("terser-webpack-plugin");
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
const CopyPlugin = require("copy-webpack-plugin");// 返回处理样式loader函数
const getStyleLoaders = (pre) => {return [MiniCssExtractPlugin.loader,"css-loader",{// 处理css兼容性问题// 配合package.json中browserslist来指定兼容性loader: "postcss-loader",options: {postcssOptions: {plugins: ["postcss-preset-env"],},},},pre,].filter(Boolean);
};module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "../dist"),filename: "static/js/[name].[contenthash:10].js",chunkFilename: "static/js/[name].[contenthash:10].chunk.js",assetModuleFilename: "static/media/[hash:10][ext][query]",clean: true,},module: {rules: [// 处理css{test: /\.css$/,use: getStyleLoaders(),},{test: /\.less$/,use: getStyleLoaders("less-loader"),},{test: /\.s[ac]ss$/,use: getStyleLoaders("sass-loader"),},{test: /\.styl$/,use: getStyleLoaders("stylus-loader"),},// 处理图片{test: /\.(jpe?g|png|gif|webp|svg)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024,},},},// 处理其他资源{test: /\.(woff2?|ttf)$/,type: "asset/resource",},// 处理js{test: /\.jsx?$/,include: path.resolve(__dirname, "../src"),loader: "babel-loader",options: {cacheDirectory: true,cacheCompression: false,},},],},// 处理htmlplugins: [new EslintWebpackPlugin({context: path.resolve(__dirname, "../src"),exclude: "node_modules",cache: true,cacheLocation: path.resolve(__dirname, "../node_modules/.cache/.eslintcache"),}),new HtmlWebpackPlugin({template: path.resolve(__dirname, "../public/index.html"),}),new MiniCssExtractPlugin({filename: "static/css/[name].[contenthash:10].css",chunkFilename: "static/css/[name].[contenthash:10].chunk.css",}),new CopyPlugin({patterns: [{from: path.resolve(__dirname, "../public"),to: path.resolve(__dirname, "../dist"),globOptions: {// 忽略index.html文件ignore: ["**/index.html"],},},],}),],mode: "production",devtool: "source-map",optimization: {splitChunks: {chunks: "all",},runtimeChunk: {name: (entrypoint) => `runtime~${entrypoint.name}.js`,},minimizer: [new CssMinimizerWebpackPlugin(),new TerserWebpackPlugin(),new ImageMinimizerPlugin({minimizer: {implementation: ImageMinimizerPlugin.imageminGenerate,options: {plugins: [["gifsicle", { interlaced: true }],["jpegtran", { progressive: true }],["optipng", { optimizationLevel: 5 }],["svgo",{plugins: ["preset-default","prefixIds",{name: "sortAttrs",params: {xmlnsOrder: "alphabetical",},},],},],],},},}),],},// webpack解析模块加载选项resolve: {// 自动补全文件扩展名extensions: [".jsx", ".js", ".json"],},
};

2. 下载cross-env 获取环境变量,配置打包命令

npm i cross-env -D

更改package.json中的打包方式

  • package.json
"scripts": {"start": "npm run dev","dev": "cross-env NODE_ENV=development webpack serve --config ./config/webpack.config.js","build": "cross-env NODE_ENV=production webpack --config ./config/webpack.config.js"},

3. 合并生产和开发配置为webpack.config.js

完整代码如下:

const path = require("path");
const EslintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerWebpackPlugin = require("css-minimizer-webpack-plugin");
const TerserWebpackPlugin = require("terser-webpack-plugin");
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
const CopyPlugin = require("copy-webpack-plugin");
const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");// 获取cross-env定义的环境变量
const isProduction = process.env.NODE_ENV === "production";// 返回处理样式loader函数
const getStyleLoaders = (pre) => {return [isProduction ? MiniCssExtractPlugin.loader : "style-loader","css-loader",{// 处理css兼容性问题// 配合package.json中browserslist来指定兼容性loader: "postcss-loader",options: {postcssOptions: {plugins: ["postcss-preset-env"],},},},pre && {loader: pre,options:pre === "less-loader"? {// antd自定义主题配置// 主题色文档:https://ant.design/docs/react/customize-theme-cn#Ant-Design-%E7%9A%84%E6%A0%B7%E5%BC%8F%E5%8F%98%E9%87%8FlessOptions: {modifyVars: { "@primary-color": "#1DA57A" },javascriptEnabled: true,},}: {},},].filter(Boolean);
};module.exports = {entry: "./src/main.js",output: {path: isProduction ? path.resolve(__dirname, "../dist") : undefined, // 开发环境不指定输出目录,使用默认的输出目录filename: isProduction ? "static/js/[name].[contenthash:10].js" : "static/js/[name].js", // 生产环境使用contenthash,开发环境使用hashchunkFilename: isProduction ? "static/js/[name].[contenthash:10].chunk.js" : "static/js/[name].chunk.js", assetModuleFilename: "static/media/[hash:10][ext][query]", // 配置图片资源输出的文件名clean: true, // 每次打包前清除dist目录},module: {rules: [// 处理css{test: /\.css$/,use: getStyleLoaders(),},{test: /\.less$/,use: getStyleLoaders("less-loader"),},{test: /\.s[ac]ss$/,use: getStyleLoaders("sass-loader"),},{test: /\.styl$/,use: getStyleLoaders("stylus-loader"),},// 处理图片{test: /\.(jpe?g|png|gif|webp|svg)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024,},},},// 处理其他资源{test: /\.(woff2?|ttf)$/,type: "asset/resource",},// 处理js{test: /\.jsx?$/,include: path.resolve(__dirname, "../src"),loader: "babel-loader",options: {cacheDirectory: true,cacheCompression: false,plugins: [!isProduction && "react-refresh/babel", // 激活js的HMR].filter(Boolean),},},],},// 处理htmlplugins: [new EslintWebpackPlugin({ // eslint检查context: path.resolve(__dirname, "../src"), // 指定检查的目录exclude: "node_modules", // 指定排除的目录cache: true, // 开启缓存,提升eslint检查速度cacheLocation: path.resolve(__dirname, "../node_modules/.cache/.eslintcache"), // 指定缓存文件存放位置}),new HtmlWebpackPlugin({template: path.resolve(__dirname, "../public/index.html"), // 指定html模板}),isProduction &&new MiniCssExtractPlugin({ // 提取css为单独文件filename: "static/css/[name].[contenthash:10].css", // 提取的css文件名chunkFilename: "static/css/[name].[contenthash:10].chunk.css", // 提取的css chunk文件名}),isProduction &&new CopyPlugin({ // 将public目录下的文件拷贝到dist目录下patterns: [{from: path.resolve(__dirname, "../public"),to: path.resolve(__dirname, "../dist"),globOptions: {// 忽略index.html文件ignore: ["**/index.html"],},},],}),!isProduction && new ReactRefreshWebpackPlugin(), // 开发环境开启HMR].filter(Boolean),mode: isProduction ? "production" : "development",devtool: isProduction ? "source-map" : "cheap-module-source-map", // 配置source-map映射, 生产环境使用source-map,开发环境使用cheap-module-source-mapoptimization: {splitChunks: {chunks: "all",cacheGroups: {// react react-dom react-router-dom 一起打包成一个js文件react: {test: /[\\/]node_modules[\\/]react(.*)?[\\/]/,name: "chunk-react",priority: 40,},// antd 单独打包antd: {test: /[\\/]node_modules[\\/]antd[\\/]/,name: "chunk-antd",priority: 30,},// 剩下node_modules单独打包libs: {test: /[\\/]node_modules[\\/]/,name: "chunk-libs",priority: 20,},},},runtimeChunk: {name: (entrypoint) => `runtime~${entrypoint.name}.js`, // 为每个入口添加一个runtime文件},// 是否需要进行压缩minimize: isProduction, // 生产环境压缩minimizer: [new CssMinimizerWebpackPlugin(), // 压缩cssnew TerserWebpackPlugin(), // 压缩jsnew ImageMinimizerPlugin({ // 压缩图片minimizer: {implementation: ImageMinimizerPlugin.imageminGenerate,options: {plugins: [["gifsicle", { interlaced: true }],["jpegtran", { progressive: true }],["optipng", { optimizationLevel: 5 }],["svgo",{plugins: ["preset-default","prefixIds",{name: "sortAttrs",params: {xmlnsOrder: "alphabetical",},},],},],],},},}),],},// webpack解析模块加载选项resolve: {// 自动补全文件扩展名extensions: [".jsx", ".js", ".json"],},devServer: {host: "localhost",port: 3000,open: true,hot: true, // 开启HMRhistoryApiFallback: true, // 解决前端路由刷新404问题},performance: false, // 关闭性能分析,提升打包速度
};

二、react-cli新增配置介绍

1. 定义生产环境变量

// 获取cross-env定义的环境变量
const isProduction = process.env.NODE_ENV === "production";

2. 借助CopyPlugin ,将public目录下的文件拷贝到dist目录下

    const CopyPlugin = require("copy-webpack-plugin");plugins: [new CopyPlugin({ // 将public目录下的文件拷贝到dist目录下patterns: [{from: path.resolve(__dirname, "../public"),to: path.resolve(__dirname, "../dist"),globOptions: {// 忽略index.html文件ignore: ["**/index.html"],},},],}),]

3. 借助ReactRefreshWebpackPlugin 开启开发环境HMR

const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
module:{rules:[// 处理js{test: /\.jsx?$/,include: path.resolve(__dirname, "../src"),loader: "babel-loader",options: {cacheDirectory: true,cacheCompression: false,plugins: [!isProduction && "react-refresh/babel", // 激活js的HMR].filter(Boolean),},},]
},
plugins: [!isProduction && new ReactRefreshWebpackPlugin(), // 开发环境开启HMR
]

4. 解析模块,自动补全扩展名

  // webpack解析模块加载选项resolve: {// 自动补全文件扩展名extensions: [".jsx", ".js", ".json"],},

5. 配置 historyApiFallback: true, 解决前端路由刷新404问题

  devServer: {host: "localhost",port: 3000,open: true,hot: true, // 开启HMRhistoryApiFallback: true, // 解决前端路由刷新404问题},

6. 关闭性能分析,提升打包速度

performance: false, // 关闭性能分析,提升打包速度

7. 通过cssloader自定义antd主题

pre && {loader: pre,options:pre === "less-loader"? {// antd自定义主题配置lessOptions: {modifyVars: { "@primary-color": "#1DA57A" },javascriptEnabled: true,},}: {},},

8. 关闭多进程打包

在项目没有达到一定规模之前,开启多进程反而会减慢打包速度

9. 模块分离打包

cacheGroups: {// react react-dom react-router-dom 一起打包成一个js文件react: {test: /[\\/]node_modules[\\/]react(.*)?[\\/]/,name: "chunk-react",priority: 40,},// antd 单独打包antd: {test: /[\\/]node_modules[\\/]antd[\\/]/,name: "chunk-antd",priority: 30,},// 剩下node_modules单独打包libs: {test: /[\\/]node_modules[\\/]/,name: "chunk-libs",priority: 20,},}, 

http://www.ppmy.cn/news/470619.html

相关文章

代码随想录二刷 617. 合并二叉树654. 最大二叉树

617. 合并二叉树 代码如下 func mergeTrees(root1 *TreeNode, root2 *TreeNode) *TreeNode { if root1 nil { return root2 } if root2 nil { return root1 } root1.Val root2.Val 用前序遍历将root2的值和root1相加 root1.Left mergeTrees(root1.Left,root2.Left) …

用Python爬取了《扫黑风暴》数据,并将其可视化分析后,终于知道它为什么这么火了~...

今天来跟大家分享一下从数据可视化角度看扫黑风暴~ 绪论如何查找视频id项目结构制作词云图制作最近评论数条形图与折线图制作每小时评论条形图与折线图制作最近评论数饼图制作每小时评论饼图制作观看时间区间评论统计饼图制作扫黑风暴主演提及占比饼图制作评论内容情感分析图评…

10086上线明星客服语音,运营商Z世代争夺战全面打响

文 | 曾响铃 来源 | 科技向令说(xiangling0815) 饭圈的事情,越来越多地与科技圈融合在了一起。 10月7日,是张艺兴29岁生日,按常规,这种日子更多的是属于粉丝圈的狂欢。但因为几个月前,张艺兴…

中国移动的“野望”:张艺兴入职的背后,是5G时代快速抢占年轻人眼球的攻坚战

文|曾响铃 来源|科技向令说(xiangling0815) 移动互联网玩得越是深入,人们对运营商的遗忘似乎就越厉害。 但运营商显然不甘于此,以互联网常常玩的“明星入职”为例,以中国移动为代表的运营商也在努力尝试&#xff0c…

【论文阅读-人机交互】通过用户参与来缓解人工智能决策中的知识失衡问题

Title: Mitigating knowledge imbalance in AI-advised decision-making through collaborative user involvement From: International Journal of Human - Computer Studies Link: https://doi.org/10.1016/j.ijhcs.2022.102977 目录 1 绪论2 方法2.1 假设2.2. 实验任务及研究…

华为手机工程模式

1、*#*#2846579#*#* 2、*#*#2846579159#*#*

华为手机备忘录怎么打开

华为手机的记事本不加内容导出功能确实是个败笔,无法理解设计者的想法,一个大气的公司就要这么刻意的限制用户的权益,其实各大手机都是这么做的,垃圾中的战斗机。 不过其内容导出,我找到了一个间接的办法: …

华为手机备忘录怎么锁起来

备忘录是我们主要的记事软件之一,我们平时生活中、工作中、学习中的碎片信息,一股脑地记录到备忘录中,有时候备忘录中还会记录一些隐私内容。如果想把备忘录锁起来该怎么办呢? 华为手机自带的备忘录中,有应用锁的功能…