webpack学习笔记02 性能优化

webpack学习笔记02 性能优化,第1张

文章目录 webpack学习笔记02面试题性能优化 ★★开发环境性能优化打包构建速度优化 HMR代码调试优化 source-map实际使用 生产环境性能优化打包构建速度优化 oneOf打包构建速度优化 babel缓存代码运行的性能优化 文件资源缓存tree shaking 去除没有使用的代码,减少代码体积坑 代码运行的性能优化 code split 代码分割第一种:多入口页面第二种:配置splitChunks第三种:单页面常用 动态import lazy loading js文件懒加载 与 预加载PWA 离线可访问打包构建速度优化 多进程打包打包构建速度优化 externals 让某些库不打包打包构建速度优化 DDL 库文件打包 clean-webpack-plugin插件

webpack学习笔记02 面试题

webpack内容比较杂乱,先整理面试题,在学习的时候可以抓住重点。

Webpack的打包过程?Webpack的 vendor 和model 有什么区别吗?Webpack 的 loader的作用?Webpack loader 和 plugin 的区别Webpack 做过哪些优化手段?有哪些优化手段?Webpack 插件的执行顺序(加载机制)?Webpack中怎么实现组件异步加载?Webpack如何配置压缩代码?压缩了什么?使用tree-shaking需要注意什么? 性能优化 ★★ 开发环境性能优化 打包构建速度优化 HMR(Hot Module Replacement) 模块热替换只重新构建发生变化的模块 代码调试优化,出错了告诉出错的源代码在哪里 source-map 提供源代码到构建后代码映射的技术 生产环境性能优化 打包构建速度优化 oneOf 找到一个loader就不会继续遍历了babel缓存 第二次构建时,会读取之前的缓存,只重新构建变化的文件tree shaking 去除没有使用的代码,减少代码体积,减少程序执行时间多进程打包externals 让某些库不打包,通过link引入dll 让某些库不打包(把库单独先打包好),后面直接用 (代码分为node_modules别人的库,和源代码,源代码可以通过import拆分打包,node_modules可以通过dll拆分打包) 代码运行的性能优化 文件资源缓存(hash-chunkhash-contenthash)tree shaking 去除没有使用的代码,减少代码体积,减少程序执行时间code split 代码分割懒加载/预加载pwa 离线也可以访问 开发环境性能优化 打包构建速度优化 HMR

HMR:hot module replacement 热模块替换
作用:一块发生变化,只会重新打包这一个模块,而不是打包所有模块。

webocak-dev-server:实现自动刷新整个页面的功能,从而实时预览代码修改后的结果。
HMR:不需要刷新整个页面,通过重新加载修改过的模块来实现实时预览

webpack.config.js里的devServer配置中开启HMR功能

devServer:{
     contentBase:resolve(__dirname,"build"),
     compress:true,
     port:3000,
     open:true,
     //开启HMR功能
     //当修改webpack配置,新配置想要生效,必须重启服务器。
     hot:true
}

只有在通过devServer启动webpack时,配置文件里的devServer才会生效,因为这些参数所对应的功能都是devServer提供的,webpack本事并不认识devServer的配置项。
启动devServer指令为:npx webpack-dev-server

不同文件的说明

HTML文件:默认不使用HMR功能,html不需要做HMR功能
同时会导致问题:html文件有修改时不能自动刷新整个页面。
解决办法:修改entry入口,将html文件引入之后html可以热更新了,但是仍然不能使用HMR功能。
module.exports={
	entry:['./src/js/index.js','./src/index.html']
}
样式文件:可以使用HMR功能是因为style-loader内部实现了,所以在开发环境可以使用style-loader(可以使用HMR优化打包构建速度),而在生产环境为了把 css单独提取成一个文件就不使用了。js文件:默认不能使用HMR功能、
如果需要使用HMR功能,需要修改js代码,让使用者触发热模块,添加支持HMR功能的代码。(不适用过于麻烦)
HRM功能只能处理非入口文件的js,入口文件一旦更新会重新引入其他模块,其他模块会重新执行
//使用者主动触发热模块
if(module.hot){
	module.hot.accept('本js文件名',fuciton(){
		//方法监听print.js文件的变化,一旦发生变化,其他文件默认不会重新打包构建
		//这个回调函数,是当该js发生变化的时候需要执行的回调函数
	})
}
代码调试优化 source-map

