如何将构建时产生的bundle自动添加到html中
使用HtmlWebpackPlugin
插件,会生成新的index.html
文件,替换我们的原有文件。
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
index: './src/index.js',
print: './src/print.js',
},
plugins: [
new HtmlWebpackPlugin({
title: 'Output Management',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
如何清理(webpack生成的文件放置在)/dist
文件夹
webpack 将生成文件并放置在 /dist 文件夹中,但是它不会追踪哪些文件是实际在项目中用到的。
使用clean-webpack-plugin
插件,在每次构建前清理/dist
文件夹,这样只会生成用到的文件。
如何将编译后的代码映射回原始源代码
当 webpack 打包源代码时,可能会很难追踪到 error(错误) 和 warning(警告) 在源代码中的原始位置。
为了更容易地追踪 error 和 warning,JavaScript 提供了 source maps
功能,可以将编译后的代码映射回原始源代码。
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
// 注意:仅用于开发环境,source map 相当消耗资源
// 避免在生产中使用 inline-*** 和 eval-***,因为它们会增加 bundle 体积大小,并降低整体性能
mode: 'development',
entry: {
index: './src/index.js',
print: './src/print.js',
},
devtool: 'inline-source-map',
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'Development',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
如何在代码发生变化后自动编译代码
webpack 提供以下方式:
webpack --watch
命令行(观察模式watch mode
)
缺点:需要手动刷新浏览器,才能看到修改后的实际效果
webpack serve --open
命令行(webpack-dev-server
)
webpack-dev-server
提供了一个简单的 web server,并具有 live reloading(实时重新加载) 功能
注意:webpack-dev-server
在编译之后不会写入到任何输出文件。而是将 bundle 文件保留在内存中,然后将它们 serve
到 server 中,就好像它们是挂载在 server 根路径上的真实文件一样。如果你的页面希望在其他不同路径中找到 bundle 文件,则可以通过 dev server 配置中的 publicPath
选项进行修改。
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
print: './src/print.js',
},
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',//从什么位置查找文件serve
},
plugins: [
new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }),
new HtmlWebpackPlugin({
title: 'Development',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
- 使用
webpack-dev-middleware
webpack-dev-middleware
是一个封装器(wrapper),它可以把 webpack 处理过的文件发送到一个 server。
webpack-dev-server
在内部使用了它,然而它也可以作为一个单独的 package 来使用,以便根据需求进行更多自定义设置。
可结合ExpressJS
(自定义设置server)等使用
需要手动刷新浏览器,才能看到修改后的实际效果,要结合使用webpack-hot-middleware
依赖包,以在你的自定义服务器或应用程序上启用HMR
server.js
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const app = express();
const config = require('./webpack.config.js');
const compiler = webpack(config);
// 告知 express 使用 webpack-dev-middleware,
// 以及将 webpack.config.js 配置文件作为基础配置。
app.use(
webpackDevMiddleware(compiler, {
publicPath: config.output.publicPath,
})
);
// 将文件 serve 到 port 3000。
app.listen(3000, function () {
console.log('Example app listening on port 3000!\n');
});
代码分离的方法
-
入口起点
缺点:
- 手动配置较多
- 如果入口 chunk 之间包含一些重复的模块,那些重复模块都会被引入到各个 bundle 中。
- 这种方法不够灵活,并且不能动态地将核心应用程序逻辑中的代码拆分出来
- 防止重复
- 入口依赖
配置dependOn
option 选项,这样可以在多个 chunk 之间共享模块
const path = require('path');
module.exports = {
mode: 'development',
entry: {
index: {
import: './src/index.js',
dependOn: 'shared',
},
another: {
import: './src/another-module.js',
dependOn: 'shared',
},
shared: 'lodash',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
//注意:要在一个 HTML 页面上使用多个入口时需设置optimization.runtimeChunk: 'single',将 runtime 代码拆分为一个单独的 chunk
optimization: {
runtimeChunk: 'single',
},
};
SplitChunksPlugin
SplitChunksPlugin
插件可以将公共的依赖模块提取到已有的入口 chunk 中,或者提取到一个新生成的 chunk。
const path = require('path');
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
print: './src/print.js',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
optimization: {
splitChunks: {
chunks: 'all',
},
},
};
- 动态导入
- ECMAScript提案的
import()
语法 (推荐) - webpack特定的
require.ensure
注意
import()
调用会在内部用到 promises
。如果在旧版本浏览器中(例如,IE 11)使用 import()
,记得使用一个 polyfill
库(例如 es6-promise
或 promise-polyfill
),来实现 Promise。
由于 import()
会返回一个 promise
,因此它可以和 async 函数
一起使用。
预获取/预加载
- prefetch(预获取):将来某些导航下可能需要的资源
//...
import(/* webpackPrefetch: true */ './path/to/LoginModal.js');
这会生成<link rel="prefetch" href="login-modal-chunk.js">
并追加到页面头部,指示着浏览器在闲置时间预取 login-modal-chunk.js
文件。
- preload(预加载):当前导航下可能需要资源
ChartComponent.js
//...
import(/* webpackPreload: true */ 'ChartingLibrary');
在页面中使用 ChartComponent
时,在请求 ChartComponent.js 的同时,还会通过 <link rel="preload">
请求 charting-library-chunk。假定 page-chunk 体积很大,加载慢,页面此时就会显示 LoadingIndicator(加载进度条)
,等到 charting-library-chunk 请求完成,LoadingIndicator
组件才消失。启动仅需要很少的加载时间,因为只进行单次往返,而不是两次往返。尤其是在高延迟环境下。
注意:不正确的使用webpackPreload会有损性能
与prefetch
指令相比,preload
指令有许多不同之处:
preload
chunk 会在父 chunk 加载时,以并行方式开始加载。prefetch
chunk 会在父 chunk 加载结束后开始加载。preload
chunk 具有中等优先级,并立即下载。prefetch
chunk 在浏览器闲置时下载。preload
chunk 会在父 chunk 中立即请求,用于当下时刻。prefetch
chunk 会用于未来的某个时刻。- 浏览器支持程度不同。
bundle分析
analyse
官方:您可以运行此命令行webpack --profile --json > stats.json
生成所需的JSON文件webpack-chart
: webpack stats 可交互饼图。webpack-visualizer
: 可视化并分析你的 bundle,检查哪些模块占用空间,哪些可能是重复使用的。webpack-bundle-analyzer
:一个 plugin 和 CLI 工具,它将 bundle 内容展示为一个便捷的、交互式、可缩放的树状图形式。webpack bundle optimize helper
:这个工具会分析你的 bundle,并提供可操作的改进措施,以减少 bundle 的大小。bundle-stats
:生成一个 bundle 报告(bundle 大小、资源、模块),并比较不同构建之间的结果。
缓存
确保 webpack 编译生成的文件能够被客户端缓存,而在文件内容变化后,能够请求到新的文件。
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
plugins: [
// 对于 CleanWebpackPlugin 的 v2 versions 以下版本,使用 new CleanWebpackPlugin(['dist/*'])
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'Caching',
}),
],
output: {
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist'),
},
optimization: {
//解决vendor bundle 会随着自身的 module.id 的变化,而发生变化
moduleIds: 'deterministic',
runtimeChunk: 'single',//提取引导模板,将 runtime 代码拆分为一个单独的 chunk
splitChunks: {
//利用 client 的长效缓存机制,命中缓存来消除请求,并减少向 server 获取资源,同时还能保证 client 代码和 server 代码版本一致
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
},
};
构建性能提升
-
loader
通过使用include
字段,仅将loader
应用在实际需要将其转换的模块 -
提高解析速度
- 减少
resolve.modules
,resolve.extensions
,resolve.mainFiles
,resolve.descriptionFiles
中条目数量,因为他们会增加文件系统调用的次数。 - 如果你不使用
symlinks
(例如npm link
或者yarn link
),可以设置resolve.symlinks: false
。 - 如果你使用自定义
resolve plugin
规则,并且没有指定context
上下文,可以设置resolve.cacheWithContext: false
- 减少
-
使用
DllPlugin
为更改不频繁的代码生成单独的编译结果 -
尽量保持
chunk
体积小- 使用数量更少/体积更小的
library
。 - 在多页面应用程序中使用
SplitChunksPlugin
。 - 在多页面应用程序中使用
SplitChunksPlugin
,并开启async
模式。 - 移除未引用代码。
- 只编译你当前正在开发的那些代码。
- 使用数量更少/体积更小的
-
不要使用太多的
worker
thread-loader
可以将非常消耗资源的loader
分流给一个worker pool
(worker池)
注意:不要使用太多的worker
,因为Node.js
的runtime
和loader
都有启动开销。最小化worker
和main process(主进程)
之间的模块传输。进程间通讯(IPC, inter process communication)是非常消耗资源的。
-
持久化缓存
-
将
ProgressPlugin
从 webpack 中删除,可以缩短构建时间。 -
最小化项目中的 preset/plugin 数量.
-
在单独的进程中使用
fork-ts-checker-webpack-plugin
进行类型检查。 -
配置
loader
跳过类型检查。 -
使用
ts-loader
时,设置happyPackMode: true / transpileOnly: true
. -
node-sass
中有个来自 Node.js 线程池的阻塞线程的 bug。 当使用thread-loader
时,需要设置workerParallelJobs: 2
如何独立配置开发和生成环境并合并公共部分
在开发环境中,我们需要:强大的source map
和一个有着live reloading(实时重新加载)
或hot module replacement(热模块替换)
能力的localhost server
。
在生产环境中,目标则转移至其他方面,关注点在于压缩bundle
、更轻量的source map
、资源优化等,通过这些优化方式改善加载时间。
使用webpack-merge
工具,此工具会引用“common”配置,避免再在环境特定(environment-specific)的配置中编写重复代码
资源模块
资源模块(asset module)是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader。
在 webpack 5 之前,通常使用:
raw-loader
将文件导入为字符串url-loader
将文件作为 data URI 内联到 bundle 中file-loader
将文件发送到输出目录
资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:
asset/resource
发送一个单独的文件并导出 URL。之前通过使用file-loader
实现。asset/inline
导出一个资源的 data URI。之前通过使用url-loader
实现。asset/source
导出资源的源代码。之前通过使用raw-loader
实现。asset
在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用url-loader
,并且配置资源体积限制实现。
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
// 自定义输出文件名 1.assetModuleFilename 来修改此模板字符串
assetModuleFilename: 'images/[hash][ext][query]'
},
module: {
rules: [
{
test: /\.(png|jpg|gif)$/i,
// 需从 asset loader 中排除来自新 URL 处理的 asset
dependency: { not: ['url'] },
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
}
},
],
// 想停止当前 asset 模块的处理,并再次启动处理,避免asset 重复
type: 'javascript/auto'
},
// Resource资源配置
{
test: /\.png/,
type: 'asset/resource'
},
// 自定义输出文件名 2.将某些资源发送到指定目录,仅适用于 asset 和 asset/resource 模块类型
{
test: /\.html/,
type: 'asset/resource',
generator: {
filename: 'static/[hash][ext][query]'
}
},
{
test: /\.svg/,
type: 'asset/inline',
// 自定义 data URI 生成器,所有 .svg 文件都将通过 mini-svg-data-uri 包进行编码
generator: {
dataUrl: content => {
content = content.toString();
return svgToMiniDataURI(content);
}
},
{
test: /\.txt/,
type: 'asset/source',
},
{
test: /\.doc/,
// 通用资源类型,webpack 将按照默认条件,自动地在 resource 和 inline 之间进行选择:小于 8kb 的文件,将会视为 inline 模块类型,否则会被视为 resource 模块类型
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 4 * 1024 // 4kb
}
}
}
]
},
}
- resource资源
import mainImage from './images/main.png';
img.src = mainImage; // '/dist/151cfcfa1bd74779aadb.png'
- inline资源
import metroMap from './images/metro.svg';
block.style.background = `url(${metroMap})`; // url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDo...vc3ZnPgo=)
- source资源
src/example.txt
Hello world
src/index.js
import exampleText from './example.txt';
block.textContent = exampleText; // 'Hello world'
- url资源
当使用new URL('./path/to/asset', import.meta.url)
,webpack也会创建资源模块。
src/index.js
const logo = new URL('./logo.svg', import.meta.url);
根据你配置中 target 的不同,webpack 会将上述代码编译成不同结果:
// target: web
new URL(__webpack_public_path__ + 'logo.svg', document.baseURI || self.location.href);
// target: webworker
new URL(__webpack_public_path__ + 'logo.svg', self.location);
// target: node, node-webkit, nwjs, electron-main, electron-renderer, electron-preload, async-node
new URL(__webpack_public_path__ + 'logo.svg', require('url').pathToFileUrl(__filename));
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!