一、webpack构建流程
四个阶段:
初始化阶段:读取配置文件、创建complier实例
编译阶段:解析入口点、模块加载(loader)
打包阶段:模块合并与优化、代码分割
输出阶段:生成输出文件
1.配置文件解析:
Webpack读取并解析webpack.config.js文件,获取构建所需的配置信息。
2.入口处理:
Webpack从入口文件开始,构建模块依赖图。
3.模块加载与转换:
使用加载器处理不同类型的模块,如CSS、图片等,将它们转换为JavaScript模块。
4.插件执行:
插件在特定的生命周期钩子上运行,执行诸如优化、资源管理等任务。
5.打包优化:
代码分割、Tree Shaking等优化手段被应用,以减少文件大小并提高性能。
6.生成输出文件:
打包后的文件根据配置输出到指定目录。
二、webpack基础配置
Webpack是一个强大的模块打包工具,它可以通过配置来实现代码压缩、代码分割、模块热替换等多种功能。以下是一些Webpack的常用配置项:
1. entry
-
作用:指定Webpack的入口文件。
-
示例:
entry: './src/index.js',
2. output
-
作用:定义输出文件的配置,包括输出文件的名称、路径等。
-
示例:
output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist'), },
3. mode
-
作用:设置Webpack的运行模式,可以是
development
(开发模式)或production
(生产模式)。 -
示例:
mode: 'production',
-
development模式:提供开发时的必要功能,如source map、错误提示等。
4. module
-
作用:配置模块的加载器和转换规则,用于处理不同类型的文件。
-
module.rules
:配置各类文件的处理规则,test属性用户匹配文件路径,use属性指定使用的loader; -
示例:
module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: 'babel-loader', }, { test: /\.css$/, use: ['style-loader', 'css-loader'], }, ], },
5. resolve
-
作用:配置模块解析规则,可以设置别名、扩展名等,方便引入模块。
-
示例:
resolve: { alias: { '@': path.resolve(__dirname, 'src'), }, extensions: ['.js', '.json'], },
6. plugins
-
作用:使用插件来增强Webpack功能,例如压缩代码、拷贝文件等。
-
示例:
const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); plugins: [ new HtmlWebpackPlugin({ template: './src/index.html', }), new MiniCssExtractPlugin({ filename: '[name].css', }), ],
7.devServer
Webpack DevServer 是一个开发工具,它可以提供一个简单的 web 服务器,并且能够实时重新加载
contentBase
:告诉服务器从哪里提供内容,默认情况下,服务器会使用当前执行目录。
compress
:告诉服务器启用 gzip 压缩。
port
:指定要监听请求的端口号。
open
:告诉服务器自动打开浏览器。
hot
:启用模块热替换(Hot Module Replacement)。
8. optimization
:代码分割、压缩
-
作用:控制构建过程中的优化行为,如代码分割、压缩等。
-
示例:
optimization: { splitChunks: { chunks: 'all', }, minimize: true, },
在 Webpack 中,
optimization
配置用于控制构建过程中的优化行为。它可以帮助你实现代码分割、代码压缩、树摇(tree shaking)等功能,从而减小输出文件的大小和提高应用程序的性能。以下是一些常用的
optimization
配置选项:splitChunks
splitChunks
用于将公共代码(如第三方库、公共组件等)提取到单独的文件中,以实现代码分割和缓存复用。
optimization: { splitChunks: { chunks: 'all', // 对所有类型的代码进行分割 minSize: 20000, // 生成 chunk 的最小体积(以字节为单位) maxSize: 0, // 生成 chunk 的最大体积(以字节为单位),0 表示不限制 minChunks: 1, // 模块被引用的最小次数 maxAsyncRequests: 30, // 按需加载时的最大并行请求数 maxInitialRequests: 30, // 入口点的最大并行请求数 automaticNameDelimiter: '~', // 文件名的连接符 name: true, // 是否根据模块和缓存组的名称自动生成文件名 cacheGroups: { // 定义缓存组,用于控制代码分割的规则 vendors: { test: /[\\/]node_modules[\\/]/, // 匹配 node_modules 中的模块 priority: -10, // 优先级,值越大优先级越高 filename: 'vendors.[contenthash].js', // 输出文件名 }, default: { minChunks: 2, priority: -20, reuseExistingChunk: true, // 如果当前 chunk 包含已从主 bundle 中拆分出的模块,则将复用它,而不是生成新的模块 filename: 'common.[contenthash].js', // 输出文件名 }, }, }, },
runtimeChunk
runtimeChunk
用于将 Webpack 的运行时代码提取到一个单独的文件中,以实现运行时代码的复用。
optimization: { runtimeChunk: 'single', // 将运行时代码提取到一个单独的文件中 },
minimize
minimize
用于控制是否压缩输出文件。当设置为true
时,Webpack 会使用默认的压缩插件(如 TerserPlugin)来压缩 JavaScript 代码。
optimization: { minimize: true, // 启用压缩 },
minimizer
minimizer
用于自定义压缩插件。你可以在这里添加自定义的压缩插件,如 TerserPlugin(用于压缩 JavaScript 代码)、CssMinimizerPlugin(用于压缩 CSS 代码)等。
const TerserPlugin = require('terser-webpack-plugin'); const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); optimization: { minimize: true, minimizer: [ new TerserPlugin({ // TerserPlugin 的配置选项 }), new CssMinimizerPlugin({ // CssMinimizerPlugin 的配置选项 }), ], },
moduleIds
和chunkIds
moduleIds
和chunkIds
用于控制模块和 chunk 的 ID 生成方式。这可以帮助你优化输出文件的大小和提高构建速度。
optimization: { moduleIds: 'hashed', // 使用模块内容的哈希值作为模块 ID chunkIds: 'named', // 使用模块名称作为 chunk ID },
通过合理地配置
optimization
选项,你可以优化 Webpack 的构建过程,提高应用程序的性能。
通过合理地配置这些选项,你可以优化Webpack的构建过程,提高应用程序的性能和可维护性。
三、loader和plugin
loader
loader
:用于转换模块的工具。主要用于文件内容的转换。
-
babel-loader
处理js文件,将es6+代码转换为es5,通常与@babel/preset-env
一起使用; -
css-loader
和saas-loader
处理css和sass文件,miniCssExtractPlugin.loader
将css提取为单独文件; -
style-loader
将css插入到dom的<style>
标签中去; -
file-loader
处理文件(如图片、字体),并返回文件的url; -
url-loader
处理图片文件,8kb以下的图片转为base64,大于8kb的图片则拷贝到输出目录; -
自定义loader
:实质上是一个函数,接受源文件内容作为输入,并输出处理后的内容。可以通过this.callbak
返回结果;module.exports = function(source){ //自定义loader:将文件内容中的‘my’替换为‘我的’ const result = source.replace(/my/g,'我的'); return rusult; }
plugin
插件系统的实现:Plugin 是一个具有 apply 方法的对象,当 Webpack 启动构建时,会调用所有 Plugin 的 apply 方法,并将 compiler 对象作为参数传入。在 apply 方法中,Plugin 可以监听事件钩子,并执行自定义的操作。
plugin
:用于扩展webpack功能的工具,可以在webpack构建过程中执行更复杂的任务,如打包优化、资源管理、环境变量注入等。Plugin主要用于处理构建过程中的各种任务。
-
CleanWebpackPlugin
在每次打包前清理输出目录,防止旧文件残留; -
HtmlWebpackPlugin
根据模板生成Html文件,并自动注入打包后的js文件等; -
MiniCssExtractPlugin
将Css提取到单独的文件中; -
DefinePlugin
创建全局变量,在编译时进行替换; -
TerserPlugin
用于压缩js代码,主要在生产环境中使用; -
自定义Plugin
:plugin是一个类,包含apply方法,apply接受一个complier
对象,通过这个对象可以钩入webpack的各个构建阶段;class MyPlugin{ //编译完成后输出提示 apply(compiler){ compiler.hooks.done.tap('MyPlugin',(stats)=>{ console.log('编译完成!') }) } }
loader 和 plugin 的区别
- loader 是文件加载器,操作的是文件,将文件A通过loader转换成文件B,是一个单纯的文件转化过程;能够加载资源文件,并对这些文件进行一些处理,诸如编译、压缩等,最终一起打包到指定的文件中
- plugin即为插件,是一个扩展器,丰富webpack本身,增强功能 ,针对的是在loader结束之后,webpack打包的整个过程,他并不直接操作文件,而是基于事件机制工作,监听webpack打包过程中的某些节点,执行广泛的任务。 插件赋予了 webpack 各种灵活的功能,例如打包优化、资源管理、环境变量注入等,目的是解决 loader 无法实现的其他事
- loader 运行在打包文件之前
- plugins 在整个编译周期都起作用
四、打包性能优化
1、按需引入组件
例如引入 element-ui ,用到哪些组件就引哪些
import {Button,Dialog} from 'element-ui';
Vue.use(Button); // 按钮组件
Vue.use(Dialog); // 对话框组件
2、externals 属性
webpack的externals
属性,将公共的或不常改动的第三方包名称,配置在属性中,打包时会自动忽略当中的包。具体实现如下:
在 build/webpack.base.conf.js文件中:
module.exports = {
externals: {
Vue: 'Vue',
Axios: 'axios'
}
}
// 其中的 key--对应 import Axios名称,value--对应原始方法名称
需要在根目录,index.html 中引入一下
<script src="https://unpkg.com/vue@2.6.11/dist/vue.min.js"></script>
<script src="https://unpkg.com/axios@0.19.2/dist/axios.min.js"></script>
3、给定文件匹配范围
include 规定需要处理的文件有哪些
enclude 排除不需要处理的文件夹
{ test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src')],
exclude: /node_modules/
}
4、noParse 属性:过滤掉不需要解析的文件
{ module: {
noParse: /jquery/,
rule: [
...
]
}
}
5、cacheDirectory 缓存属性
babel-loader
提供了cacheDirectory
选项参数,默认为false。
设置空或true时,会利用系统的临时文件夹缓存经过 babel 处理好的模块,对于 rebuild js 有着非常大的性能提升。
{ test: /\.js$/,
loader: 'babel-loader?cacheDirectory',
include: [resolve('src')],
exclude: /node_modules/
}
6、happyPack 多个子进程并行
webpack 在打包过程中,loader 转化js、css、img等文件是同步进行的,一个一个的转换。
happyPack 的原理是,将这些任务分解到多个子进程中,并行执行,执行完成后把结果发送到主进程,从而减少整体的打包时间。
7、压缩文件
- 压缩JS文件
- Webpack 4.x以上版本内置了
uglifyjs-webpack-plugin
插件,会对JS文件自动压缩,不需要做其它的任何操作。也可以手动安装这个插件,设置一些另外的参数,比如开启并行压缩,加快打包的速度。 - 使用
TerserWebpackPlugin
来压缩 JavaScript 代码。
-
压缩CSS文件和图片
css-minimizer-webpack-plugin
minicssExtraPlugin
用于压缩 CSS 文件
ImageMinimizerWebpaxkPlugin
进行压缩图片压缩 -
压缩HTML文件
HtmlWebpackPlugin
插件除了可以帮助我们简化 HTML 文件的创建,也可以压缩 HTML 文件。首先,需要先安装
HtmlWebpackPlugin
插件:npm install --save-dev html-webpack-plugin
// webpack.config.js const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { plugins: [new HtmlWebpackPlugin()], };
如果不添加任何配置的话,会生成一个默认 index.html 文件,并自动注入所有的 chunk 和压缩。
也可以通过自定义配置参数,以下几个是常见的参数:
template
:模板的路径,默认会去寻找 src/index.ejs 是否存在。
filename
:输出文件的名称,默认为 index.html。
inject
:是否将资源注入到模版中,默认为 true。
minify
:压缩参数。在生产模式下(production),默认为 true;否则,默认为false。
如果 minify 为 true,生成的 HTML 将使用 html-minifier-terser 和以下选项进行压缩:{ collapseWhitespace: true, keepClosingSlash: true, removeComments: true, removeRedundantAttributes: true, removeScriptTypeAttributes: true, removeStyleLinkTypeAttributes: true, useShortDoctype: true }
五、Webpack和Vite区别
1、Webpack:是一个强大的静态模块打包工具,它可以将各种类型的文件,如JavaScript、CSS、图片等,作为模块进行打包,并生成最终的静态资源文件。Webpack使用各种loader和plugin来处理不同类型的文件,还可以进行代码分割、懒加载、压缩等优化操作。
2、Vite:是一个基于ES模块的快速开发工具,它利用浏览器原生的ES模块机制,将每个模块作为一个独立的请求来加载,而不是像Webpack那样把所有模块打包成一个文件。这样可以在开发过程中实现快速的热模块替换,减少构建时间,提高开发效率。
Vite的设计初衷是为了解决传统打包工具的一些问题,传统的打包工具在开发过程中会将所有的模块打包成一个或多个最终的捆绑文件,然后在浏览器环境中执行。这种方式在大型项目中可能会导致开发服务器启动慢,因为需要将所有的模块进行打包。Vite通过利用ES模块的特性,在开发过程中仅对需要的模块进行编译和构建,从而提升了开发服务器的启动速度。Vite还支持热模块替换。
总结
Webpack是一个成熟和功能强大的前端构建工具,提供了丰富的功能和配置选项。而Vite是一个新兴的前端构建工具,通过利用ES模块和HMR等特性,提供了更快的开发体验。两者都在前端项目中发挥着重要的作用,开发者可以根据项目需求选择适合的工具。Vite在开发阶段更加轻量级和高效,因为它不需要进行复杂的打包过程,只需简单地使用浏览器原生支持的ES模块加载机制。但是在生产环境下,还是需要使用类似Webpack这样的构建工具来进行打包和优化。
六、热更新
Webpack 配置中的 hot: true
是 webpack-dev-server
的一个选项,用于启用热更新(Hot Module Replacement,简称 HMR)。当设置为 true
时,webpack-dev-server
将尝试使用 HMR 更新应用程序,而不是刷新整个页面。
仅仅设置 hot: true
是不够的。为了使 HMR 正常工作,您还需要执行以下操作:
-
安装
webpack-dev-server
:确保已经通过 npm 或 yarn 安装了webpack-dev-server
。npm install webpack-dev-server --save-dev
-
配置
webpack.config.js
:在webpack.config.js
文件中,确保已经正确配置了devServer
选项,并将hot
设置为true
。module.exports = { // ... devServer: { hot: true, // 其他选项... }, // ... };
-
添加
HotModuleReplacementPlugin
插件:在webpack.config.js
文件的plugins
数组中,添加new webpack.HotModuleReplacementPlugin()
。const webpack = require('webpack'); module.exports = { // ... plugins: [ new webpack.HotModuleReplacementPlugin(), // 其他插件... ], // ... };
-
处理模块更新:在项目的入口文件(如
index.js
)中,添加以下代码以处理模块更新。(需要热更新的文件)if (module.hot) { module.hot.accept(); }
只有完成以上步骤,HMR 才能在项目中正常工作。