前端工程化-Webpack5优化实操

152 阅读7分钟

1.基础概念

entry:入口
output:输出
loader:模块转换器,将模块的原内容按照需求转换成新内容
plugin:扩展插件,在webpack构建过程的特定时机注入扩展逻辑,用来改变或优化构建结果
mode:控制打包环境,通过选择development,production或none中的一个,设置mode参数,可以启用webpack内置在相应环境的优化。默认值为production环境
devserver:小型的node.js expr服务器,使用webpack-dev-middleware中间件来通过webpack打包生成的资源文件提供web服务\

2.构建性能构建效率

2.1分析工具
2.1.1 内置工具profile选项:profile:true

image.png 执行 "build:report": "webpack --config ./webpack.config.js  --json=stats.json"可生成json格式文件 image.png 官方可视化工具:webpack.github.io/analyse,加载生成文件,可看到性能 image.png

2.1.2 SpeedMeasurePlugin(每个Plugin,Loader耗时)

新建speedMeasure.js文件,执行  "build:speed": "webpack --config ./speedMeasure.js“
image.png
或者建立base.js,build.js,speedMeasure.js,修改执行命令为:"build": "webpack --config ./service/build.js"
image.png image.png image.png image.png\

2.2持久化缓存

在开发过程中,如果轻微的修改就需要运行全部的打包过程是一种巨大的浪费。使用缓存技术,存储中间产物就成了最有效的优化手段。
cache将首次构建持久化到文件系统中,在下次执行是跳过解析、链接、编译过程
image.png

2.3并行处理

HappyPack:多进程运行loader(停止维护)
Thread-loader:多进程运行资源加载(官方驱动)
Paraller-webpack:多进程运行多个webpack构建示例(多入口场景)
TerserWebpackPlugin:支持多进程方式执行压缩丑化
以Thread-loader举例:创建与销毁进程可能会带来反向优化,很多loader有不兼容的情况
image.png\

2.4减少编译范围
2.4.1 LazyCompilation

异步引用模块的按需编译,require(‘./xxx’)或import(‘./xxx‘)不会立即编译 image.png

2.4.2 约束loader执行范围-exclude

处理node_modules中的js文件时直接跳过这个rule项,不执行文件的loader逻辑
image.png

2.4.3 对于已经打包集成的代码,无需再次编译

noParse跳过文件编译,其中提前打包的无需编译:前提条件是不包含import内容,无法TreeShaking,必须保证程序无语法错误
image.png

2.5 简化构建步骤
2.5.1开发模式禁用优化

Minimize压缩、Splitchunks分包、ConcatenateModules模块连接、UsedExports tree-shaking功能 image.png\

2.5.2 关闭sourcemap

image.png

2.6 使用高效编译器(Esbuild、swc)

代码压缩优化 image.png

3.页面性能

3.1性能指标

web.dev/metrics,以用户为中心的性能指标是了解和改善网站体验的重要工具。TTFB(第一字节时间)、FCP(首次内容绘制)、LCP(最大内容绘制)、TTI(可交互时间)..

3.2性能指标工具

Chrome性能分析工具、Lightouse性能分析工具、Webpack-bundle-analyze image.png image.png

3.3模拟线上Webserver

image.png 运行"dev:server": "node  ./service/index.js"

3.4分包策略与chunk

解决的问题:页面初始代码包过大,影响首屏渲染,无法有效应用缓存

3.4.1什么是chunk

此Webpack术语在内部用于管理捆绑过程。Bundle(输出束)由chunk组成,其中有几种类型(entry和child)。通常,chunk直接和bundle对应,但是有些配置不会产生一对一的关系 image.png

3.4.2分包策略

Initial chunk:entry模块及相应子模块打包成initial chunk
Async chunk:通过import(‘./xx‘)等语句导入的异步模块及相应子模块组成的async chunk
image.png Runtime chunk:运行代码抽离成runtime chunk,可通过entry.runtime配置项实现(异步加载)
image.png

3.5代码压缩

减少体积,减少传输时间
Js:TerserWebpackPlugin
Css:cssminimizerwebpackplugin
Html:htmlMinifierTerser
Js压缩:terser是一个js的parse (解释)、mangler(绞肉机、丑化)、compressor(压缩机)移除不使用的代码,压缩多行代码为一行的工具集 image.png

3.6动态加载

模块懒加载,解决首屏性能

3.6.1实现原理

()=>import()函数、路由的支持、Webpack对import()函数的分包 image.png

3.7Treeshaking

别名叫树摇,最早是由Rollup实现,是一种采用删除不需要的额外代码的方式优化代码体积的技术。

3.7.1原理

