webpack基本使用以及相关配置

703 阅读13分钟

1. webpack是什么

Webpack是一个现代化的JavaScript应用程序打包工具,它可以将多个模块打包成一个或多个文件,使得应用程序的加载速度更快、代码更加优化。Webpack支持各种各样的文件类型,包括JavaScript、CSS、图片、字体等等。它还支持各种各样的插件和加载器,可以让开发者更加灵活地配置和定制打包过程。

2. webpack的基本使用

(1)初始化包环境

npm init
或者
yarn init 

初始化包环境我们就得到了一个 package.json 文件 , 该文件就是用来保存,运行脚本 , 依赖包 , 版本信息等等配置文件。

然后,我们需要在根目录下创建一个 webpack.config.js文件 ,该文件就是编写webpack的一些配置的文件 (2)安装webpack

npm install webpack webpack-cli webpack-dev-server -D

这其实是一条合并的命令,分开来写就是

npm install webpack -D
npm install webpack-cli -D
npm install webpack-dev-server -D
  • install 是安装的意思; -D 表示安装到本地开发环境,不使用全局安装是因为每个项目可能用的 webpack 版本不一样导致冲突

  • 第一条安装的是 webpack 的核心文件,就好比是安装包

  • 第二条是让 webpack 支持类似 npm run dev 这种命令行命令

  • 第三条安装的是可以使 webpack 支持实时编译的拓展包

(3)配置script

scripts: {
    "build": "webpack"
}

(4)运行打包命令

yarn build
#或者 npm run build

默认会打包src下的index.js

3. webpack的配置

3.1 基础配置

1、入口

入口指 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。

默认值是 ./src/index.js,但你可以通过配置 entry 属性,来指定一个(或多个)不同的入口起点。例如:

webpack.config.js

module.exports = {
  entry: './path/to/my/entry/file.js',
};

2、出口

出口属性告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。主要输出文件的默认值是 ./dist/main.js,其他生成文件默认放置在 ./dist 文件夹中。

你可以通过在配置中指定一个 output 字段,来配置这些处理过程:

webpack.config.js

const path = require('path');

module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js',
  },
};

在上面的示例中,我们通过 output.filename 和 output.path 属性,来告诉 webpack bundle 的名称,以及我们想要 bundle 生成(emit)到哪里

3、修改package.json

自定义打包命令 - 让webpack使用配置文件

"scripts": {
    "build": "webpack"
}

重点: 所有要被打包的资源都要跟入口产生直接/间接的引用关系

3.2 插件配置

loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。

想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建一个插件实例。

const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack'); // 用于访问内置插件

module.exports = {
  module: {
    rules: [{ test: /.txt$/, use: 'raw-loader' }],
  },
  plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
};

(1). HtmlWebpackPlugin:用于生成HTML文件,并自动将打包后的资源文件(如JS、CSS)添加到HTML中。它还提供了一些选项,例如模板、文件名等。配置示例:

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  // ...
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html', // HTML模板文件路径
      filename: 'index.html' // 生成的HTML文件名
    })
  ]
};
(

(2). MiniCssExtractPlugin:用于将CSS代码提取为独立的文件,并支持CSS文件的压缩和代码分割。配置示例

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  // ...
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css', // 提取的CSS文件名
      chunkFilename: '[id].css' // 代码分割的CSS文件名
    })
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader']
      }
    ]
  }
};

(3). CleanWebpackPlugin:用于清理构建目录中的旧文件,防止构建产生的垃圾文件堆积。配置示例:

const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  // ...
  plugins: [
    new CleanWebpackPlugin()
  ]
};

(4). DefinePlugin:用于在构建过程中定义全局变量,可以用于注入环境变量或设置全局常量。配置示例:

const webpack = require('webpack');

module.exports = {
  // ...
  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify('production'), // 注入环境变量
      'API_BASE_URL': JSON.stringify('https://api.example.com') // 设置全局常量
    })
  ]
};

(5). VueLoaderPlugin:用于解析Vue单文件组件(.vue文件),并将其转换为可被Webpack处理的模块。配置示例:

const { VueLoaderPlugin } = require('vue-loader');

module.exports = {
  // ...
  plugins: [
    new VueLoaderPlugin()
  ],
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      }
    ]
  }
};

3.3 mode模式