source-map:**提供源代码到构建后代码映射的技术,**如果构建后代码出错了,会通过映射追踪到源代码错误。

module.exports = {
//.....
devtool:"source-map"
}

devtool参数可选值:[inline-|hidden-|eval-] [nosources-] [cheap-[module]]source-map

可选值分类 内联和外部的区别
1.外部生成了单独的文件,内联没有,嵌入打包输出的文件中
2.内联速度构建更快
3.内联会让输出文件体积变得非常大,所以在生成环境不适用内联。

参数内部/外部补充错误代码提示是否可以追踪源代码的错误位置
source-map外部错误代码准确信息,如:源文件xx文件的xx错误
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加入错误代码准确信息
实际使用

开发坏境:速度快,调试更友好。
速度快(eval>inline>cheap>…)eval-cheap-source-map>eval-source-map…
调试友好 source-map cheap-module-source-map
一般使用:eval-source-map

生产环境
× 内联 会让体积变大,所以选择外部
有隐藏需求:hidden-source-map(只隐藏源代码,会提示构建后代码的错误信息) nosources-source-map(全部隐藏)
一般使用:source-map

生产环境性能优化 打包构建速度优化 oneOf

oneOf:oneOf数组里的loader只会匹配一个,一旦匹配到,则不会在继续匹配下面的loader了。**不能有两个配置处理同一种类型的文件
**,所以之前的两个js处理loader需要有一个放在外部。

比如css文件,我们匹配到css的loader之后可以返回了,不用继续去匹配less、js等loader

