提高开发体验:sourceMap
它会生成一个xxx.map文件,里面包含源代码和构建后的代码每一行和每一列映射的关系。当构建后的代码报错了,它可以通过这个map文件,找到对应的源文件的位置,然后让浏览器显示出来。开发和生产模式下,都默认显示错误的行号,不暴露源代码文件。
1.开发模式配置devtool为'eval-source-map'的好处:
如果不配置,代码报错后显示的文件是打包后的文件,行号会和源代码不一致,不利于寻找错误的原因。配置后则点击错误信息会显示源文件,行号也对应上。
2.如何设置:
配置webpack.config.js文件
module.exports = {
// 显示正确的行号,暴露源代码
devtool:'eval-source-map',
},
生产模式的参数:
- 'source-map':显示正确的行号,暴露源代码
- 'nosources-source-map':显示正确的行号,不暴露源代码,折中的方法
3.注意:
出于安全性考虑,发布项目时一般不设置devtool或设置为'nosources-source-map',防止源代码泄露。
更多详情查看:Devtool | webpack 中文文档 | webpack 中文文档 | webpack 中文网 (webpackjs.com)
提升打包速度
HMR
在webpack4及以下的版本中,当我们修改了某个模块的代码,会默认将所有模块重新编译打包,速度很慢。另外,在vue,react等脚手架,它们所有模块都配置好了HMR。
1.是什么?
全称:HotModuleReplacement,又叫热模块替换。设置HMR后,更改代码,只会更新某个模块,而无需加载整个页面。
2.怎么用?
CSS样式经过style-loader处理后,已经开启了HMR,而js还没有,需要手动配置。
在入口文件的尾部添加如下代码
if (module.hot) {
module.hot.accept("./js/count");
module.hot.accept("./js/sum");
}
当修改count文件后
OneOf
打包时,每个文件都会经过所有loader,这比较耗时。
1.是什么?
顾名思义,就是每个文件只匹配一个loader,剩下的就不匹配了
2.怎么用?
rules: [
{
oneOf: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
],
},
],
Include-Exclude
开发时,我们经常会用到第三方库和插件,相关的文件被放到node_modules中,这些文件都是编译好的,无需再处理。所以,我们可以跳过这些文件。
1.是什么?
Include:选择处理哪些文件
Exclude:不处理哪些文件
2.怎么用?
{
test: /\.js$/,
// node_modules中的js文件不做处理
// exclude: /(node_modules)/,
// 只处理src目录下的js文件
include: path.join(__dirname, "../src"),
// loader只能写一个loader,而use数组可以写多个loader
loader: "babel-loader",
},
Eslint和Bable缓存
每次打包js文件,都会经过Eslint检查和Babel编译,速度比较慢。
1.是什么?
缓存第一次Eslint检查和Babel编译的结果,修改js文件只局部更新,提高之后的打包速度
2.怎么用?
开启Babel缓存
{
test: /\.js$/,
// node_modules中的js文件不做处理
// exclude: /(node_modules)/,
// 只处理src目录下的js文件
include: path.join(__dirname, "../src"),
// loader只能写一个loader,而use数组可以写多个loader
loader: "babel-loader",
options: {
// 开启babel缓存
cacheDirectory: true,
// 关闭缓存文件压缩
cacheCompression:false
}
},
开启Eslint缓存
plugins: [
new ESLintPlugin({
// 指定要检查的文件目录
context: path.join(__dirname, "../src"),
cache: true,
cacheLocation:path.join(__dirname,'../node_modules/.cache/eslintCache')
}),
],
多进程打包
因为一般的项目js代码是最多的,所以我们可以针对Eslint(语法检查),Babel(代码编译)和terser(代码压缩)入手
1.是什么?
开启电脑的多个进程干同一件事,只有项目较大时,打包速度才有明显的提升;项目较小不推荐使用,因为进程的启动需要时间,可能导致打包速度变得更慢。
2.怎么用
下载依赖:
npm i thread-loader -D
获取cpu核数:
const os = require('os');
const threads = os.cpus().length
配置:
Eslint
plugins: [
new ESLintPlugin({
// 指定要检查的文件目录
context: path.join(__dirname, "../src"),
cache: true,
cacheLocation: path.join(__dirname, "../node_modules/.cache/eslintCache"),
// 开启多进程和设置进程数量
threads,
}),
],
Babel
{
test: /\.js$/,
// node_modules中的文件不做处理
exclude: /(node_modules)/,
// loader只能写一个loader,而use数组可以写多个loader
use: [
{
// 开启多进程
loader: "thread-loader",
options: {
// 进程数量
works: threads,
},
},
{
loader: "babel-loader",
options: {
// 开启babel缓存
cacheDirectory: true,
// 关闭缓存文件压缩
cacheCompression: false,
},
},
],
},
terser
const terserWebpackPlugin = require("terser-webpack-plugin");
// 优化
optimization: {
minimizer: [
new terserWebpackPlugin({
// 开启多进程和设置进程数量
parallel: threads,
}),
],
},
减少代码体积
TreeShaking
1.作用
用于移除javascript中没有引用的代码
2.怎么用
webpack的生产模式已经默认开启了该功能
Babel runtime
Babel在每个文件都添加了辅助代码,使代码体积过大,可以引入 Babel runtime 作为一个独立模块。
1.作用
避免重复引入,减少代码体积
2.怎么用
下载依赖:
npm i @babel/plugin-transform-runtime -D
配置:
rules: [
// 'transform-runtime' 插件告诉 Babel
// 要引用 runtime 来代替注入。
{
test: /\.js$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
plugins: ['@babel/plugin-transform-runtime']
}
}
}
]
压缩图片
优化代码运行性能
打包后所有js代码都放在同一个文件中,体积太大了。如果我们打开首页,就应该只加载首页相关的js代码,而不是加载所有js代码。
code split
作用:
- 将打包后的js文件分割成多个js文件
- 抽取公共js代码到一个文件中
- 按需加载,需要哪个js文件就加载哪个js文件
配置:
const path = require("path");
const htmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// 多入口
entry: {
app:"./src/app.js",
index:"./src/index.js",
},
output: {
path: path.join(__dirname, "dist"),
// [name]对应入口文件名
filename:"[name].js"
},
plugins: [
new htmlWebpackPlugin({
template:path.join(__dirname,'public/index.html')
})
],
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
default: {
minSize: 0,
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
mode:"production",
};
按需加载:
document.getElementById("btn").addEventListener("click", function () {
// 按需加载,动态引入add.js文件,只有点击按钮后才会加载该文件
import("./add").then(({ add}) => {
console.log(add(10,10));
});
});
Preload/Prefetch(预加载)
按需加载后,如果要加载的文件太大,则容易卡顿,给用户不好的体验。我们可以用Preload/Prefetch来解决这个问题。
1.是什么
- Preload:让浏览器立即加载资源
- Prefetch:让浏览器空闲时加载资源
2.缺点:兼容性差
3.如何使用
下载依赖
npm install --save-dev @vue/preload-webpack-plugin
// 引入
const PreloadWebpackPlugin = require('@vue/preload-webpack-plugin');
plugins: [
new PreloadWebpackPlugin({
rel: 'prefetch'
})
]
Core-js
1.作用:彻底解决兼容性问题
2.如何使用
下载依赖:npm i core-js
配置:
*const* PreloadWebpackPlugin = *require*("@vue/preload-webpack-plugin");
plugins: [
new PreloadWebpackPlugin({
rel: "prefetch",
}),
],
PWA
1.作用:实现离线访问项目
2.如何使用
下载依赖:npm install workbox-webpack-plugin --save-dev
配置:
const WorkboxPlugin = require('workbox-webpack-plugin');
plugins: [
new WorkboxPlugin.GenerateSW({
// 这些选项帮助快速启用 ServiceWorkers
// 不允许遗留任何“旧的” ServiceWorkers
clientsClaim: true,
skipWaiting: true,
}),
],
在index中注册service work
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js').then(registration => {
console.log('SW registered: ', registration);
}).catch(registrationError => {
console.log('SW registration failed: ', registrationError);
});
});
}
下载http-server,通过http-server dist 启动dist目录中的项目