前端性能优化
为什么出现白屏
spa单页面加载过程
首先是html:FP 阶段:首次渲染
其次是css,js FCP阶段:首次内容渲染
最后是FMP:页面的主要内容出现在屏幕上
总结:FP->FMP之间的会出现白屏,header背景色会出现在屏幕上,但是数据会在ajax结束后解析放到html中去
如何解决白屏
- webpack预渲染:没有ajax的交互
- 同构渲染:一套代码多端使用 vue->json->vue-sever-render->html
- SSR:服务端渲染 服务端把所有数据渲染完成然后返回到客户端 ssr->请求->node->解析->客户端
- seo 首屏优化
- 路由懒加载:
- 在需要的时候去加载路由
- 按照路由将页面进行划分,一个路由文件分成若干个文件
- const app = () => import('')
- 1、异步组件:vue-router配置路由
- { path: '/problem', name: 'problem', component: resolve => require(['../pages/home/problemList'], resolve) }
- 2、es6 标准语法 import
- { path: '/vipcard', component: ()=> import('../views/vipcard/VipCard.vue') }
- 3、webpack的require.ensure()
- quicklink:浏览器在空闲时加载数据
- Gzip压缩:后端开启gzip 前端:npm i compression-webpack-plugin -D test匹配压缩文件的类型
- 外链css,js文件
- webpack-entry:将单页改成多页应用,比如一些组件中,vue.js vue-router等插件已经在某个页面使用了,然后给它缓存起来,下次就无需加载
- 骨架屏:
- loading:
首屏加载慢
原因:
1、网络延时问题
2、资源文件体积是否过大
3、资源是否重复发送请求去加载了
4、加载脚本的时候,渲染内容堵塞了
如何首屏优化
路由懒加载
打包文件中去掉map文件:找到config文件夹下index.js文件,把这个build里面的productionSourceMap改成false即可
减小入口文件体积
静态资源本地缓存
UI框架按需加载
图片资源的压缩
组件重复打包
开启GZip压缩
使用SSR
预渲染
做过哪些vue的性能优化
- 对象层级不要过深,否则性能就会差
- 不需要响应式的数据不要放到 data 中(可以用 Object.freeze() 冻结数据)
- v-if 和 v-show 区分使用场景
- computed 和 watch 区分使用场景
- v-for 遍历必须加 key,key 最好是 id 值,且避免同时使用 v-if
- 大数据列表和表格性能优化-虚拟列表/虚拟表格
- 防止内部泄漏,组件销毁后把全局变量和事件销毁
- 图片懒加载
- 路由懒加载
- 第三方插件的按需引入
- 适当采用 keep-alive 缓存组件
- 防抖、节流运用
- 服务端渲染 SSR or 预渲染
做过什么前端性能优化
从页面的渲染过程来建立自己的一个前端性能优化的体系
要优化一个网站的性能,首先需要学会如何衡量一个网站的性能。
rail 模型:response animation load idie
以用户为中心的性能指标
- First Paint 首次绘制(FP)
- First contentful paint 首次内容绘制 (FCP)
- 核心1、Largest contentful paint 最大内容绘制 (LCP)
- 核心2、First input delay 首次输入延迟 (FID)渲染完成到交互耗时
- Time to Interactive 可交互时间 (TTI)首次可交互时间
- Total blocking time 总阻塞时间 (TBT)
- 核心3、Cumulative layout shift 累积布局偏移 (CLS)
1、Lighthouse
2、web-vitals-extension
你可以通过安装 web-vitals-extension 插件来获取三大核心指标
3、web-vitals库
>使用:
import {getCLS, getFID, getLCP} from 'web-vitals';
getCLS(console.log);
getFID(console.log);
getLCP(console.log);
4、Chrome
页面渲染过程中优化
网页输入网址按下回车键发生了什么
输入网址
解析url:协议 主机 端口 路径 参数 锚点6部分
查找浏览器缓存:强缓存和协商缓存
开启浏览器缓存:cache-control
- 浏览器发送请求前,根据请求头的`expires`和`cache-control`判断是否命中(包括是否过期)强缓存策略,如果命中,直接从缓存获取资源,并不会发送请求。如果没有命中,则进入下一步。
- 没有命中强缓存规则,浏览器会发送请求,根据请求头的`If-Modified-Since`和`If-None-Match`判断是否命中协商缓存,如果命中,直接从缓存获取资源。如果没有命中,则进入下一步。
- 如果前两步都没有命中,则直接从服务端获取资源。
第三方库公共模块抽取
- 如何提取第三方库呢?在 webpack4.x 中, SplitChunksPlugin 插件取代了 CommonsChunkPlugin 插件来进行公共模块抽取,我们可以对SplitChunksPlugin 进行配置进行 **拆包** 操作
dns解析:域名解析系统
优化:DNS负载均衡技术 访问量过大 应答dns查询 对每个请求返回不同的结果 引到不同的服务器上去
tcp/ip连接:osi7️⃣层模型
三次握手:建立连接
客户端syn=1 seq=1 告诉我要发请求了,看你能收到吗
服务端收到 发送ack=seq+1 syn=1 表达我收到了
客户端验证ack 和 seq的值 发送ack 服务端的seq+1(告诉服务器我马上要发了,准备接受),服务端收到验证完建立连接
http请求:https区别
优化:采用http2
优点:多路复用:提升效率 流的概念 二进制数据比文本效率高 头部信息压缩:减少带宽的浪费
服务器响应:
浏览器渲染页面:
字节(编码)→ 字符(令牌化) → 令牌(词法分析转成对象) → 节点 → 对象模型(dom构建)。
DOM+CSSOM -> Layout Tree
渲染流程:
1、DOM分割为多个图层
2、对每个图层的节点计算样式结果
3、每个节点生成图形和位置(重排、回流)
4、每个节点绘制填充到图层位中(重绘)
5、图层作为纹理上传至GPU
6、组合多个图层到页面上生成最终图像
如何引起的回流和重绘
1、页面首次渲染
2、浏览器窗口变化
3、元素尺寸或者位置变化
4、元素内容字体大小变化
5、添加删除元素
6、激活css伪类
7、查询属性或者调用某些方法
优化:
1、静态资源使用CDN:让用户离服务器更近 减少请求的时间
2、防止脚本阻塞:css头部 js尾部延迟加载
3、图片优化:压缩图片 延迟加载图片 webp格式图片 响应式图片
- 使用url-loader
4、使用webpack压缩文件:
- JavaScript:UglifyPlugin
- CSS :MiniCssExtractPlugin
- HTML:HtmlWebpackPlugin
或者gzip 压缩:向 HTTP 请求头中的 Accept-Encoding 头添加 gzip 标识来开启这一功能。服务器也得支持这一功能。
5、减少回流和重绘
css
- 减少使用table布局
- 避免设置深层次样式
- 避免使用css表达式
- dom的末端修改css
js
- 避免重复操作样式
- 使用文档碎片 操作dom 结束后将他插入到页面
- 动画元素使用定位 脱离文档流
- 使用display:none;元素操作完成后显示出来
6、代码分割
组件和路由
7、tree-shaking
去除死代码无用代码
可以通过在启动 webpack 时追加参数 --optimize-minimize 来实现
8、骨架屏
9、服务端渲染
断开连接:四次挥手
两者都属于establish状态
1、客户端发送关闭连接请求 fin报文(含序列号) 处于-fin_wait状态
2、收到fin,ack=序列号+1,close_wait状态 表示我收到请求了
3、服务端数据处理完成,也想断开连接了,发送syn报文序列号,处于last_ack状态
4、客户端收到了,ack=服务端的ack+1 time_wait状态,等一会确保服务端收到报文之后进入closed状态
webpack优化性能
优化的是webpack的打包结果 时间层面和体积层面
时间层面:webpack的构建速度
-
定向查找:resolve.modules,resolve.extensions
-
减少执行构建的模块:合理配置loader的includes,excludes
-
开启缓存:babel-loader,cache-loader
-
并行构建:happypack
-
并行压缩:uglifywebpackplugin开启paralle
体积层面:构建结果
- 压缩代码:uglifyJsPlugin parannelUglifyJsPlugin 压缩js文件,cssnano(css-loader)压缩css文件
- html:html-webpack-plugin
module.export = {
plugins: [
new HtmlWebpackPlugin({
// 动态生成 html 文件
template: "./index.html",
minify: {
// 压缩HTML
removeComments: true, // 移除HTML中的注释
collapseWhitespace: true, // 删除空⽩符与换⾏符
minifyCSS: true // 压缩内联css
},
})
]
}
- css:
- webpack4: optimize-css-assets-webpack-plugin
- webpack5: css-minimizer-webpack-plugin
// webpack5
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
module: {
rules: [
{
test: /\.jsx?$/,
use: ["babel-loader"],
exclude: /node_modules/, //排除 node_modules 目录
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"],
exclude: /node_modules/, //排除 node_modules 目录
},
]
},
plugins: [
new MiniCssExtractPlugin()
],
optimization: {
// 是否需要压缩
minimize: true, // 开发环境需要开启
// 配置压缩工具
minimizer: [
// 添加 css 压缩配置
new OptimizeCssAssetsPlugin({}), // 需要安装
],
},
}
//entry文件
const names = ["randy", "jack"];
const say = (_name) => {
console.log(_name);
};
say(Math.random() > 0.5 ? names[1] : names[0]);
// 配置
const UglifyJsPlugin = require("uglifyjs-webpack-plugin"); // 需要安装
module.exports = {
module: {
rules: [
{
test: /\.jsx?$/,
use: ["babel-loader"],
exclude: /node_modules/, //排除 node_modules 目录
},
]
},
optimization: {
// 是否需要压缩
minimize: true, // 开发环境需要开启
// 配置压缩工具
minimizer: [
new UglifyJsPlugin({})
],
},
}
- image: file-loader之后加上image-webpack-loader
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|gif|jpeg|webp|svg)$/,
use: [
"file-loader",
{
loader: "image-webpack-loader",
options: {
mozjpeg: {
progressive: true,
},
// optipng.enabled: false will disable optipng
optipng: {
enabled: false,
},
pngquant: {
quality: [0.65, 0.9],
speed: 4,
},
gifsicle: {
interlaced: false,
},
},
},
],
exclude: /node_modules/, //排除 node_modules 目录
},
]
},
}
- 按需加载:路由懒加载或者组件
- 提前加载:prefetch:true preload
- tree shaking:去除死代码 js插件
// webpack.config.js
optimization: {
usedExports: true
},
// package.json
"sideEffects": false
css插件:purgecss-webpack-plugin
const path = require("path");
const PurgecssPlugin = require("purgecss-webpack-plugin");
const glob = require("glob"); // 文件匹配模式
plugins: [
// ...
new PurgecssPlugin({
// 这里我的样式在根目录下的index.html里面使用,所以配置这个路径
paths: glob.sync(`${path.join(__dirname)}/index.html`, { nodir: true }),
}),
]
- 代码分割:SplitChunksPlugin
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'async', // 值有 `all`,`async` 和 `initial`
minSize: 20000, // 生成 chunk 的最小体积(以 bytes 为单位)。
minRemainingSize: 0,
minChunks: 1, // 拆分前必须共享模块的最小 chunks 数。
maxAsyncRequests: 30, // 按需加载时的最大并行请求数。
maxInitialRequests: 30, // 入口点的最大并行请求数。
enforceSizeThreshold: 50000,
cacheGroups: {
defaultVendors: {
test: /[\/]node_modules[\/]/,
priority: -10,
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
};
- 提取第三方库
- cdn加速
- gzip:安装
compression-webpack-plugin并在plugins配置
前端除了在打包的时候将无用的代码或者
console、注释剔除之外。我们还可以使用Gzip对资源进行进一步压缩。Gzip原本是UNIX系统的文件压缩,后来逐步成为web领域主流的压缩工具。那么浏览器和服务端是如何通信来支持Gzip呢?
-
当用户访问 web 站点的时候,会在
request header中设置accept-encoding:gzip,表明浏览器是否支持Gzip。 -
服务器在收到请求后,判断如果需要返回
Gzip压缩后的文件那么服务器就会先将我们的JS\CSS等其他资源文件进行Gzip压缩后再传输到客户端,同时将response headers设置content-encoding:gzip。反之,则返回源文件。 -
浏览器在接收到服务器返回的文件后,判断服务端返回的内容是否为压缩过的内容,是的话则进行解压操作。
作者:苏苏同学
链接:juejin.cn/post/724481…
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
const CompressionWebpackPlugin = require("compression-webpack-plugin"); // 需要安装
module.exports = {
plugins: [
new CompressionWebpackPlugin()
]
}
常用分析工具:
- 时间: speed-measure-webpack-plugin
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin"); // 需要安装
const smp = new SpeedMeasurePlugin();
module.exports = () => smp.wrap(config); // 使用smp包裹webpack的配置
- 构建结果: webpack-bundle-analyze
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; // 需要安装
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
}