webpack中的mode是一个配置选项,用于指定构建的模式。它有三个可选的值:developmentproductionnone。其默认值为 production

  • development模式会将process.env.NODE_ENV的值设置为development。在这个模式下,webpack会开启一些针对开发环境的优化,例如添加注释、更易于调试的源映射等。同时,它也会启用一些用于开发的默认配置,如更快的构建速度和更好的开发体验。
  • production模式会将process.env.NODE_ENV的值设置为production。在这个模式下,webpack会启用一系列针对生产环境的优化和压缩策略,例如代码压缩、去除无用代码、作用域提升等。这能够最大程度地减小打包文件的体积,提高加载速度,并为生产环境提供最佳性能。
  • none模式意味着不启用任何默认配置。在这个模式下,你需要自己定义所有的优化和配置。

mode的配置方式非常简单。只需在webpack配置文件中添加如下代码:

module.exports = {
  mode: 'development' // 或 'production' 或 'none'
  // ...
};

3.4 webpack-dev-server

3.4.1 webpack-dev-server介绍

webpack-dev-server是Webpack提供的一个开发服务器,它能够实时监听文件的改动并自动重新构建项目,以便开发人员可以在开发过程中快速预览和调试应用程序的变化。

以下是webpack-dev-server的一些特点和配置方式:

实时刷新:webpack-dev-server使用WebSocket在浏览器和服务器之间创建一个实时连接,当文件发生改变时,它能够自动重新构建并刷新浏览器页面,使开发人员能够立即看到最新的更改。

热模块替换(HMR) :webpack-dev-server支持热模块替换,在不刷新整个页面的情况下,只更新发生更改的模块。这极大地提高了开发效率,可以在不丢失应用程序状态的情况下进行快速迭代开发。

配置方式:可以通过webpack配置文件或命令行选项进行配置。通过webpack配置文件的方式,可以在配置文件中设置devServer对象来配置webpack-dev-server。例如:

module.exports = {
  // ...
  devServer: {
    port: 8080, // 指定开发服务器的端口号,默认为8080
    contentBase: './dist', // 指定静态文件目录
    hot: true // 启用热模块替换
  }
};

此外,还可以使用命令行选项进行配置,例如npx webpack-dev-server --port 8080 --hot

代理:webpack-dev-server支持代理功能,可以将请求代理到其他服务器。这在开发过程中处理跨域资源请求时非常有用,可以通过配置proxy选项来设置代理。例如:

module.exports = {
  // ...
  devServer: {
    proxy: {
      '/api': {
        target: 'http://api.example.com',
        pathRewrite: { '^/api': '' }
      }
    }
  }
};

上述配置表示将以/api开头的请求代理到http://api.example.com,并将/api路径重写为空。

3.4.2 安装webpack-dev-server

npm install --save-dev webpack-dev-server

3.4.3 修改配置文件

告知 dev server,从什么位置查找文件: webpack.config.js

const path = require('path');
 const HtmlWebpackPlugin = require('html-webpack-plugin');

 module.exports = {
   mode: 'development',
   entry: {
     index: './src/index.js',
     print: './src/print.js',
   },
   devtool: 'inline-source-map',
+  devServer: {
+    static: './dist',
+  },
   plugins: [
     new HtmlWebpackPlugin({
       title: 'Development',
     }),
   ],
   output: {
     filename: '[name].bundle.js',
     path: path.resolve(__dirname, 'dist'),
     clean: true,
   },
+  optimization: {
+    runtimeChunk: 'single',
+  },
 };

因为在示例中单个 HTML 页面有多个入口,所以添加了 optimization.runtimeChunk: 'single' 配置。没有这个配置的话,我们可能会遇到问题,具体配置根据项目进行调整。

package.json

{
   "name": "webpack-demo",
   "version": "1.0.0",
   "description": "",
   "private": true,
   "scripts": {
     "test": "echo "Error: no test specified" && exit 1",
     "watch": "webpack --watch",
+    "start": "webpack serve --open",
     "build": "webpack"
   },
   "keywords": [],
   "author": "",
   "license": "ISC",
   "devDependencies": {
     "html-webpack-plugin": "^4.5.0",
     "webpack": "^5.4.0",
     "webpack-cli": "^4.2.0",
     "webpack-dev-server": "^3.11.0"
   },
   "dependencies": {
     "lodash": "^4.17.20"
   }
 }

3.5 常见问题

3.5.1 CSS文件未生效或样式丢失

