前端主要是交互,那么其优化是为了响应更快,展示更快
三省:懒
能不能不做?
不变的:减少渲染+缓存静态资源;
如果要做,能不能拖延?
增改的:延迟非必要+预处理必要;
做的时候,能不能少做?
少做:压缩体积,多路复用
目录
React.lazy 实现原理:动态导入import懒加载组件
使用 CDN(内容分发网络):托管全球分布的 CDN 服务器
减少 HTTP 请求,使用 url-loader,limit 限制图片大小,小图片转 base64
公共资源:Vue、Vue-router、Vuex、echarts
减少 CSS 阻塞时间:减少非必要字符css-minimizer-webpack-plugin
延迟加载非关键 CSS:rel="preload"主线程启动之前
将图像转换为更新的格式(JPEG 2000、JPEG XR 或 WebP)
1.框架:VDOM
React
React.lazy 实现原理:动态导入import
懒加载组件
-
返回一个懒加载组件包装器,内部包含一个动态
import()
函数。 -
当应用程序首次渲染时,懒加载组件包装器会被渲染为一个空占位符,而不会立即加载真正的组件代码。
-
当应用程序运行到需要渲染懒加载组件的代码时,动态
import()
函数才会被触发,开始异步加载实际的组件代码。这个过程是非阻塞的,所以应用程序可以继续进行其他操作。 -
一旦组件代码加载完成,
React.lazy
会自动渲染实际的组件内容,替换之前的占位符。import React, { Suspense, lazy } from 'react'; const LazyComponent = lazy(() => import('./LazyComponent')); function App() { return ( <Suspense fallback={<div>Loading...</div>}> <LazyComponent /> </Suspense> ); }
OtherComponent
只有在其首次需要渲染时才会被加载,组件加载期间,用户将看到 "Loading..." 消息
React.memo
2.网络
首屏
必要资源(预加载、解析)
非必要资源(延迟加载)
渲染速度:服务端渲染(SSR)【尤其需要搜索引擎优化(SEO)】
静态资源
缓存
使用 CDN(内容分发网络):托管全球分布的 CDN 服务器
就近访问: 当用户请求访问某个静态资源时,CDN 会自动将用户请求路由到离用户最近的边缘服务器。这样可以减少网络延迟,提高资源的加载速度。
负载均衡: CDN 使用负载均衡算法确保各个边缘服务器之间的负载均衡,避免某个服务器过载,提高整体性能。
请求:压缩,多路
服务器 Gzip 压缩、减少请求内容的体积
使用 HTTP2/3,接口解析速度快、多路复用、首部压缩等
减少 HTTP 请求,使用 url-loader,limit 限制图片大小,小图片转 base64
3.打包
分析打包后的文件:webpack插件生成分析图
elementUI
的按需引入
1)可以使用webpack-bundle-analyzer插件生成资源分析图
2)vue 项目可以在 build 命令上添加--report 指令,"build": "vue-cli-service build --report"
,打包时会生成 report.html
页面,即资源分析图
我们要清楚的知道项目中使用了哪些三方依赖,以及依赖的作用。特别对于体积大的依赖,分析是否能优化
比如:组件库如elementUI
的按需引入、Swiper轮播图
组件打包后的体积约 200k,看是否能替换成体积更小的插件、momentjs
去掉无用的语言包等
antd 按需加载实现原理:减少体积
babel-plugin-import插件
公共资源:Vue、Vue-router、Vuex、echarts
配置externals,从CDN(内容分发网络)引入
如果项目支持 CDN,可以配置externals
,将Vue、Vue-router、Vuex、echarts
等公共资源,通过 CDN 的方式引入,不打到项目里边
DllPlugin
动态链接库,分开打包,强缓存
如果项目不支持 CDN,可以使用DllPlugin
动态链接库,将业务代码和公共资源代码相分离,公共资源单独打包,给这些公共资源设置强缓存(公共资源基本不会变),这样以后可以只打包业务代码,提升打包速度
首屏必要资源:预加载、解析
{首字节时间|Time to First Byte}(TTFB
) :服务器响应时间
preload 预加载:rel="preconnect"
preload 预加载是告诉浏览器页面必定需要的资源,浏览器会优先加载这些资源;
使用 link 标签创建(vue 项目打包后,会将首页所用到的资源都加上 preload)
注意:preload 只是预加载资源,但不会执行,还需要引入具体的文件后才会执行 <script src='/path/home.js'>
<link rel="preconnect" href="https://example.com" />
DNS 预解析:rel="dns-prefetc"
不支持preload时
DNS Prefetch
是一种 DNS 预解析技术,当你浏览网页时,浏览器会在加载网页时,对网页中的域名进行解析缓存
这样在你单击当前网页中的连接时就无需进行DNS
的解析,减少用户等待时间,提高用户体验
使用dns-prefetch
,如<link rel="dns-prefetch" href="//img1.taobao.com">
很多大型的网站,都会用N
个CDN
域名来做图片、静态文件等资源访问。解析单个域名同样的地点加上高并发难免有点堵塞,通过多个 CDN 域名来分担高并发下的堵塞
<link rel="dns-prefetch" href="https://example.com" />
缓存
本地
静态HTML缓存到本地磁盘
Service Worker
会在浏览器后台运行,并可以拦截来自服务器的请求。此级别的程序化缓存控制使得缓存部分或全部 HTML 页面内容得以实现,并且只会在内容发生更改时更新缓存。
代理
配置反向代理(Varnish、nginx)来提供缓存内容
将用户路由到附近的CDN(内容分发网络)
将用户路由到附近的CDN(内容分发网络)是一种优化网络性能和用户体验的策略。这个过程通常涉及以下步骤:
-
CDN的选择和配置: 首先,网站所有者需要选择一个或多个CDN提供商,然后将其网站的内容部署到这些CDN上。这通常涉及将静态资源(如图像、脚本、样式表)复制到CDN服务器上。
-
全球CDN节点: CDN提供商通常在全球范围内拥有多个节点或服务器位置。这些节点分布在各个地理位置,通常位于不同的城市和国家。目的是接近最终用户,减少数据传输的延迟和提高内容加载速度。
-
DNS解析: 当用户尝试访问网站时,其浏览器会发出DNS(Domain Name System)请求,将域名解析为IP地址。这个步骤称为DNS解析。如果网站启用了CDN,DNS解析通常会返回一个与用户地理位置最近的CDN节点的IP地址。
-
内容缓存和交付: 一旦用户的请求路由到了附近的CDN节点,CDN服务器会负责响应该请求。如果CDN上已经缓存了所请求的内容,它将直接从缓存中提供内容,从而减少了请求的响应时间。如果内容尚未缓存,CDN服务器将从源服务器获取内容,然后将其缓存以供将来的请求使用。
-
内容交付到用户: CDN节点会将所请求的内容交付给用户的设备,通常通过最快的网络路径。这有助于减少网络延迟,提高内容加载速度。CDN还可以通过提供压缩、负载均衡和其他性能优化来进一步改善用户体验。
-
动态内容和边缘计算: 除了静态资源,一些CDN还提供边缘计算服务,允许在CDN节点上运行动态内容和应用程序逻辑,以加速动态内容的交付。
总的来说,将用户路由到附近的CDN节点有助于改善网站的性能,减少加载时间,提高用户体验,并降低网络流量成本。这种策略尤其对全球分布式的网站和应用程序非常重要,因为它可以减少地理距离对性能的不利影响。
首屏不必要资源、延迟加载
defer、async
使用 script 标签的defer或async
属性,这两种方式都是异步加载 js,不会阻塞 DOM 的渲染
async 是无顺序的加载,而 defer 是有顺序的加载
1)使用 defer 可以用来控制 js 文件的加载顺序
比如 jq 和 Bootstrap,因为 Bootstrap 中的 js 插件依赖于 jqery,所以必须先引入 jQuery,再引入 Bootstrap js 文件
2)如果你的脚本并不关心页面中的 DOM 元素(文档是否解析完毕),并且也不会产生其他脚本需要的数据,可以使用 async,如添加统计、埋点等资源
依赖动态引入
项目依赖的资源,推荐在各自的页面中动态引入,不要全部都放到 index.html 中
比如echart.js
,只有 A 页面使用,可以在 A 页面的钩子函数中动态加载,在onload事件
中进行 echart 初始化
// url 要加载的资源
// isMustLoad 是否强制加载
cont asyncLoadJs = (url, isMustLoad = false) => {
return new Promise((resolve, reject) => {
if (!isMustLoad) {
let srcArr = document.getElementsByTagName("script");
let hasLoaded = false;
let aTemp = [];
for (let i = 0; i < srcArr.length; i++) {
// 判断当前js是否加载上
if (srcArr[i].src) {
aTemp.push(srcArr[i].src);
}
}
hasLoaded = aTemp.indexOf(url) > -1;
if (hasLoaded) {
resolve();
return;
}
}
let script = document.createElement("script");
script.type = "text/javascript";
script.src = url;
document.body.appendChild(script);
// 资源加载成功的回调
script.onload = () => {
resolve();
};
script.onerror = () => {
// reject();
};
});
}
import():动态加载路由和组件
使用import() 动态加载路由和组件
,对资源进行拆分,只有使用的时候才进行动态加载
// 路由懒加载
const Home = () => import(/* webpackChunkName: "home" */ '../views/home/index.vue')
const routes = [ { path: '/', name: 'home', component: Home} ]
// 组件懒加载
// 在visible属性为true时,动态去加载demoComponent组件
<demoComponent v-if="visible == true" />
components: {
demoComponent: () => import(/* webpackChunkName: "demoComponent" */ './demoComponent.vue')
},
阻塞渲染的 JavaScript 和 CSS
减少 CSS
阻塞时间:减少非必要字符css-minimizer-webpack-plugin
削减 CSS
: CSS
文件可以包含空格、缩进或注释等字符。这些字符对于浏览器来说都不是必要的,而对这些文件进行削减能够确保将这些字符删除。使用模块打包器或构建工具,那么可以在其中包含一个相应的插件来在每次构建时削减 CSS 文件:对于 webpack5
:css-minimizer-webpack-plugin i
延迟加载非关键 CSS
:rel="preload"
主线程启动之前
使用 Chrome
开发者工具中的代码覆盖率Coverage 查找网页上任何未使用的 CSS
。
对于任何初始渲染时不需要的 CSS
,使用 loadCSS
来异步加载文件,这里运用了rel="preload"
和onload
。<link rel="preload" href="stylesheet.css" as="style" onload="this.rel='stylesheet'">
<link>
元素的rel
属性的preload
值允许你在HTML的<head>
中声明获取请求,指定页面将很快需要的资源,你希望在页面生命周期的早期开始加载这些资源,在浏览器的主线程启动之前。
内联关键 CSS
:<head>
把用于首屏内容的任何关键路径 CSS 直接包括在<head>
中来将这些 CSS
进行内联。
减少 JavaScript
阻塞时间
缩小和压缩 JavaScript
文件:
缩小是删除空格和不需要的代码
从而创建较小但完全有效的代码文件的过程。Terser
是一种流行的 JavaScript
压缩工具;
压缩是使用压缩算法修改数据的过程
Gzip
是用于服务器和客户端交互的最广泛使用的压缩格式。Brotli
是一种较新的压缩算法,可以提供比 Gzip
更好的压缩结果。
静态压缩:WebpackPlugin
涉及提前压缩和保存资产。这会使构建过程花费更长的时间,尤其是在使用高压缩级别的情况下,但可确保浏览器获取压缩资源时不会出现延迟。如果您的 web
服务器支持 Brotli
,那么请使用 BrotliWebpackPlugin
等插件通过 webpack
压缩资产,将其纳入构建步骤。否则,请使用 CompressionPlugin
通过 gzip
压缩您的资产。
延迟加载未使用的 JavaScript
代码拆分
通过代码拆分减少 JavaScript
负载,- SplitChunksPlugin
最大限度减少未使用的 polyfill
缓慢的资源加载速度
优化和压缩图像
对于许多网站来说,在页面加载完毕后,图像会是视图中的最大元素。这种情况的常见示例包括首图、大型轮播或横幅图像
改善这些类型的图像进行加载和渲染所需的时间将直接提升 LCP 的速度。实现方式:
首先考虑不使用图像。如果图像与内容无关,请将其删除。
压缩图像(例如使用 Imagemin
)
将图像转换为更新的格式(JPEG 2000、JPEG XR 或 WebP)
使用响应式图像
考虑使用图像 CDN
预加载重要资源:rel="preload"
有时,在某个 CSS
或 JavaScript
文件中声明或使用的重要资源可能会比所期望的要晚一点被获取,例如深藏在应用程序众多 CSS 文件中的某个字体。
知道某个特定资源应该被优先获取,请使用<link rel="preload">
来更加及时地获取该资源
多种类型的资源都可以进行预加载,但您应该首先侧重于预加载关键资产,例如字体、首屏图像或视频,以及关键路径 CSS 或 JavaScript
<link rel="preload" as="script" href="script.js" />
<link rel="preload" as="style" href="style.css" />
<link rel="preload" as="image" href="img.png" />
压缩文本文件
压缩诸如 Gzip
和 Brotli
之类的算法可以显著缩减在服务器和浏览器之间传输的文本文件(HTML
、CSS
、JavaScript
)大小。所有浏览器都有效支持 Gzip
,而 Brotli
几乎可以在所有较新的浏览器中使用,并能提供更好的压缩结果。
首先,检查您的服务器是否已经自动压缩文件。大多数托管平台、CDN 和反向代理服务器在默认情况下都会对资产进行压缩编码,或者使您能够轻松配置资产。
如果您需要对服务器进行修改来使其压缩文件,请考虑使用 Brotli
,而不是 gzip
,因为 Brotli
可以提供更好的压缩率。
选择您要使用的压缩算法后,请在构建过程中提前压缩资产,而不是在浏览器请求时实时压缩资产。这样能够最大限度地减少服务器开销并防止在发出请求时出现延迟,尤其是在使用高压缩比的情况下。
使用 Service Worker
缓存资产
Service Worker
可用于缓存任何静态资源,并在收到重复请求时将资源直接提供给浏览器,而无需通过网络。
使用 Service Worker
预缓存关键资源可以显著减少资源加载时间