第一章 Webpack 简介
1.1 Webpack 是什么
- 是一种前端资源构建工具,一个静态模块打包器(nodule bundle)
- 前端所有资源文件(js/json/css/img…)都会作为模块处理
- 它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源(bundle)
1.2 Webpack 打包流程
index.js
//引入 js 资源
>>import $ from 'jquery'
//引入样式资源
>>import './index.less'
1.首先告诉 Webpack 一个入口文件,如 index.js 为起点作为打包
2.将里面的所有依赖项引入进来,这些依赖会跟入口文件形成一个文件(代码块 chunk) ,再将这个代码块进行处理,比如把 less 文件编译成 css,js 资源编译成浏览器能识别的 js 语法等等操作,这些就叫做打包
3.将打包好的资源再输出出去,这个文件叫 bundle
简而言之,就是将静态资源(模块)进行处理(打包),所以就是静态模块打包器
1.3 Webpack 五个核心概念
-
Entry
入口(Entry)指示 Webpack 以哪个文件为入口起点开始打包,分析内部构件依赖图
-
Output
输出(Output)指示 Webpack 打包后的资源 bundles 输出到哪里去,以及如何命名
-
Loader
Loader 能让 Webpack 处理非 JavaScript/json 文件(Webpack 自身只理解 JavaScript/json )
-
Plugins
插件(Plugins)可以用于执行范围更广的任务,包括从打包优化和压缩到重新定义环境中的变量
-
Mode
模式(Mode)指示 Webpack 使用相应模式的配置
选项 描述 特点 development(开发模式) 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置 为 development。启用 NamedChunksPlugin 和 NamedModulesPlugin 能让代码本地调试运行的环境 production (生产模式) 能让代码优化上线运行的环境
第二章 Webpack 初体验
- src 文件夹 --项目源文件目录
- build文件夹 --打包后文件输出目录
- src 》 index.js --入口文件
运行指令
- 开发环境:webpack ./src/index.js -o ./build/build.js --mode=development
- webpack 会以 ./src/index.js 为入口文件开始打包,打包后输出到 ./build/build.js,打包环境是开发环境
- 功能:webpack 能够编译打包 js 和 json 文件,并且能将 es6 的模块化语法转换成浏览器能识别的语法
- 生产环境:webpack ./src/index.js -o ./build/build.js --mode=production
- 功能:在开发配置功能上多一个功能,压缩代码
重写运行指令
-
执行 webpack 命令默认是找 webpack.config.js 文件
-
执行其他文件进行打包
-
webpack --config 文件名
-
或者在 package.json 文件里配置一项命令
"scripts": { "build": "webpack --config ./base.config.js" }
这样执行
npm run build
就等于指定当前目录的base.config.js
-
实例
index.js >>
function add(x, y) {
return x + y;
}
console.log(add(1, 2));
webpack ./src/index.js -o ./build/build.js --mode=development
index.html >>
<script src="../build/build.js"></script>
</body>
//3
结论
-
webpack 能够编译打包 js 和 json 文件
-
能将 es6 的模块化语法转换成浏览器能识别的语法
-
能压缩代码
第三章 打包其他资源
webpack.config.js --webpack 配置文件
- 作用:指示 webpack 干哪些活(当运行 webpack 指令时,默认加载里面的配置)
- 所有构建工具都是基于 node.js 平台运行,模块化默认采用 CommonJS
基本配置
const path = require('path');
//配置
module.exports = {
//1.入口
entry: path.join(__dirname,'src/index.js'),
//2.输出
output: {
//输出文件名
filename: 'build.js',
//输出路径,使用绝对路径
path: path.join(__dirname,'build')
},
//3.loader 配置
module: {
rules: [
//详细的 loader 配置
],
},
//4.plugins 配置
plugins: [
//详细的 plugins 配置
],
//5.模式
mode: 'development', //开发模式
//mode: 'production' //生产模式
}
3.1 打包样式资源
需要安装 css-loader、style-loader 包,打包 less 还需安装 less 和 less-loader 包
// webpack.config.js
const path = require('path');
module.exports = {
entry: path.join(__dirname,'src/index.js'),
output: {
filename: 'build.js',
path: path.join(__dirname,'build')
},
module: {
rules: [
{
//匹配以 .css 结尾的文件
test: /\.css$/,
//使用哪些 loader 进行处理,use 数组中执行顺序从下到上依次执行
use: [
//创建 style 标签,将 js 中的样式资源插入进行,添加到 head 中生效
'style-loader',
//将 css 文件变成 CommonJS 模块加载 js 中,里面内容是样式字符串
'css-loader'
]
},
{
//匹配以 .less 结尾的文件
test: /\.less$/,
use: [
'style-loader',
//将 less 文件编译成 css 文件
'css-loader',
//需要安装 less-loader 和 less 包
'less-loader'
]
}
]
},
mode: 'development'
}
// index.js
import './index.css'
import './index.less'
// index.html
<h1 id="title">hello less</h1>
<script src="../build/build.js"></script>
命令:webpack
3.2 打包 html 资源
需要安装 html-webpack-plugin 插件,配置 plugins
const path = require('path');
// 引入 html-webpack-plugin 插件
const HtmlPlugin = require('html-webpack-plugin');
module.exports = {
// 入口文件
entry: path.join(__dirname, 'src/index.js'),
// 输出
output: {
filename: 'build.js',
path: path.join(__dirname, 'build')
},
plugins: [
// 功能:默认会创建一个空的 HTML,自动引入打包输出的所有资源(JS/CSS)
// 需求:需要有结构的 HTML 文件
new HtmlPlugin({
// 复制 './src/index.html' 文件,并自动引入打包输出的所有资源(JS/CSS)
template: path.join(__dirname,'./src/index.html')
})
],
mode: 'development'
}
-
build 文件夹下会生成 build.js 和 index.html 文件
-
index.html 文件会自动引入 build.js
3.3 打包 img资源
需要安装 url-loader 和 file-loader 包(处理样式中的 img),处理 html 中的 img 还需要安装 html-loader 包
- 小于 limit 的值使用 url-loader 处理
- 大于 limit 的值使用 file-loader 处理
const path = require('path');
const HtmlPlugin = require('html-webpack-plugin');
module.exports = {
entry: path.join(__dirname, 'src/index.js'),
output: {
filename: 'build.js',
path: path.join(__dirname, 'build')
},
module: {
rules: [
// 处理 less 资源
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader'
]
},
// 处理图片资源
{
// 问题:默认处理不了 html 中 img 图片,现在只能处理 less 中的 img
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
// 图片大小小于 8kb,就会被 base64 处理
// 优点: 减少请求数量(减轻服务器压力)
// 缺点:图片体积会更大(文件请求速度更慢)
limit: 8 * 1024,
// 问题:因为 url-loader 默认使用 es6 模块化解析,而 html-loader 引入图片是 commonjs
// 解析时会出问题:[object Module]
// 解决:关闭 url-loader 的 es6 模块化,使用 commonjs 解析
esModule: false,
// 给图片进行重命名,[hash:10]取图片的 hash 的前 10 位,[ext]取文件原来扩展名
name: '[hash:10].[ext]'
}
},
// 处理 html 文件中的 img
{
test: /\.html$/,
loader: 'html-loader'
}
]
},
plugins: [
new HtmlPlugin({
template: path.join(__dirname, 'src/index.html')
})
],
mode: 'development'
}
3.4 打包其他资源
除了css/js/html/less 资源,依赖 file-loader 打包
const path = require('path');
const HtmlPlugin = require('html-webpack-plugin');
module.exports = {
entry: path.join(__dirname,'src/index.js'),
output: {
filename: 'build.js',
path: path.join(__dirname,'build')
},
module: {
rules: [
{
test: /\.css$/,
use:['style-loader','css-loader']
},
// 打包其他资源(除了 html/js/css 资源以外的资源)
{
// 排除 css/js/html/less 资源
exclude: /\.(html|js|css|less)/,
loader: 'file-loader',
options: {
name: '[hash:10].[ext]'
}
}
]
},
plugins: [
new HtmlPlugin({
template: path.join(__dirname,'src/index.html')
})
],
mode: 'development'
}
3.5 清空 dist 目录
重新打包自动清空 dist 目录
npm install clean-webpack-plugin --save-dev
// 导入插件
const {
CleanWebpackPlugin } = require('clean-webpack-plugin');
// 配置插件
module.exports = {
plugins: [
new CleanWebpackPlugin()
]
}
3.6 打包vue资源
npm install -D vue-loader vue-template-compiler
// webpack.config.js
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
module: {
rules: [
// ... 其它规则
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
},
plugins: [
// 请确保引入这个插件!
new VueLoaderPlugin()
]
}
第四章 devServer
用于开发环境
- 自动编译,自动打开浏览器,自动刷新浏览器
- 安装 webpack-dev-server 包
- 启动命令: npx webpack-dev-server
配置:
devServer: {
//运行代码目录(项目构建后路径)
contentBase: path.join(__dirname,'build'),
//监视 contentBase 目录下所有文件,一旦文件发生变化就 刷新
watchContentBase: true,
//改变文件页面实时刷新
inline: true,
//忽略监视 node_modules 目录下文件
watchOptions: {
ignored: /node_modules/
}
//启动gzip压缩
compress:true,
//域名,默认就是localhost
host: 'localhost',
//端口号 5000
port: 5000,
//自动打开浏览器
open:true,
// 开启HMR功能
hot: true,
//不显示启动服务器日志信息
clientLogLevel: 'none',
//除了一些基本启动信息,其他内容都不要显示
quiet: true,
//如果出错,不要全屏提示
overlay: false,
//服务器代理,解决开发环境跨域问题
proxy: {
'/api': {
target: 'http://localhost:3000',
pathRewrite: {
'/api': ''
},
changeOrigin: true,
secure: false
}
/**
* 参数说明:
* '/api': 如果浏览器向服务器发送'/api'标志的请求,则会转发到代理服务器上,然后由代理服务器去请求
* target: 代理的IP地址(目标服务器)
* pathRewrite: 路径重写,设置 '/api': '' 后,会去掉 /api
* changeOrigin: target 参数是域名的话,需要设置为true
* secure: 不检查安全问题,协议是https的话,需设置为false
*/
}
}
第五章 开发环境配置
参考文档
第六章 CSS文件处理
提取css成单独文件,避免js体积过大或者出现闪屏现象
6.1 提取 css 文件
安装 mini-css-extract-plugin 插件
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: path.join(__dirname,'src/js/index.js'),
output: {
filename: 'js/built.js',
path: path.join(__dirname, 'build')
},
module: {
rules: [{
test: /\.css$/,
use: [
// 这个 loader 取代 style-loader。作用:提取 js 中的 css 成单独文件
MiniCssExtractPlugin.loader,
// 将 css 文件整合到 js 文件中
'css-loader'
]
}]
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname,'src/index.html')
}),
new MiniCssExtractPlugin({
// 对输出的 css 文件进行重命名
filename: 'css/built.css'
})
],
mode: 'development'
}
6.2 css兼容性处理
安装 postcss-loader 和 postcss-preset-env 包
module: {
rules: [{
test: /\.css$/,
use: [
// 这个 loader 取代 style-loader。作用:提取 js 中的 css 成单独文件
MiniCssExtractPlugin.loader,
// 将 css 文件整合到 js 文件中
'css-loader',
//修改 loader 配置
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
// postcss 的插件
require('postcss-preset-env')()
]
}
}
]
}]
}
修改 package.json
"browserslist": {
// 开发环境配置
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
//生产环境配置
"production": [
">0.2%",
"not dead",
"not op_mini all"
]
}
6.3 压缩css
1.安装插件
npm install optimize-css-assets-webpack-plugin
2.webpack.config.js 引包
const OptimizeCssAssetsWebpackPlugin = require(‘optimize-css-assets-webpack-plugin’ )
3.plugins 里配置
new OptimizeCssAssetsWebpackPlugin()
4.命令 webpack
css代码被压缩成一行
第七章 JS文件处理
7.1 JS语法检查(一般不用)
保证团队中 JS 语法一致,只检查自己写的源代码,第三方库不用检查
检查规则:以 airbnb JavaScript 语法为规则
eslint-disable-next-line:忽略下一行警告
npm install eslint-loader eslint eslint-config-airbnb-base eslint-plugin-import
// webpack.config.js
rules: [
{
test: /\.js$/,
// 排除 node_modules 目录
exclude: /node_modules/,
// 优先执行,先检查,后兼容
enforce: 'pre',
loader: 'eslint-loader',
options: {
// 自动修复语法错误
fix: true
}
}
]
// 在 package.json 中 eslintConfig 中设置
"eslintConfig": {
"extends": "airbnb-base",
"env": {
"browser": true
}
}
7.2 JS兼容性处理
1.装包
npm install babel-loader @babel/core @babel/preset-env @babel/polyfill core-js
2.处理
- 基本处理 --> @babel/preset-env
问题:只能转换基本语法,ES6高级语法不能转换
全部处理 --> @babel/polyfill
- 入口文件 index.js 里引入 import ‘@babel/polyfill’
问题:只需处理部分兼容,但是将所有兼容性代码都引入了,体积太大
按需处理 --> core-js
- 使用这种方案就不能使用 @babel/polyfill
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
// 预设:指示 babel 做怎么样的兼容性处理
presets: [
[
'@babel/preset-env',
{
// 按需加载
useBuiltIns: 'usage',
// 指定 core-js 版本
corejs: {
version: 3
},
// 指定兼容性做到哪个版本浏览器
targets: {
chrome: '60',
firefox: '60',
ie: '9',
safari: '10',
edge: '17'
}
}
]
]
}
}]
}
7.3 压缩js和html
-
压缩js
-
将 mode 调成 production(生产模式),打包就会自动压缩
mode: 'production'
-
开发模式压缩(不使用,一旦压缩代码可阅读性差,不利于开发)
//1.装插件 npm i uglifyjs-webpack-plugin -D //2.引入 const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin') //3.plugins 配置,每次打包就会压缩 js new UglifyjsWebpackPlugin()
2.压缩html
-
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
// 压缩 html 代码
minify: {
// 移除空格
collapseWhitespace: true,
// 移除注释
removeComments: true
}
})
]
第八章 生产环境配置
参考文档
第九章 webpack 性能优化
- 开发环境性能优化
- 优化打包构建速度
- HMR
- css --> style-loader
- html --> 不需要
- js --> 人为写
- HMR
- 优化代码调试
- source-map
- 开发环境:eval–source-map
- 生产环境:source-map
- source-map
- 优化打包构建速度
- 生产环境性能优化
- 优化打包构建速度
- oneOf
- babel 缓存
- 多进程打包
- externals
- dll
- 优化代码运行的性能
- 缓存(hash-chunkhash-contenthash)
- tree shaking 树摇
- code split 分割
- 懒加载/预加载
- PWA
- 优化打包构建速度
9.1 HMR 热模块替换
基于devServer,生产环境不需要devServer,所以生产环境不能用HMR功能
优化打包构建速度
一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块),极大提升构建速度
devServer: {
contentBase: path.join(__dirname, 'build'),
compress: true,
port: 3000,
open: true,
// 开启HMR功能
// 当修改了webpack配置,新配置要想生效,必须重新webpack服务
hot: true
}
-
样式文件:可以使用HMR功能,因为style-loader内部实现了
- 开发环境使用style-loader性能更好,打包速度更快
-
JS文件:默认没有HMR功能
-
需要修改js代码,添加支持HMR功能,入口文件做不了,只能处理非入口js文件
if (module.hot) { // 一旦 module.hot 为true,说明开启了HMR功能。 --> 让HMR功能代码生效 module.hot.accept('./print.js', function() { // 方法会监听 print.js 文件的变化,一旦发生变化,其他模块不会重新打包构建。 // 会执行后面的回调函数 print() }) }
-
-
HTML文件:默认没有HMR功能,同时会导致 html 文件不能热更新(即修改没有任何反应)
-
解决不能热更新:修改entry入口,将html文件引入
-
不用做HMR功能,因为只有一个html文件
entry:['./src/js/index.js','./src/index.html']
-
9.2 source-map 映射
一种 提供源代码到构建后代码映射 技术(如果构建后代码出错了,通过映射可以追踪源代码错误的具体位置)
// webpack.config.js
devtool: 'source-map'
/*
* inline-source-map // 内联
* 只生成一个内联的 source-map
* 错误代码准确信息 和 源代码准确位置
*/
/*
* eval-source-map // 内联
* 每一个文件都生成对应的 source-map
* 错误代码准确信息 和 源代码准确位置
*/
/*
* source-map // 外部
* 错误代码准确信息 和 源代码准确位置
*/
/*
* hidden-source-map // 外部
* 错误代码信息,追踪不到位置
*/
内联 和 外部的区别
- 外部生成了文件,内部没有
- 内联构建速度快
开发环境:速度快,调试更友好
- 速度快
- eval-source-map
- 调试更友好
- source-map
推荐 --> eval-source-map
生产环境:源代码要不要隐藏?调试要不要更友好?
- 内联会让代码体积变大,所以在生产环境不用内联
推荐 --> source-map
9.3 oneOf 优化
默认情况下,假设设置了7、8个loader,每一个文件都得通过这7、8个loader处理(过一遍),浪费性能,使用 oneOf 找到了就能直接用,提升性能
正常来讲,一个文件只能被一个 loader 处理,不能有两个配置处理同一种类型文件
rules: [
{
// 在package.json中eslintConfig --> airbnb
test: /\.js$/,
exclude: /node_modules/,
// 优先执行
enforce: 'pre',
loader: 'eslint-loader',
options: {
fix: true
}
},
{
oneOf: [
/*
正常来讲,一个文件只能被一个loader处理。
当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
先执行eslint 在执行babel
*/
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: {
version: 3},
targets: {
chrome: '60',
firefox: '50'
}
}
]
]
}
}
]
}
]
9.4 缓存
项目中js代码是最多的,开启 babel 缓存,当一个 js 文件发生变化时,其它 js 资源不用变,适用于生产环境
缓存:
- babel缓存
- –> 让第二次打包构建速度更快
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: {
version: 3 },
targets: {
chrome: '60',
firefox: '50'
}
}
]
],
// 开启babel缓存
// 第二次构建时,会读取之前的缓存
cacheDirectory: true
}
}
浏览器访问网站后会强缓存资源,第二次刷新就不会请求服务器(一般会定个时间再去请求服务器),假设有了bug改动了文件,但是浏览器又不能及时请求服务器,所以就用到了文件资源缓存(改变文件名的hash值)
- 文件资源缓存
- hash: 不管文件变不变化,每次wepack构建时都会生成一个唯一的hash值
- 问题: 因为js和css同时使用一个hash值
- 如果重新打包,会导致所有缓存失效。(可能我却只改动一个文件)
- chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样
- 问题: js和css的hash值还是一样的
- 因为css是在js中被引入的,所以同属于一个chunk
- contenthash: 根据文件的内容生成hash值。不同文件hash值一定不一样
- –> 让代码上线运行缓存更好使用
- hash: 不管文件变不变化,每次wepack构建时都会生成一个唯一的hash值
new MiniCssExtractPlugin({
filename: 'css/built.[contenthash:10].css'
})
9.5 tree shaking 树摇
去除无用代码
前提:必须使用 ES6 模块化 和 开启 production 环境
- 满足这两个条件打包就能自动去除无用代码
作用:减小代码体积
在package.json中配置
“sideEffects”: false 所有代码都没有副作用(都可以进行tree shaking)
问题:可能会把css / @babel/polyfill (副作用)文件干掉
“sideEffects”: [".css", ".less"]
9.6 code split 分割
将js文件打包分割成多个bundle,避免体积过大
多入口
entry: {
// 多入口:有一个入口,最终输出就有一个bundle
index: './src/js/index.js',
test: './src/js/test.js'
}
多入口公共的文件打包成一个bundle
-
可以将node_modules中代码单独打包一个bundle最终输出
-
自动分析多入口chunk中,有没有公共的文件。如果有会打包成单独一个bundle
// webpack.config.js
entry: {
// 多入口:有一个入口,最终输出就有一个bundle
index: './src/js/index.js',
test: './src/js/test.js'
},
optimization: {
splitChunks: {
chunks: 'all'
}
}
单入口将某个文件单独打包
entry: './src/js/index.js',
optimization: {
splitChunks: {
chunks: 'all'
}
}
index.js >>
//通过js代码,让某个文件被单独打包成一个chunk
//import动态导入语法:能将某个文件单独打包
//webpackChunkName: 'test',设置文件名为test
import(/* webpackChunkName: 'test' */'./test')
.then(({
mul }) => {
// 文件加载成功~
// eslint-disable-next-line
console.log(mul(2, 5));
});
9.7 懒加载和预加载
懒加载:当文件需要使用时才加载
预加载 prefetch:会在使用之前,提前加载js文件,等其他资源加载完毕,浏览器空闲了,再偷偷加载资源
正常加载可以认为是并行加载(同一时间加载多个文件)
懒加载
index.js >>
document.getElementById('bth').onclick = function() {
import(/* webpackChunkName: 'test' */'./test').then(({ mul }) => {
console.log(mul(4, 5));
});
}
test.js >>
console.log('test.js文件被加载了~'); //这段话一开始不会加载
export function mul(x, y) {
return x * y;
}
预加载
webpackPrefetch: true
index.js >>
document.getElementById('bth').onclick = function() {
import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({ mul }) => {
console.log(mul(4, 5));
});
}
test.js >>
console.log('test.js文件被加载了~'); //这段话一开始不会加载
export function mul(x, y) {
return x * y;
}
9.8 PWA 网站离线访问
-
安装插件 workbox-webpack-plugin , 然后引入
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
-
plugins 里配置
new WorkboxWebpackPlugin.GenerateSW({ /* 1. 帮助serviceworker快速启动 2. 删除旧的 serviceworker 打包后生成一个 serviceworker.js.map 配置文件 */ clientsClaim: true, skipWaiting: true })
-
入口文件 index.js 里注册 serviceworker
// 注册serviceWorker // 处理兼容性问题 if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker .register('/service-worker.js') .then(() => { console.log('sw注册成功了~'); }) .catch(() => { console.log('sw注册失败了~'); }); }); /* 问题: 1. eslint不认识 window、navigator全局变量 解决:需要修改package.json中eslintConfig配置 "env": { "browser": true // 支持浏览器端全局变量 } 2. sw代码必须运行在服务器上 --> nodejs --> npm i serve -g serve -s build 启动服务器,将build目录下所有资源作为静态资源暴露出去 */
9.9 多进程打包
开启多进程打包,主要处理js文件(babel-loader干的活久),进程启动大概为600ms,只有工作消耗时间比较长,才需要多进程打包,提升打包速度
-
安装包 thread-loader ,在 babel-loader 上面配置
use: [ { loader: 'thread-loader', options: { workers: 2 // 进程2个 } }, { loader: 'babel-loader', options: { presets: [ [ '@babel/preset-env', { useBuiltIns: 'usage', corejs: { version: 3 }, targets: { chrome: '60', firefox: '50' } } ] ], // 开启babel缓存 // 第二次构建时,会读取之前的缓存 cacheDirectory: true } } ]
9.10 externals 拒绝打包
有些第三方库不希望打包,而是通过CDN的方式引入,从而提升打包速度
直接配置
externals: {
// 拒绝jQuery被打包进来
jquery: 'jQuery'
}
然后 index.html 文件需手动引入 CDN
<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
9.11 dll 打包第三方库
- code split将第三方库都打包成一个bundle,这样体积过大,会造成打包速度慢
- dll 是将第三方库打包成多个bundle,从而进行速度优化
-
另外写一个配置文件 webpack.dll.js,单独打包库
- 运行 webpack 时,默认查找 webpack.config.js 配置文件
- 运行指令:webpack --config webpack.dll.js
const path = require('path'); const webpack = require('webpack'); module.exports = { entry: { //打包生成的 [name] --> jquery //打包的库是 --> jquery jquery: ['jquery'] }, output: { filename: '[name].js', path: path.join(__dirname,'dll'), //打包向外暴露出去的名字 library: '[name]_[hash:10]' }, plugins: [ // 打包生成一个 manifest.json --> 提供和jquery映射 new webpack.DllPlugin({ //映射库的暴露的内容名字,跟上面library一样 name: '[name]_[hash:10]', // 输出文件路径 path: path.join(__dirname,'dll/manifest.json') }) ], mode: 'production' }
-
webpack.config.js 里配置哪些库不参与打包
- 安装插件 add-asset-html-webpack-plugin
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const webpack = require('webpack'); const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin'); module.exports = { entry: './src/index.js', output: { filename: 'js/build.js', path: path.join(__dirname,'build') }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html' }), // 告诉webpack哪些库不参与打包,同时使用时的名称也得变 new webpack.DllReferencePlugin({ manifest: path.join(__dirname,'dll/manifest.json') }), // 将某个文件打包输出去,并在html中自动引入该资源 new AddAssetHtmlWebpackPlugin({ filepath: path.join(__dirname,'dll/jquery.js') }) ], mode: 'production' };
第十章 配置详解
10.1 entry 入口
-
String(单入口)
entry: './src/index.js'
- 打包形成一个chunk,输出一个bundle
- chunk的名称默认是main,如果output不设置输出文件名,那么打包后就是 main.js
-
Array(多入口) --> 一般不用
entry: ['./src/js/index.js','./src/index.html']
- 所有入口文件最终只会形成一个chunk,输出一个bundle
- 只有一个作用,在HMR功能中让html热更新生效
-
Object(多入口)
entry: { index: './src/js/index.js', add: './src/js/add.js' } //特殊用法 entry: { //所有入口文件最终只会形成一个chunk,输出一个bundle index: ['./src/js/index.js','./src/js/count.js'], //形成一个chunk,输出一个chunk add: './src/js/add.js' }
- 有几个入口文件就形成几个chunk,输出几个bundle
- chunk名称是 key
10.2 output 输出
output: {
//文件名称(指定名称+目录)
filename: 'js/[name].js',
//输出文件目录(将来所有资源输出的公共目录)
path: path.join(__dirname,'build'),
//所有资源引入公共路径前缀 -->
//'js/build.js' => 'dist/js/build.js'
publicPath: 'dist/'
//非入口chunk名称
chunkFilename: 'js/[name]_chunk.js'
//打包后整个库向外暴露的变量名(参考 dll)
library: '[name]',
//变量名加到哪个属性上
libraryTarget: 'commonjs' //以commonjs标准暴露
}
10.3 module
module: {
//loader 的配置
rules: [
{
//多个loader用use
test: '/\.css$/',
use: ['style-loader','css-loader']
},
{
//单个loader用 loader
test: /\.js$/,
loader: 'eslint-loader',
//排除node_modules下的js文件
exclude: /node_modules/,
//只检查 src 下的js文件
include: path.join(__dirname,'src'),
//优先执行, 'post' 延后执行,不写就是按顺序执行
enfore: 'pre'
//loader配置项
options: {
}
},
{
//以下配置只会生效一个
oneOf: []
}
]
}
10.4 resollve 解析模块
resolve: {
//配置解析模块路径别名:优点简写路径 缺点路径没有提示
alias: {
//定义变量$css,后续引入css文件只需 import '$css/index.css'
$css: path.join(__dirname,'src/css'),
//以 .vue 结尾的文件指定使用版本 runtime-compiler 代码中可以有 template
"vue$": 'vue/dist/vue.esm.js'
},
//配置省略文件路径的后缀名,默认可以省略 js 和 json
//import './src/index',默认先找 js ,再找 json,避免出错,文件名不要取相同的
extensions: ['.js','.json','.jsx'],
//告诉webpack解析模块是去找哪个目录
modules: path.join(__dirname,'node_moudles')
}
10.5 optimization
只在生产环境中使用
optimization: {
chunks: 'all',
/ 将当前模块的记录其他模块的hash单独打包为一个文件 runtime
// 解决:修改a文件导致b文件的contenthash变化
runtimeChunk: {
name: entrypoint => `runtime-${
entrypoint.name}`
},
// 配置生产环境的压缩方案:js和css,安装插件 terser-webpack-plugin
minimizer: [
new TerserWebpackPlugin({
// 开启缓存
cache: true,
// 开启多进程打包
parallel: true,
// 启动source-map
sourceMap: true
})
]
}
第十一章 Webpack 5
11.1 安装
npm i webpack@next webpack-cli -D
11.2 关注内容
- 通过持久缓存提高构建性能.
- 使用更好的算法和默认值来改善长期缓存.
- 通过更好的树摇和代码生成来改善捆绑包大小.
- 清除处于怪异状态的内部结构,同时在 v4 中实现功能而不引入任何重大更改.
- 通过引入重大更改来为将来的功能做准备,以使我们能够尽可能长时间地使用 v5
11.3 优化
-
自动删除 Node.js Polyfills
早期,webpack 的目标是允许在浏览器中运行大多数 node.js 模块,但是模块格局发生了变化,许多模块用途现在主要是为前端目的而编写的。webpack <= 4 附带了许多 node.js 核心模块的 polyfill,一旦模块使用任何核心模块(即 crypto 模块),这些模块就会自动应用。
尽管这使使用为 node.js 编写的模块变得容易,但它会将这些巨大的 polyfill 添加到包中。在许多情况下,这些 polyfill 是不必要的。
webpack 5 会自动停止填充这些核心模块,并专注于与前端兼容的模块。
迁移:
- 尽可能尝试使用与前端兼容的模块。
- 可以为 node.js 核心模块手动添加一个 polyfill。错误消息将提示如何实现该目标
-
Chunk 和模块 ID
- 添加了用于长期缓存的新算法。在生产模式下默认情况下启用这些功能。
chunkIds: "deterministic", moduleIds: "deterministic"
-
Chunk ID
-
可以不用使用
import(/* webpackChunkName: "name" */ "module")
在开发环境来为 chunk 命名,生产环境还是有必要的 -
webpack 内部有 chunk 命名规则,不再是以 id(0, 1, 2)命名了
-
-
Tree Shaking
- webpack 现在能够处理对嵌套模块的 tree shaking
// inner.js export const a = 1; export const b = 2; // module.js import * as inner from './inner'; export { inner }; // user.js import * as module from './module'; console.log(module.inner.a);
在生产环境中, inner 模块暴露的
b
会被删除- webpack 现在能够多个模块之前的关系
import { something } from './something'; function usingSomething() { return something; } export function test() { return usingSomething(); }
当设置了
"sideEffects": false
时,一旦发现test
方法没有使用,不但删除test
,还会删除"./something"
- webpack 现在能处理对 Commonjs 的 tree shaking
-
Output
-
webpack 4 默认只能输出 ES5 代码
-
webpack 5 开始新增一个属性 output.ecmaVersion, 可以生成 ES5 和 ES6 / ES2015 代码.
如:
output.ecmaVersion: 2015
-
-
SplitChunk
// webpack4 minSize: 30000; //最小30kb
// webpack5 minSize: { javascript: 30000, //js最小30kb style: 50000, //css最小50kb }
-
Caching
// 配置缓存 cache: { // 磁盘存储 type: "filesystem", buildDependencies: { // 当配置修改时,缓存失效 config: [__filename] } }
缓存将存储到 node_modules/.cache/webpack
-
监视输出文件
-
之前 webpack 总是在第一次构建时输出全部文件,但是监视重新构建时会只更新修改的文件。
-
此次更新在第一次构建时会找到输出文件看是否有变化,从而决定要不要输出全部文件。
-
-
默认值
entry: "./src/index.js
output.path: path.resolve(__dirname, "dist")
output.filename: "[name].js"
-
更多内容
https://github.com/webpack/changelog-v5