如果在项目中的CSS文件未应用或样式丢失,可能原因是配置有误或加载顺序不正确。解决方法包括:

  • 确保在Webpack配置中正确配置了CSS相关的loader,例如css-loader和style-loader。
  • 检查Webpack Loader的加载顺序,确保style-loader在css-loader之前。
  • 对于使用MiniCssExtractPlugin提取CSS文件的情况,确保MiniCssExtractPlugin.loader在其他loader之前,并在插件配置中设置正确的输出文件名。

使用css-loader和style-loader

yarn add style-loader css-loader -D

css-loader负责解析CSS文件。而style-loader将解析后的CSS以<style>标签的形式插入到HTML文件中。这种方式适用于较小的项目或开发环境,配置示例:

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  }
};

使用MiniCssExtractPlugin:MiniCssExtractPlugin可以将CSS提取为独立的文件,而不是以<style>标签的形式插入到HTML中,适用于生产环境。配置示例:

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader'
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: '[id].css'
    })
  ]
};

3.5.2 处理CSS预处理器出错

处理预处理器(例如Sass、Less)

yarn add less less-loader -D

如果你使用Sass、Less等预处理器,需要安装对应的loader和插件,并在Webpack配置中进行相应的配置。以处理Sass为例,需要安装sass-loader和node-sass模块,然后修改配置:

module.exports = {
  module: {
    rules: [
      {
        test: /\.scss$/,
        use: [
          'style-loader',
          'css-loader',
          'sass-loader'
        ]
      }
    ]
  }
};

这些配置可以根据具体需求和项目情况进行调整和扩展。

3.5.3 引用的图片或字体文件无法正确加载

当CSS文件中引用了图片或字体文件时,可能会出现路径错误或文件无法加载的问题。解决方法包括:

  • 在Webpack配置的module.rules中添加相应的loader,例如file-loader或url-loader来处理图片和字体文件的引用,并配置正确的路径解析规则。
  • 在CSS文件中使用正确的相对路径或URL来引用图片或字体文件,确保路径正确。
yarn add url-loader file-loader -D
    {
  test: /\.(png|jpg|gif|jpeg)$/i,
  use: [
    {
      loader: 'url-loader', // 匹配文件, 尝试转base64字符串打包到js中
      // 配置limit, 超过8k, 不转, file-loader复制, 随机名, 输出文件
      options: {
        limit: 8 * 1024,
      },
    },
  ],
}

url-loader 把文件转base64 打包进js中, 会有30%的增大, file-loader 把文件直接复制输出

3.5.4 处理字体文件

Webpack可以通过使用合适的loader来处理字体文件。以下是处理常见字体文件的示例配置:

处理WOFF文件

module.exports = {
  module: {
    rules: [
      {
        test: /.woff$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              outputPath: 'fonts/', // 输出路径
            },
          },
        ],
      },
    ],
  },
};

你可以使用file-loader来处理WOFF文件,并将它们复制到指定的输出目录中。

处理TTF、EOT和SVG文件

module.exports = {
  module: {
    rules: [
      {
        test: /.(ttf|eot|svg)$/,
        use: {
          loader: 'file-loader',
          options: {
            name: '[name].[ext]',
            outputPath: 'fonts/',
          },
        },
      },
    ],
  },
};

这个配置使用file-loader处理TTF、EOT和SVG文件,并将它们保存到指定的输出目录中。

根据你的项目需求和字体文件类型,你可以按照上述示例进行适当修改和扩展。另外,你还可以使用url-loader来将字体文件转换为Data URI,并在CSS中内联使用,以减少HTTP请求。这样可以减小字体文件的加载时间,但会增加CSS文件的体积。

注意,在使用上述配置之前,确保你已经安装了相应的loader,例如file-loader或url-loader。另外,你也可以根据需要设置不同的选项,如输出路径、文件名等。

3.5.5 处理高版本的JavaScript语法

要让Webpack处理高版本的JavaScript语法,可以使用Babel来转换和兼容这些语法。以下是处理高版本JavaScript语法的示例配置:

安装Babel依赖:

npm install --save-dev @babel/core @babel/preset-env babel-loader

这会安装@babel/core作为Babel的核心库,@babel/preset-env用于转换高版本语法,以及babel-loader用于在Webpack中使用Babel。

在Webpack配置中,添加Babel的loader规则:

module.exports = {
  module: {
    rules: [
      {
        test: /.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      }
    ]
  }
};

这个配置会告诉Webpack在处理JavaScript文件时,使用Babel进行转换。.babelrc文件也可以用于存放Babel的配置信息。

