万字长文详解webpack知识图谱

webpack概念
概念
Webpack 是一种用于构建 JavaScript 应用程序的静态模块打包器,它能够以一种相对一致且开放的处理方式,加载应用中的所有资源文件(图片、CSS、视频、字体文件等),并将其合并打包成浏览器兼容的 Web 资源文件。
功能
- 模块的打包:通过打包整合不同的模块文件保证各模块之间的引用和执行
- 代码编译:通过丰富的
loader可以将不同格式文件如.sass/.vue/.jsx转译为浏览器可以执行的文件 - 扩展功能:通过社区丰富的
plugin可以实现多种强大的功能,例如代码分割、代码混淆、代码压缩、按需加载等等
entry
单入口
将源文件加入到 webpack 构建流程,可以是单入口
module.exports = {entry: `./index.js`,
}
构建包名称 [name]为 main
多入口
也可以是多入口
module.exports = {entry: { "index": `./index.js`,"product":`./product.js`,},
}
key:value 键值对的形式:
- key:构建包名称,即
[name],在这里为index - value:入口路径
入口决定 webapck 从哪个模块开始生成依赖关系图(构建包),每一个入口文件都对应着一个依赖关系图。
动态配置入口文件
动态打包所有子项目
当构建项目包含多个子项目时,每次增加一个子系统都需要将入口文件写入 webpack 配置文件中,其实我们让 webpack 动态获取入口文件
// 使用 glob 等工具使用若干通配符,运行时获得 entry 的条目
module.exports = {entry: glob.sync('./project/**/index.js').reduce((acc, path) => {const entry = path.replace('/index.js', '')acc[entry] = pathreturn acc}, {}),
}
则会将所有匹配 ./project/**/index.js 的文件作为入口文件进行打包,如果你想要增加一个子项目,仅仅需要在 project 创建一个子项目目录,并创建一个 index.js 作为入口文件即可。
这种方式比较适合入口文件不集中且较多的场景。
动态打包某一子项目
在构建多系统应用或组件库时,我们每次打包可能仅仅需要打包某一模块,此时,可以通过命令行的形式请求打印某一模块
npm run build --project components
在打包的时候解析命令行参数:
// 解析命令行参数
const argv = require('minimist')(process.argv.slice(2))
// 项目
const project = argv['project'] || 'index'
然后配置入口:
module.exports = {entry: { "index": `./${project}/index.js`,}
}
相当于:
module.exports = {entry: { "index": `./components/index.js`,}
}
output
用于告知 webpack 如何构建编译后的文件,可以自定义输出文件的位置和名称:
module.exports = {output: { // path 必须为绝对路径// 输出文件路径path: path.resolve(__dirname, '../../dist/build'),// 包名称filename: "[name].bundle.js",// 或使用函数返回名(不常用)// filename: (chunkData) => {// return chunkData.chunk.name === 'main' ? '[name].js': '[name]/[name].js';// },// 块名,公共块名(非入口)chunkFilename: '[name].[chunkhash].bundle.js',// 打包生成的 index.html 文件里面引用资源的前缀// 也为发布到线上资源的 URL 前缀// 使用的是相对路径,默认为 ''publicPath: '/', }
}
文件指纹策略
对于我们开发的每一个应用,浏览器都会对静态资源进行缓存,如果我们更新了静态资源,而没有更新静态资源名称(或路径),浏览器就可能因为缓存的问题获取不到更新的资源。
Webpack 文件指纹策略是将文件名后面加上 hash 值。特别在使用 CDN 的时候,缓存是它的特点与优势,但如果打包的文件名,没有 hash 后缀的话,你肯定会被缓存折磨的够呛
在定义包名称(例如 chunkFilename 、 filename ),我们一般会用到哈希值,不同的哈希值使用的场景不同
hash
build-specific , 哈希值对应每一次构建( Compilation ),即每次编译都不同,即使文件内容都没有改变,并且所有的资源都共享这一个哈希值,此时,浏览器缓存就没有用了,可以用在开发环境,生产环境不适用。
chunkhash
chunk-specific , 哈希值对应于 webpack 每个入口点,每个入口都有自己的哈希值。如果在某一入口文件创建的关系依赖图上存在文件内容发生了变化,那么相应入口文件的 chunkhash 才会发生变化,适用于生产环境
contenthash
content-specific ,根据包内容计算出的哈希值,只要包内容不变, contenthash 就不变,适用于生产环境
webpack 也允许哈希的切片。如果你写 [hash:8] ,那么它会获取哈希值的前 8 位。
打包成库
当使用 webapck 构建一个可以被其它模块引用的库时:
module.exports = {output: { // path 必须为绝对路径// 输出文件路径path: path.resolve(__dirname, '../../dist/build'),// 包名称filename: "[name].bundle.js",// 块名,公共块名(非入口)chunkFilename: '[name].[chunkhash].bundle.js',// 打包生成的 index.html 文件里面引用资源的前缀// 也为发布到线上资源的 URL 前缀// 使用的是相对路径,默认为 ''publicPath: '/', // 一旦设置后该 bundle 将被处理为 librarylibrary: 'webpackNumbers',// export 的 library 的规范,有支持 var, this, commonjs,commonjs2,amd,umdlibraryTarget: 'umd',}
}
配置模式 mode
设置 mode ,可以让 webpack 自动调起相应的内置优化
module.exports = {// 可以是 none、development、production// 默认为 productionmode: 'production'
}
或在命令行里配置:
"build:prod": "webpack --config config/webpack.prod.config.js --mode production"
在设置了 mode 之后, webpack 会同步配置 process.env.NODE_ENV 为 development 或 production 。
production
配置:
// webpack.prod.config.js
module.exports = {mode: 'production',
}
相当于默认内置了:
// webpack.prod.config.js
module.exports = {performance: {// 性能设置,文件打包过大时,会报警告hints: 'warning'},output: {// 打包时,在包中不包含所属模块的信息的注释pathinfo: false},optimization: {// 不使用可读的模块标识符进行调试namedModules: false,// 不使用可读的块标识符进行调试namedChunks: false,// 设置 process.env.NODE_ENV 为 productionnodeEnv: 'production',// 标记块是否是其它块的子集// 控制加载块的大小(加载较大块时,不加载其子集)flagIncludedChunks: true,// 标记模块的加载顺序,使初始包更小occurrenceOrder: true,// 启用副作用sideEffects: true,// 确定每个模块的使用导出,// 不会为未使用的导出生成导出// 最小化的消除死代码// optimization.usedExports 收集的信息将被其他优化或代码生成所使用usedExports: true,// 查找模块图中可以安全的连接到其它模块的片段concatenateModules: true,// SplitChunksPlugin 配置项splitChunks: {// 默认 webpack4 只会对按需加载的代码做分割chunks: 'async',// 表示在压缩前的最小模块大小,默认值是30kbminSize: 30000,minRemainingSize: 0,// 旨在与HTTP/2和长期缓存一起使用 // 它增加了请求数量以实现更好的缓存// 它还可以用于减小文件大小,以加快重建速度。maxSize: 0,// 分割一个模块之前必须共享的最小块数minChunks: 1,// 按需加载时的最大并行请求数maxAsyncRequests: 6,// 入口的最大并行请求数maxInitialRequests: 4,// 界定符automaticNameDelimiter: '~',// 块名最大字符数automaticNameMaxLength: 30,cacheGroups: { // 缓存组vendors: {test: /[\\/]node_modules[\\/]/,priority: -10},default: {minChunks: 2,priority: -20,reuseExistingChunk: true}}},// 当打包时,遇到错误编译,将不会把打包文件输出// 确保 webpack 不会输入任何错误的包noEmitOnErrors: true,checkWasmTypes: true,// 使用 optimization.minimizer || TerserPlugin 来最小化包minimize: true,},plugins: [// 使用 terser 来优化 JavaScriptnew TerserPlugin(/* ... */),// 定义环境变量new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }),// 预编译所有模块到一个闭包中,提升代码在浏览器中的执行速度new webpack.optimize.ModuleConcatenationPlugin(),// 在编译出现错误时,使用 NoEmitOnErrorsPlugin 来跳过输出阶段。// 这样可以确保输出资源不会包含错误new webpack.NoEmitOnErrorsPlugin()]
}
development
// webpack.dev.config.js
module.exports = {mode: 'development',
}
相当于默认内置了:
// webpack.dev.config.js
module.exports = {devtool: 'eval',cache: true,performance: {// 性能设置,文件打包过大时,不报错和警告,只做提示hints: false},output: {// 打包时,在包中包含所属模块的信息的注释pathinfo: true},optimization: {// 使用可读的模块标识符进行调试namedModules: true,// 使用可读的块标识符进行调试namedChunks: true,// 设置 process.env.NODE_ENV 为 developmentnodeEnv: 'development',// 不标记块是否是其它块的子集flagIncludedChunks: false,// 不标记模块的加载顺序occurrenceOrder: false,// 不启用副作用sideEffects: false,usedExports: false,concatenateModules: false,splitChunks: {hidePathInfo: false,minSize: 10000,maxAsyncRequests: Infinity,maxInitialRequests: Infinity,},// 当打包时,遇到错误编译,仍把打包文件输出noEmitOnErrors: false,checkWasmTypes: false,// 不使用 optimization.minimizer || TerserPlugin 来最小化包minimize: false,removeAvailableModules: false},plugins: [// 当启用 HMR 时,使用该插件会显示模块的相对路径// 建议用于开发环境new webpack.NamedModulesPlugin(),// webpack 内部维护了一个自增的 id,每个 chunk 都有一个 id。// 所以当增加 entry 或者其他类型 chunk 的时候,id 就会变化,// 导致内容没有变化的 chunk 的 id 也发生了变化// NamedChunksPlugin 将内部 chunk id 映射成一个字符串标识符(模块的相对路径)// 这样 chunk id 就稳定了下来new webpack.NamedChunksPlugin(),// 定义环境变量new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") }),]
}
none
不进行任何默认优化选项。
配置:
// webpack.base.config.js
module.exports = {mode: 'none',
}
相当于默认内置了:
// webpack.base.config.js
module.exports = {performance: {// 性能设置,文件打包过大时,不报错和警告,只做提示hints: false},optimization: {// 不标记块是否是其它块的子集flagIncludedChunks: false,// 不标记模块的加载顺序occurrenceOrder: false,// 不启用副作用sideEffects: false,usedExports: false,concatenateModules: false,splitChunks: {hidePathInfo: false,minSize: 10000,maxAsyncRequests: Infinity,maxInitialRequests: Infinity,},// 当打包时,遇到错误编译,仍把打包文件输出noEmitOnErrors: false,checkWasmTypes: false,// 不使用 optimization.minimizer || TerserPlugin 来最小化包minimize: false,},plugins: []
}
三种模式对比
| 选项 | 描述 |
|---|---|
| development | 开发模式,打包更加快速,省了代码优化步骤 |
| production | 生产模式,打包比较慢,会开启 tree-shaking 和 压缩代码 |
| none | 不使用任何默认优化选项 |
Loader
为什么需要 Loader
webpack 默认支持处理 JS 与 JSON 文件,其他类型都处理不了,这里必须借助 Loader 来对不同类型的文件的进行处理
Loader 的作用
Loader 就是将 Webpack 不认识的内容转化为认识的内容
常用的 Loader 及其作用
babel-loader:是一个JavaScript的编译器,可以将新版本的JavaScript或者采用JavaScript语法外延扩展,比如将ES6代码翻译成ES5代码file-loader:可以指定要复制和放置资源文件的位置,以及如何使用版本哈希命名以获得更好的缓存,并在代码中通过URL去引用输出的文件url-loader: 和file-loader功能相似,但是可以通过指定阈值来根据文件大小使用不同的处理方式(小于阈值则返回base64格式编码并将文件的data-url内联到bundle中)raw-loader: 加载文件原始内容image-webpack-loader: 加载并压缩图片资源awesome-typescirpt-loader: 将typescript转换为javaScript并且性能由于ts-loadersass-loader: 将SCSS/SASS代码转换为CSScss-loader: 加载CSS代码 支持模块化、压缩、文件导入等功能特性style-loader: 把CSS代码注入到js中,通过DOM操作去加载CSS代码source-map-loader: 加载额外的Source Map文件eslint-loader: 通过ESlint检查js代码cache-loader: 可以在一些开销较大的Loader之前添加可以将结果缓存到磁盘中,提高构建的效率thread-loader: 多线程打包,加快打包速度
Loader 的本质
Loader 本质上是一个函数,负责代码的转译,即对接收到的内容进行转换后将转换后的结果返回 配置 Loader 通过在 modules.rules 中以数组的形式配置
Loader 的执行有以下两个阶段:
Pitching阶段:loader上的pitch方法,按照后置(post)、行内(inline)、普通(normal)、前置(pre)的顺序调用。Normal阶段:loader上的 常规方法,按照前置(pre)、普通(normal)、行内(inline)、后置(post)的顺序调用。模块源码的转换, 发生在这个阶段。
如何保证众多Loader按照想要的顺序执行
可以通过 enforce 来强制控制 Loader 的执行顺序 。( pre 表示在所有正常的 loader 执行之前执行, post 则表示在之后执行)
Plugin
与 Loader 用于转换特定类型的文件不同,插件( Plugin )可以贯穿 Webpack 打包的生命周期,执行不同的任务,从而扩展Webpack 的功能
常用的 Plugin 及其作用
define-plugin: 定义环境变量web-webpack-plugin: 为单页面应用输出HTML性能优于html-webpack-pluginclean-webpack-plugin: 每次打包时删除上次打包的产物, 保证打包目录下的文件都是最新的。
tips :Webpack 5.20以后可以通过output.clean属性达到同样的效果,所以此插件用处不大了terser-webpack-plugin: 内部封装了terser库,用于处理js的压缩和混淆
tips :webpack v5开箱即带有最新版本的terser-webpack-plugin。如果你使用的是webpack v5或更高版本,同时希望自定义配置,那么仍需要安装terser-webpack-plugin。如果使用webpack v4,则必须安装terser-webpack-plugin v4的版本。mini-css-extract-plugin: 将CSS提取为独立文件,支持按需加载css-minimize-webpack-plugin: 压缩CSS代码
tips:css文件的压缩需要mini-css-extract-plugin和css-minimize-webpack-plugin的配合使用 即先使用mini-css-extract-plugin将css代码抽离成单独文件,之后使用css-minimize-webpack-plugin对css代码进行压缩compression-webpack-plugin: 生产环境采用gzip压缩JS和CSSwebpack-bundle-analyzer: 可视化webpack输出文件大小的根据speed-measure-webpack-plugin: 用于分析各个loader和plugin的耗时,可用于性能分析webpack-dashboard: 可以更友好地展示打包相关信息
Plugin 的本质
Plugin 本质上是一个带有 apply(compiler) 的函数,基于 tapable 这个事件流框架来监听 webpack 构建/打包过程中发布的 hooks 来通过自定义的逻辑和功能来改变输出结果。 Plugin 通过 plugins 以数组的形式配置
与 Loader 的区别
Loade r主要负责将代码转译为 webpack 可以处理的 JavaScript 代码,而 Plugin 更多的是负责通过接入 webpack 构建过程来影响构建过程以及产物的输出。
Loader 的职责相对比较单一简单,而 Plugin 更为丰富多样。
devServer
可以通过 webpack-dev-server 启动本地服务
安装 webpack-dev-server
npm intall webpack-dev-server -D
配置 devServer
const path = require('path');module.exports = {//...devServer: {static: {directory: path.join(__dirname, 'public'),},compress: true,port: 9000,},
};
tips : 为什么要配置 static.directory?
因为 webpack 在进行打包的时候,对静态文件的处理,例如图片,都是直接 copy 到 dist 目录下面。但是对于本地开发来说,这个过程太费时,也没有必要,所以在设置 static.directory 之后,就直接到对应的静态目录下面去读取文件,而不需对文件做任何移动,节省了时间和性能开销。
devServer 详细配置请参考 官网
devtool ( sourceMap )
SourceMap 是一种映射关系,当项目运行后,如果出现错误,我们可以利用 SourceMap 反向定位到源码位置。在 webpack 中通过 devtool 字段来配置 sourceMap 策略。
配置 devtool
const config = {entry: './src/index.js', // 打包入口地址output: {filename: 'bundle.js', // 输出文件名path: path.join(__dirname, 'dist'), // 输出文件目录},devtool: 'source-map',module: { // ...}// ...
sourcemap 取值比较
| devtool | build | rebuild | 显示代码 | SourceMap 文件 |
|---|---|---|---|---|
| (none) | 很快 | 很快 | 无 | 无 |
| eval | 快 | 很快(cache) | 编译后 | 无 |
| source-map | 很慢 | 很慢 | 源代码 | 有 |
| eval-source-map | 很慢 | 一般(cache) | 编译后 | 有(dataUrl) |
| eval-cheap-source-map | 一般 | 快(cache) | 编译后 | 有(dataUrl) |
| eval-cheap-module-source-map | 慢 | 快(cache) | 源代码 | 有(dataUrl) |
| inline-source-map | 很慢 | 很慢 | 源代码 | 有(dataUrl) |
| hidden-source-map | 很慢 | 很慢 | 源代码 | 有 |
| nosource-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)。
生产环境
这些选项通常用于生产环境中:
(none)(省略devtool选项) - 不生成source map。这是一个不错的选择。source-map- 整个source map作为一个单独的文件生成。它为bundle添加了一个引用注释,以便开发工具知道在哪里可以找到它。hidden-source-map- 与source-map相同,但不会为bundle添加引用注释。如果你只想source map映射那些源自错误报告的错误堆栈跟踪信息,但不想为浏览器开发工具暴露你的source map,这个选项会很有用。nosources-source-map- 创建的source map不包含sourcesContent(源代码内容)。它可以用来映射客户端上的堆栈跟踪,而无须暴露所有的源代码。你可以将source map文件部署到web服务器。
个人推荐
本地开发
推荐:eval-cheap-module-source-map
理由:
- 本地开发首次打包慢点没关系,因为
eval缓存的原因,rebuild会很快 - 开发中,我们每行代码不会写的太长,只需要定位到行就行,所以加上
cheap - 我们希望能够找到源代码的错误,而不是打包后的,所以需要加上
module
生产环境
推荐:(none)
理由: 不生成 source map。这是一个不错的选择
resolve
配置模块如何解析。webpack 提供合理的默认值,但是还是可能会修改一些解析的细节。
module.exports = {resolve: {modules: [path.resolve(__dirname,'src'), 'node_modules'],extensions: ['.js', '.jsx', '.react.js', '.css', '.json'],// import导入时别名,减少耗时的递归解析操作alias: {'@components': path.resolve(`${project}/components`),'@style': path.resolve('asset/style'),},},
}
modules
- 设置模块导入规则,
import/require时会直接在这些目录找文件 - 可以指明存放第三方模块的绝对路径,以减少寻找
- 默认值为
node_modules - 告诉
webpack优先src目录下查找需要解析的文件,会大大节省查找时间
extensions
- 配置
extensions后,import导入时可省略后缀 - 注意:尽可能的减少后缀尝试的可能性,故高频文件后缀名放前面
- 手动配置后,默认配置会被覆盖
- 如果想保留默认配置,可以用 … 扩展运算符代表默认配置
如:
const config = {//...resolve: {extensions: ['.ts', '...'], },
};
alias
alias 用的创建 import 或 require 的别名,用来简化模块引用,项目中基本都需要进行配置
optimization
从 webpack 4 开始,会根据你选择的 mode 来执行不同的优化, 不过所有的优化还是可以手动配置和重写。
主要涉及两方面的优化:
- 最小化包
- 拆包
最小化包
removeAvailableModules
如果模块已经包含在所有父级模块中,告知 webpack 从 chunk 中检测出这些模块,或移除这些模块。将 optimization.removeAvailableModules 设置为 true 以启用这项优化。在 production 模式 中默认会被开启。
module.exports = {//...optimization: {removeAvailableModules: true,},
};
removeEmptyChunks
如果 chunk 为空,告知 webpack 检测或移除这些 chunk,(简单来说就是删除空模块)。默认值为true,将 optimization.removeEmptyChunks 设置为 false 可以禁用这项优化。
module.exports = {//...optimization: {removeEmptyChunks: false,},
};
providedExports
告知 webpack 去确定那些由模块提供的导出内容,为 export * from ... 生成更多高效的代码。默认 optimization.providedExports 会被启用。
module.exports = {//...optimization: {providedExports: true,},
};
usedExports
告知 webpack 去决定每个模块使用的导出内容。这取决于 optimization.providedExports 选项。由 optimization.usedExports 收集的信息会被其它优化手段或者代码生成使用,比如未使用的导出内容不会被生成,当所有的使用都适配,导出名称会被处理做单个标记字符。 在压缩工具中的无用代码清除会受益于该选项,而且能够去除未使用的导出内容。 默认值为 true
sideEffects
告知 webpack 去辨识 package.json 中的 副作用 标记或规则,以跳过那些当导出不被使用且被标记不包含副作用的模块。
optimization.sideEffects 取决于 optimization.providedExports 被设置成启用。这个依赖会有构建时间的损耗,但去掉模块会对性能有正面的影响,因为更少的代码被生成。该优化的效果取决于你的代码库, 可以尝试这个特性以获取一些可能的性能优化。
splitChunks
提取公共包 。
webpack 将根据以下条件自动拆分 chunks :
- 新的
chunk可以被共享,或者模块来自于node_modules文件夹 - 新的
chunk体积大于20kb(在进行min+gz之前的体积) - 当按需加载
chunks时,并行请求的最大数量小于或等于 30 - 当加载初始化页面时,并发请求的最大数量小于或等于 30
当尝试满足最后两个条件时,最好使用较大的 chunks 。
下面这个配置对象代表 SplitChunksPlugin 的默认行为。
module.exports = {//...optimization: {splitChunks: {chunks: 'async',minSize: 20000,minRemainingSize: 0,minChunks: 1,maxAsyncRequests: 30,maxInitialRequests: 30,enforceSizeThreshold: 50000,cacheGroups: {defaultVendors: {test: /[\\/]node_modules[\\/]/,priority: -10,reuseExistingChunk: true,},default: {minChunks: 2,priority: -20,reuseExistingChunk: true,},},},},
};
关于 splitChunks 的详细配置请参考 官网
minimizer
允许你通过提供一个或多个定制过的 TerserPlugin 实例,覆盖默认压缩工具(minimizer)
配合 TerserPlugin 实现代码压缩和变量混淆,以及生产环境去除 console 和 debugger 等
const TerserPlugin = require('terser-webpack-plugin');module.exports = {optimization: {minimizer: [new TerserPlugin({parallel: true,terserOptions: {// https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions},}),],},
};
在 optimization.minimizer 中可以使用'...'来访问默认值。
module.exports = {optimization: {minimizer: [new CssMinimizer(), '...'],},
};
拆包
当包过大时,如果我们更新一小部分的包内容,那么整个包都需要重新加载,如果我们把这个包拆分,那么我们仅仅需要重新加载发生内容变更的包,而不是所有包,有效的利用了缓存。
拆分 node_modules
很多情况下,我们不需要手动拆分包,可以使用 optimization.splitChunks :
const path = require('path');
module.exports = {entry: path.resolve(__dirname, 'src/index.js'),output: {path: path.resolve(__dirname, 'dist'),filename: '[name].[contenthash].js',},optimization: {splitChunks: {// 对所有的包进行拆分chunks: 'all',},},
};
我们不必制定拆包策略, chunks: all 会自动将 node_modules 中的所有内容放入一个名为 vendors〜main.js 的文件中。
拆分业务代码
module.exports = {entry: {main: path.resolve(__dirname, 'src/index.js'),ProductList: path.resolve(__dirname, 'src/ProductList/ProductList.js'),ProductPage: path.resolve(__dirname, 'src/ProductPage/ProductPage.js'),Icon: path.resolve(__dirname, 'src/Icon/Icon.js'),},output: {path: path.resolve(__dirname, 'dist'),filename: '[name].[contenthash:8].js',},
};
采用多入口的方式,当有业务代码更新时,更新相应的包即可
拆分第三方库
const path = require('path');
const webpack = require('webpack');module.exports = {entry: path.resolve(__dirname, 'src/index.js'),output: {path: path.resolve(__dirname, 'dist'),filename: '[name].[contenthash].js',},optimization: {runtimeChunk: 'single',splitChunks: {chunks: 'all',maxInitialRequests: Infinity,minSize: 0,cacheGroups: {vendor: {test: /[\\/]node_modules[\\/]/,name(module) {// 获取第三方包名const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];// npm 软件包名称是 URL 安全的,但是某些服务器不喜欢@符号return `npm.${packageName.replace('@', '')}`;},},},},},
};
当第三方包更新时,仅更新相应的包即可。
注意,当包太多时,浏览器会发起更多的请求,并且当文件过小时,对代码压缩也有影响。
performance
当打包时出现超过特定文件限制的资产和入口点, performance 配置如何显示性能提示
module.exports = {performance: {// 可选 warning、error、false// false:性能设置,文件打包过大时,不报错和警告,只做提示// warning:显示警告,建议用在开发环境// error:显示错误,建议用在生产环境,防止部署太大的生产包,从而影响网页性能hints: false}
}
watch
监视文件更新,并在文件更新时重新编译:
module.export = {// 启用监听模式watch: true,
}
在 webpack-dev-server 和 webpack-dev-middleware 中,默认启用了监视模式。
或者我们可以在命令行里启动监听( --watch ):
webpack --watch --config webpack.dev.config.js
watchOptions
module.export = {watch: true,// 自定义监视模式watchOptions: {// 排除监听ignored: /node_modules/,// 监听到变化发生后,延迟 300ms(默认) 再去执行动作,// 防止文件更新太快导致重新编译频率太高aggregateTimeout: 300,// 判断文件是否发生变化是通过不停的去询问系统指定文件有没有变化实现的// 默认 1000ms 询问一次poll: 1000}
}
externals
排除打包时的依赖项,不纳入打包范围内,例如你项目中使用了 jquery ,并且你在 html 中引入了它,那么在打包时就不需要再把它打包进去:
<scriptsrc="https://code.jquery.com/jquery-3.1.0.js"integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="crossorigin="anonymous">
</script>
配置:
module.exports = {// 打包时排除 jquery 模块externals: {jquery: 'jQuery'}
};
target
构建目标,用于为 webpack 指定一个环境:
module.exports = {// 编译为类浏览器环境里可用(默认)target: 'web'
};
cache
缓存生成的 webpack 模块和块以提高构建速度。在开发模式中,缓存设置为 type: 'memory' ,在生产模式中禁用。 cache: true 是 cache: {type: 'memory'} 的别名。要禁用缓存传递 false :
module.exports = {cache: true
}
在内存中,缓存仅在监视模式下有用,并且我们假设你在开发中使用监视模式。 在不进行缓存的情况下,内存占用空间较小。
name
module.exports = {name: 'react-app'
};
相关文章:
万字长文详解webpack知识图谱
webpack概念 概念 Webpack 是一种用于构建 JavaScript 应用程序的静态模块打包器,它能够以一种相对一致且开放的处理方式,加载应用中的所有资源文件(图片、CSS、视频、字体文件等),并将其合并打包成浏览器兼容的 Web…...
模板测试(Stencil Test)
模板测试可以用来针对特殊的区域进行渲染控制,实现有趣的效果,例如绘制物体轮廓。在 使用模板测试的时候,一般的步骤如下: 启用模板测试,以便写入数值到模板缓冲中渲染物体,根据渲染的物体将特定的数值写入到模板缓冲中禁用模板缓冲写入设置模板函数,根据于模板缓冲中的…...
【Go语言学习】安装与配置
文章目录前言一、Go语言学习站二、安装与配置1.安装2.环境变量配置3.Gland编辑器安装与配置Hello, World!总结前言 Go语言特性 Go,又称为 Golang,是一门开源的编程语言,由 Google 开发。Go 语言的设计目标是提供一种简单、快速、高效、安全…...
HCIP-5OSPF区域类型学习笔记
1、OSPF区域类型 OSPF提出了区域的概念(AREA),区域是将所有运行OSPF 的路由器人为的分成不同的组,以区域ID来标示。在区域内路由计算的方法不变,由于划分区域之后,每个区域内的路由器不会很多,…...
C语言再学习第三章
例题3-1 编写一个函数,实现华氏度和摄氏度的转化。 已知公式:c (5/9)*(f-32) #include <stdio.h>double f_value 0; double c_value 0; int main(void) {printf("请输入华氏温度\n");scanf("%lf",&f_valu…...
【aiy篇】小目标检测综述
小目标检测(Small Object Detection)是指在图像中检测尺寸较小的目标物体,通常是指物体的尺寸小于图像大小的1/10或者更小,COCO为例,面积小于等于1024像素的对象维下目标。小目标检测是计算机视觉领域的一个重要研究方…...
常用Linux命令的基本使用
序号 命令 对应英文 作用 1 ls list 查看当前文件夹下的内容 2 pwd print work directory 查看当前所在文件夹 3 cd[目录名] changge directory 切换文件夹 4 touch[文件名] touch 如果文件不存在,新建文件 5 mkdir[目录名] make directory …...
对跳表的深入理解
一,如何理解跳表 简单说跳表(Skip list)就是链表的“二分查找”。redis 的有序集合用的就是跳表算法。跳表是一种各方面性能都比较优秀的动态数据结构,可以支持快速地插入、删除、查找操作,写起来也不复杂,…...
C++017-C++冒泡排序与插入排序
文章目录C017-C冒泡排序与插入排序冒泡排序与插入排序目标冒泡排序排序规则冒泡排序优化插入排序题目描述在线练习:总结C017-C冒泡排序与插入排序 在线练习: http://noi.openjudge.cn/ https://www.luogu.com.cn/ 冒泡排序与插入排序 参考:…...
数据结构基础之链表
目录 前言 1、什么是链表 2、添加元素 3、虚拟头结点 4、查询&修改元素 5、删除元素 附:完整代码 前言 又到周末了,修整了一天,继续来写点东西吧,今天,我们来学习数据结构中的另一种基础的数据结构——链表…...
css 的渲染层合成是什么,浏览器如何创建新的渲染层
在 DOM 树中每个节点都会对应一个渲染对象(RenderObject),当它们的渲染对象处于相同的坐标空间(z 轴空间)时,就会形成一个 RenderLayers,也就是渲染层。渲染层将保证页面元素以正确的顺序堆叠&a…...
nacos-sdk-rust binding to NodeJs
广告时间 nacos-sdk-rust-binding-node : nacos-sdk-rust binding to NodeJs with napi. Tip: nacos-sdk-nodejs 仓库暂未提供 2.x gRPC 交互模式,为了能升级它,故而通过 node addon 方式调用 nacos-sdk-rust npm 包 -> https://www.npmjs.com/packa…...
MySQL下载安装以及环境配置教程
目录MySQL 下载MySQL 安装配置环境变量MySQL 下载 进入官方网站 https://www.mysql.com/ 点击 DOWNLOADS 进入下载页面 免费版本点击下方的 MySQL Community (GPL) Downloads 点击 MySQL Community Server 点击 Go to Download Page 进入下载页面 点击 Download 点击 No thank…...
概率论 1.3 古典概型与几何概型
1.3.1 排列与组合排列从n个不同元素任取r(r<n)个元素排成一列(考虑元素出现的先后次序),称此为一个排列,此种排列的总数为n(n-1)....(n-r1)n!/(n-r)!,若rn,则称为全排列,2.重复排列从n个不同元素中每次取出一个,放回…...
HTML DOM
通过 HTML DOM,可访问 JavaScript HTML 文档的所有元素。HTML DOM (文档对象模型)当网页被加载时,浏览器会创建页面的文档对象模型(Document Object Model)。HTML DOM 定义了用于 HTML 的一系列标准的对象,以及访问和处…...
Vue组件-$refs、$nextTick和name属性的使用
Vue组件-$refs和$nextTick使用一、获取DOM二、$refs获取组件对象三、$nextTick异步更新DOM四、组件name属性的使用一、获取DOM 通过id或ref属性获取原生DOM 在mounted生命周期 – 2种方式获取原生DOM标签 目标标签 – 添加id / ref恰当时机, 通过id / 通过ref属性 获取目标标签…...
【Spark】Spark的DataFrame向Impala写入数据异常及源码解析
背景 事情是这样的,当前业务有一个场景: 从业务库的Mysql抽取数据到Hive 由于运行环境的网络限制,当前选择的方案: 使用spark抽取业务库的数据表,然后利用impala jdbc数据灌输到hive。(没有spark on hive 的条件&…...
学习笔记-架构的演进之限流-3月day03
文章目录前言限流的目标流量统计指标限流设计模式流量计数器模式滑动时间窗模式漏桶模式令牌桶模式分布式限流总结附前言 任何一个系统的运算、存储、网络资源都不是无限的,当系统资源不足以支撑外部超过预期的突发流量时,就应该要有取舍,建…...
动态规划 背包问题
动态规划 背包问题 问题描述: 有一个背包,总容量为12。有6件物品,每件物品的重量和价值不同,求在背包总容量12的前提下,装进物品的最大价值以及装进物品的编号 单个物品重量和价值: 为方便进行思考&#…...
C++ Primer Plus 学习笔记(四)—— 内存模型和名称空间
1 单独编译 C允许将组件函数放在独立的文件即头文件中,头文件中可以包含以下内容: 函数原型;使用#define或const定义的符号常量;结构声明;类声明;模板声明;内联函数。 注意,在包含…...
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...
Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...
uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案
在移动互联网营销竞争白热化的当下,推客小程序系统凭借其裂变传播、精准营销等特性,成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径,助力开发者打造具有市场竞争力的营销工具。 一、系统核心功能架构&…...
WPF八大法则:告别模态窗口卡顿
⚙️ 核心问题:阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程,导致后续逻辑无法执行: var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题:…...
