webpack优化环境配置
Posted ~往无前
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了webpack优化环境配置相关的知识,希望对你有一定的参考价值。
一、webpack性能优化概述
- 开发环境性能优化
- 生产环境性能优化
1.1开发环境性能优化
- 优化打包构建速度
- HMR
- 优化代码调试
- source-map
1.2生产环境性能优化
- 优化打包构建速度
- oneOf
- babel缓存
- 多进程打包
- externals
- dll
- 优化代码运行的性能
- 缓存(hash-chunkhash-content)
- tree shaking
- code split
- 懒加载/预加载
- pwa
二 、开发环境webpack性能优化
2.1 优化打包构建速度
-
HMR
使用devServer绑定端口,时刻进行监听,如果代码中的内容发生了变化就立马做出响应。
HMR:hot module replacement 热模块替换/模块热替换
作用:一个模块发生变化,只会重新打包这一个模块,(而不是打包所有的模块)
极大提升构建速度。
样式文件:可以使用HMR功能:因为style-loader内部实现了
js文件:默认不能使用HMR功能–需要修改js代码,添加支持HMR功能的代码,比如下面对print.js文件的修改,可以使得该文件具有热部署的功能。注意:HMR功能对js的处理,只能处理非入口js文件的其他文件。入口文件是无法实现HMR功能的。
html文件:默认不能使用HMR功能,同时会导致html代码不能热更新(不用做HMR功能)
解决:修改entry入口,将html文件引入
1.安装
npm install wepbakc-dev-server -D
2 .配置
devServer:
contentBase:resolve(__dirname,"build"),
compress:true,
port:3000,
open:true,
//开启HMR功能
//当修改了webpck配置,新配置要想生效,必须重新webpack服务
hot:true,
,
3 .修改js代码使其具有热部署的功能
在index.js文件中加入:
if(module.hot)
module.hot.accept("./print.js",function()
//方法会监听print.js文件的变化,一旦发生变化,其他默认不会重新打包构建
//会执行后面的回到函数
console.log(p(1,4));
)
2.2优化代码调试
- source-map
当 webpack 打包源代码时,可能会很难追踪到 error(错误) 和 warning(警告) 在源代码中的原始位置。例如,如果将三个源文件(a.js
, b.js
和 c.js
)打包到一个 bundle(bundle.js
)中,而其中一个源文件包含一个错误,那么堆栈跟踪就会直接指向到 bundle.js
。你可能需要准确地知道错误来自于哪个源文件,所以这种提示这通常不会提供太多帮助。
为了更容易地追踪 error 和 warning,javascript 提供了 source maps 功能,可以将编译后的代码映射回原始源代码。如果一个错误来自于 b.js
,source map 就会明确的告诉你。简单说,Souce map就是一个信息文件,里面存储这位置信息,也就说,转换后代码的没有给位置,所对应的转换前的位置。
有了它,出错的时候,出错功能据将直接显示原始代码,而不是转换后的代码。
- 文件的配置
devtool:'hidden-source-map',
source-map:一种提供源代码到构建后代码映射技术(如果构建后代码出错了,通过映射可以追踪源代码错误)
[inline-|hideen-|eval-][nosources-][cheap-[module-]]source-map
source-map 外部 错误代码准确信息和源代码的错误位置
inline-source-map 内联 错误代码准确信息和源代码的错误位置
hidden-source-map 外部 错误代码错误原因,但是没有错误位置 不能追踪源代码错误,只能提示到构建后代码的位置
eval-source-map 内联 错误代码准确信息和源代码的错误位置
nosources-source-map:外部 错误代码准确信息和源代码的错误位置,但是没有和源代码信息
cheap-source-map:外部 错误代码准确信息和源代码的错误位置 只能精确的行
cheap-module-resource-map:外部 错误代码准确信息和源代码的错误位置 module会将loader的source map加入
和外部的区别就是:1.外部生成了文件,内敛是没有的 2.内联构建速度更快
开发环境:速度快,调试更友好
速度快:(eval>inline>cheap...)
eval-cheap-source-map
sval-source-map
调试更友好
cheap-module-source-map
调试友好 性能更好
-->eval-source-amp /eval-cheap-module-source-map
生产环境:源代码要不要隐藏?调试要必要更友好
内联会让代码体积变大
nosources-source-map 隐藏全部代码
hidden-source-map 只隐藏源代码,会提示构建后代码错误信息
三、生产环境性能优化
3.1优化打包构建速度
- oneOf
1.我们希望是在loader处理的时候,可以对一种类型的文件,只进行一次的匹配,而不是多次的匹配,这时候就需要使用oneOf来实现这个功能。\\
2.文件配置
代码复用:
const commonCssLoader=[
MiniCssExtractPlugin.loader,
'css-loader',
//还需在package.json中定义browserslist
loader:'postcss-loader',
options:
ident:'postcss',
plugins:()=>
require("postcss-preset-env")()
,
]
rules:[
test:/\\.js$/,
exclue:/node_modules/,
//优先执行
enfore:'pre',
loader:'eslint-loader',
options:
fix:true,
,
//以下loader只会匹配一个
//注意:不能有两个配置处理同一种类型文件
oneOf:[
test:/\\.css$/,
use:[...commonCssLoader]
,
test:/\\.less$/,
use:[...commonCssLoader,'less-loader',]
,
test:/\\.js$/,
exclue:/node_modules/,
loader:'babel-loader',
options:
presets:[
[
'@babel/preset-env',
useBuiltIns:'usage',
corejs:version:3,
targets:
chrome:'60',
firefox:'50'
]
],
,
test:/\\.(jpg|png|gif)/,
loader:'url-loader',
options:
limit:8*1024,
name:'[hash:10].[ext]',
outputPath:'imgs',
esModule:false,
,
test:/\\.html$/,
loader:'html-loader'
,
exclude:/\\.(js|css|less|html|jpg|png|gif)/,
loader:'file-loader',
options:
outputPath:'media'
]
],
- babel缓存
cacheDirectory:true
让第二次打包构建速度更快
文件资源缓存:
hash:每次webpack构件时会生成一个唯一的hash值。问题:js和css同时使用一个hash值。
如果重新打包,会导致所有缓存失效。 (可能我却只改动一个文件)
chunkhash:根据chunk生成的hash值,如果打包来源于同一个chunk,那么hash值就一样
问题:js和css的hash值还是一样的 因为css是在js中被引入的,所以同属于一个chunk 同一个起始文件是同一个hash值。
contenthash:根据文件的内容生成hash值。不同文件hash值一定不一样,所以当一个文件发生改变的时候,就会因为hash值跟上一次的不一样,发起一次新的请求。
而对于没有发生改变的文件,hash值不变,所以直接来自于缓存 —>让代码上线运行缓存更好使用。
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
,
- 多进程打包
1.安装
npm install thread-loader
2.webpack配置如下:
//开启多进程打包,一般用在babel-loader后面
module:
rules: [
test: /\\.js$/,
use: [
loader: 'thread-loader',
options:
workers: 2
,
'babel-loader']
]
但是多进程也有一定的问题:就是当开启一个进程的时候,也会耗费一定的时间,一般是600ms,所以如果项目的打包过程不是很耗时,没必要使用多进程打包。
- externals
作用:
防止将某一些包打包到我们最终的bundle里面从而导致包体积变大。
使用场景:
query我们希望通过csdn链接引入使用,不希望打包到最后的bundle里面,为了避免打包的时候将jquery最终也给打包了,可以配置externals,禁止将jquery打包。
文件配置:
//webpack.config.js
externals:
// 忽略库名 --- npm包名
jquery: 'jQuery'
- dll
dll是什么?
类似于externals,指使哪些库是不需要打包的。和externals区别在于 dll 会单独的对某些库进行单独打包,将多个库打包成一个chunk。
那么它有什么意义呢?
正产情况下一个项目下 node_modules 里面的包会打包成一个chunk,但是第三方库很大,打包成一个chunk文件体积过大。所以通过 dll 将所有库进行单独打包成不同的chunk,更加有利于我们的性能优化,提高构建速度。
应该如何配置呢?
- 先新建一个 webpack.dll.js 文件:配置专门打包某些第三方库(jquery、react、vue 等等),将来构建就不用重复打包了 (重复打包性能差,构建速度会慢)
webpack.dll.js文件配置:
/*
使用dll技术,对某些库(第三方库:jquery,react,vue...)进行单独打包
当运行webpack时,默认查找webpack.config.js配置文件
需求:需要运行webpack.dll.js文件
-->webpack --config webpack.dll.js
*/
const resolve=require('path');
const webpack=require('webpack');
// const webpackConfig = require('../18.HMR copy/webpack.config');
module.exports=
entry:
//最终打包生成的[name]-->jquery
//['jquery]-->要打包的库是jquery
jquery:['jquery']
,
output:
filename:'[name].js',
path:resolve(__dirname,'dll'),
library:'[name]_[hash]',//打包的库里面向外暴露出去的内容叫什么名字
,
plugins:[
//打包生成一个manifest.json--->提供和jquery映射
new webpack.DllPlugin(
name:'[name]_[hash]',//映射库的暴露的名称
path:resolve(__dirname,'dll/manifest.json')//输出文件路径
)
],
mode:'production'
2.webpack文件配置:
plugins:[
new HtmlWebpackPlugin(
template:'./src/index.html'
),
//告诉webpack那些库不参与打包,同时使用时的名称也得变
new webpack.DllReferencePlugin(
manifest:resolve(__dirname,'dll/manifest.json')
),
//但是这时候打包出去的文件中并不包含jquery所以使用下面的插件,将提前打包好的库自动引入html中
//将某个文件打包输出去,并在html中自动引入该资源
new AddAssetHtmlWebpackPlugin(
filepath:resolve(__dirname,'dll/jquery.js')
)
],
3.2 优化代码运行的性能
- tree shaking
tree-shaking可以理解为通过工具"摇"我们的JS文件,将其中用不到的代码"摇"掉,是一个性能优化的范畴。具体来说,在 webpack 项目中,有一个入口文件,相当于一棵树的主干,入口文件有很多依赖的模块,相当于树枝。实际情况中,虽然依赖了某个模块,但其实只使用其中的某些功能。通过 tree-shaking,将没有使用的模块摇掉,这样来达到删除无用代码的目的。
在使用tree shaking之前当然也是有前提的:
1.必须使用ES6模块化
2.开启production
使用ES6模块的原因有以下几点原因:
ES6 module 特点:
- 只能作为模块顶层的语句出现
- import 的模块名只能是字符串常量
- import binding 是 immutable的
ES6模块依赖关系是确定的,和运行时的状态无关,可以进行可靠的静态分析,这就是tree-shaking的基础。
所谓静态分析就是不执行代码,从字面量上对代码进行分析,ES6之前的模块化,比如我们可以动态require一个模块,只有执行后才知道引用的什么模块,这个就不能通过静态分析去做优化。
这是 ES6 modules 在设计时的一个重要考量,也是为什么没有直接采用 CommonJS,正是基于这个基础上,才使得 tree-shaking 成为可能,这也是为什么 rollup 和 webpack 2 都要用 ES6 module syntax 才能 tree-shaking。
文件配置
通过package.josn的sideEffects
属性来实现这种方式:
“sideEffects”:false
问题:所有的导入文件都会的收到tree-shaking的影响。这意味着,如果在项目中使用类似 css-loader并import一个css文件,则需要将其添加到side effect列表中,或者是polyfill,这种只在执行的时候作用的导入模块,所以很可能会把css /@babel/polyfill文件干掉。解决方法如下:
“sideEffects”:["./src/some-side-effectful-file.js", "*.css"]
- code split
关于代码分割:我们经常在自己的项目中导入第三方包,这样如果加载的第三包比较多的时候,这会使得打包的构建速度变慢,解决方法就是进行代码分割。在多入口文件下,更加能体现出来优势,因为有多个入口文件,每个入口文件都会构建,如果其中导入了第三方包,那么每个文件都得引入,就会导致构建的效率降低。
解决方法:通过下面的配置,可以使得第三方包可以单独构建。
webpack.config.json文件:
optimization:
splitChunks:
chunks:'all'
,
但是通过上面这种直接的方式,会使得导出第三方文件的文件名是一个hash值,不利于阅读,这时候我们通过chunkFilename配合魔法注释,可以修改默认的文件名。
index.js文件:
/*
项目中webpack会自动识别出import这种语法,从而进行代码分割。
import之后会返回一个promise对象。魔法注释 webpackChunkName
通过js代码,让某个文件被单独打包成一个chunk
import动态导入语法:能将某个文件单独打包
*/
import(/*webpackChunkName:'test'*/'./test')
.then((mul,count)=>
//文件加载成功
//eslint-disable-next-line
console.log(mul(2,5));
//eslint-disable-next-line
console.log(count(4,1));
)
.catch(()=>
console.log("文件加载失败");
)
import(/*webpackChunkName:'jquery'*/'jquery')
webpack.config.js配置文件
output:
//配置属性chunkFilename配合魔法注释,可以修改默认的文件名
filename:'js/built.[contenthash:10].js',
path:resolve(__dirname,'build'),
//这里的[name]使用的上面的魔法注释
chunkFilename:'js/[name].[hash:8].js'
,
optimization:
splitChunks:
chunks:'all'
,
- 懒加载
懒加载就是,我不需要直接将js代码加载,而是在我需要的时候在加载所需要的内容。
加载:是指已经是否被浏览器所加载。
先进行代码分割,然后再进行懒加载。
懒加载:当文件需要使用时才加载。
预加载:webpackPrefectch:会在使用之前,提前加载js文件。当使用的时候直接显示,会有兼容性问题,慎用。
真正的加载可以认为做并行加载(同一时间加载多个个文件)
预加载prefetch:等其他资源加载完毕,等浏览器空闲了,在偷偷加载资源,这种使用与当文件比较大的时候。
如果使用懒加载,那么会由于文件太大无法很快地显示给用户。
下面举个实例:
实例:
//这里给元素添加一个点击事件,当用户端架的时候会触发这部分js代码
document.getElementById('btn').onclick = function ()
// 懒加载
import(/* webpackChunkName: 'test' */"./test.js")
.then((multi) =>
console.log(multi)
console.log('multi=', multi(3, 3))
).catch(() =>
console.log('test文件加载失败!')
)
//在import语法中添加一个参数 webpackPrefetch表示预加载
document.getElementById("btn").onclick=function()
import(/*webpackChunkName:'test',webpackPrefetch:true*/"./test").then((mul)=>
console.log(mul(4,5));
)
以上是关于webpack优化环境配置的主要内容,如果未能解决你的问题,请参考以下文章
[万字逐步详解]使用 webpack 打包 vue 项目(优化生产环境)
webpack自定义配置的方法及webpack-merge优化
webpack开发与生产环境 性能优化配置 - HMR - 缓存 -tree shaking - 代码分割 - 懒加载 - 预加载 - PWA - 多进程打包 - externals - dll(代码