创建.babelrc文件并配置Babel的转换规则:

{
  "presets": ["@babel/preset-env"]
}

这里使用@babel/preset-env预设来兼容目标环境中所需的JavaScript语法和功能。

运行Webpack进行构建,Babel会根据配置转换和兼容高版本的JavaScript语法。

请注意,转换和兼容高版本JavaScript语法需要根据目标浏览器或环境的要求进行配置。你可以在.babelrc文件或babel-loader的选项中,通过设置targets来指定需要兼容的环境。例如,"targets": "> 0.5%, not dead"表示兼容市场份额大于0.5%的浏览器,并不包括不再活跃的浏览器。

3.5.6 常见优化

在Webpack打包过程中,可能会遇到一些常见的问题。以下是一些可能出现的问题以及解决方法:

打包速度慢:当项目较大或使用复杂的配置时,Webpack打包过程可能会变得缓慢。解决方法包括:

  • 使用Webpack的生产模式(mode: 'production'),以启用代码压缩和优化,提高打包速度。
  • 使用Webpack的多线程或并行构建工具,如thread-loader或HappyPack,以并行处理多个任务,提高打包效率。
  • 优化Webpack配置,减少不必要的处理和重复构建,例如使用cache-loader进行缓存或合理设置excludeinclude选项。

体积过大:生成的打包文件体积过大可能会导致加载时间延长,降低用户体验。解决方法包括:

  • 使用Webpack的优化功能,如代码分割 (splitChunks)、压缩插件(TerserWebpackPlugin、OptimizeCSSAssetsPlugin)、Tree Shaking等,减小打包体积。
  • 检查项目中是否存在未使用的依赖,删除或优化掉未使用的代码。
  • 按需加载页面或模块,使用动态导入(Dynamic Import)或懒加载技术,减少初始加载的资源量。

依赖冲突:当项目中使用的第三方库存在版本冲突或兼容性问题时,可能会导致构建错误或运行时错误。解决方法包括:

  • 升级或降级冲突的依赖项版本,使其兼容。
  • 使用Webpack的resolve选项指定正确的模块解析路径和顺序,以确保正确的依赖被加载。
  • 使用alias选项为某些依赖项创建别名,避免与其他依赖项的命名冲突。

缓存问题:Webpack使用了文件哈希来生成唯一的文件名,但有时可能会遇到缓存问题,导致旧文件被缓存而无法及时更新。解决方法包括:

  • 使用contenthash代替hash作为输出文件名中的哈希部分,以避免更改其他文件时触发全部文件的重新缓存。
  • 使用Webpack的插件,如CleanWebpackPluginHashedModuleIdsPlugin,清除旧的构建文件和模块 ID 的哈希值,以确保文件名和缓存一致性。

4. webpack5的模块联邦

4.1 Webpack5模块联邦是什么

Webpack 5 的模块联邦(Module Federation)是一项功能强大的技术,它允许将多个独立的应用程序或团队的代码组合在一起,实现跨应用程序的模块共享和交互。

4.2 模块联邦的优点

模块联邦的主要思想是将一个应用程序的模块(被称为提供者)暴露给另一个应用程序(被称为消费者)使用,而不需要将所有代码打包在一起。下面是模块联邦的一些优点:

1. 模块共享与代码复用:  模块联邦允许应用程序共享模块,避免重复编写相同的代码。提供者应用程序可以将某些模块暴露给其他应用程序作为共享功能,消费者应用程序可以直接使用这些模块,提高了代码复用性和开发效率。

2. 独立构建和部署:  模块联邦使每个应用程序可以独立进行构建和部署。它们可以具有自己的构建系统和独立的开发流程,而不需要将所有依赖项打包在一起。这样可以提高开发和部署的灵活性和效率。

3. 动态加载和远程模块:  模块联邦支持动态加载模块,应用程序可以在运行时根据需要从其他应用程序中动态加载模块。这也可以与远程服务器结合使用,从远程服务器获取并使用模块。这种动态加载的能力为构建分布式的应用程序提供了更大的灵活性。

4. 管理和隔离依赖:  模块联邦支持对依赖项的版本管理和隔离。每个应用程序可以有自己的依赖版本,从而避免版本冲突和兼容性问题。这有助于管理复杂的项目,保持不同应用程序之间的依赖关系清晰和可控。

4.3 为什么要使用模块联邦

