webpack 性能優(yōu)化
開(kāi)發(fā)環(huán)境性能優(yōu)化
優(yōu)化打包構(gòu)建速度
HMR優(yōu)化代碼調(diào)試source-map
生產(chǎn)環(huán)境性能優(yōu)化
優(yōu)化打包構(gòu)建速度
oneOf babel 緩存 多進(jìn)程打包 externals dll 優(yōu)化代碼運(yùn)行的性能 緩存(hash,chunkhash,contenthash) tree shaking code split 懶加載和預(yù)加載 pwa
優(yōu)化 開(kāi)發(fā)環(huán)境 打包構(gòu)建速度
HMR hot module replacement 熱模塊替換/模塊熱替換
作用:一個(gè)模塊發(fā)生變化,只會(huì)重新打包這一個(gè)模塊 而不是重新打包所有,極大提升構(gòu)建速度
樣式文件: 可以使用HMR功能,因?yàn)閟tyle-loader內(nèi)部實(shí)現(xiàn)了 js文件: 默認(rèn)不能使用HMR功能 -->解決:需要修改js代碼,添加支持HMR功能的代碼。注意,HMR功能對(duì)js的處理,只能處理非入口js文件的其他文件。
- if(module.hot){
- //一旦module.hot是true,說(shuō)明開(kāi)啟HMR功能,讓HMR功能代碼生效
- module.hot.accept('./xxx.js',function(){
- //此方法會(huì)監(jiān)聽(tīng)print.js文件的變化,一旦發(fā)生變化,其他默認(rèn)不會(huì)重新打包構(gòu)建
- //會(huì)執(zhí)行后面的回調(diào)函數(shù)
- xxx();
- })
- }
html文件: 默認(rèn)不能使用HMR功能,同時(shí)會(huì)導(dǎo)致問(wèn)題:html文件不能熱更新了 解決:改 entry:['入口js','html'] ,但html文件只有一個(gè),所以不用做HMR功能
- devServer:{
- //項(xiàng)目構(gòu)建后的目錄
- contentBase: resolve(__dirname,'build'),
- //啟用gzip壓縮
- compress:true,
- //端口號(hào)
- port:3000,
- //自動(dòng)打開(kāi)瀏覽器
- open:true
- }
優(yōu)化 開(kāi)發(fā)環(huán)境 代碼調(diào)試
source-map 一種提供源代碼到構(gòu)建后代碼映射的技術(shù)
如果構(gòu)建后代碼出錯(cuò)了,通過(guò)映射可以追蹤到錯(cuò)誤的代碼
- webpack.config.js
- devtools:'source-map'
- //其他 參數(shù) [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
- source-map : 外部
- 錯(cuò)誤代碼的準(zhǔn)確信息和錯(cuò)誤位置
- inline-source-map : 內(nèi)聯(lián)
- 錯(cuò)誤代碼的準(zhǔn)確信息和錯(cuò)誤位置
- hidden-source-map : 外部
- 錯(cuò)誤代碼的錯(cuò)誤原因 但沒(méi)有錯(cuò)誤位置,不能追蹤到源代碼的錯(cuò)誤,只能提示到構(gòu)建后代碼的位置
- eval-source-map : 內(nèi)聯(lián)
- 每一個(gè)文件都生成對(duì)應(yīng)的source-map,都在eval
- 錯(cuò)誤代碼的準(zhǔn)確信息和錯(cuò)誤位置
- nosources-source-map : 外部
- 能找到錯(cuò)誤代碼的準(zhǔn)確信息 但沒(méi)有任何源代碼信息
- cheap-source-map : 外部
- 錯(cuò)誤代碼的準(zhǔn)確信息和錯(cuò)誤位置 只精確到行,不精確到列
- cheap-module-source-map : 外部
- 錯(cuò)誤代碼的準(zhǔn)確信息和錯(cuò)誤位置
- module 會(huì)將 loader 的 source-map加入
- 內(nèi)聯(lián) 和 外部的區(qū)別 :
- 1.外部生成了文件但內(nèi)聯(lián)沒(méi)有生成
- 2.內(nèi)聯(lián)構(gòu)建速度更快
開(kāi)發(fā)環(huán)境:速度快,調(diào)試更友好
- 速度快 eval>inline>cheap>...
- 調(diào)試更友好 souce-map>cheap-module-souce-map>cheap-souce-map
- 所以 一般用eval-source-map
生產(chǎn)環(huán)境:源代碼要不要隱藏,調(diào)試要不要友好??jī)?nèi)聯(lián)會(huì)讓體積編碼,所以一般不用內(nèi)聯(lián)
- nosources-source-map 隱藏源代碼
- hidden-source-map 只隱藏源代碼,會(huì)提示構(gòu)建購(gòu)代碼錯(cuò)誤信息
- --> source-map /cheap-module-souce-map
優(yōu)化生產(chǎn)環(huán)境
oneOf
rules里中有許多個(gè)loader,這樣會(huì)導(dǎo)致每個(gè)文件都會(huì)被所有的loader過(guò)一遍,有些能處理,有些處理不了。所以可以利用oneOf達(dá)到以下loader只會(huì)匹配到第一個(gè)。但需要注意,不能有兩個(gè)loader同時(shí)處理同一個(gè)文件
- webpack.config
- module.exports={
- //....
- module:{
- rule:[
- //正常來(lái)講,一個(gè)文件只能被一個(gè)loader處理
- //當(dāng)一個(gè)文件要被多個(gè)loader處理,那么一定要指定loader的執(zhí)行順序
- // 先執(zhí)行eslint 再執(zhí)行babel
- {
- test:/\.js$/,
- exclude:/node_modules/,
- //優(yōu)先執(zhí)行
- enforce:'pre',
- loader:'eslint-loader',
- options:{
- fix:true
- }
- },
- {
- oneOf:[
- {
- test: /\.css$/,
- use:[
- ...commonCssLoader
- ]
- },
- {
- test:/\.less$/,
- use:[
- ...commonCssLoader,'less-loader'
- ]
- },
- {
- test:/\.js$/,
- exclude:/node_modules/,
- loader:'babel-loader',
- options:{
- // 預(yù)設(shè) :指示babel做怎樣的兼容性處理
- presets:[
- [
- '@babel/preset-env',
- {
- //按需加載
- useBuiltIns:'usage',
- //指定core-js版本
- corejs:{
- version:3
- },
- //指定兼容性做到哪個(gè)版本的瀏覽器
- targerts:{
- chrome: '40',
- fixfox: '50',
- ie: '9',
- safari: '10',
- edge: '17'
- }
- }
- ]
- ]
- }
- },
- {
- test:/\.(png|jpg|gif)/,
- loader:'url-loader',
- enModule:true,
- options:{
- limit:8*1024,
- name: '[hash:10].[ext]',
- outputpath:''
- }
- },
- {
- test:/\.html$/,
- loader:'html-loader'
- },
- {
- exclude:/\.(js|less|css|png|jpg|gif)/,
- loader:'file-loader',
- options:{
- name:'[hash:10].[ext]'
- }
- }
- ]
- }
- ]
- },
- }
緩存
1.babel緩存-->第二次打包更快
- {
- test:/\.js$/,
- exclude:/node_modules/,
- loader:'babel-loader',
- options:{
- // 預(yù)設(shè) :指示babel做怎樣的兼容性處理
- presets:[
- [
- '@babel/preset-env',
- {
- //按需加載
- useBuiltIns:'usage',
- //指定core-js版本
- corejs:{
- version:3
- },
- //指定兼容性做到哪個(gè)版本的瀏覽器
- targerts:{
- chrome: '40',
- fixfox: '50',
- ie: '9',
- safari: '10',
- edge: '17'
- }
- }
- ]
- ],
- //第二次構(gòu)建時(shí),會(huì)讀取之前的緩存
- cacheDirectory: true
- }
- },
2.文件資源緩存-->上線緩存優(yōu)化
- hash:每次webpack構(gòu)建會(huì)生成一個(gè)唯一hash值
- 問(wèn)題: 因?yàn)閖s和css同時(shí)使用一個(gè)hash值,如果重新打包,會(huì)導(dǎo)致所有緩存失效,可能我卻只改了一個(gè)文件,
- chunkhash:根據(jù)chunk生成hash值,如果打包來(lái)源于同一個(gè)chunk,那么hash值也一樣
- 問(wèn)題:js和css的hash值還是一樣的。
- 原因:css是由js引入的,所以屬于同一個(gè)chunk
- contenthash: 根據(jù)文件內(nèi)容生成hash值,
- webpack.config.js
tree shaking
去除應(yīng)用程序中沒(méi)有使用的代碼
- 前提:
- 1.必須使用es6模塊化
- 2.開(kāi)啟production模式
- 在package.json中配置
- "sideEffects":false 所有代碼都沒(méi)有副作用,都可以鏡像tree sharking
- 問(wèn)題 可能會(huì)把css/@babel/polyfille 干掉
- "sideEffects": ["*.css","*.less"] 哪些文件不 tree sharking
code split
1.入口配置
- 單入口 //單頁(yè)面應(yīng)用
- entry:'./src/js/index.js'
- 多入口 //多頁(yè)面應(yīng)用
- entry:{
- index:'./src/js/index.js',
- test:'./src/js/test.js'
- }
2.optimization
- module.exports={
- //...
- // 可以將nodemudules中的代碼單獨(dú)打包成一個(gè)chunk最終輸出
- // 還可以自動(dòng)分析多入口chunk中,有沒(méi)有公共的文件,如果有會(huì)打包成一個(gè)單獨(dú)的chunk
- optimization:{
- splitChunks:{
- chunks:'all'
- }
- }
- }
3.import 動(dòng)態(tài)導(dǎo)入語(yǔ)法,能將某個(gè)文件單獨(dú)打包
通過(guò)js代碼,讓某個(gè)文件被單獨(dú)打包成一個(gè)chunk,通過(guò)注釋可以固定此文件的名稱
- import(/*webpackChunkName: 'xxxName' */'./xx/xxx.js')
- .then(res =>{
- //加載成功
- })
- .catch(()=>{
- //加載失敗
- })
懶加載和預(yù)加載
1.懶加載 當(dāng)文件需要用時(shí)才加載
import 動(dòng)態(tài)導(dǎo)入語(yǔ)法
- document.getElementById('btn').onclick = function(){
- import(/*webpackChunkName: 'xxxName' */'./xx/ss.js')
- .then(res=>{
- //干啥干啥
- })
- }
2.預(yù)加載 webpackPrefetch:true
./xx/ss.js 已經(jīng)被加載了,點(diǎn)擊的時(shí)候再?gòu)木彺嬷屑虞d,
- document.getElementById('btn').onclick = function(){
- import(/*webpackChunkName: 'xxxName',webpackPrefetch:true */'./xx/ss.js')
- .then(res=>{
- //干啥干啥
- })
- }
正常加載可以認(rèn)為是并行加載(同一時(shí)間加載多個(gè)文件) 預(yù)加載prefectch 等其他資源加載完畢,瀏覽器空閑了,再偷偷加載資源 兼容性比較差 慎用
PWA 漸進(jìn)式網(wǎng)絡(luò)開(kāi)發(fā)應(yīng)用程序
網(wǎng)絡(luò)離線可訪問(wèn)
webbox-->webbox-webpack-plugin
- const WebboxWebpackPlugin = require('webbox-webpack-plugin')
- module.exports={
- plugins:[
- new WebboxWebpackPlugin.GenerateSW({
- /*
- 1.幫助 serviceWorker 快速啟動(dòng)
- 2.刪除舊的 serviceWorker
- 生成一個(gè) serviceWorker 配置文件
- */
- clientsClaim:true,
- skipWaiting:true
- })
- ]
- }
- index.js 中注冊(cè)serviceworker
- //處理兼容性
- if('serviceWorker' in navigator){
- window.addEventListener('load',()=>{
- navigator.serviceWorker
- .register('./service-work.js')
- .then(()=>{
- //成功
- })
- .catch(()=>{
- //失敗
- })
- })
- }
- 1.可能會(huì)出現(xiàn)問(wèn)題 eslint不認(rèn)識(shí)window和navigator
- 解決 package.json中eslintConfig中配置
- "env":{
- "browser":true //支持瀏覽器端的變量
- }
- 2. sw代碼必須運(yùn)行在服務(wù)器上
- -->node.js
- --> npm i serve -g
- serve -s build 啟動(dòng)一個(gè)服務(wù)器將build下的資源作為靜態(tài)資源暴露出去
多進(jìn)程打包
thread-loader 一般給babel-loader用
但需要注意
進(jìn)程啟動(dòng)大約需500ms,進(jìn)程間通信也有開(kāi)銷。只有工作消耗時(shí)間比較長(zhǎng),才需要多進(jìn)程打包
- {
- test:/\.js$/,
- exclude:/node_modules/,
- use:[
- //'thread-loader',
- {
- loader:'thread-loader',
- options:{
- workers: 2 //進(jìn)程2個(gè)
- }
- }
- {
- loader:'babel-loader',
- options:{
- // 預(yù)設(shè) :指示babel做怎樣的兼容性處理
- presets:[
- [
- '@babel/preset-env',
- {
- //按需加載
- useBuiltIns:'usage',
- //指定core-js版本
- corejs:{
- version:3
- },
- //指定兼容性做到哪個(gè)版本的瀏覽器
- targerts:{
- chrome: '40',
- fixfox: '50',
- ie: '9',
- safari: '10',
- edge: '17'
- }
- }
- ]
- ],
- //第二次構(gòu)建時(shí),會(huì)讀取之前的緩存
- cacheDirectory: true
- }
- }
- ]
- },
externals
- module.exports={
- externals:{
- //忽略/拒絕 庫(kù)名 -- npm 包名
- //可以在index.html中引入cdn
- }
- }
dll 動(dòng)態(tài)連接
使用dll技術(shù) 對(duì)某些庫(kù)(第三方庫(kù)) 進(jìn)行單獨(dú)打包
- 指令 webpack --config webpack.dll.js
- webpack.dll.js
- const {resolve} = require('path')
- const webpack = require('webpack')
- module.exports = {
- entry:{
- //最終打包生成的[name]-->jquery
- //['jquery']-->要打包的庫(kù)是jquery
- jquery:['jquery']
- },
- ouput:{
- filename:'[name].js',
- path:resolve(__dirname,'dll'),
- library:'[name]_[hash]' //打包的庫(kù)里面向外暴露出去的內(nèi)容叫什么名字
- },
- plugin:[
- new webpacl.DllPlugin({
- //打包生成一個(gè)manifest.json --> 提供和jquery映射
- name: '[name]_[hash]',//映射庫(kù)的暴露內(nèi)容名稱
- path:resolve(__dirname,'dll/manifest.json')
- })
- ],
- mode:'production'
- }
- webpack.config.js
- const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')
- module.exports={
- plugins:[
- //告訴webpack哪些庫(kù)不參與打包,同時(shí)名稱也得變
- new webpack.DllReferencePlugin({
- manifest: resolve(__dirname,'dll/manifest.json')
- }),
- //將某個(gè)文件打包輸出出去,并在html中引入該資源
- new AddAssetHtmlWebpackPlugin({
- filepath:resolve(__dirname,'dll/jquery.js')
- })
- ]
- }
編輯推薦