Tree Shaking在去除代码冗余的过程中,程序会从入口文件出发,扫描所有的模块依赖,以及模块的子依赖,然后将它们链接起来形成一个“抽象语法树”(AST。随后,运行所有代码,查看哪些代码是用到过的,做好标记。最后,再将“抽象语法树”中没有用到的代码“摇落”。经历这样一个过程后,就去除了没有用到的代码。

3.7.2为什么CommonJS无法Treeshaking

CommonJS与ES6 Module模块的依赖的区别在于,CommonJS是动态的,ES6 Module是静态的。CommonJS导入时,require的路径参数是支持表达式的,路径在代码执行时是可以动态改变的,所以如果在代码编译阶段就建立各个模块的依赖关系,那么一定是不准确的,只有在代码运行了以后,才可以真正确认模块的依赖关系,因此说CommonJS是动态的。 image.png

3.7.3webpack精准使用TreeShaking的条件

(1)使用ES6 Module语法(即import和export)。 (2)确保没有@babel/preset-env等工具将ES6 Module语法转换为CommonJS模块。module:false (3)使用支持Tree Shaking的第三方库。

3.7.4开启Treeshaking

image.png

3.8作用域提升ScopeHoisting

好处:代码体积变小,声明代码被省略、代码运行时作用域更少,内存开销变小 image.png image.png

3.9HTTP缓存机制优化

强缓存:也称为本地缓存,不向服务器发送请求,直接使用客户端本地缓存数据 强缓存失效的方法:文件名加版本号:index.js?version=1index.css?version=1、文件名哈希值~文件指纹
协商缓存:也称304缓存,向服务器发送请求,由服务器判断请求文件是否发生改变。如果未发生改变,则返回304状态码,通知客户端直接使用本地缓存;如果发生改变,则直接返回请求文件。 image.png

3.9.1缓存策略

(1)访问入口永远不缓存
(2)资源文件
设置强缓存并设置超长过期时间Cache-Control:max-age=31536000
资源文件更新时使用新的文件指纹

3.9.2哈希值与文件指纹

摘要与哈希算法:摘要算法又称哈希算法、散列算法。摘要也称哈希值,表示输入任意长度的数据,都会输出固定长度的数据。通过摘要算法(比如MD5和SHA-1)就可以得到该哈希值。 image.png 不定长输入转定长、摘要满足雪崩效应(剧烈变化)、单项不可逆

3.9.3Webpack与文件指纹

版本管理:
(1)在发布版本时,通过文件指纹来区分修改的文件和未修改的文件
(2)使用缓存:未修改的文件,文件指纹保持不变,浏览器继续使用缓存访问。
文件指纹设置:
我们在配置文件中,通过占位符设置文件指纹。
image.png

3.9.4利用express实现缓存
const http=require('http')
const express=require("express")
const ecstatic=require("ecstatic")
const history=require("connect-history-api-fallback")
const path=require("path")
const app=express()
app.use(history())
app.use(function(req,res,next){
        console.log('url:',req.url)
        if(req.url==='/index.html'){
           res.set({
            'Cache-control':'public,max-age=0',
            'Expires':new Date(Date.now()+0).toUTCString()
           })
        }else{
            res.set({
                'Cache-control':'public,max-age=31536000',
                'Expires':new Date(Date.now()+31536000).toUTCString()
               })
        }
        next()
})
app.use(ecstatic({root:path.resolve(__dirname,'../dist')}))
http.createServer(app).listen(8889,()=>{
    console.log('服务启动')
})
3.10静态资源内联与压缩
3.10.1静态资源内联:图片、CSS、html

Webpack的内联功能允许您将静态资源(如图像,字体或CSS)内联到您的代码中,而无需将它们单独打包。这样做的好处是可以减少网络请求,提高应用程序的性能。减少网络请求次数;有利于缓存;防止资源加载失败。
image.png image.png

3.10.2图片压缩
    let loader=base.module.rules.find(
        v=>v.test.toString()==="/\.(png|jpe?g|gif|webp)(\?.*)?$/"
    )
    Object.assign(loader,{
        parser:{
            dataUrlCondition:{
                maxSize:20*1024
            }
        },
        use:[
            {
             loader:'image-webpack-loader',
             options:{
                mozjpeg:{
                    progressive:true
                },
                optipng:{
                    enabled:false
                },
                pngquant:{
                    quanlity:[0.65,0.90],
                    speed:4
                },
                gifsicle:{
                    interlaced:false
                },
                webp:{
                    quanlity:75
                }
             }
            }
        ]
    })
}
3.11骨架屏

在页面数据加载完成前,先给用户展示出页面的大致结构(灰色占位图)使界面加载过程变得更加流畅;不会造成长时间白屏或者闪烁。
简单来说,骨架屏就是在页面内容未加载完成的时候,先使用一些图形进行占位,待内容加载完成之后再把它替换掉

3.11.1生成骨架屏方案

(1)手工编辑骨架屏
(2)利用puppeteer或者Chrome插件生成
(3)利用SSR服务器端渲染技术生成
(4)组件级骨架屏技术
image.png

image.png

image.png