webpack 学习笔记
webpack 简介
webpack 是什么
webpack 是一种前端资源构建工具,一个静态模块打包器(module bundler)。 在 webpack 看来, 前端的所有资源文件(js/json/css/img/less/…)都会作为模块处理。 它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源(bundle)。
webpack 五个核心概念
Entry
入口(Entry)指示 webpack 以哪个文件为入口起点开始打包,分析构建内部依赖图。
Output
输出(Output)指示 webpack 打包后的资源 budles 输出到哪里去,以及如何命名。
Loader
Loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。
Plugins
插件(Plugins)可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩, 一直到重新定义环境中的变量等。
Mode
模式(Mode)指示 webpack 使用相应模式的配置。
选项 | 描述 | 特点 |
---|---|---|
development | 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置 为 development。启用 NamedChunksPlugin 和 NamedModulesPlugin。 | 能让代码本地调试 运行的环境 |
production | 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置 为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 TerserPlugin | 能让代码优化上线 运行的环境 |
webpack 的简易使用
初始化配置
-
初始化
package.json
npm init
-
下载并安装
webpack
// 全局安装 npm install webpack webpack-cli -g // 安装到开发环境 npm install webpack webpack-cli -D
编译打包应用
-
创建文件
-
运行指令
// 开发环境 webpack src/js/index.js -o build/js/built.js --mode=development
功能:webpack 能够编译打包 js 和 json 文件,并且能将 es6 的模块化语法转换成 浏览器能识别的语法。
// 生产环境 webpack src/js/index.js -o build/js/built.js --mode=production
功能:在开发配置功能上多一个功能,压缩代码。
-
效果
webpack 能够编译打包 js 和 json 文件。 能将 es6 的模块化语法转换成浏览器能识别的语法。 能压缩代码。
-
问题
不能编译打包 css、img 等文件。 不能将 js 的 es6 基本语法转化为 es5 以下语法。
webpack 开发环境的基本配置
创建配置文件
-
创建文件 webpack.config.js
-
配置内容如下
// resolve用来拼接绝对路径的方法 const { resolve } = require('path'); module.exports = { // 入口起点 entry: './src/index.js', // 输出 output: { // 输出文件名 filename: 'built.js', // 输出路径 path: resolve(__dirname, 'build'), }, // 模式 mode: 'development', // 开发模式 // mode: 'production' };
-
运行指令:
webpack
打包样式资源
-
创建
less
文件 -
下载安装 loader 包
npm install css-loader style-loader less-loader --save-dev
-
修改配置文件
// resolve用来拼接绝对路径的方法 const { resolve } = require('path'); module.exports = { // 入口起点 entry: './src/index.js', // 输出 output: { // 输出文件名 filename: 'built.js', // 输出路径 // __dirname nodejs的变量,代表当前文件的目录绝对路径 path: resolve(__dirname, 'build'), }, // loader的配置 module: { rules: [ // 详细loader配置 // 不同文件必须配置不同loader处理 { // 匹配哪些文件 test: /\.css$/, // 使用哪些loader进行处理 use: [ // use数组中loader执行顺序:从右到左,从下到上 依次执行 // 创建style标签,将js中的样式资源插入进行,添加到head中生效 'style-loader', // 将css文件变成commonjs模块加载js中,里面内容是样式字符串 'css-loader', ], }, { test: /\.less$/, use: [ 'style-loader', 'css-loader', // 将less文件编译成css文件 // 需要下载 less-loader和less 'less-loader', ], }, ], }, // plugins的配置 plugins: [ // 详细plugins的配置 ], // 模式 mode: 'development', // 开发模式 // mode: 'production' };
-
运行指令:
webpack
打包 HTML 资源
-
创建 HTML 文件
-
下载安装 plugin 包
npm install --save-dev html-webpack-plugin
-
修改配置文件,html-webpack-plugin 的详细配置见 Github
const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/index.js', output: { filename: 'built.js', path: resolve(__dirname, 'build'), }, module: { rules: [ // loader的配置 ], }, plugins: [ // plugins的配置 // html-webpack-plugin // 功能:默认会创建一个空的HTML,自动引入打包输出的所有资源(JS/CSS) // 需求:需要有结构的HTML文件 new HtmlWebpackPlugin({ // 复制 './src/index.html' 文件,并自动引入打包输出的所有资源(JS/CSS) template: './src/index.html', }), ], mode: 'development', };
-
运行指令:
webpack
打包图片资源
-
创建图片文件
-
下载安装 loader 包,在 webpack5 中,增加了资源模块用来处理资源文件而无需加载 loader,下载的 html-loader 只是将图片 require 到 js 目录以进行打包。
npm install --save-dev html-loader
-
修改配置文件,其中
const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/index.js', output: { filename: 'built.js', path: resolve(__dirname, 'build'), }, module: { rules: [ // css-loader 会对 @import 和 url() 进行处理(导入) { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'], }, // 将HTML中的可加载属性导入到js进行打包 { test: /\.html$/, loader: 'html-loader', }, // 处理import的图片路径问题,实际上使用 Asset Modules 可以接收并加载任何文件,然后将其输出到构建目录 { test: /\.(png|svg|jpg|jpeg|gif)$/i, type: 'asset', // 自动地在 resource 和 inline 之间进行选择:小于 8kb 的文件,将会视为 inline 模块类型,否则会被视为 resource 模块类型。 parser: { dataUrlCondition: { maxSize: 8 * 1024, // 8kb 是默认值 }, }, }, ], }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html', }), ], mode: 'development', };
打包其他资源
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'built.js',
path: resolve(__dirname, 'build'),
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
// 打包其他资源(除了html/js/css资源以外的资源)
{
// 排除css/js/html资源
exclude: /\.(css|js|html|less)$/,
type: 'asset', // 自动地在 resource 和 inline 之间进行选择:小于 8kb 的文件,将会视为 inline 模块类型,否则会被视为 resource 模块类型。
parser: {
dataUrlCondition: {
maxSize: 8 * 1024, // 8kb 是默认值
},
},
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
}),
],
mode: 'development',
};
source map
当 webpack 打包源代码时,可能会很难追踪到 error(错误) 和 warning(警告) 在源代码中的原始位置。可以通过一定设置来追踪错误文件。在配置中加入如下属性:
devtool: 'inline-source-map',
开发工具
在每次编译代码时,手动运行 npm run build
会显得很麻烦。webpack 提供几种可选方式,帮助你在代码发生变化后自动编译代码:
但一般使用 webpack-dev-server,其具有 live reloading(实时重新加载) 功能,webpack-dev-middleware 是 webpack-dev-server 的中间件。配置如下:
devServer: {
// 项目构建后路径
contentBase: resolve(__dirname, 'build'),
// 启动gzip压缩
compress: true,
// 端口号
port: 3000,
// 自动打开浏览器
open: true
}
运行应使用webpack serve
而不是 webpack-dev-server
,见issues。
若要更改代码时浏览器自动刷新可做如下设置:
module.exports = {
target: 'web',
};
webpack 生产环境的基本配置
生产环境下默认使用
TerserPlugin
启用 source map
在生产环境中启用 source map,因为它们对 debug(调试源码) 和运行 benchmark tests(基准测试) 很有帮助。
devtool: 'eval-source-map',
提取所有的 CSS 到一个文件中
使用插件 mini-css-extract-plugin,同时将 Loader style-loader
换成 MiniCssExtractPlugin.loader
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// "style-loader"换成如下配置,其中publicPath为当前文件处理后的公共路径
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: "../",
},
},
// 增加 mini-css-extract-plugin 插件
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css',
}),
],
css 兼容性处理
使用 postcss-loader 和 postcss-preset-env 两个 loader。
npm install --save-dev postcss-loader postcss postcss-preset-env
创建配置文件 postcss.config.js
module.exports = {
plugins: [
[
'postcss-preset-env',
{
// 其他选项
},
],
],
};
webpack.config.js 中添加 postcss-loader
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader', 'postcss-loader'],
},
],
},
};
在 package.json 中增加如下配置
{
"browserslist": ["> 1%", "not dead", "not op_mini all"]
}
压缩 css
使用插件 CssMinimizerWebpackPlugin,安装:
npm install css-minimizer-webpack-plugin --save-dev
接着在 webpack
配置中加入该插件,与此同时 html 也被压缩了(貌似是默认压缩?)。
optimization: {
minimize: true,
minimizer: [
new CssMinimizerPlugin(),
],
},
js 语法检查 Eslint
为了解决 eslint-loader 的问题,eslint-loader 被弃用,改用EslintWebpackPlugin插件。
安装:
npm install eslint-webpack-plugin --save-dev
// 需要用到 eslint
npm install eslint --save-dev
使用 airbnb 的 js 语法检查设置
npm install eslint-config-airbnb-base eslint-plugin-import --save-dev
在 package.json 中添加配置
{
"eslinkConfig": {
"extends": "airbnb-base"
}
}
注意:可能有非常多的错误警告,建议搭配 vs code 插件 ESlint 做格式化使用。
调试过程中会有 console 语句,可以在 console 语句前加入如下注释用来忽略对 console 的检查。
// eslint-disable-next-line no-console
console.log('no-console');
js 兼容性处理
在之前使用 @babel/polyfill 来处理 js 兼容性问题,但 babel 官网显示已被弃用,建议直接用 core-js/stable
和 regenerator-runtime/runtime
:
🚨 As of Babel 7.4.0, this package has been deprecated in favor of directly including
core-js/stable
(to polyfill ECMAScript features) andregenerator-runtime/runtime
(needed to use transpiled generator functions):import 'core-js/stable'; import 'regenerator-runtime/runtime';
安装:
npm i --save core-js regenerator-runtime
在 js 中引入 core-js/stable
和 regenerator-runtime/runtime
:
import 'core-js/stable';
import 'regenerator-runtime/runtime';
拓展
@babel/preset-env
可以做到按需加载,具体见 链接,没看明白怎么用,暂时不做学习,同时 webpack 官网建议 “不加选择地和同步地加载所有 polyfill/shim,尽管这会导致额外的 bundle 体积成本。”,因为 pyolyfill 被弃用,所以我们要不加选择地加载core-js/stable
和 regenerator-runtime/runtime
,因为有些浏览器并没有很好地支持 ECMAScript 的新特性。取舍问题建议查看 webpack 官网描述。
js 压缩
官网有如下内容:
如果你使用的是 webpack v5 或以上版本,你不需要安装这个插件。webpack v5 自带最新的
terser-webpack-plugin
。如果使用 webpack v4,则必须安装terser-webpack-plugin
v4 的版本。
生产环境下自动压缩,但通过以上的配置流程之后,我的 js 并没有被压缩,甚至还达到了 427kb 的大小,以至于报出错误警告说文件过大(限制 244kb),所以自行配置。
添加到开发依赖项:
npm install terser-webpack-plugin --save-dev
配置 webpack.config.js
:
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
optimization: {
minimize: true,
minimizer: [new TerserPlugin()],
},
};
性能优化/功能提升
模块热替换(hot module replacement)
模块热替换(HMR - hot module replacement)功能会在应用程序运行过程中,替换、添加或删除 模块,而无需重新加载整个页面。更多介绍见 模块热替换。
设置方法:在 [开发工具](### 开发工具) 中添加如下设置:
hot: true,
可以看到此时 css 和 js 都热更新了,但 html 并没有热更新,所以要修改 webpack 配置,在 entry 入口中添加上 html 文件。
entry: ['./src/index.js', './src/index.html'],
但是,没那么简单,单页面的流行使得 html 不需要更改,因为都是 js 生成的虚拟 dom 树再添加到页面中的,而且 html 页面的变化使得所有其他的页面都要去变化,设置 html 热加载还不如不设置热加载。
生产环境和开发环境配置分离
development(开发环境) 和 production(生产环境) 这两个环境下的构建目标存在着巨大差异。在开发环境中,我们需要:强大的 source map 和一个有着 live reloading(实时重新加载) 或 hot module replacement(热模块替换) 能力的 localhost server。而生产环境目标则转移至其他方面,关注点在于压缩 bundle、更轻量的 source map、资源优化等,通过这些优化方式改善加载时间。由于要遵循逻辑分离,我们通常建议为每个环境编写彼此独立的 webpack 配置。
虽然,以上我们将 生产环境 和 开发环境 做了细微区分,但是,请注意,我们还是会遵循不重复原则(Don’t repeat yourself - DRY),保留一个 “common(通用)” 配置。为了将这些配置合并在一起,我们将使用一个名为 webpack-merge
的工具。此工具会引用 “common” 配置,因此我们不必再在环境特定(environment-specific)的配置中编写重复代码。
我们先从安装 webpack-merge
开始,并将之前指南中已经成型的那些代码进行分离:
npm install --save-dev webpack-merge
webpack.dev.js
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
},
});
webpack.prod.js
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'production',
});
package.json
"scripts": {
"build": "webpack --config webpack.prod.js",
"dev-build": "webpack serve --open --config webpack.dev.js"
},
eslint 检测依赖问题,在文件开头添加注释
/* eslint-disable import/no-extraneous-dependencies */
source-map 详解
source-map: 一种提供源代码到构建后代码映射技术 (如果构建后代码出错了,通过映射可以追踪源代码错误)。
格式:[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
source-map:外部
错误代码准确信息 和 源代码的错误位置
inline-source-map:内联
只生成一个内联 source-map
错误代码准确信息 和 源代码的错误位置
hidden-source-map:外部
错误代码错误原因,但是没有错误位置
不能追踪源代码错误,只能提示到构建后代码的错误位置
eval-source-map:内联
每一个文件都生成对应的 source-map,都在 eval
错误代码准确信息 和 源代码的错误位置
nosources-source-map:外部
错误代码准确信息, 但是没有任何源代码信息
cheap-source-map:外部
错误代码准确信息 和 源代码的错误位置
只能精确的行
cheap-module-source-map:外部
错误代码准确信息 和 源代码的错误位置
module 会将 loader 的 source map 加入
生产环境:eval-source-map / eval-cheap-module-souce-map
开发环境:source-map / cheap-module-souce-map
oneOf
在前面我们设置了许多 loader 规则,但是过多的 loader 会使得打包速度变慢,所以我们需要一种规则使得打包的时候只进行一次匹配处理,这个规则就是 oneOf,配置方法如下:
module: {
rules: [
{
oneOf: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
],
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader',
'postcss-loader',
],
},
],
},
],
},
Tree Shaking
得益于 es6 模块的静态分析,可以在打包时直接去掉不适用的代码。
条件:1. 必须使用 ES6 模块化 2. 开启 production 环境
作用:减少代码体积
在 package.json 中配置:"sideEffects": false
表示所有代码都没有副作用(都可以进行 tree shaking)
这样会导致的问题:可能会把 css / @babel/polyfill 文件干掉(副作用)
所以可以配置:"sideEffects": ["*.css", "*.less"]
不会对 css/less 文件 tree shaking 处理。
代码分割
多入口模式
在设置中可以设置 entry 为一个对象,实现多入口,此时可以从但入口中去除掉其他入口的 import。
entry: {
index: './src/js/index.js',
test: './src/js/test.js'
},
开箱即用的 SplitChunksPlugin
对于大部分用户来说非常友好。
默认情况下,它只会影响到按需加载的 chunks,因为修改 initial chunks 会影响到项目的 HTML 文件中的脚本标签。
module.exports = {
//...
optimization: {
splitChunks: {
// include all types of chunks
chunks: 'all',
},
},
};
同时输出文件名需要添加上 [chunkhash:10]
此类字段,否则输出文件名相同而报错。
因为默认最小文件为 20kb 所以可以使用 import 动态导入语法来使得引入的文件强制单独打包。
更多配置可以参见官网描述。
其他见 代码分离
懒加载
懒加载或者按需加载,是一种很好的优化网页或应用的方式。这种方式实际上是先把你的代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或即将引用另外一些新的代码块。这样加快了应用的初始加载速度,减轻了它的总体体积,因为某些代码块可能永远不会被加载。
懒加载同时是上一小节“代码分离”的一部分,但因时常被单独提及,这里另加一小节。
当涉及到动态代码拆分时,虽然提供了两种技术,但require.ensure
已经是不建议使用的了,所以我们用符合 ECMAScript 提案 的 import()
语法 来实现动态导入,因为返回的是 Promise,所以需要使用像 es6-promise 或者 promise-polyfill 这样 polyfill 库,来预先填充(shim) Promise
环境。更好的方法还是使用[js 兼容性处理](### js 兼容性处理) 的方案。
示例:
import('./test').then(({ mul }) => {
console.log(mul(2, 5));
});
可以使用 魔法注释 来设置文件名、加载方式、预获取/预加载模块
渐进式网络应用程序
渐进式网络应用程序(progressive web application - PWA),是一种可以提供类似于 native app(原生应用程序) 体验的 web app(网络应用程序)。
添加 Workbox
添加 workbox-webpack-plugin 插件,然后调整配置文件
npm install workbox-webpack-plugin --save-dev
const WorkboxPlugin = require('workbox-webpack-plugin');
module.exports = {
plugins: [
new WorkboxPlugin.GenerateSW({
// 这些选项帮助快速启用 ServiceWorkers
// 不允许遗留任何“旧的” ServiceWorkers
clientsClaim: true,
skipWaiting: true,
}),
],
};
注册 Service Worker
index.js
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker
.register('./service-worker.js')
.then((registration) => {
// eslint-disable-next-line no-console
console.log('SW registered: ', registration);
})
.catch((registrationError) => {
// eslint-disable-next-line no-console
console.log('SW registration failed: ', registrationError);
});
});
}
多进程打包
需要安装 thread-loader
来对 babel 新开进程,从名字可以看出,这是一个 loader,可以添加到 use 中使得打包时新开进程,另外还可以设置进程数量来优化打包速度,需要注意的是,进程启动大概需要 600ms,进程通信需要消耗时间,所以加上该 loader 并不一定会有正向优化,应该视具体情况而定。
{
loader: 'thread-loader',
options: {
workers: 2 // 设置进程数为2个
}
}
外部扩展(externals)
externals
配置选项提供了「从输出的 bundle 中排除依赖」的方法。官网提供多种方法,以下介绍几个 web 常用的。
方法 1:字符串
module.exports = {
//...
externals: {
jquery: 'jQuery',
},
};
将 jquery 模块替换为全局变量 jQuery。
方法 2:函数
function ({ context, request, contextInfo, getResolve }, callback)
>function ({ context, request, contextInfo, getResolve }) => promise
5.15.0+
函数接收两个入参:
-
ctx (object):包含文件详情的对象。
ctx.context (string): 包含引用的文件目录。
ctc.request (string): 被请求引入的路径。
ctx.contextInfo (string): 包含 issuer 的信息(如,layer)
ctx.getResolve 5.15.0+: 获取当前解析器选项的解析函数。
-
callback (function (err, result, type)): 用于指明模块如何被外部化的回调函数
回调函数接收三个入参:
-
err (Error): 被用于表明在外部外引用的时候是否会产生错误。如果有错误,这将会是唯一被用到的参数。
-
result (string [string] object): 描述外部化的模块。可以接受形如 ${type} ${path} 格式的字符串,或者其它标准化外部化模块格式,(string, [string],或 object)。
-
type (string): 可选的参数,用于指明模块的类型(如果它没在 result 参数中被指明)。
externals: [
function ({ request }, callback) {
if (/^jquery$/.test(request)) {
// 使用 request 路径,将一个 commonjs 模块外部化
return callback(null, 'root jQuery');
}
// 继续下一步且不外部化引用
callback();
},
],
以上设置表明只对 /^jquery$/
匹配的模块做处理,其中 callback 的参数含义为可以通过一个全局变量访问 library(例如,通过 script 标签)且全局变量为 jQuery。
以上两种方法都需要在 index.html 中通过 cdn 引入:
<script src='https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js'></script>
DLL
DllPlugin
和 DllReferencePlugin
用某种方法实现了拆分 bundles,同时还大幅度提升了构建的速度。“DLL” 一词代表微软最初引入的动态链接库。换句话说就是让某些库单独打包,后续再直接引入到 build 中。
注意:DLL 拆分出来 bundles 是通过单独的配置文件来单独执行 webpack 打包这一过程实现的,不可与项目配置文件混淆。
webpack.dll.js 配置:(将 jquery 单独打包)
const { resolve } = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
// 最终打包生成的[name] --> jquery
// ['jquery] --> 要打包的库是jquery
jquery: ['jquery'],
},
output: {
// 输出出口指定
filename: '[name].js', // name就是jquery
path: resolve(__dirname, 'dll'), // 打包到dll目录下
library: '[name]_[hash]', // 打包的库里面向外暴露出去的内容叫什么名字
},
plugins: [
// 打包生成一个manifest.json --> 提供jquery的映射关系(告诉webpack:jquery之后不需要再打包和暴露内容的名称)
new webpack.DllPlugin({
name: '[name]_[hash]', // 映射库的暴露的内容名称
path: resolve(__dirname, 'dll/manifest.json'), // 输出文件路径
entryOnly: true, // 则仅暴露入口,确保 DLL 中的 tree shaking 正常工作
}),
],
mode: 'production',
};
webpack.config.js 配置:(告诉 webpack 不需要再打包 jquery,并将之前打包好的 jquery 跟其他打包好的资源一同输出到 build 目录下)
// 引入插件
const webpack = require('webpack');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
// plugins中配置:
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
// 告诉webpack哪些库不参与打包,同时使用时的名称也得变
new webpack.DllReferencePlugin({
manifest: resolve(__dirname, 'dll/manifest.json')
}),
// 将某个文件打包输出到build目录下,并在html中自动引入该资源
new AddAssetHtmlWebpackPlugin({
filepath: resolve(__dirname, 'dll/jquery.js')
})
],
提升
enrty
entry: 入口起点
-
string –> ‘./src/index.js’,单入口
打包形成一个 chunk。 输出一个 bundle 文件。此时 chunk 的名称默认是 main
-
array –> [’./src/index.js’, ‘./src/add.js’],多入口
所有入口文件最终只会形成一个 chunk,输出出去只有一个 bundle 文件。
(一般只用在 HMR 功能中让 html 热更新生效)
-
object,多入口
有几个入口文件就形成几个 chunk,输出几个 bundle 文件,此时 chunk 的名称是 key 值
output
output: {
// 文件名称(指定名称+目录)
filename: 'js/[name].js',
// 输出文件目录(将来所有资源输出的公共目录)
path: resolve(__dirname, 'build'),
// 所有资源引入公共路径前缀(可以做cdn) --> 'imgs/a.jpg' --> '/imgs/a.jpg'
publicPath: '/',
chunkFilename: 'js/[name]_chunk.js', // 指定非入口chunk的名称
library: '[name]', // 打包整个库后向外暴露的变量名
libraryTarget: 'window' // 变量名添加到哪个上 browser:window
// libraryTarget: 'global' // node:global
// libraryTarget: 'commonjs' // conmmonjs模块 exports
},
resolve
// 解析模块的规则
resolve: {
// 配置解析模块路径别名: 优点:当目录层级很复杂时,简写路径;缺点:路径不会提示
alias: {
$css: resolve(__dirname, 'src/css')
},
// 配置省略文件路径的后缀名(引入时就可以不写文件后缀名了)
extensions: ['.js', '.json', '.jsx', '.css'],
// 告诉 webpack 解析模块应该去找哪个目录
modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
}
这样配置后,引入文件就可以这样简写:import '$css/index';
dev server
devServer: {
// 运行代码所在的目录
contentBase: resolve(__dirname, 'build'),
// 监视contentBase目录下的所有文件,一旦文件变化就会reload
watchContentBase: true,
watchOptions: {
// 忽略文件
ignored: /node_modules/
},
// 启动gzip压缩
compress: true,
// 端口号
port: 5000,
// 域名
host: 'localhost',
// 自动打开浏览器
open: true,
// 开启HMR功能
hot: true,
// 不要显示启动服务器日志信息
clientLogLevel: 'none',
// 除了一些基本信息外,其他内容都不要显示
quiet: true,
// 如果出错了,不要全屏提示
overlay: false,
// 服务器代理,--> 解决开发环境跨域问题
proxy: {
// 一旦devServer(5000)服务器接收到/api/xxx的请求,就会把请求转发到另外一个服务器3000
'/api': {
target: 'http://localhost:3000',
// 发送请求时,请求路径重写:将/api/xxx --> /xxx (去掉/api)
pathRewrite: {
'^/api': ''
}
}
}
}
optimization
contenthash 缓存会导致一个问题:修改 a 文件导致 b 文件 contenthash 变化。 因为在 index.js 中引入 a.js,打包后 index.js 中记录了 a.js 的 hash 值,而 a.js 改变,其重新打包后的 hash 改变,导致 index.js 文件内容中记录的 a.js 的 hash 也改变,从而重新打包后 index.js 的 hash 值也会变,这样就会使缓存失效。(改变的是 a.js 文件但是 index.js 文件的 hash 值也改变了) 解决办法:runtimeChunk –> 将当前模块记录其他模块的 hash 单独打包为一个文件 runtime,这样 a.js 的 hash 改变只会影响 runtime 文件,不会影响到 index.js 文件
output: {
filename: 'js/[name].[contenthash:10].js',
path: resolve(__dirname, 'build'),
chunkFilename: 'js/[name].[contenthash:10]_chunk.js' // 指定非入口文件的其他chunk的名字加_chunk
},
optimization: {
splitChunks: {
chunks: 'all',
/* 以下都是splitChunks默认配置,可以不写
miniSize: 30 * 1024, // 分割的chunk最小为30kb(大于30kb的才分割)
maxSize: 0, // 最大没有限制
minChunks: 1, // 要提取的chunk最少被引用1次
maxAsyncRequests: 5, // 按需加载时并行加载的文件的最大数量为5
maxInitialRequests: 3, // 入口js文件最大并行请求数量
automaticNameDelimiter: '~', // 名称连接符
name: true, // 可以使用命名规则
cacheGroups: { // 分割chunk的组
vendors: {
// node_modules中的文件会被打包到vendors组的chunk中,--> vendors~xxx.js
// 满足上面的公共规则,大小超过30kb、至少被引用一次
test: /[\\/]node_modules[\\/]/,
// 优先级
priority: -10
},
default: {
// 要提取的chunk最少被引用2次
minChunks: 2,
prority: -20,
// 如果当前要打包的模块和之前已经被提取的模块是同一个,就会复用,而不是重新打包
reuseExistingChunk: true
}
} */
},
// 将index.js记录的a.js的hash值单独打包到runtime文件中
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`
},
minimizer: [
// 配置生产环境的压缩方案:js/css
new TerserWebpackPlugin({
// 开启缓存
cache: true,
// 开启多进程打包
parallel: true,
// 启用sourceMap(否则会被压缩掉)
sourceMap: true
})
]
}
webpack 中的默认配置
entry: "./src/index.js"
output.path: path.resolve(__dirname, "dist")
output.filename: "[name].js"
题外话
webpack 东西太多了!!!这学的脑瓜子难受,感觉受到了 dos 攻击,我大脑都要拒绝服务(拒绝思考)了,不过学的时候看弹幕其他人都是先用 vue 后学 webpack,我这直接上来就 webpack,真的头大。还有,这笔记记录的只不过 webpack 的冰山一角,看看官方文档的目录就直接跪了,更不要提写自己的 loader 和 plugin 了,学无止境,webpack 先到此为止吧,啥时候有时间再啃文档,真尼玛多。