模块联邦提供了一种更灵活、更可扩展的方式来构建应用程序。它适用于大型项目、多个团队或多个应用程序之间的协作开发。使用模块联邦可以提高代码复用性、加速开发速度、降低维护成本,并支持构建分布式、组合性强的应用程序。它使得应用程序之间的边界更加模糊,促进了更好的团队合作和代码共享。

4.4 模块联邦的配置

在Webpack 5的模块联邦中,有两个核心概念:提供者(Provider)和消费者(Consumer)

提供者(Provider):  提供者是一个Webpack构建的应用程序,它通过模块联邦将自己的模块暴露给其他应用程序使用。提供者通常包含一些希望共享的功能或代码模块。通过配置Webpack的Module Federation插件,提供者可以将这些模块暴露出去,使其他应用程序可以使用它们。

提供者需要将自己的模块通过模块联邦插件中的exposes选项进行配置,指定要提供的模块。它会生成一个模块清单(例如 remoteEntry.js),包含可暴露模块的路径和信息。其他应用程序可以通过远程加载提供者的模块清单,然后动态加载并使用提供者的模块。

消费者(Consumer):  消费者是另一个Webpack构建的应用程序,它希望使用提供者暴露的模块。消费者通过配置Webpack的Module Federation插件,告诉Webpack要使用哪些远程模块。

消费者需要指定远程模块的名称和模块清单文件(例如 remoteEntry.js)的URL。在消费者应用程序的代码中,可以通过动态加载(如import()require.ensure())提供者的模块,并在需要时使用这些模块。消费者可以以与本地模块相同的方式使用提供者的模块,从而实现代码共享和功能复用。

通过提供者和消费者的配合,模块联邦实现了应用程序之间的模块共享和交互,提供了更大的灵活性和可扩展性。不同应用程序可以独立构建和部署,并共享模块,实现代码复用和协作开发。

在Webpack 5中配置模块联邦需要以下步骤:

提供者(Provider)配置步骤:

在提供者的Webpack配置中添加experiments选项,启用模块联邦:

experiments: {
  module: true,
},

在提供者的Webpack配置中,使用ContainerPlugin插件,将要共享的模块暴露给其他应用程序:

const { ModuleFederationPlugin } = require('webpack').container;

plugins: [
  //...
  new ModuleFederationPlugin({
    name: 'myApp',
    filename: 'remoteEntry.js',
    exposes: {
      './sharedModule': './src/sharedModule',
    },
  }),
],

其中:

  • name 是提供者应用程序的名称。
  • filename 是用于存储提供者的模块清单的文件名。
  • exposes 是要共享的模块列表。

执行提供者应用程序的构建命令,生成模块清单(remoteEntry.js)。

消费者(Consumer)配置步骤:

在消费者的Webpack配置中,同样启用实验特性 experiments

experiments: {
  module: true,
},

在消费者的Webpack配置中,使用ContainerPlugin插件,指定提供者应用程序的信息和要使用的模块:

const { ModuleFederationPlugin } = require('webpack').container;

plugins: [
  //...
  new ModuleFederationPlugin({
    name: 'myApp',
    remotes: {
      remoteApp: 'remoteApp@http://localhost:3001/remoteEntry.js',
    },
  }),
],

其中:

  • name 是消费者应用程序的名称。
  • remotes 是远程应用程序列表,包含远程应用程序名称和模块清单所在的URL。

在消费者应用程序的代码中,通过import()require.ensure()等方式来动态加载和使用提供者的模块:

import('remoteApp/sharedModule')
  .then((module) => {
    // 使用提供者的模块
  })
  .catch((error) => {
    // 处理加载失败的情况
  });

配置完成后,通过构建提供者和消费者应用程序,就可以实现模块联邦的功能。

4.5 可能会遇到的问题

  1. 版本兼容性问题:不同应用程序使用不同的依赖版本可能导致冲突和兼容性问题。需要确保依赖的版本符合要求,并避免不同版本的依赖共存。
  2. 网络请求问题:在加载远程模块时,需要确保提供者应用程序的模块清单文件(remoteEntry.js)能够通过网络访问,并确保网络连接的稳定性。
  3. 调试和错误处理:由于模块联邦涉及到不同应用程序之间的交互,可能会导致一些调试和错误处理上的复杂性。需要进行适当的错误处理,以及良好的调试和日志记录机制。
  4. 性能问题:模块联邦涉及远程加载模块,可能会对性能产生一定影响。需要考虑模块的大小和依赖关系,以及网络环境和优化策略,以确保性能表现良好。