前置
开发环境
- 优化打包构建速度 HMR
- HMR(模块热替换)
- 优化代码调试 souceMap
生产环境
- 优化打包构建速度
* oneof
* babel缓存
* 多进程打包(提升打包速度,但是进程开启交流有时间开销)
* externals (不让一些资源包去打包,排除掉)-- cdn
* dil (先打包好,后面直接用,就不用打包了)-- 自己的服务器 - 优化代码运行的性能
* 缓存(hash-chunkhash-contenthash)
* tree shaking
* code split 代码分割
* 懒加载/与加载
* pwa(离线访问)
HMR
(hot module replacement) 热模块替换 / 模块热替代
作用:
- 一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块)
- 极大提升构建速度
- 样式文件: 可以 使用HMR功能:因为style-loader内部实现了
- js文件:默认不能使用HMR功能 --> 需要修改js代码,添加支持HMR功能的代码
注意:HMR功能对js的处理,只能处理非入口js文件的其他文件。 - html文件: 默认不能使用HMR功能, 同时会导致问题,html文件不能热更新了!(不用做HMR功能,因为框架基本都是虚拟dom加载到页面上–单页面、组件化)
解决:修改entry入口,将html文件引入。[’./src/js/index.js’, ‘./src/index.html’]devServer: { ... // 开启HMR功能,!!修改了配置,要重启webpack服务 hot: true }
source-map
一种提供源代码到构建后代码技术映射(如果构建后代码出错了,通过映射关系可以追踪到源代码错误)便于debug
// [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
devtool: 'inline-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加入
内联和外部的区别
- 外部生成了文件,内联没有 (xx.js.map文件)
- 内联构建速度更快
- 内联会是代码体积变大
具体环境应用
开发环境:速度快,调试更友好。 —> eval-source-map / eval-cheap-source-map
- 速度快(eval>inline>cheap>…) eval-cheap-source-map: 最快 > eval-source-map
- 调试友好 source-map> cheap-module-source-map >cheap-source-map
生产环境:源代码要不要隐藏,调试要不要友好。—> source-map/ cheap-module-source-map
-
内联会让代码体积变大,所以生产环境不用内联
-
nosources-source-map 全部隐藏
-
hidden-source-map 只隐藏源代码,会提示构建后代码错误信息
oneOf
loader只会匹配一个(注意: 不能有两个配置处理同一种类型文件)
module: {
rules: [
{
oneOf: [
{
...
}
]
}
]
}
缓存
同HMR 但是生产环境是不会配置HMR的,所以需要开启babel缓存
-
babel缓存 ==》让第二次打包构建速度更快
cacheDirectory: true
{
test: /\.js$/,
....,
options: {
...,
// 开启babel缓存
// 第二次构建时,才会读取之前缓存,速度更快
cacheDirectory: true
}
}
-
文件资源缓存 ==》 让代码上线运行缓存更好使用
hash: built.[hash:10].js 每次webpack构建时会生成一个唯一的hash值
【问题】因为js和css同时使用一个hash值,如果重新打包,会导致所有缓存失效。(可能我却只改动一个 文件)
【解决】chunkhash: built.[chunkhash:10].js 根据chunk(代码块)生成的hash值。如果打包来源于用一个chunk, 那么hash值一样
【问题】: js和css的hash值还是一样的,因为css是在js中被引入的,所以同属于一个chunk
【解决】 contenthash: built.[contenthash:10].js 根据文件的内容生成hash值。不同文件hash不一样
tree shaking
作用:1.去除在应用程序中无用的代码,使打包后的代码体积变小(把一个模块中没用到的代码过滤掉)
前提:1.必须用es6模块化 2.开启production环境(就自动开启了) 3.只应用于js
在package.json中配置
“sideEffects”: false 所有代码都没有副作用(都可以进行tree shaking )
【问题】 可能会把css / @babel/polyfill(副作用)文件干掉
【解决】“sideEffects“:["*.css", "*.less"]
注释不想删除的代码
名词解释
Babel: 是一个广泛使用的 ES6 转码器,可以将 ES6 代码转为 ES5 代码。注意:Babel 默认只转换新的 JavaScript 句法(syntax),而不转换新的 API。
Polyfill: 用于实现浏览器并不支持的原生API的代码。
code split 代码分割
- 第一种: 多入口
entry:{
// 多入口: 有一个入口,最终输出就有一个bundle
main: './js/index.js',
test: './js/test.js'
},
output: {
// [name]:取文件名,生成对应文件的输出文件
filename: 'js/[name].[contenthash:10].js',
path: resolve(__dirname, 'build')
}
- 第二种 单入口
- 可以将node_modules中代码单独打包成一个chunk最终输出
- 自动分析多入口chunk中,有没有公共的文件,如果有,会打包成单独的一个chunk
optimization: {
splitChunks: {
chunks: 'all'
}
}
- 第三种 单入口
通过js代码,让某个文件被单独打包成一个chunk
import动态导入语法:能将某个文件单独打包
// 魔法注释去命名输出文件的名字
//!该代码在input入口文件中写
import(/* webpackChunkName: 'test'*/'./test')
.then((res)=>{
// 文件加载成功~
...
})
.catch(()=>{
...
})
懒加载和预加载
懒加载:用到的时候再加载,不用的时候不要加载 (加载过多,会出现触发事件后需要等待的问题)
预加载: 等其他资源加载完毕,浏览器空闲了,再偷偷加载资源(慎用-兼容问题)
正常加载:可以认为是并行加载(同一时间加载多个文件)
预加载事例:
document.getElementById('btn').onClick = function() {
// 必须打包成单独的文件,才能用时候再调用
// 懒加载:当文件需要使用时,才加载
// 预加载: webpackPrefetch: true 就是初始化先加载了js文件,等用的时 候直接调用
import(/* webpackChunkName: 'test', webpackPrefetch: true*/'./test').then(({mul}) => {
....
})
}
PWA -不常用,知道就行
渐进式网络应用程序(progressive web application)是一种可以提供类似于native app (原生应用程序)体验的web app (网络应用程序)
离线可访问
workbox -> workbox-webpack-plugin
// webpack.config.js
plugins: [
/*
1.帮助serviceworker快速启动
2.删除旧的 serviceworker
生成一个serviceworker配置文件~
*/
new workboxwebpackplugin.GenerateSW({
clientsClaim: true,
skipWatting: true
})
]
多进程打包
缺点:进程启动大概需要600ms, 进程通信也要花时间,只有工作时间消耗比较长,才需要多进程打包.
使用:loader: 'thread-loader’
test: /\.js$/,
use: [
// 开启多进程打包
{
loader: 'thread-loader',
options: {
workers: 2 // 进程2个
}
},
{
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: {version: 3}
...
}
]
]
}
}
...
]
@babel/preset-env: 它可以根据开发者的配置,按需加载插件,并不是一股脑都加载,因为现在需要兼容的越来越少,多余的转换不单降低执行效率,还浪费带宽。
externals
项目用cdn请求资源时,用这个可以忽略它去打包
cdn: 内容分发网络,分布式存放静态资源,缓解服务器压力。
externals: {
// 忽略库名 -- npm 包名
// 拒绝JQuery打包进来
jquery: 'JQuery'
}
dil
使用dil技术,对某些库(第三方库:jquery、react、vue…)进行单独打包
当你运行webpack时,默认查找webpack.config.js
当运行webpack.bil.js文件 --> webpack --config webpack.dil.js
/*webpack.bil.js*/
module.exports = {
entry: {
// 最终打包生成【name】 --> jquery
//['jquery'] 要打包的库是jquery
jquery: ['jquery']
},
output: {
filename: '[name].js',
path: resolve(__dirname, 'dil'),
library: '[name]_[hash]', // 打包的库里面向外暴露出去的内容叫什么名字
},
plugins: [
// 打包生成一个manifast.json --> 提供和jquery映射
new webpack.DilPlugin({
name: '[name]_[hash]', //映射库暴露的内容名称
path: resolve(__dirname, 'dil/manifest.json') // 输出文件路径
}),
new webpack.DilReferencePlugin({
manifest: resolve(__dirname, 'dil/manifest.json')
})
],
mode: 'production'
}
/*webpack.config.js*/
// add-asset-html-webpack-plugins
plugins: [
...
// 告诉webpack哪些库不参与打包,同时使用时的名称也得变
new webpack.DilReferencePlugin({
manifest: resolve(__dirname, 'dil/manifest.json')
}),
// 将某个文件打包输出去,并在html中自动引入该文件
new AddAssetHtmlWebpackPlugin({
filename: resolve(__dirname, 'dil/dil/jquery.jss')
})
],