webpack基础配置代码-react
1.webpack.config.js
// nodejs中的核心模块,用来处理路径 const path = require("path"); // eslint 检查js文件规范 const ESLintWebpackPlugin = require("eslint-webpack-plugin"); // 处理HTML文件,以public中的HTML文件为模板,自动引入打包后的主文件 const HtmlWebpackPlugin = require("html-webpack-plugin"); // react 框架中实现热更新 激活js的HMR功能 const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin"); // 提取css成单独的文件,替换style-loader为MiniCssExtractPlugin;并在plugins中能new一下 const MiniCssExtractPlugin = require("mini-css-extract-plugin"); // 压缩css const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); // 压缩JS const TerserWebpackPlugin = require("terser-webpack-plugin"); // 压缩图片 const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin"); // 复制插件 从哪里复制到哪里 可以解决public中的title图标打包之后没有复制到dist的问题 const CopyPlugin = require("copy-webpack-plugin"); // 需要通过 cross-env 定义环境变量 获取进程上的环境变量 const isProduction = process.env.NODE_ENV === "production"; // 将用到的样式loader代码复用 const getStyleLoaders = (preProcessor) => { return [ // 生产模式下需要将css打包成单独文件 isProduction ? MiniCssExtractPlugin.loader : "style-loader", "css-loader", { loader: "postcss-loader", //为了解决各个浏览器版本兼容性问题等 options: { postcssOptions: { plugins: [ "postcss-preset-env", // 能解决大多数样式兼容性问题,提供的浏览器的预置环境
{
browsers: 'last 2 versions', //配置兼容的浏览器版本,最新两个版本的浏览器
}
], }, }, }, preProcessor && { loader: preProcessor, options: // 如果是less-loader,修改antd的主题色 preProcessor === "less-loader" ? { // antd的自定义主题 lessOptions: { modifyVars: { // 其他主题色:https://ant.design/docs/react/customize-theme-cn "@primary-color": "#1DA57A", // 全局主色 }, javascriptEnabled: true, }, } : {}, }, ].filter(Boolean); }; module.exports = { // 入口 entry: "./src/main.js", // 出口 output: { // 生产模式下输出到dist目录下,开发模式没有输出 path: isProduction ? path.resolve(__dirname, "../dist") : undefined, /** * 一旦将来发布新版本,因为文件名没有变化导致浏览器会直接读取缓存,不会加载新资源,项目也就没法更新了。 * 利用:contenthash--确保更新前后文件名不一样,这样就可以做缓存了。 * 根据文件内容生成 hash 值,只有文件内容变化了,hash 值才会变化。所有文件 hash 值是独享且不同的 */ // 输出的文件名 name会已打包的chunk名自动补全 ~ 入口文件的打包目录 filename: isProduction ? "static/js/[name].[contenthash:10].js" : "static/js/[name].js", // 打包多余的chunk ~ import动态引入的,或者nodeModules中的 chunkFilename: isProduction ? "static/js/[name].[contenthash:10].chunk.js" : "static/js/[name].chunk.js", // 图片,字体等资源 assetModuleFilename: "static/js/[hash:10][ext][query]", // 打包之前清空之前所有的打包文件 clean: true, }, module: { rules: [ { oneOf: [ /*========= 处理样式资源 =============*/ { // 用来匹配 .css 结尾的文件 test: /\.css$/, // use 数组里面 Loader 执行顺序是从右到左 use: getStyleLoaders(), }, { test: /\.less$/, use: getStyleLoaders("less-loader"), }, { test: /\.s[ac]ss$/, use: getStyleLoaders("sass-loader"), }, { test: /\.styl$/, use: getStyleLoaders("stylus-loader"), }, /*========= 处理图片等资源 =============*/ { test: /\.(png|jpe?g|gif|svg)$/, type: "asset", parser: { dataUrlCondition: { maxSize: 10 * 1024, // 小于10kb的图片会被base64处理 }, }, }, /*========= 处理其他资源(字体图标,mp3,mp4等) =============*/ // asset/resource配置不会转换成base64,原封不动的输出 { test: /\.(ttf|woff2?)$/, type: "asset/resource", }, /*========= 处理js资源(eslint对代码进行检查;babel对代码进行转换) =============*/ { test: /\.(jsx|js)$/, // 需要处理的资源文件目录 include: path.resolve(__dirname, "../src"), loader: "babel-loader", options: { cacheDirectory: true, // 开启babel编译缓存 -让第二次打包时候更快 cacheCompression: false, // 缓存文件不要压缩,缓存速度更快 plugins: [ // "@babel/plugin-transform-runtime", // presets中包含了 /*=== 激活js的HMR功能 ==*/ !isProduction && "react-refresh/babel", ].filter(Boolean), }, }, ], }, ], }, plugins: [ /*========= Eslint-检查js文件规范 =============*/ new ESLintWebpackPlugin({ extensions: [".js", ".jsx"], // 处理文件的范围 应用绝对路径 path会返回绝对路径 context: path.resolve(__dirname, "../src"), // 排除node-modules文件 exclude: "node_modules", // 缓存 cache: true, // 配置缓存的目录 cacheLocation: path.resolve( __dirname, "../node_modules/.cache/.eslintcache" ), }), /*========= 处理HTML文件 =============*/ new HtmlWebpackPlugin({ template: path.resolve(__dirname, "../public/index.html"), }), /*========= 处理CSS文件,打包成单独文件 =============*/ isProduction && new MiniCssExtractPlugin({ filename: "static/css/[name].[contenthash:10].css", chunkFilename: "static/css/[name].[contenthash:10].chunk.css", }), /*========= 解决热更新问题 =============*/ !isProduction && new ReactRefreshWebpackPlugin(), /*========= 复制-插件 =============*/ // 将public下面的资源复制到dist目录去(除了index.html) new CopyPlugin({ patterns: [ { from: path.resolve(__dirname, "../public"), to: path.resolve(__dirname, "../dist"), toType: "dir", noErrorOnMissing: true, // 不生成错误 globOptions: { // 忽略文件 ignore: ["**/index.html"], }, info: { // 跳过terser压缩js minimized: true, }, }, ], }), ].filter(Boolean), /*========= 处理打包chunk文件,分割代码,使打包文件不要放到一个文件中 =============*/ optimization: { // 控制是否需要进行压缩 true需要压缩 生产环境需要压缩 minimize: isProduction, // 压缩的操作 minimizer: [ // 压缩css new CssMinimizerPlugin(), // 压缩js 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", }, }, ], }, ], ], }, }, }), ], // 代码分割配置 主要: node_modules 及import动态导入的进行单独打包 splitChunks: { chunks: "all", cacheGroups: { // layouts通常是admin项目的主体布局组件,所有路由组件都要使用的 // 可以单独打包,从而复用 // 如果项目中没有,请删除 layouts: { name: "layouts", test: path.resolve(__dirname, "../src/layouts"), priority: 40, }, // 如果项目中使用antd,此时将所有node_modules打包在一起,那么打包输出文件会比较大。 // 所以我们将node_modules中比较大的模块单独打包,从而并行加载速度更好 // 如果项目中没有,请删除 antd: { name: "chunk-antd", test: /[\\/]node_modules[\\/]antd(.*)/, priority: 30, }, // 将react相关的库单独打包,减少node_modules的chunk体积。 react: { name: "react", // 检测什么文件 react react-dom react-router-dom 等 // [\\/] \ /两种符号都可以 test: /[\\/]node_modules[\\/]react(.*)?[\\/]/, chunks: "initial", // 打包权重 priority: 20, }, // 剩下的 如node——modules打包到libs中 libs: { name: "chunk-libs", test: /[\\/]node_modules[\\/]/, priority: 10, // 权重最低,优先考虑前面内容 chunks: "initial", }, }, // 其他都用默认值 }, // 代码分割会导致缓存失效,需要配置runtime进行关联映射 runtimeChunk: { // 命名runtime文件 name: (entrypoint) => `runtime~${entrypoint.name}`, }, }, /*========= webpack解析模块时加载的选项 =============*/ resolve: { // 从.jsx 到.json顺序,如果三个类型都没有就会报错 extensions: [".jsx", ".js", ".json"], // 自动补全文件扩展名,让jsx可以使用 }, /*========= 自动化配置:运行指令时需要加serve 激活devServer配置开发模式 =============*/ devServer: { open: true, //自动打开浏览器 host: "localhost", //域名 port: 8000, //端口号 hot: true, //HMR 开启热模替换(样式HMR,style-loader配合处理) compress: true, historyApiFallback: true, //******解决react-router刷新404问题 *****/ }, /*========= 开发模式配置(代码运行时读取的环境变量) =============*/ mode: isProduction ? "production" : "development", /*========= 为了开发调试更友好,精确 =============*/ devtool: isProduction ? "source-map" : "cheap-module-source-map", performance: false, // 关闭性能分析,提升打包速度吧 };
2.babel.config.js
// 制定bebel需要使用的规则 安装babel-preset-react-app module.exports = { // 预设 ~ 使用react官方规则 // @babel/preset-env 预设编译eslint6的其他语法,corejs处理语法兼容性问题 // @babel/plugin-transform-runtime 把引入的代码做关联,是打包的体积更小 // https://github.com/facebook/create-react-app/blob/main/packages/babel-preset-react-app/create.js presets: ["react-app"], };
3.eslintrc.js
// 制定eslint应用规范 安装eslint-config-react-app module.exports = { extends: ["react-app"], // 继承 react 官方规则 parserOptions: { babelOptions: { presets: [ // 解决页面报错问题 ["babel-preset-react-app", false], "babel-preset-react-app/prod", ], }, }, };
4.package.json
备注:
<!-- cross-env 配置babel读取的环境变量 --> cross-env NODE_ENV=development <!-- webpack配置,dev及prod分开配置 --> "dev": "cross-env NODE_ENV=development webpack serve --config ./config/webpack.dev.js", "build": "cross-env NODE_ENV=production webpack --config ./config/webpack.prod.js"
代码:
{ "name": "webpack-react", "version": "1.0.0", "description": "", "main": "./src/main.js", "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" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "@babel/core": "^7.17.10", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.5", "babel-loader": "^8.2.5", "babel-preset-react-app": "^10.0.1", "copy-webpack-plugin": "^10.2.4", "cross-env": "^7.0.3", "css-loader": "^6.7.1", "css-minimizer-webpack-plugin": "^3.4.1", "eslint-config-react-app": "^7.0.1", "eslint-webpack-plugin": "^3.1.1", "html-webpack-plugin": "^5.5.0", "image-minimizer-webpack-plugin": "^3.2.3", "imagemin": "^8.0.1", "imagemin-gifsicle": "^7.0.0", "imagemin-jpegtran": "^7.0.0", "imagemin-optipng": "^8.0.0", "imagemin-svgo": "^10.0.1", "less-loader": "^10.2.0", "mini-css-extract-plugin": "^2.6.0", "postcss-loader": "^6.2.1", "postcss-preset-env": "^7.5.0", "react-refresh": "^0.13.0", "sass-loader": "^12.6.0", "style-loader": "^3.3.1", "stylus-loader": "^6.2.0", "webpack": "^5.72.0", "webpack-cli": "^4.9.2", "webpack-dev-server": "^4.9.0" }, "dependencies": { "antd": "^4.20.2", "react": "^18.1.0", "react-dom": "^18.1.0", "react-router-dom": "^6.3.0" }, "browserslist": [ "last 2 version", "> 1%", "not dead" ] }
5.基础总结
- 两种开发模式
- 开发模式:代码能编译自动化运行
- 生产模式:代码编译优化输出
- Webpack 基本功能
- 开发模式:可以编译 ES Module 语法
- 生产模式:可以编译 ES Module 语法,压缩 js 代码
- Webpack 配置文件
- 5 个核心概念
- entry
- output
- loader
- plugins
- mode
- devServer 配置
- Webpack 脚本指令用法
webpack
直接打包输出webpack serve
启动开发服务器,内存编译打包没有输出
6.优化总结
我们从 4 个角度对 webpack 和代码进行了优化:
- 提升开发体验
- 使用
Source Map
让开发或上线时代码报错能有更加准确的错误提示。
- 提升 webpack 提升打包构建速度
- 使用
HotModuleReplacement
让开发时只重新编译打包更新变化了的代码,不变的代码使用缓存,从而使更新速度更快。 - 使用
OneOf
让资源文件一旦被某个 loader 处理了,就不会继续遍历了,打包速度更快。 - 使用
Include/Exclude
排除或只检测某些文件,处理的文件更少,速度更快。 - 使用
Cache
对 eslint 和 babel 处理的结果进行缓存,让第二次打包速度更快。 - 使用
Thead
多进程处理 eslint 和 babel 任务,速度更快。(需要注意的是,进程启动通信都有开销的,要在比较多代码处理时使用才有效果)
- 减少代码体积
- 使用
Tree Shaking
剔除了没有使用的多余代码,让代码体积更小。 - 使用
@babel/plugin-transform-runtime
插件对 babel 进行处理,让辅助代码从中引入,而不是每个文件都生成辅助代码,从而体积更小。 - 使用
Image Minimizer
对项目中图片进行压缩,体积更小,请求速度更快。(需要注意的是,如果项目中图片都是在线链接,那么就不需要了。本地项目静态图片才需要进行压缩。)
- 优化代码运行性能
- 使用
Code Split
对代码进行分割成多个 js 文件,从而使单个文件体积更小,并行加载 js 速度更快。并通过 import 动态导入语法进行按需加载,从而达到需要使用时才加载该资源,不用时不加载资源。 - 使用
Preload / Prefetch
对代码进行提前加载,等未来需要使用时就能直接使用,从而用户体验更好。 - 使用
Network Cache
能对输出资源文件进行更好的命名,将来好做缓存,从而用户体验更好。 - 使用
Core-js
对 js 进行兼容性处理,让我们代码能运行在低版本浏览器。 - 使用
PWA
能让代码离线也能访问,从而提升用户体验。