module.exports = {
module:{
	 rules:[
 	{eslint...},
 	{	//这里面只会匹配一个loader
   		oneOf:[{...},{另外一个js...}...]
 	}
 	]
}
打包构建速度优化 babel缓存

babel会对js代码进行编译处理,编译成浏览器能够识别的语法。
babe缓存: 第二次构建时,会读取之前的缓存,只重新构建变化的文件。

HMR基于devServer,生产环境不需要devServer

output:{
filename:"js/built.[hash:10].js"
//filename:"js/built.[chunkhash:10].js"
}

//兼容性处理
{
test:/\.js$/,
exclude:/node_modules/,
loader: "babel-loader",
options: {
	presets:[//....],
 	//开启babel缓存
 	cacheDirectory:true
	 }
}
代码运行的性能优化 文件资源缓存

文件资源缓存:输出的文件,缓存在浏览器,一定时间内不会重新获取。


解决办法
资源名称添加版本号,当资源名称发生改变时,会重新请求资源。如果没有变才会走缓存。

输出文件后面加上hash值,每次构建都会生产一个新的hash值,所有文件共用这个hash值。这样在缓存期如果hash值变化,每次构建都会变化,都会重新获取。
存在问题:重新打包就会改变hash,即使没有修改的文件。
moudule.exports={
	output:{
		filename:"js/built.[hash:10].js"
	}
	plugins:[
		new MiniCssExtractPlugin({
			filename:'css/built.[hash:10].css'
		})
	]
}
给资源名添加chunkhash值
根据chunk生成的hash值,如果打包来源于同一个chunk,那么hash就一样。

以入口文件构建依赖关系图 --> 引入依赖形成chunk代码块

存在问题
css与js的hash值一样,因为css被打包进了js,所以同属于一个chunk。

moudule.exports={
	output:{
		filename:"js/built.[chunkhash:10].js"
}
contenthash:**根据文件的内容生成hash值,不同文件的hash值不一样。**内容变化hash值才改变。
moudule.exports={
	output:{
		filename:"js/built.[contenthash:10].js"
}
tree shaking 去除没有使用的代码,减少代码体积

前提

使用 ES6 规范编写模块代码 ES6的模块依赖关系是确定的,和运行时状态无关生产环境下

说明
Tree-Shaking 是一种基于 ES Module 规范的 Dead Code Elimination 技术,它会在运行过程中静态分析模块之间的导入导出,确定 **ESM 模块中哪些导出值未曾被其它模块使用,并将其删除,**以此实现打包产物的优化。

作用
tree shaking:去除没有使用的代码,减少代码体积,减少程序执行时间。
Tree shaking的本质是消除无用的JavaScript代码。

optimization: {
  minimize: true // 此项不能设置为false,否者是导致 tree shaking 失效
}

webpack的tree shaking,默认只在 使用 mode 为 “production” 的配置项以启用,但是如果配置了不丑化压缩代码,webpack也会默认不是"production",然后就没开启tree shaking

代码运行的性能优化 code split 代码分割

打包生产的一个chunk,拆分成多个文件,可以并行加载,提高加载速度,同时实现按需加载。

第一种:多入口页面

例如有两个JS文件就输出两个JS文件

module.exports={
    entry:{
        //多入口:有一个入口,输出就有一个bundle
        main:"./src/js/index.js",
        test:"./src/js/test.js"
    },
    output:{
        //[name]取入口文件的key比如main、test
        filename:"js/[name].[contenthash:10].js",
        path:Resolve(__dirname,"build")
    },
    plugins:[...],
    mode:"production",
}
第二种:配置splitChunks

1.可以将node__mudules中代码单独打包成一个chunk输出(比如使用了jqury?)
2.会自动分析多入口chunk中,有没有公共的文件,如果有会打包成单独的一个chunk不会重复打包

module.exports={
    entry:"./src/js/index.js",
    output:{...},
    plugins:[...],
    optimization:{
        splitChunks:{
            chunks:"all"
        }
    },
    mode:"production",
}
第三种:单页面常用 动态import

通过js代码,让某个文件被单独打包成一个chunk
import动态导入语法能将某个文件单独打包,添加该注释可以设置打包的名字

import(/* webpackChunkName:"test" */ "./test").then().catch()
lazy loading js文件懒加载 与 预加载

懒加载当文件需要用时才加载
预加载等其他资源加载完毕,浏览器空闲了,在偷偷加载资源,ie会有兼容性问题
正常加载可以认为是并行加载,同时加载多个文件

//懒加载,当文件需要用时才加载 ES11的动态import
//仅在点击按钮之后再加载,第二次加载直接从缓存中读取
document.getElementById("btn").onclick=function(){
	import("./test").then(({mul})=>{
		console.log(mul(4,5));
	}).catch()
}

添加webpackPrefetch:true 实现预加载

//预加载,会提前加载在浏览器
document.getElementById("btn").onclick=function(){
	import((/* webpackChunkName:"test",webpackPrefetch:true */"./test").then().catch()
}

PWA 离线可访问

PWA:Progressive Web App。渐进式网络开发应用程序(离线可访问)

需要依赖: workbox –> workbox-weboack-plugin

1.安装

npm i workbox-weboack-plugin

2.插件的引入及使用

//引入插件
const WorkboxWeboackPlugin = require('workbox-weboack-plugin');
plugins:[
    new WorkboxWeboackPlugin.GenerateSW({//生成一个serviceworker配置文件,
        /*
        1.帮助serviceworker快速启动
        2.删除旧的serviceworker
        
        */
        clientsClaim:true,
        skipWaiting:true
    })
]

生成serviceworke配置文件后,一般在index.js里面注册serviceworker

//注册serviceworker
//处理兼容性问题
if("serviceWorker" in navigator){
    window.addEventListener("load",()=>{
        navigator.serviceWorker.register('/service-worker.js') //workbox-weboack-plugin生成/service-worker.js文件
        .then(()=>{})
        .catch(()=>{})
    })
}

可能出现的问题

1.eslit不认识window、navigator全局变量
解决:需要修改package.json中eslintConfig配置

eslintConfig:{
	"env":{
    "browser"true //支持浏览器端全局变量
	}
}

2.sw代码必须运行在服务器上
nodejs

打包构建速度优化 多进程打包

进程启动和进程通信都有开销,工作时间比较长,才需要多进程打包

1.安装依赖thread-loader -D

npm i thread-loader -D

2.在babel中使用

{
	test:/\.js$/,
	exclude:/node_modules/,
	usr:[
		{
		  loader:'thread-loader',
		  options:{
				workers:2 //两个进程
			}
		}, //开启多进程打包
		{//...}
	]
}
打包构建速度优化 externals 让某些库不打包

externals拒绝打包某资源,自己手动通过link引入

module.exports={
    entry:"./src/js/index.js",
    output:{...},
    plugins:[...],
    mode:"production",
    externals:{
        //库名 -- npm包名
        //拒绝jQuery被打包进来,拒绝被打包时需要手动link引进来
        jquery:'jQuery'
    }
}
打包构建速度优化 DDL 库文件打包

正常情况下node_module会被打包成一个文件
使用dll技术,对某些库(第三方)进行单独打包,生成一个chunk

当运行webpack时,默认查找webpack.config.js 配置文件
需求:需要运行wbpack.dll.js文件,这个名字可以自己取
解决办法:webpack --config webpack.dll.js


const{ resolve } =require("path");
const webpack = require("webpack");//引入webpack就足够了
module.exports = {
	entry:{
		//最终打包生成的name为属性名,属性值为要打包的库
 		jquery:['jquery']
	},
	output:{
    	filename:'[name].js',//输出jquery.js
    	path:resolve(__dirname),'dll',
    	library:"[name]_[hash]"  //打包的库里面向外暴露出去的内容叫什么,打包后的文件 var jQuery_hash值 = ...
	},
	//以上内容专门用于打包jquery

	plugins:[
	    //打包生成一个 manifest.json --> 提供和jquery映射
 		new webpack.DllPlugin(
     		name:'[name]_[hash]',//映射库的暴露的内容名称
     		path:resolve(__dirname,"dll/manifest.json") //输出文件的路径{"name":"var jQuery_hash值","content":在node_modules下的路径}
     	})
		]mode:"production"
}

打包之后配置webpack.config.js,这样重复打包webpack.config.js时不再打包jquery库了

//-----webpack.config.js
const webpack = require("webpack");
const AddAssetHtmlWebpackPlugin = require("add-asset-html-webpack-plugin")
//....
//告诉webpack哪些库不参与打包,同时使用时的名称也要变。所以打包出去的资源没有jQuery
new webpack.DllReferencePlugin({
    manifest:resolve(__dirname,"dll/manifest.json")
}),
//需要将之前输出的jquery在html中自动引入该文件
new AddAssetHtmlWebpackPlugin({
	filepath:resolve(__dirname,"dll/jquery.js")
})
clean-webpack-plugin插件

clean-webpack-plugin:在打包时,先删除原来的再进行打包新的。

1.安装

npm i clean-webpack-plugin -D

2.引入清除文件插件与使用

const CleanWebpackPlugin = require('clean-webpack-plugin');
//使用
plugins:[
//匹配删除的文件
new CleanWebpackPlugin([
  'dist',         //删除dist文件夹
  'build/*.*',    //删除build文件夹下的所有文件
],{
  //下面可以省略
  root: __dirname,  //根目录
  verbose:  true,  //开启在控制台输出信息
  dry: false    //启用删除文件
}),
]

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/940196.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-17
下一篇 2022-05-17

发表评论

登录后才能评论

评论列表(0条)

保存