本文对应仓库地址
webpack 初体验 运行命令初始化 package.json。
安装 webpack 和 webpack-cli。
1 yarn add webpack webpack-cli -D
根目录新建文件夹 src,里面新建文件夹 js。
js 文件夹下新建两个文件 math.js 和 foo.js。内容如下:
1 2 3 4 5 6 7 8 export const sum = (a, b ) => { return a + b; }; export const mul = (a, b ) => { return a * b; };
1 2 3 4 5 6 7 8 function foo ( ) { console .log("foo" ); } module .exports = { foo, };
src 目录下新建文件 index.js,文件内容如下:
1 2 3 4 5 6 7 import { sum, mul } from "./js/math" ;const { foo } = require ("./js/foo" );console .log(sum(2 , 3 ));console .log(mul(2 , 3 ));foo();
根目录下新建 index.html,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta http-equiv ="X-UA-Compatible" content ="IE=edge" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > Webpack Learning</title > </head > <body > <script src ="./dist/main.js" > </script > </body > </html >
运行命令打包文件。
在 dist/main.js 中,可以看到打包的结果。
在 package.json 中配置 build 运行命令。
css-loader & style-loader src/js 目录下新建文件 title.js,内容如下:
1 2 3 4 5 6 7 8 9 10 import "../css/title.css" ;function setTitle (title ) { const h1 = document .createElement("h1" ); h1.innerHTML = title; h1.className = "title" ; document .body.appendChild(h1); } export default setTitle;
修改 index.js 内容为:
1 2 3 import setTitle from "./js/title" ;setTitle("hello world" );
src 目录下新建文件夹 css,css 文件夹下新建文件 title.css,内容如下:
在 index.html 中引入 css 文件。
运行命令 yarn build
,报错,原因是缺少 loader。
安装 loader。
根目录下新建文件 webpack.config.js,进行配置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const path = require ("path" );module .exports = { entry : "./src/index.js" , output : { filename : "[name].js" , path : path.join(__dirname, "./dist" ), }, module : { rules : [ { test : /\.css$/ , use: ["css-loader" ], }, ], }, };
但是样式并没有展示,因为现在只是把 css 语法识别为了 js 语法,但是还没有挂载到页面上,我们需要 style-loader 来把内容挂载到 <style>
标签上。
安装 style-loader
1 yarn add style-loader -D
修改配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const path = require ("path" );module .exports = { entry : "./src/index.js" , output : { filename : "[name].js" , path : path.join(__dirname, "./dist" ), }, module : { rules : [ { test : /\.css$/ , use: ["style-loader" , "css-loader" ], }, ], }, };
less-loader 首先安装 less。
配置文件修改为下面的内容。
安装 less-loader。
1 2 3 4 5 6 7 8 9 10 rules: [ { test : /\.css$/ , use: ["style-loader" , "css-loader" ], }, { test : /\.less$/ , use: ["style-loader" , "css-loader" , "less-loader" ], }, ],
browserslitrc 浏览器使用比例可以查看 caniuse 官网
在 package.json 中新增内容
1 2 3 4 5 "browserslist" : [ ">1%" , "last 2 version" , "not dead" ]
运行命令
可查看当前兼容的浏览器选项。
postcss-loader postcss 用来处理 css 的兼容性问题。
先安装 postcss 和 postcss-loader
css 文件夹下新建文件 test.css,内容如下:
1 2 3 4 .title { user-select: none; transition : all 1s ; }
然后把文件引入 title.js 中。
修改 webpack.config.js 为:
1 2 3 4 5 6 7 8 { test : /\.css$/ , use: ["style-loader" , "css-loader" , "postcss-loader" ], }, { test : /\.less$/ , use: ["style-loader" , "css-loader" , "postcss-loader" , "less-loader" ], },
打包以后发现没有任何效果,原因是 postcss 本身其实不具备修改 css 的功能,还需要额外的插件才行。
autoprefixer 安装插件 autoprefixer。
1 yarn add autoprefixer -D
修改配置文件为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 { test : /\.css$/ , use: [ "style-loader" , "css-loader" , { loader : "postcss-loader" , options : { postcssOptions : { plugins : [require ("autoprefixer" )], }, }, }, ], },
postcss-preset-env 安装 postcss-preset-env。
1 yarn add postcss-preset-env -D
修改 test.css 内容为:
1 2 3 .title { color : #123456 78; }
postcss-loader 配置的 plugins 中增加以下内容
1 require ("postcss-preset-env" )
打包以后颜色会以 rgba 形式展示。
避免重复配置 为了避免在 css-loader 和 less-loader 中都要进行重复的配置,postcss 还支持我们通过配置文件进行配置。
根目录下新建文件 postcss.config.js,内容为:
1 2 3 module .exports = { plugins : [require ("autoprefixer" ), require ("postcss-preset-env" )], };
这样我们就不需要在 webpack.config.js 中书写 postcss-loader 的 plugins 了。
1 2 3 4 5 6 7 8 { test : /\.css$/ , use: ["style-loader" , "css-loader" , "postcss-loader" ], }, { test : /\.less$/ , use: ["style-loader" , "css-loader" , "postcss-loader" , "less-loader" ], },
filer-loader 处理 img 标签 在 src 文件夹下新建文件夹 img,里面塞入一张图片。
在 js 文件夹下新建文件 image.js,内容如下:
1 2 3 4 5 6 7 8 9 10 11 import imgSrc from "../img/ai.jpg" ;function setImage ( ) { const div = document .createElement("div" ); const img = document .createElement("img" ); img.src = imgSrc; div.appendChild(img); document .body.appendChild(div); } export default setImage;
修改 index.js,内容如下:
1 2 3 import setImage from "./js/image" ;setImage();
打包以后报错缺少 loader。
安装 file-loader。
修改 webpack.config.js,添加如下内容
1 2 3 4 { test : /\.(png|svg|gif|jpe?g)$/ , use: ["file-loader" ], },
处理 css 背景图片 修改 js/image.js 为以下内容:
1 2 3 4 5 6 7 8 9 10 11 import "../css/bg.css" ;function setImage ( ) { const div = document .createElement("div" ); const backgroundImg = document .createElement("div" ); backgroundImg.className = "bg-img" ; div.appendChild(backgroundImg); document .body.appendChild(div); } export default setImage;
css 文件夹下新建文件 bg.css,内容如下:
1 2 3 4 5 6 .bg-img { background-image : url (../img/ai.jpg ); border : 1px solid black; width : 400px ; height : 400px ; }
先删除 dist 目录,再运行打包命令。此时发现 dist 目录下会出现两张图片。
其中一张是正常的,另一张点击打开以后内容如下:
里面的文本内容是一个指向我们需要的图片的导出语句。
这是因为图片是嵌在 css-loader 里面,没有被 file-loader 处理。
css-loader 会把 url 路径处理为 require 语句,而 require 语句使用时需要把 css-loader 的 esModule 属性设置为 false。
1 2 3 4 5 6 7 8 9 10 11 12 13 { test : /\.css$/ , use: [ "style-loader" , { loader : "css-loader" , options : { esModule : false , }, }, "postcss-loader" , ], },
处理以后打包问题就解决了。
图片名称和路径设置 [ext] 拓展名 [name] 文件名 [hash] 哈希 [hash:] 哈希截取长度 [path] 文件路径 修改 webpack.config.js 如下
1 2 3 4 5 6 7 8 9 10 11 12 { test : /\.(png|svg|gif|jpe?g)$/ , use: [ { loader : "file-loader" , options : { name : "[name].[hash:6].[ext]" , outputPath : "img" , }, }, ], },
url-loader 安装 url-loader。
把配置文件的 file-loader 修改为 url-loader,进行打包。
打包后发现页面显示正常,但是 dist 目录下没有出现 img 文件夹。
它会以 base64 形式把图片嵌入代码中。
limit url-loader VS file-loader
url-loader 会把文件转换为 base64 格式,可以减少请求次数,但是会增加单次请求文件的体积,不利于首屏渲染 file-loader 会将资源拷贝到指定目录,分开请求 url-loader 可以调用 file-loader,通过设置 limit 进行阈值限制,控制文件小于多少的时候使用 url-loader 在 img 文件夹下加入一张新图片
修改 image.js 如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import "../css/bg.css" ;import imgSrc from "../img/ai2.jpg" ;function setImage ( ) { const div = document .createElement("div" ); const img = document .createElement("img" ); img.src = imgSrc; div.appendChild(img); const backgroundImg = document .createElement("div" ); backgroundImg.className = "bg-img" ; div.appendChild(backgroundImg); document .body.appendChild(div); } export default setImage;
修改 webpack.config.js 文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 { test : /\.(png|svg|gif|jpe?g)$/ , use: [ { loader : "url-loader" , options : { name : "[name].[hash:6].[ext]" , outputPath : "img" , limit : 25 * 1024 , }, }, ], },
运行打包命令,会发现只有一张较大的图片,另一张小的图片被以 base64 的形式嵌入文件里了。
asset 处理图片 webpack5 之后不需要再使用 file-loader 和 url-loader 了。
asset/resource => file-loader asset/inline => url-loader asset => 阈值限制 修改 webpack.config.js 如下:
1 2 3 4 5 6 7 { test : /\.(png|svg|gif|jpe?g)$/ , type: "asset/resource" , generator : { filename : "img/[name].[hash:6][ext]" , }, },
如果需要设置 limit,则修改如下:
1 2 3 4 5 6 7 8 9 10 11 12 { test : /\.(png|svg|gif|jpe?g)$/ , type: "asset" , generator : { filename : "img/[name].[hash:6][ext]" , }, parser : { dataUrlCondition : { maxSize : 25 * 1024 , }, }, },
asset 处理字体图标 先去 iconfont 官网 下载图标。
在 src 下新建文件夹 font,里面留着下面图片中这几个文件即可。
src/js 下新建文件 font.js,内容如下:
1 2 3 4 5 6 7 8 9 10 11 function setFont ( ) { const div = document .createElement("div" ); const span = document .createElement("span" ); span.className = "iconfont icon-gift lg-icon" ; div.appendChild(span); document .body.appendChild(div); } export default setFont;
修改 index.js 内容如下:
1 2 3 import setFont from "./js/font" ;setFont();
运行打包命令,报错,这是因为我们没有办法处理 iconfont 里面的路径。
我们直接把字体当成资源文件进行拷贝即可,因此可以使用前面所说的 asset/resource ,给 webpack.config.js 添加如下内容即可:
1 2 3 4 5 6 7 { test : /\.(ttf|woff2?)$/ , type: "asset/resource" , generator : { filename : "font/[name].[hash:6][ext]" , }, },
plugin VS loader loader:webpack 只认识 js 和 json 文件,为了让 webpack 认识其它文件,如 css、jpg、png 等等,需要将其它类型文件转换 为 js 格式,让 webpack 认识,这个转换的作用就是 loader 提供的。 plugin:plugin 可以做更多的事情,比如在打包开始之前做一些预处理,或者打包进行过程中做一些处理。loader 的作用时机只有当 webpack 要读取某个文件的时候,但是 plugin 的作用时机很多。 clean-webpack-plugin 安装 clean-webpack-plugin
1 yarn add clean-webpack-plugin -D
作用是每次打包之前先把 dist 目录删除。
修改 webpack.config.js 内容:
在现版本的 webpack 中,已经不需要再加入此插件了,直接设置 output 的 clean 属性为 true 即可。
html-webpack-plugin 安装 html-webpack-plugin
1 yarn add html-webpack-plugin -D
引入 HtmlWebpackPlugin 类
1 const HtmlWebpackPlugin = require ("html-webpack-plugin" );
引入插件:
1 2 3 new HtmlWebpackPlugin({ title : "Webpack Learning" , }),
这里打包以后产出的 js 文件是 defer 引入的。但是这样很不灵活,其实我们可以自己书写打印出的模板。
在 src 目录下新建文件夹 public。public 下新建文件 index.html。我们把 vue-cli 的 index.html 拷贝过来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <!DOCTYPE html > <html lang ="" > <head > <meta charset ="utf-8" > <meta http-equiv ="X-UA-Compatible" content ="IE=edge" > <meta name ="viewport" content ="width=device-width,initial-scale=1.0" > <link rel ="icon" href ="<%= BASE_URL %>favicon.ico" > <title > <%= htmlWebpackPlugin.options.title %> </title > </head > <body > <noscript > <strong > We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong > </noscript > <div id ="app" > </div > </body > </html >
打包以后报错说找不到 BASE_URL。webpack 可以自定义一些常量,我们在这还没有定义所以会报错。这里用到的是 webpack 内置的一个插件。
1 2 3 4 5 const { DefinePlugin } = require ("webpack" );new DefinePlugin({ BASE_URL : '"./"' , }),
babel Babel 的作用:JSX TS ES6+ => 转换为浏览器可以直接使用的语法
将 js/foo.js 内容修改如下:
1 2 3 4 5 const foo = () => { console .log("hello babel" ); }; export default foo;
修改 index.js 如下:
1 2 3 import foo from "./js/foo" ;foo();
打包后,main.js 内容含有箭头函数,可能在某些浏览器无法正常显示。我们需要使用 babel 处理。
babel-loader 安装 @babel/core(核心模块)
安装 babel-loader
1 yarn add babel-loader -D
安装插件 @babel/preset-env -D
1 yarn add @babel/preset-env -D
修改配置文件如下:
1 2 3 4 5 6 7 8 9 10 11 { test : /\.js$/ , use: [ { loader : "babel-loader" , options : { presets : ["@babel/preset-env" ], }, }, ], },
然后就可以把语法转成 ES5 了。
还可以新建文件 babel.config.js,里面写入一下配置内容:
1 2 3 module .exports = { presets : ["@babel/preset-env" ], };
然后就能简写 webpack.config.js 的信息为:
1 2 3 4 { test : /\.js$/ , use: ["babel-loader" ], },
polyfill preset-env 并不能把所有的语法都转换,此时我们需要 polyfill。polyfill 即字面意思,填充,意思是填充一些旧版本没有的新语法(比如 Promise)。webpack4 会默认加入 polyfill,所以打包速度很不乐观,在 webpack5 就去掉了。
修改 index.js 内容为:
1 2 3 4 const p1 = new Promise ((resolve, reject ) => { console .log("promsie" ); resolve(); });
安装 core-js、regenerator-runtime,注意此处不是开发依赖。
1 yarn add core-js regenerator-runtime
然后修改 babel.config.js,内容如下:
1 2 3 4 5 6 7 8 9 10 11 module .exports = { presets : [ [ "@babel/preset-env" , { useBuiltIns : "entry" , corejs : 3 , }, ], ], };
在 index.js 中引入核心模块。
1 2 3 4 5 6 7 import "core-js/stable" ;import "regenerator-runtime/runtime" ;const p1 = new Promise ((resolve, reject ) => { console .log("promsie" ); resolve(); });
copy-webpack-plugin 可以进行一些资源的拷贝,如 favicon 图标。
安装 copy-webpack-plugin。
1 yarn add copy-webpack-plugin -D
引入插件:
1 const CopyWebpackPlugin = require ("copy-webpack-plugin" );
修改 webpack.config.js:
1 2 3 4 5 6 7 8 9 10 new CopyWebpackPlugin({ patterns : [ { from : "public" , globOptions : { ignore : ["**/index.html" ], }, }, ], }),
修改 index.js 内容:
1 2 import "./js/font" ;import "./js/image" ;
webpack-dev-server 在 webpack.config.js 中添加 watch: true,如下所示。
当文件修改以后,会自动触发打包。但是这样会影响速度,因为每次保存都要重新打包一次。我们可以在本地开启一个服务器,把文件存储在内存中。
安装 webpack-dev-server。
1 yarn add webpack-dev-server -D
在 package.json 中配置命令:
1 "start" : "npx webpack serve"
运行命令
这样就可以实现开启本地服务器。
HMR HMR 即 hot-module-replacement,模块热替换,也叫热更新,就是可以对页面的局部内容进行替换。
首先修改 index.js 内容为
修改 public 文件夹下的 index.html,增加一个输入框,用于验证热替换是否开启。
1 2 3 4 5 6 7 8 9 10 <body > <noscript > <strong > We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong > </noscript > <div id ="app" > <input type ="text" > </div > </body >
webpack.config.js 中加入内容:
1 2 3 4 devServer: { hot : true , port : 3000 , },
此时事实上还没有开启热更新。还需要把热更新的选项加入配置中。因为开发中通常是使用框架进行,所以这部分意义不大,省略。
React HMR 安装 @babel/preset-react,这是一个用于转换 jsx 语法的插件。
1 yarn add @babel/preset-react -D
安装 react。
1 yarn add react react-dom
src 目录下新建文件 App.jsx,内容如下:
1 2 3 4 5 6 7 8 9 import React, { useState } from "react" ;import "./App.css" ;const App = () => { const [title, setTitle] = useState("Hello World" ); return <h1 className ="title" > {title}</h1 > ; }; export default App;
修改 index.js,内容如下:
1 2 3 4 5 import App from "./App.jsx" ;import React from "react" ;import ReactDOM from "react-dom" ;ReactDOM.render(<App /> , document .getElementById("app" ));
修改 babel.config.js,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 module .exports = { presets : [ [ "@babel/preset-env" , { useBuiltIns : "entry" , corejs : 3 , }, ], ["@babel/preset-react" ], ], };
此时可以支持 React 语法,为了实现热更新,还需要引入其它插件。
安装 @pmmmwh/react-refresh-webpack-plugin 和 react-refresh
1 yarn add @pmmmwh/react-refresh-webpack-plugin react-refresh -D
在 webpack.config.js 中引入插件:
1 const ReactRefreshWebpackPlugin = require ("@pmmmwh/react-refresh-webpack-plugin" );
1 new ReactRefreshWebpackPlugin(),
修改 babel.config.js,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 module .exports = { presets : [ [ "@babel/preset-env" , { useBuiltIns : "entry" , corejs : 3 , }, ], ["@babel/preset-react" ], ], plugins : [["react-refresh/babel" ]], };
此时就能实现 React 的热更新了。
devServer 其它属性设置 1 2 3 4 5 6 7 devServer: { hot : true , port : 3000 , compress : true , open : true , historyApiFallback : true , },
compress 表示是否压缩资源(使用 gzip),open 表示是否自动打开文件,当使用 History API 时,任意的 404 响应会被替代为 index.html。
hot: ‘only’ 和 hot: true 的区别: 如果文件报错了,修改成了对的以后,hot: true 会直接刷新整个页面,而 hot: ‘only’ 不会刷新整个页面。
proxy 先开启一个 node 服务。
1 2 3 4 5 6 7 8 9 10 11 12 const http = require ("http" );const port = 8000 ;http .createServer((req, res ) => { res.end(JSON .stringify("hello world" )); }) .listen(port, function ( ) { console .log("server is listening on port " + port); });
安装 axios
修改 index.js 内容为:
1 2 3 4 5 6 7 8 9 10 import App from "./App.jsx" ;import React from "react" ;import ReactDOM from "react-dom" ;import axios from "axios" ;axios.get("http://127.0.0.1:8000/" ).then((res ) => { console .log(res.data); }); ReactDOM.render(<App /> , document .getElementById("app" ));
此时请求出现跨域,需要设置代理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 devServer: { hot : true , port : 3000 , compress : true , open : true , proxy : { "/api" : { target : "http://127.0.0.1:8000/" , pathRewrite : { "^api" : "" }, }, }, },
前端更改为:
1 2 3 axios.get("http://127.0.0.1:8000/" ).then((res ) => { console .log(res.data); });
此时跨域问题得到解决。
source-map devtool 选项控制是否需要 source-map。
source-map 是一种映射方式,可以在调试的时候定位到源代码中的位置。
设置 devtool: 'source-map'
,运行 yarn build
,dist 目录下除了原来的文件外还会多出一个 main.js.map,然后也可以定位到错误的具体位置了。
通过 devtool 还可以进行更多的配置。
source-map:错误信息有行也有列,推荐使用 inline-source-map:直接把 map 信息塞入 main.js 中,可以减少一次请求 cheap-source-map:错误信息只显示行,不显示列 ts-loader 编译 ts src 目录下新建文件 index.ts,内容如下:
1 2 3 4 5 const add = (a: number , b: number ) => { return a + b; }; console .log(add(1 , 3 ));
修改 webpack.config.js 文件中的 entry 为 “./src/index.ts”
执行命令
生成 tsconfig.json 文件。
安装 ts-loader。
安装 typescript。
修改 webpack.config.js 文件内容:
1 2 3 4 { test : /\.ts$/ , use: ["ts-loader" ], },
babel-loader 编译 ts 此时 ts 已经可以被正确编译,但是一些新的语法没有被转换为低级的语法,可能会存在兼容性问题,所以还需要 babel-loader 进行进一步的处理。
安装 @babel/preset-typescript
1 yarn add @babel/preset-typescript -D
修改 babel.config.js,内容为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 module .exports = { presets : [ [ "@babel/preset-env" , { useBuiltIns : "entry" , corejs : 3 , }, ], ["@babel/preset-react" ], ["@babel/preset-typescript" ], ], plugins : [["react-refresh/babel" ]], };
ts-loader 如果出现数据类型错误,会在 build 的时候暴露;而 babel-loader 可以进行 polyfill。
我们可以选择使用 babel-loader,然后通过命令进行类型校验。输入命令 tsc
,可以实现数据类型的校验。
也可以选择直接修改打包命令如下:
分离生产和开发环境 修改 package.json 中的脚本命令:
在根目录下新建文件夹 config,然后在 config 文件夹下新建 3 个文件 webpack.common.js、webpack.dev.js、webpack.prod.js。
安装 webpack-merge,来进行文件合并。
1 yarn add webpack-merge -D
文件内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 const HtmlWebpackPlugin = require ("html-webpack-plugin" );const { DefinePlugin } = require ("webpack" );const CopyWebpackPlugin = require ("copy-webpack-plugin" );module .exports = { entry : "./src/index.js" , resolve : { extensions : [".tsx" , ".ts" , ".jsx" , ".js" ], }, module : { rules : [ { test : /\.css$/ , use: [ "style-loader" , { loader : "css-loader" , options : { esModule : false , }, }, "postcss-loader" , ], }, { test : /\.ts$/ , use: ["babel-loader" ], }, { test : /\.less$/ , use: ["style-loader" , "css-loader" , "less-loader" ], }, { test : /\.(png|svg|gif|jpe?g)$/ , type: "asset" , generator : { filename : "img/[name].[hash:6][ext]" , }, parser : { dataUrlCondition : { maxSize : 25 * 1024 , }, }, }, { test : /\.(ttf|woff2?)$/ , type: "asset/resource" , generator : { filename : "font/[name].[hash:6][ext]" , }, }, { test : /\.js$/ , use: ["babel-loader" ], }, { test : /\.jsx$/ , use: ["babel-loader" ], }, ], }, plugins : [ new HtmlWebpackPlugin({ title : "Webpack Learning" , template : "./public/index.html" , }), new DefinePlugin({ BASE_URL : '"./"' , }), new CopyWebpackPlugin({ patterns : [ { from : "public" , globOptions : { ignore : ["**/index.html" ], }, }, ], }), ], };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const ReactRefreshWebpackPlugin = require ("@pmmmwh/react-refresh-webpack-plugin" );const { merge } = require ("webpack-merge" );const common = require ("./webpack.common" );module .exports = merge(common, { mode : "development" , devtool : "source-map" , devServer : { hot : true , port : 3000 , compress : true , open : true , }, plugins : [new ReactRefreshWebpackPlugin()], });
1 2 3 4 5 6 7 8 9 10 11 12 13 const path = require ("path" );const { merge } = require ("webpack-merge" );const common = require ("./webpack.common" );module .exports = merge(common, { mode : "production" , output : { filename : "[name].js" , path : path.join(__dirname, "../dist" ), clean : true , }, });
修改 babel.config.js,区分生产和开发环境:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const isDev = process.env.NODE_ENV !== "production" ;const config = { presets : [ [ "@babel/preset-env" , { useBuiltIns : "entry" , corejs : 3 , }, ], ["@babel/preset-react" ], ["@babel/preset-typescript" ], ], }; if (isDev) { config.plugins = [["react-refresh/babel" ]]; } module .exports = config;