Webpack 5 超详细解读(四)

2023-11-20

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 会将 DefinePluginprocess.env.NODE_ENV 的值设置为 development. 为模块和 chunk 启用有效的名。
production 会将 DefinePluginprocess.env.NODE_ENV 的值设置为 production。为模块和 chunk 启用确定性的混淆名称,FlagDependencyUsagePluginFlagIncludedChunksPluginModuleConcatenationPluginNoEmitOnErrorsPluginTerserPlugin
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进行编码开发,所以需要使用WebpackTypeScript进行编译,编译为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-loaderTypeScript进行编译。

之前一直在使用@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,并安装vuevue-loadervue-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)。因为开发环境和生产环境并不是所有的配置都完全不同,所以说需要一个公共的文件来去抽象两者之间相同的配置。

    项目目录:

    image-20210106222819337

    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命令,指定webpack打包的配置文件(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
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Webpack 5 超详细解读(四) 的相关文章

随机推荐

  • 汽配企业如何利用MES管理系统优化生产流程

    随着汽车市场个性化需求不断增强 汽配企业急需数字化转型来满足客户需求 如何优化生产流程 提高生产效率和产品质量成为了关键问题 汽配企业MES管理系统可以通过数字化技术和智能化设备 优化生产流程 提高生产效率和产品质量 下面 我们将介绍汽配企
  • react(craco)移动端使用postcss-px-to-viewport自适应布局

    Craco 使用postcss px to viewport devDependencies craco craco 6 4 3 xianzhengquan postcss px 2 vw 0 0 1 postcss 8 3 0 peerD
  • java I/0操作 FileWriter类

    package filewriter p1 import java io FileWriter import java io IOException public class FileWriterDemo 获取当前系统的换行符 获取指定键指
  • ssh Forward X11

    参考链接 https www jianshu com p 24663f3491fa https www cnblogs com tsfh p 9022170 html https blog csdn net lvbian article d
  • Vue中修改浏览器图标Logo

    1 找到index html文件 2 修改这段代码中的图片 3 图标没有变化的话清除一下游览器缓存
  • linux查看根目录下所有文件夹大小的方法

    linux查看根目录下所有文件夹大小的方法如下 1 进入根目录 cd 2 使用命令 du sh 查看根目录下每个文件夹的大小3 进入占用空间比较大的文件夹 然后再使用2中命令查找大文件 原文地址 https zhidao baidu com
  • git的使用场景

    Git 是一种分布式版本控制系统 常用于管理软件开发过程中的代码 以下是 Git 的一些常见使用场景 代码版本控制 Git 可以跟踪代码的历史变更 并在必要时还原代码到以前的某个版本 分支管理 Git 可以创建多个分支 使得多个开发者能够并
  • 以太坊创始人Vitalik Buterin北京演讲:Casper与分片技术最新进展

    三言财经6月3日现场报道 在今天的以太坊技术及应用大会上 以太坊创始人Vitalik Buterin做了题为 Casper与分片技术最新进展 的主题演讲 V神在演讲中阐述了Casper和分片的技术流程 以及如何在系统中成为验证者验证节点 对
  • 中国中间件第一人---袁红岗

    最早开发Windows上的企业应用软件 打造独立知识产权的EJB服务器Apusus 很多JAVA程序员对袁红岗极其佩服 源于他做了很多人不敢想更不敢做的事情 这就是他打造了国产的EJB服务器 很快 金蝶将在国内推出自主产权EJB服务器的3
  • 小白也能快速学会的Micropython编译指南

    小白也能快速学会的Micropython编译指南 大家好 我是CSDN上的 上坂龍二 哦 今天给大家带来的是 如何快速一次成功地将Micropython和自己喜欢的模块编译进自己的Esp32固件中哦 事前准备 Python python的环
  • 【MySQL系列】--初识数据库

    个人主页 阿然成长日记 点击可跳转 个人专栏 数据结构与算法 C语言进阶 不能则学 不知则问 耻于问人 决无长进 文章目录 一 何为数据库 二 数据库的发展历程 三 数据库的分类 1 关系数据库 2 非关系型数据库 NoSQL 3 键值数据
  • 国军标 软件测评 静态分析常见问题总结

    违背国军标R x x x 禁止 define被重复定义 没有用 undef 解除前面的定义 违背国军标R 1 1 7 以函数形式定义的宏 参数和结果必须用括号括起来 违背国军标R 1 1 13 函数声明中必须对参数类型进行声明 并带有变量名
  • C/C++ n的阶乘 图解递归过程【简单易懂,代码可以直接运行】

    C C n的阶乘 简单易懂 代码可以直接运行 输入一个整数 n 请你编写一个函数 int fact int n 计算并输出 n 的阶乘 输入格式 共一行 包含一个整数 n 输出格式 共一行 包含一个整数表示 n 的阶乘的值 数据范围 1 n
  • 关于pom中mysql-connector-java的jar包引入高版本报错的解决过程

    如果你是类似下面的2点配置 1 jdbc properties文件 jdbc driver com mysql jdbc Driver jdbc url jdbc mysql localhost 3306 o2o characterEnco
  • python字典中如何添加键值对

    添加键值对 首先定义一个空字典 gt gt gt dic 直接对字典中不存在的key进行赋值来添加 gt gt gt dic name zhangsan gt gt gt dic name zhangsan 如果key或value都是变量也
  • Qt Win 10窗口毛玻璃效果

    直接看效果 标题 核心代码 HWND hWnd HWND winId HMODULE hUser GetModuleHandle L user32 dll if hUser pfnSetWindowCompositionAttribute
  • 网络编程知识

    网络编程知识 一 网络七层模型 OSI模型 OSI 模型 Open System Interconnection model 是一个由国际标准化组织 提出的概念模型 试图提供一个使各种不同的计算机和网络在世界范围内实现互联的标准框架 它将计
  • windows 远程ssh 登录linux 网络连接超时

    该方法适用于已经配置过的ssh服务 当电脑休眠 重启或关机再开机后windows ssh 远程登录ubuntu失败 此前都是正常使用 首先查看自己Ubuntu是否有网络 ifconfig一下 如果有 再去查看自己的ssh服务器是否开启 sy
  • 基于javaEE的图书管理系统

    极简的图书管理系统 无任何样式修饰 适合新手练手 图文并释 1 实现了用户注册 登录 图书的添加 修改 删除和修改操作 2 工具需要 eclipse mysql Tomcat 3 做系统之前在eclipse需要配置Tomcat服务器和导入m
  • Webpack 5 超详细解读(四)

    31 proxy 代理设置 为什么开发阶段需要设置代理 在开发阶段 我们需要请求后端接口 但是一般后端接口地址和我们本地的不在同一个服务中提供 这时进行访问就会存在跨域的问题 所以我们需要对我们的请求进行转啊操作 模拟跨域请求代码如下 ht