31.proxy 代理设置
为什么开发阶段需要设置代理,在开发阶段,我们需要请求后端接口,但是一般后端接口地址和我们本地的不在同一个服务中提供,这时进行访问就会存在跨域的问题,所以我们需要对我们的请求进行转啊操作。模拟跨域请求代码如下:
https://api.github.com/users是github提供的公开接口,可正常请求
在React demo中,index.js使用axios进行请求。
import './title'
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App.jsx'
import axios from 'axios'
if (module.hot) {
module.hot.accept(['./title.js'], () => {
console.log('title.js模块更新')
})
}
ReactDOM.render(<App />, document.getElementById('app'))
axios.get('/api/users').then((res) => {
console.log(res.data)
})
由于该接口不存在跨域问题,这里默认他会存在这个问题
...
proxy: {
// /api/users
// http://localhost:4000/api/users
// https://api.github.com/info/users
// /api/users---> 返回
'/api': {
target: 'https://api.github.com',
pathRewrite: { "^/api": "" },
changeOrigin: true
}
}
...
首先,在devServer配置中添加proxy配置,添加/api
标记,当我们本地服务进行接口请求时,通过/api
标记会进入下面的配置中。
设置target属性,告诉Webpack-dev-server检测到该标记后,去请求那个路径(target: 'https://api.github.com'
)。配置过后,我们回到index.js中,修改请求的路径为/api/users
,这是发现依然无法请求成功,该接口依然抛出500服务端异常。
这是因为,github提供的接口下,并没有一个名为/api
的服务,所以需要对/api接口 进行路径重写,添加pathRewrite配置,将其值配置为pathRewrite: { "^/api": "" }
,告诉Webpack-dev-server遇到https://api.github.com/api
时,自动替换路径为https://api.github.com/
。
这时我们依然发现无法访问,这又是因为github对我们的请求来源进行校验,他拒绝了我们的请求。需要设置changeOrigin
属性,更改host
来源,changeOrigin: true
。
这时就可以正常访问该接口。
32.resolve 模块解析规则
配置模块如何解析。例如,当在 ES2015 中调用
import 'lodash'
,resolve
选项能够对 Webpack 查找'lodash'
的方式去做修改(查看模块
)。resolve文档
32.1 什么是 resolve 模块解析
在开发中我们会有各种各样的模块依赖,例如 js 文件、css 文件、vue 文件等,有自己编写的,也有第三方库。resolve 可以让 Webpack 在 require/import 语句中,找到需要解析的模块代码
32.2 配置自动寻找依赖的路径
模块路径:在 resolve.modules 中配置:到时导入下载好的依赖就会去 node_modules 文件夹里找
resolve: {
module: ["node_modules"], // 到时就会在 node_modules文件夹里面查找依赖包
}
拓展名配置:配置指定文件后就可以不写此文件的扩展名了
resolve: {
module: ["node_modules"], // 到时就会在 node_modules文件夹里面查找依赖包
extendsions: [".js", ".json", ".mjs", "vue"], // 添加了 vue 后导入 vue 文件就不需要加文件扩展名了
},
配置路径别名:为了简化相对路径的书写,我们直接配置路径给它一个别名:alias
我们需要在 index.js 导入 js 里面的 api.js 时,我们需要写成 ./js/api.js
我们希望写成 js/api ,配置如下:
resolve: {
modules: ["node_modules"], // 到时就会在 node_modules文件夹里面查找依赖包
extensions: [".js", ".json", ".mjs", "vue"], // 添加了 vue 后导入 vue 文件就不需要加文件扩展名了
alias: {
"js": path.resolve(__dirname, "./js"), // '以后可以使用 js 代替 ./js
"@": path.resolve(__dirname, "./src"), // @ 替换根目录
},
},
32.3 不同环境下的 Webpack 配置文件
我们在不同的环境下需要不同的配置,显然一个 Webpack.config.js 配置文件是不够的,在不同的环境使用不同的配置,比如我们在生产环境不需要 clearn-Webpack-plugin 的插件清理旧文件。
我们在根目录下新键文件夹:config,在里面新建 三个文件,一个是公共的配置、一个开发环境、一个生产环境。
配置:
先把 Webpack.config.js 文件内容复制一份去 comm.config.js 里, 然后按需提取至不同的配置文件。
把共同的文件留在 comm.config.js 里,使用插件再去各自合并
merge 插件:
安装:npm install Webpack-merge -D
使用:将公共配置和开发环境的配置结合在一起
const { merge } = require("Webpack-merge");
const commconfig = require("./Webpack.comm.config");
module.exports = merge(commconfig, {
开发环境的配置
});
注意相对路径的变化,除了有些路径的会默认根目录查找,其他正常路径需要修改。
完整代码演示:
33.source-map 作用
js变异之后生成的具体源码,然后再找回到编译之前的源代码的source-map操作。
代码目录如下:
顺带一提mode
33.1 模式(Mode)
提供 mode
配置选项,告知 Webpack 使用相应模式的内置优化。
string = 'production': 'none' | 'development' | 'production'
用法
只需在配置对象中提供 mode
选项:
module.exports = {
mode: 'development',
};
或者从 CLI 参数中传递:
Webpack --mode=development
支持以下字符串值:
选项 | 描述 |
---|---|
development | 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 development . 为模块和 chunk 启用有效的名。 |
production | 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production 。为模块和 chunk 启用确定性的混淆名称,FlagDependencyUsagePlugin ,FlagIncludedChunksPlugin ,ModuleConcatenationPlugin ,NoEmitOnErrorsPlugin 和 TerserPlugin 。 |
none | 不使用任何默认优化选项 |
如果没有设置,Webpack 会给 mode
的默认值设置为 production
。
如果
mode
未通过配置或 CLI 赋值,CLI 将使用可能有效的NODE_ENV
值作为mode
。
Mode: development
如果设置为development时,Webpack会自动帮我们加上devtool:'eval'
,这就是下图红框中的内容。
// Webpack.development.config.js
module.exports = {
mode: 'development',
};
Mode: production(默认值)
// Webpack.production.config.js
module.exports = {
mode: 'production',
};
Mode: none
// Webpack.custom.config.js
module.exports = {
mode: 'none',
};
如果要根据 Webpack.config.js 中的 mode 变量更改打包行为,则必须将配置导出为函数,而不是导出对象:
var config = {
entry: './app.js',
//...
};
module.exports = (env, argv) => {
if (argv.mode === 'development') {
config.devtool = 'source-map';
}
if (argv.mode === 'production') {
//...
}
return config;
};
当我们代码中出现了一些错误,但是我们并未设置source-map配置项,这就导致在浏览器中的报错,我们无法定位到具体报错的位置,如下图:
在配置了source-map的时候,本地进行yarn build打包过后,发现打包结果多了main.js.map
文件,同时main.js
中的内容也更利于查看。
source-map工作流程
- 根据源文件中的源代码,生成source-map文件
- 浏览器开启source-map功能,浏览器基于生成的source-map来进行查找
34.devtool 详细说明
此选项控制是否生成,以及如何生成 source map。
使用 SourceMapDevToolPlugin
进行更细粒度的配置。查看 source-map-loader
来处理已有的 source map。
devtool
string = 'eval'` `false
选择一种 source map 风格来增强调试过程。不同的值会明显影响到构建(build)和重新构建(rebuild)的速度。
Webpack 仓库中包含一个 显示所有
devtool
变体效果的示例。这些例子或许会有助于你理解这些差异之处。
你可以直接使用
SourceMapDevToolPlugin
/EvalSourceMapDevToolPlugin
来替代使用devtool
选项,因为它有更多的选项。切勿同时使用devtool
选项和SourceMapDevToolPlugin
/EvalSourceMapDevToolPlugin
插件。devtool
选项在内部添加过这些插件,所以你最终将应用两次插件。
devtool | performance | production | quality | comment |
---|---|---|---|---|
(none) | build: fastest rebuild: fastest | yes | bundle | Recommended choice for production builds with maximum performance. |
eval | build: fast rebuild: fastest | no | generated | Recommended choice for development builds with maximum performance. |
eval-cheap-source-map | build: ok rebuild: fast | no | transformed | Tradeoff choice for development builds. |
eval-cheap-module-source-map | build: slow rebuild: fast | no | original lines | Tradeoff choice for development builds. |
eval-source-map | build: slowest rebuild: ok | no | original | Recommended choice for development builds with high quality SourceMaps. |
cheap-source-map | build: ok rebuild: slow | no | transformed | |
cheap-module-source-map | build: slow rebuild: slow | no | original lines | |
source-map | build: slowest rebuild: slowest | yes | original | Recommended choice for production builds with high quality SourceMaps. |
inline-cheap-source-map | build: ok rebuild: slow | no | transformed | |
inline-cheap-module-source-map | build: slow rebuild: slow | no | original lines | |
inline-source-map | build: slowest rebuild: slowest | no | original | Possible choice when publishing a single file |
eval-nosources-cheap-source-map | build: ok rebuild: fast | no | transformed | source code not included |
eval-nosources-cheap-module-source-map | build: slow rebuild: fast | no | original lines | source code not included |
eval-nosources-source-map | build: slowest rebuild: ok | no | original | source code not included |
inline-nosources-cheap-source-map | build: ok rebuild: slow | no | transformed | source code not included |
inline-nosources-cheap-module-source-map | build: slow rebuild: slow | no | original lines | source code not included |
inline-nosources-source-map | build: slowest rebuild: slowest | no | original | source code not included |
nosources-cheap-source-map | build: ok rebuild: slow | no | transformed | source code not included |
nosources-cheap-module-source-map | build: slow rebuild: slow | no | original lines | source code not included |
nosources-source-map | build: slowest rebuild: slowest | yes | original | source code not included |
hidden-nosources-cheap-source-map | build: ok rebuild: slow | no | transformed | no reference, source code not included |
hidden-nosources-cheap-module-source-map | build: slow rebuild: slow | no | original lines | no reference, source code not included |
hidden-nosources-source-map | build: slowest rebuild: slowest | yes | original | no reference, source code not included |
hidden-cheap-source-map | build: ok rebuild: slow | no | transformed | no reference |
hidden-cheap-module-source-map | build: slow rebuild: slow | no | original lines | no reference |
hidden-source-map | build: slowest rebuild: slowest | yes | original | no reference. Possible choice when using SourceMap only for error reporting purposes. |
shortcut | explanation |
---|---|
performance: build | How is the performance of the initial build affected by the devtool setting? |
performance: rebuild | How is the performance of the incremental build affected by the devtool setting? Slow devtools might reduce development feedback loop in watch mode. The scale is different compared to the build performance, as one would expect rebuilds to be faster than builds. |
production | Does it make sense to use this devtool for production builds? It’s usually no when the devtool has a negative effect on user experience. |
quality: bundled | You will see all generated code of a chunk in a single blob of code. This is the raw output file without any devtooling support |
quality: generated | You will see the generated code, but each module is shown as separate code file in browser devtools. |
quality: transformed | You will see generated code after the preprocessing by loaders but before additional Webpack transformations. Only source lines will be mapped and column information will be discarded resp. not generated. This prevents setting breakpoints in the middle of lines which doesn’t work together with minimizer. |
quality: original lines | You will see the original code that you wrote, assuming all loaders support SourceMapping. Only source lines will be mapped and column information will be discarded resp. not generated. This prevents setting breakpoints in the middle of lines which doesn’t work together with minimizer. |
quality: original | You will see the original code that you wrote, assuming all loaders support SourceMapping. |
eval-* addition | generate SourceMap per module and attach it via eval. Recommended for development, because of improved rebuild performance. Note that there is a windows defender issue, which causes huge slowdown due to virus scanning. |
inline-* addition | inline the SourceMap to the original file instead of creating a separate file. |
hidden-* addition | no reference to the SourceMap added. When SourceMap is not deployed, but should still be generated, e. g. for error reporting purposes. |
nosources-* addition | source code is not included in SourceMap. This can be useful when the original files should be referenced (further config options needed). |
验证 devtool 名称时, 我们期望使用某种模式, 注意不要混淆 devtool 字符串的顺序, 模式是:
[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
.
其中一些值适用于开发环境,一些适用于生产环境。对于开发环境,通常希望更快速的 source map,需要添加到 bundle 中以增加体积为代价,但是对于生产环境,则希望更精准的 source map,需要从 bundle 中分离并独立存在。
查看
output.sourceMapFilename
自定义生成的 source map 的文件名。
品质说明(quality)
打包后的代码
- 将所有生成的代码视为一大块代码。你看不到相互分离的模块。
生成后的代码
- 每个模块相互分离,并用模块名称进行注释。可以看到 Webpack 生成的代码。示例:你会看到类似 var module__Webpack_IMPORTED_MODULE_1__ = __Webpack_require__(42); module__Webpack_IMPORTED_MODULE_1__.a();
,而不是 import {test} from "module"; test();
。
转换过的代码
- 每个模块相互分离,并用模块名称进行注释。可以看到 Webpack 转换前、loader 转译后的代码。示例:你会看到类似 import {test} from "module"; var A = function(_test) { ... }(test);
,而不是 import {test} from "module"; class A extends test {}
。
原始源代码
- 每个模块相互分离,并用模块名称进行注释。你会看到转译之前的代码,正如编写它时。这取决于 loader 支持。
无源代码内容
- source map 中不包含源代码内容。浏览器通常会尝试从 web 服务器或文件系统加载源代码。你必须确保正确设置 output.devtoolModuleFilenameTemplate
,以匹配源代码的 url。
(仅限行)
- source map 被简化为每行一个映射。这通常意味着每个语句只有一个映射(假设你使用这种方式)。这会妨碍你在语句级别上调试执行,也会妨碍你在每行的一些列上设置断点。与压缩后的代码组合后,映射关系是不可能实现的,因为压缩工具通常只会输出一行。
对于开发环境
以下选项非常适合开发环境:
eval
- 每个模块都使用 eval()
执行,并且都有 //# sourceURL
。此选项会非常快地构建。主要缺点是,由于会映射到转换后的代码,而不是映射到原始代码(没有从 loader 中获取 source map),所以不能正确的显示行数。
eval-source-map
- 每个模块使用 eval()
执行,并且 source map 转换为 DataUrl 后添加到 eval()
中。初始化 source map 时比较慢,但是会在重新构建时提供比较快的速度,并且生成实际的文件。行数能够正确映射,因为会映射到原始代码中。它会生成用于开发环境的最佳品质的 source map。
eval-cheap-source-map
- 类似 eval-source-map
,每个模块使用 eval()
执行。这是 “cheap(低开销)” 的 source map,因为它没有生成列映射(column mapping),只是映射行数。它会忽略源自 loader 的 source map,并且仅显示转译后的代码,就像 eval
devtool。
eval-cheap-module-source-map
- 类似 eval-cheap-source-map
,并且,在这种情况下,源自 loader 的 source map 会得到更好的处理结果。然而,loader source map 会被简化为每行一个映射(mapping)。
特定场景
以下选项对于开发环境和生产环境并不理想。他们是一些特定场景下需要的,例如,针对一些第三方工具。
inline-source-map
- source map 转换为 DataUrl 后添加到 bundle 中。
cheap-source-map
- 没有列映射(column mapping)的 source map,忽略 loader source map。
inline-cheap-source-map
- 类似 cheap-source-map
,但是 source map 转换为 DataUrl 后添加到 bundle 中。
cheap-module-source-map
- 没有列映射(column mapping)的 source map,将 loader source map 简化为每行一个映射(mapping)。
inline-cheap-module-source-map
- 类似 cheap-module-source-map
,但是 source mapp 转换为 DataUrl 添加到 bundle 中。
对于生产环境
这些选项通常用于生产环境中:
(none)
(省略 devtool
选项) - 不生成 source map。这是一个不错的选择。
source-map
- 整个 source map 作为一个单独的文件生成。它为 bundle 添加了一个引用注释,以便开发工具知道在哪里可以找到它。
你应该将你的服务器配置为,不允许普通用户访问 source map 文件!
hidden-source-map
- 与 source-map
相同,但不会为 bundle 添加引用注释。如果你只想 source map 映射那些源自错误报告的错误堆栈跟踪信息,但不想为浏览器开发工具暴露你的 source map,这个选项会很有用。
你不应将 source map 文件部署到 web 服务器。而是只将其用于错误报告工具。
nosources-source-map
- 创建的 source map 不包含 sourcesContent(源代码内容)
。它可以用来映射客户端上的堆栈跟踪,而无须暴露所有的源代码。你可以将 source map 文件部署到 web 服务器。
这仍然会暴露反编译后的文件名和结构,但它不会暴露原始代码。
如果默认的 Webpack
minimizer
被覆盖 (例如自定义terser-Webpack-plugin
选项), 请确保将其替换配置为sourceMap: true
以启用 SourceMap 支持。由上面了解到,设置为development后,默认的devtool为eval,这里我们将它更改为source-map
35.ts-loader 编译 TS
在项目开发中我们可能使用TypeScript
进行编码开发,所以需要使用Webpack
对TypeScript
进行编译,编译为JavaScript
文件。
首先对ts-loader
进行安装
yarn add ts-loader
安装完成后,查看项目目录及TypeScript
代码:
TypeScript
的编译可以使用TypeScript Compiler
进行编译:
tsc ./scr/index.ts
编译完成后,在src目录下生成index.js
文件。但是这样会存在问题,比如需要编译很多TypeScript文件,或者需要将打包好的文件放到指定的目录。
TypeScript
的编译可以使用ts-loader进行编译。
Webpack.config.js配置
const path = require('path')
const { DefinePlugin } = require('Webpack')
const { CleanWebpackPlugin } = require('clean-Webpack-plugin')
const HtmlWebpackPlugin = require('html-Webpack-plugin')
module.exports = {
mode: 'development',
entry: './src/index.ts',
devtool: 'nosources-source-map',
output: {
filename: 'js/main.js',
path: path.resolve(__dirname, 'dist')
},
target: 'web',
devServer: {
hot: true,
port: 4000
},
module: {
rules: [
{
test: /\.jsx?$/,
use: ['babel-loader']
},
{
test: /\.ts$/,
use: ['ts-loader']
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'copyWebpackPlugin',
template: './public/index.html'
}),
new DefinePlugin({
BASE_URL: '"./"'
})
]
}
// source-map cheap-module-source-map
编译完成后,在浏览器中可以正常执行。
36.babel-loader 编译 TS
当TypeScript
中存在较新的JavaScript
代码,比如Promise
,可以发现使用ts-loader
进行编译没有报错,但是在编译后的文件中,并没有对Promise
进行特殊的兼容性处理。所以需要使用babel-loader
对TypeScript
进行编译。
之前一直在使用@bebel/preset-env
,这里我们需要使用@bebel/preset-typescript
预设对TypeScript
进行编译兼容性处理。
安装@bebel/preset-typescript
yarn add @babel/preset-typescript
在babel.config.js中进行相应配置:
module.exports = {
presets: [
['@babel/preset-env', {
useBuiltIns: 'usage',
corejs: 3
}],
['@babel/preset-typescript']
]
}
再次打包编译后,发现编译后的main.js代码量大了很多,对Promise等ES6+的语法也做了相应的兼容。
ts-loader和@babel/preset-typescript的区别:
ts-loader
虽然不能在编译阶段进行polyfill
的填充,但是他可以在编译阶段,提前暴露出来语法错误问题。
反而babel-loader
可以进行polyfill
填充,但是在编译阶段它不可以进行提前暴露错误,只有在运行阶段时才抛出错误。
如果既想要对TypeScript进行语法转换又想要实时的暴露出错误,TypeScript官网也给出了建议,分先后,在打包之前对语法先做校验,完事之后再做build操作。
37.加载 vue 文件
Webpack
对.vue
文件加载操作:
创建App.vue,并安装vue
、vue-loader
、vue-template-compiler
。
首先,在index.js中导入vue
import Vue from 'vue'
import App from './App.vue'
new Vue({
render: h => h(App)
}).$mount('#app')
其次在Webpack.config.js中配置相应的loader
,由于vue文件中存在less
模式的css
样式,所以需要单独对css
文件进行loader
转换。
const path = require('path')
const { DefinePlugin } = require('Webpack')
const { CleanWebpackPlugin } = require('clean-Webpack-plugin')
const HtmlWebpackPlugin = require('html-Webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
mode: 'development',
entry: './src/index.js',
devtool: false,
output: {
filename: 'js/main.js',
path: path.resolve(__dirname, 'dist')
},
target: 'web',
devServer: {
hot: true,
port: 4000
},
module: {
rules: [
{
test: /\.less$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'postcss-loader',
'less-loader'
]
},
{
test: /\.jsx?$/,
use: ['babel-loader']
},
{
test: /\.ts$/,
use: ['babel-loader']
},
{
test: /\.vue$/,
use: ['vue-loader']
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'copyWebpackPlugin',
template: './public/index.html'
}),
new DefinePlugin({
BASE_URL: '"./"'
}),
new VueLoaderPlugin()
]
}
完成打包编译后,发现页面正常显示,样式文件也生效了。
38.区分打包环境
尝试为不同的工作环境以创建不同的Webpack配置。创建不同的环境配置的方式主要有两种:
-
第一种是在配置文件中添加相应的配置判断条件,根据环境的判断条件的不同导出不同的配置。
Webpack配置文件支持导出函数,函数中返回所需要的的配置对象,函数接受两个参数,第一个是env(cli传递的环境名参数),第二个是argv(运行cli过程中传递的所有参数)。可以借助这样一个特点来去实现不同的开发环境和生产环境分别返回不同的配置。
const Webpack = require('Webpack') const { CleanWebpackPlugin } = require('clean-Webpack-plugin') const HtmlWebpackPlugin = require('html-Webpack-plugin') const CopyWebpackPlugin = require('copy-Webpack-plugin') module.exports = (env, argv) => { const config = { mode: 'development', entry: './src/main.js', output: { filename: 'js/bundle.js' }, devtool: 'cheap-eval-module-source-map', devServer: { hot: true, contentBase: 'public' }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.(png|jpe?g|gif)$/, use: { loader: 'file-loader', options: { outputPath: 'img', name: '[name].[ext]' } } } ] }, plugins: [ new HtmlWebpackPlugin({ title: 'Webpack Tutorial', template: './src/index.html' }), new Webpack.HotModuleReplacementPlugin() ] } if (env === 'production') { config.mode = 'production' config.devtool = false config.plugins = [ ...config.plugins, // ES6将几个数组组合起来,生产环境下需要clean-Webpack-plugin和copy-Webpack-plugin new CleanWebpackPlugin(), new CopyWebpackPlugin(['public']) ] } return config }
命令行运行:yarn Webpack,当没有传递env参数时,Webpack会默认mode为开发阶段(development),对应的public下的文件不会被复制。
命令行运行:yarn Webpack --env production,传递env参数后,Webpack以生产环境(production)进行打包,额外的插件会工作,public目录下的文件会被复制。
这就是通过在导出函数中对环境进行判断,从而去实现为不同的环境倒出不同的配置,当然也可以直接在全局去判断环境变量,然后直接导出不同的配置,这样也是可以的。
-
第二种是为不同的环境单独添加一个配置文件,确保每一个环境下面都会有一个对应的配置文件。
通过判断环境参数数据返回不同的配对象,这种方式只适用于中小型项目。因为一旦项目变得复杂,配置文件也会一起变得复杂起来,所以说对于大型的项目,还是建议大家使用不同环境去对应不同配置文件的方式来实现。一般在这种方式下面,项目当中至少会有三个Webpack配置文件,其中两个(Webpack.dev.js/Webpack.prod.js)是用来适配不同的环境的,那另外一个是一个公共的配置(Webpack.common.js)。因为开发环境和生产环境并不是所有的配置都完全不同,所以说需要一个公共的文件来去抽象两者之间相同的配置。
项目目录:
Webpack.common.js
const HtmlWebpackPlugin = require('html-Webpack-plugin') module.exports = { entry: './src/main.js', output: { filename: 'js/bundle.js' }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.(png|jpe?g|gif)$/, use: { loader: 'file-loader', options: { outputPath: 'img', name: '[name].[ext]' } } } ] }, plugins: [ new HtmlWebpackPlugin({ title: 'Webpack Tutorial', template: './src/index.html' }) ] }
Webpack.dev.js
const Webpack = require('Webpack') const merge = require('Webpack-merge') const common = require('./Webpack.common') module.exports = merge(common, { mode: 'development', devtool: 'cheap-eval-module-source-map', devServer: { hot: true, contentBase: 'public' }, plugins: [ new Webpack.HotModuleReplacementPlugin() ] })
Webpack.prod.js
const merge = require('Webpack-merge') const { CleanWebpackPlugin } = require('clean-Webpack-plugin') const CopyWebpackPlugin = require('copy-Webpack-plugin') const common = require('./Webpack.common') module.exports = merge(common, { mode: 'production', plugins: [ new CleanWebpackPlugin(), new CopyWebpackPlugin(['public']) ] })
Webpack-merge提供了更加智能的配置合并,使用yarn add Webpack-merge --dev安装到生产环境中。将common中的配置分别于dev和prod组合,生产新的配置。
命令行运行
yarn Webpack --config Webpack.prod.js # --config用于指定配置文件 # 或者 yarn Webpack --config Webpack.dev.js
如果觉得使用命令行太过麻烦,也可以在package.json进行配置
"scripts": { "prod": "Webpack --config Webpack.prod.js", "dev": "Webpack --config Webpack.dev.js" },
随后命令行运行
yarn prod # 或者yarn dev
39.合并生产环境配置
代码目录如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9oGAU04h-1669024600612)(/media/202211/2022-11-16_083157_7965410.4980056499150869.png)]
通过package.json
中的scripts
命令,指定webpac
k打包的配置文件(build、build2)。
{
"name": "02_Webpack_config_start",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "Webpack",
"serve": "Webpack serve",
"build2": "Webpack --config ./config/Webpack.common.js --env production",
"serve2": "Webpack serve --config ./config/Webpack.common.js --env development"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/cli": "^7.14.8",
"@babel/core": "^7.15.0",
"@babel/plugin-transform-arrow-functions": "^7.14.5",
"@babel/plugin-transform-block-scoping": "^7.14.5",
"@babel/preset-env": "^7.15.0",
"@babel/preset-react": "^7.14.5",
"@pmmmwh/react-refresh-Webpack-plugin": "^0.4.3",
"autoprefixer": "^10.3.1",
"axios": "^0.21.1",
"babel-loader": "^8.2.2",
"clean-Webpack-plugin": "^4.0.0-alpha.0",
"copy-Webpack-plugin": "^9.0.1",
"css-loader": "^6.2.0",
"html-Webpack-plugin": "^5.3.2",
"less": "^4.1.1",
"less-loader": "^10.0.1",
"postcss": "^8.3.6",
"postcss-cli": "^8.3.1",
"postcss-loader": "^6.1.1",
"postcss-preset-env": "^6.7.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-refresh": "^0.10.0",
"react-router-dom": "^5.2.0",
"style-loader": "^3.2.1",
"Webpack": "^5.47.1",
"Webpack-cli": "^4.7.2",
"Webpack-dev-server": "^3.11.2",
"Webpack-merge": "^5.8.0"
},
"dependencies": {
"core-js": "^3.16.0",
"express": "^4.17.1",
"regenerator-runtime": "^0.13.9",
"Webpack-dev-middleware": "^5.0.0"
}
}
paths.js
const path = require('path')
const appDir = process.cwd()
const resolveApp = (relativePath) => {
return path.resolve(appDir, relativePath)
}
module.exports = resolveApp
Webpack.common.js
const resolveApp = require('./paths')
const HtmlWebpackPlugin = require('html-Webpack-plugin')
const { merge } = require('Webpack-merge')
// 导入其它的配置
const prodConfig = require('./Webpack.prod')
const devConfig = require('./Webpack.dev')
// 定义对象保存 base 配置信息
const commonConfig = {
entry: './src/index.js', // 反而没有报错( 相对路径 )
resolve: {
extensions: [".js", ".json", '.ts', '.jsx', '.vue'],
alias: {
'@': resolveApp('./src')
}
},
output: {
filename: 'js/main.js',
path: resolveApp('./dist')
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
esModule: false
}
},
'postcss-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
},
{
test: /\.(png|svg|gif|jpe?g)$/,
type: 'asset',
generator: {
filename: "img/[name].[hash:4][ext]"
},
parser: {
dataUrlCondition: {
maxSize: 30 * 1024
}
}
},
{
test: /\.(ttf|woff2?)$/,
type: 'asset/resource',
generator: {
filename: 'font/[name].[hash:3][ext]'
}
},
{
test: /\.jsx?$/,
use: ['babel-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin({
title: 'copyWebpackPlugin',
template: './public/index.html'
})
]
}
module.exports = (env) => {
const isProduction = env.production
// 依据当前的打包模式来合并配置
const config = isProduction ? prodConfig : devConfig
const mergeConfig = merge(commonConfig, config)
return mergeConfig
}
Webpack.dev.js
const path = require('path')
const CopyWebpackPlugin = require('copy-Webpack-plugin')
const { DefinePlugin } = require('Webpack')
const { CleanWebpackPlugin } = require('clean-Webpack-plugin')
const HtmlWebpackPlugin = require('html-Webpack-plugin')
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-Webpack-plugin')
module.exports = (env) => {
const isProduction = env.production
return {
mode: 'development',
devtool: false,
entry: './src/index.js',
resolve: {
extensions: [".js", ".json", '.ts', '.jsx', '.vue'],
alias: {
'@': path.resolve(__dirname, 'src')
}
},
output: {
filename: 'js/main.js',
path: path.resolve(__dirname, 'dist')
},
target: 'web',
devServer: {
hot: true,
hotOnly: true,
port: 4000,
open: false,
compress: true,
historyApiFallback: true,
proxy: {
'/api': {
target: 'https://api.github.com',
pathRewrite: { "^/api": "" },
changeOrigin: true
}
}
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
esModule: false
}
},
'postcss-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
},
{
test: /\.(png|svg|gif|jpe?g)$/,
type: 'asset',
generator: {
filename: "img/[name].[hash:4][ext]"
},
parser: {
dataUrlCondition: {
maxSize: 30 * 1024
}
}
},
{
test: /\.(ttf|woff2?)$/,
type: 'asset/resource',
generator: {
filename: 'font/[name].[hash:3][ext]'
}
},
{
test: /\.jsx?$/,
use: ['babel-loader']
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'copyWebpackPlugin',
template: './public/index.html'
}),
new DefinePlugin({
BASE_URL: '"./"'
}),
new CopyWebpackPlugin({
patterns: [
{
from: 'public',
globOptions: {
ignore: ['**/index.html']
}
}
]
}),
new ReactRefreshWebpackPlugin()
]
}
}
Webpack.prod.js
const CopyWebpackPlugin = require('copy-Webpack-plugin')
const { CleanWebpackPlugin } = require('clean-Webpack-plugin')
module.exports = {
mode: 'production',
plugins: [
new CleanWebpackPlugin(),
new CopyWebpackPlugin({
patterns: [
{
from: 'public',
globOptions: {
ignore: ['**/index.html']
}
}
]
})
]
}
40.合并开发环境配置
代码目录结构如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BVLkcq9h-1669024600614)(/media/202211/2022-11-16_083724_5479610.34202323107310295.png)]
同样通过package.json
中的scripts
指定Webpack
配置文件
package.json
{
"name": "02_Webpack_config_start",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "Webpack",
"serve": "Webpack serve",
"build2": "Webpack --config ./config/Webpack.common.js --env production",
"serve2": "Webpack serve --config ./config/Webpack.common.js --env development"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/cli": "^7.14.8",
"@babel/core": "^7.15.0",
"@babel/plugin-transform-arrow-functions": "^7.14.5",
"@babel/plugin-transform-block-scoping": "^7.14.5",
"@babel/preset-env": "^7.15.0",
"@babel/preset-react": "^7.14.5",
"@pmmmwh/react-refresh-Webpack-plugin": "^0.4.3",
"autoprefixer": "^10.3.1",
"axios": "^0.21.1",
"babel-loader": "^8.2.2",
"clean-Webpack-plugin": "^4.0.0-alpha.0",
"copy-Webpack-plugin": "^9.0.1",
"css-loader": "^6.2.0",
"html-Webpack-plugin": "^5.3.2",
"less": "^4.1.1",
"less-loader": "^10.0.1",
"postcss": "^8.3.6",
"postcss-cli": "^8.3.1",
"postcss-loader": "^6.1.1",
"postcss-preset-env": "^6.7.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-refresh": "^0.10.0",
"react-router-dom": "^5.2.0",
"style-loader": "^3.2.1",
"Webpack": "^5.47.1",
"Webpack-cli": "^4.7.2",
"Webpack-dev-server": "^3.11.2",
"Webpack-merge": "^5.8.0"
},
"dependencies": {
"core-js": "^3.16.0",
"express": "^4.17.1",
"regenerator-runtime": "^0.13.9",
"Webpack-dev-middleware": "^5.0.0"
}
}
paths.js
const path = require('path')
const appDir = process.cwd()
const resolveApp = (relativePath) => {
return path.resolve(appDir, relativePath)
}
module.exports = resolveApp
Webpack.common.js
const resolveApp = require('./paths')
const HtmlWebpackPlugin = require('html-Webpack-plugin')
const { merge } = require('Webpack-merge')
// 导入其它的配置
const prodConfig = require('./Webpack.prod')
const devConfig = require('./Webpack.dev')
// 定义对象保存 base 配置信息
const commonConfig = {
entry: './src/index.js', // 反而没有报错( 相对路径 )
resolve: {
extensions: [".js", ".json", '.ts', '.jsx', '.vue'],
alias: {
'@': resolveApp('./src')
}
},
output: {
filename: 'js/main.js',
path: resolveApp('./dist')
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
esModule: false
}
},
'postcss-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
},
{
test: /\.(png|svg|gif|jpe?g)$/,
type: 'asset',
generator: {
filename: "img/[name].[hash:4][ext]"
},
parser: {
dataUrlCondition: {
maxSize: 30 * 1024
}
}
},
{
test: /\.(ttf|woff2?)$/,
type: 'asset/resource',
generator: {
filename: 'font/[name].[hash:3][ext]'
}
},
{
test: /\.jsx?$/,
use: ['babel-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin({
title: 'copyWebpackPlugin',
template: './public/index.html'
})
]
}
module.exports = (env) => {
const isProduction = env.production
process.env.NODE_ENV = isProduction ? 'production' : 'development'
// 依据当前的打包模式来合并配置
const config = isProduction ? prodConfig : devConfig
const mergeConfig = merge(commonConfig, config)
return mergeConfig
}
Webpack.dev.js
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-Webpack-plugin')
module.exports = {
mode: 'development',
devtool: 'cheap-module-source-map',
target: 'web',
devServer: {
hot: true,
hotOnly: true,
port: 4000,
open: false,
compress: true,
historyApiFallback: true,
proxy: {
'/api': {
target: 'https://api.github.com',
pathRewrite: { "^/api": "" },
changeOrigin: true
}
}
},
plugins: [
new ReactRefreshWebpackPlugin()
]
}
Webpack.prod.js
const CopyWebpackPlugin = require('copy-Webpack-plugin')
const { CleanWebpackPlugin } = require('clean-Webpack-plugin')
module.exports = {
mode: 'production',
plugins: [
new CleanWebpackPlugin(),
new CopyWebpackPlugin({
patterns: [
{
from: 'public',
globOptions: {
ignore: ['**/index.html']
}
}
]
})
]
}
babel.config.js
const presets = [
['@babel/preset-env'],
['@babel/preset-react'],
]
const plugins = []
console.log(process.env.NODE_ENV, '<------')
// 依据当前的打包模式来决定plugins 的值
const isProduction = process.env.NODE_ENV === 'production'
if (!isProduction) {
plugins.push(['react-refresh/babel'])
}
module.exports = {
presets,
plugins
}