基于webpack開(kāi)發(fā)react-cli的詳細(xì)步驟
在前面的章節(jié)中我們學(xué)習(xí)了webpack
的基礎(chǔ)配置(五大核心屬性),以及一些高級(jí)優(yōu)化配置(source map、Tree Shaking、 HMR、Code Split
等),并且分別開(kāi)發(fā)了webpack.dev.js
(開(kāi)發(fā)環(huán)境配置),和webpack.prod.js
(生產(chǎn)環(huán)境配置)。
這一章節(jié)我們將兩個(gè)配置合并為一個(gè)整體配置,內(nèi)部差異通過(guò)process.env.NODE_ENV
環(huán)境變量區(qū)分,并再結(jié)合一下react的插件,自己實(shí)現(xiàn)一個(gè)react-cli腳手架
一、react-cli開(kāi)發(fā)步驟
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函數(shù) const getStyleLoaders = (pre) => { return [ "style-loader", "css-loader", { // 處理css兼容性問(wèn)題 // 配合package.json中browserslist來(lái)指定兼容性 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 ], }, }, ], }, // 處理html plugins: [ 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解析模塊加載選項(xiàng) resolve: { // 自動(dòng)補(bǔ)全文件擴(kuò)展名 extensions: [".jsx", ".js", ".json"], }, devServer: { host: "localhost", port: 3000, open: true, hot: true, // 開(kāi)啟HMR historyApiFallback: true, // 解決前端路由刷新404問(wèn)題 }, };
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函數(shù) const getStyleLoaders = (pre) => { return [ MiniCssExtractPlugin.loader, "css-loader", { // 處理css兼容性問(wèn)題 // 配合package.json中browserslist來(lái)指定兼容性 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, }, }, ], }, // 處理html plugins: [ 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解析模塊加載選項(xiàng) resolve: { // 自動(dòng)補(bǔ)全文件擴(kuò)展名 extensions: [".jsx", ".js", ".json"], }, };
2. 下載cross-env 獲取環(huán)境變量,配置打包命令
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. 合并生產(chǎn)和開(kāi)發(fā)配置為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定義的環(huán)境變量 const isProduction = process.env.NODE_ENV === "production"; // 返回處理樣式loader函數(shù) const getStyleLoaders = (pre) => { return [ isProduction ? MiniCssExtractPlugin.loader : "style-loader", "css-loader", { // 處理css兼容性問(wèn)題 // 配合package.json中browserslist來(lái)指定兼容性 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%8F lessOptions: { modifyVars: { "@primary-color": "#1DA57A" }, javascriptEnabled: true, }, } : {}, }, ].filter(Boolean); }; module.exports = { entry: "./src/main.js", output: { path: isProduction ? path.resolve(__dirname, "../dist") : undefined, // 開(kāi)發(fā)環(huán)境不指定輸出目錄,使用默認(rèn)的輸出目錄 filename: isProduction ? "static/js/[name].[contenthash:10].js" : "static/js/[name].js", // 生產(chǎn)環(huán)境使用contenthash,開(kāi)發(fā)環(huán)境使用hash chunkFilename: 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), }, }, ], }, // 處理html plugins: [ new EslintWebpackPlugin({ // eslint檢查 context: path.resolve(__dirname, "../src"), // 指定檢查的目錄 exclude: "node_modules", // 指定排除的目錄 cache: true, // 開(kāi)啟緩存,提升eslint檢查速度 cacheLocation: path.resolve(__dirname, "../node_modules/.cache/.eslintcache"), // 指定緩存文件存放位置 }), new HtmlWebpackPlugin({ template: path.resolve(__dirname, "../public/index.html"), // 指定html模板 }), isProduction && new MiniCssExtractPlugin({ // 提取css為單獨(dú)文件 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(), // 開(kāi)發(fā)環(huán)境開(kāi)啟HMR ].filter(Boolean), mode: isProduction ? "production" : "development", devtool: isProduction ? "source-map" : "cheap-module-source-map", // 配置source-map映射, 生產(chǎn)環(huán)境使用source-map,開(kāi)發(fā)環(huán)境使用cheap-module-source-map optimization: { splitChunks: { chunks: "all", cacheGroups: { // react react-dom react-router-dom 一起打包成一個(gè)js文件 react: { test: /[\\/]node_modules[\\/]react(.*)?[\\/]/, name: "chunk-react", priority: 40, }, // antd 單獨(dú)打包 antd: { test: /[\\/]node_modules[\\/]antd[\\/]/, name: "chunk-antd", priority: 30, }, // 剩下node_modules單獨(dú)打包 libs: { test: /[\\/]node_modules[\\/]/, name: "chunk-libs", priority: 20, }, }, }, runtimeChunk: { name: (entrypoint) => `runtime~${entrypoint.name}.js`, // 為每個(gè)入口添加一個(gè)runtime文件 }, // 是否需要進(jìn)行壓縮 minimize: isProduction, // 生產(chǎn)環(huán)境壓縮 minimizer: [ new CssMinimizerWebpackPlugin(), // 壓縮css new TerserWebpackPlugin(), // 壓縮js 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解析模塊加載選項(xiàng) resolve: { // 自動(dòng)補(bǔ)全文件擴(kuò)展名 extensions: [".jsx", ".js", ".json"], }, devServer: { host: "localhost", port: 3000, open: true, hot: true, // 開(kāi)啟HMR historyApiFallback: true, // 解決前端路由刷新404問(wèn)題 }, performance: false, // 關(guān)閉性能分析,提升打包速度 };
二、react-cli新增配置介紹
1. 定義生產(chǎn)環(huán)境變量
// 獲取cross-env定義的環(huán)境變量 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 開(kāi)啟開(kāi)發(fā)環(huán)境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(), // 開(kāi)發(fā)環(huán)境開(kāi)啟HMR ]
4. 解析模塊,自動(dòng)補(bǔ)全擴(kuò)展名
// webpack解析模塊加載選項(xiàng) resolve: { // 自動(dòng)補(bǔ)全文件擴(kuò)展名 extensions: [".jsx", ".js", ".json"], },
5. 配置 historyApiFallback: true, 解決前端路由刷新404問(wèn)題
devServer: { host: "localhost", port: 3000, open: true, hot: true, // 開(kāi)啟HMR historyApiFallback: true, // 解決前端路由刷新404問(wèn)題 },
6. 關(guān)閉性能分析,提升打包速度
performance: false, // 關(guān)閉性能分析,提升打包速度
7. 通過(guò)cssloader自定義antd主題
pre && { loader: pre, options: pre === "less-loader" ? { // antd自定義主題配置 lessOptions: { modifyVars: { "@primary-color": "#1DA57A" }, javascriptEnabled: true, }, } : {}, },
8. 關(guān)閉多進(jìn)程打包
在項(xiàng)目沒(méi)有達(dá)到一定規(guī)模之前,開(kāi)啟多進(jìn)程反而會(huì)減慢打包速度
9. 模塊分離打包
cacheGroups: { // react react-dom react-router-dom 一起打包成一個(gè)js文件 react: { test: /[\\/]node_modules[\\/]react(.*)?[\\/]/, name: "chunk-react", priority: 40, }, // antd 單獨(dú)打包 antd: { test: /[\\/]node_modules[\\/]antd[\\/]/, name: "chunk-antd", priority: 30, }, // 剩下node_modules單獨(dú)打包 libs: { test: /[\\/]node_modules[\\/]/, name: "chunk-libs", priority: 20, }, },
到此這篇關(guān)于基于webpack開(kāi)發(fā)react-cli的文章就介紹到這了,更多相關(guān)webpack開(kāi)發(fā)react-cli內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解基于webpack搭建react運(yùn)行環(huán)境
本篇文章主要介紹了詳解基于webpack搭建react運(yùn)行環(huán)境,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06優(yōu)雅的在React項(xiàng)目中使用Redux的方法
這篇文章主要介紹了優(yōu)雅的在React項(xiàng)目中使用Redux的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-11-11ReactQuery系列之?dāng)?shù)據(jù)轉(zhuǎn)換示例詳解
這篇文章主要為大家介紹了ReactQuery系列之?dāng)?shù)據(jù)轉(zhuǎn)換示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11React Native 混合開(kāi)發(fā)多入口加載方式詳解
這篇文章主要介紹了React Native 混合開(kāi)發(fā)多入口加載方式詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09