start
2025年,回首过往,思绪如窗前的苍蝇,嗡嗡作响。此刻,我打开记忆的窗,倒一倒脑子,看看那些岁月里留下了什么。
减少DOM操作
使用文档片段 DocumentFragment
频繁的 DOM 操作会严重影响性能,尤其是当操作大量元素时。使用文档片段可以将多次 DOM 操作合并为一次,从而减少性能损耗。
const f = document.createDocumentFragment();
for (let i = 0; i < 10000; i++) {
const d = document.createElement('div');
d.className = `d_item d_${i + 1}`;
d.textContent = `我是div,我的位置是:${i + 1}`;
f.appendChild(d);
}
document.body.appendChild(f);
事件委托
将事件处理程序绑定到父元素上,而不是每个子元素上,可以减少事件处理程序的数量,提高性能。
// 生成大量 dom
const f = document.createDocumentFragment();
for (let i = 0; i < 10000; i++) {
const d = document.createElement('div');
d.className = `d_item d_${i + 1}`;
d.id = `${i + 1}`;
d.textContent = `我是div,我的位置是:${i + 1}`;
f.appendChild(d);
}
document.body.appendChild(f);
// 在 body 上面进行委托
document.querySelector('body').addEventListener('click', function(e) {
console.log('位置为:', e.target.id)
})
优化资源加载
懒加载
对于非关键资源(如图片、组件等),可以使用懒加载技术,仅在需要时才加载,从而减少初始加载时间。
滚动的时候,查看浏览器控制台「网络」栏,能观察到图片加载情况
浏览器提供的API IntersectionObserver
<style>
img {
height: 300px;
}
div {
height: 320px;
border: solid 1px red;
background: pink;
}
</style>
<div></div>
<div></div>
<div></div>
<div></div>
<img class="lazy_img" data-src="1.jpg" alt="懒加载图片1.jpg" />
<div></div>
<div></div>
<img class="lazy_img" data-src="2.jpg" alt="懒加载图片2.jpg" />
<div></div>
<div></div>
<img class="lazy_img" data-src="3.jpg" alt="懒加载图片3.jpg" />
<script>
// 选择所有需要懒加载的图片
const imgs = document.querySelectorAll('.lazy_img');
// 创建一个 IntersectionObserver 实例
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src; // 将图片的真实地址赋值给src
img.onload = () => img.classList.remove('lazy_img'); // 图片加载完成后移除lazy_img类
observer.unobserve(img); // 停止观察该图片
}
});
}, {
rootMargin: '0px 0px 300px 0px' // 提前加载300px范围内的图片
});
// 开始观察每个懒加载图片
imgs.forEach(image => observer.observe(image));
</script>
html属性
<style>
img {
height: 300px;
}
div {
height: 320px;
border: solid 1px red;
background: pink;
}
</style>
<div></div>
<div></div>
<div></div>
<div></div>
<img class="lazy_img" src="1.jpg" alt="懒加载图片1.jpg" loading="lazy" />
<div></div>
<div></div>
<img class="lazy_img" src="2.jpg" alt="懒加载图片2.jpg" loading="lazy" />
<div></div>
<div></div>
<img class="lazy_img" src="3.jpg" alt="懒加载图片3.jpg" loading="lazy" />
使用滚动事件scroll监听
<style>
img {
height: 300px;
}
div {
height: 320px;
border: solid 1px red;
background: pink;
}
</style>
<div></div>
<div></div>
<div></div>
<div></div>
<img class="lazy_img" data-src="1.jpg" alt="懒加载图片1.jpg" />
<div></div>
<div></div>
<img class="lazy_img" data-src="2.jpg" alt="懒加载图片2.jpg" />
<div></div>
<div></div>
<img class="lazy_img" data-src="3.jpg" alt="懒加载图片3.jpg" />
<div></div>
<script>
window.addEventListener('scroll', () => {
const imgs = document.querySelectorAll('.lazy_img');
imgs.forEach(img => {
if (img.getBoundingClientRect().top < window.innerHeight + 300) { // 预加载300px范围内的图片
img.src = img.dataset.src;
img.onload = () => img.classList.remove('lazy_img');
img.onerror = () => img.classList.remove('lazy_img'); // 错误监听
}
});
});
</script>
使用 CDN
将静态资源(如图片、JS、CSS)托管在 CDN 上,可以大幅提高资源的加载速度。
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
使用 WebP 格式图片
WebP 是一种现代的图片格式,提供了更好的压缩率和更小的文件大小,从而加快图片加载速度。
代码优化
代码分割与按需加载
通过代码分割和按需加载,可以减少初始加载时的资源请求,从而提高加载速度。
const routes = [{
path: "/center",
component: () => import("@/views/center/index.vue"),
meta: {
title: "账户中心",
},
}, {
path: "/review",
component: () => import("@/views/review/index.vue"),
meta: {
title: "提现审核",
},
}, {
path: "/cash",
component: () => import("@/views/cash/index.vue"),
meta: {
title: "审核提现",
},
}]
使用 Web Workers
对于复杂的计算任务,可以使用 Web Workers 将其移到后台线程中执行,避免阻塞主线程。
<button>点击测试</button>
<script>
document.querySelector('button').addEventListener('click', function() {
const data = {
value: 10
}; // 示例数据
// 主线程
const worker = new Worker('test.js');
worker.postMessage(data);
worker.onmessage = function(event) {
console.log('计算结果:', event.data);
};
})
</script>
self.onmessage = function(event) {
const result = complexCalculation(event.data);
self.postMessage(result);
};
function complexCalculation(data) {
// 示例复杂计算:将传入的值乘以2
return data.value * 2;
}
渲染优化
虚拟列表
对于长列表数据,可以使用虚拟列表技术,仅渲染视口内的元素,从而减少渲染压力。
<style>
.viewport {
border: solid 1px red;
}
.scroll_container {
border: solid 1px purple;
}
.item {
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
position: relative;
}
.item::before {
content: '';
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 1px;
background: pink;
}
</style>
<div class="viewport" style="height: 300px; overflow-y: auto;">
<div class="scroll_container" style="height: 10000px;">
<div class="visible_items" style="position: relative; top: 0;">
<!-- 可见的数据项 -->
</div>
</div>
</div>
<script>
const viewport = document.querySelector('.viewport');
const scrollContainer = document.querySelector('.scroll_container');
const visibleItemsContainer = document.querySelector('.visible_items');
const itemHeight = 50; // 每条数据的高度
const totalItems = 1000; // 数据总量
const visibleItemCount = Math.ceil(viewport.clientHeight / itemHeight) + 1; // 可见数据数量
// 设置滚动区域的总高度
scrollContainer.style.height = `${totalItems * itemHeight}px`;
function renderVisibleItems() {
const scrollTop = viewport.scrollTop;
const startIndex = Math.floor(scrollTop / itemHeight);
// 检测最大值
const endIndex = Math.min(startIndex + visibleItemCount, totalItems);
// 清空当前可见的数据项
visibleItemsContainer.innerHTML = '';
// 渲染可见的数据项
for (let i = startIndex; i < endIndex; i++) {
const item = document.createElement('div');
const num = i + 1;
item.style.height = `${itemHeight}px`;
item.textContent = `Item ${num}`;
item.className = 'item';
item.id = num;
visibleItemsContainer.appendChild(item);
}
// 设置可见数据项的偏移量
visibleItemsContainer.style.top = `${startIndex * itemHeight}px`;
}
// 监听滚动事件
viewport.addEventListener('scroll', renderVisibleItems);
// 初始渲染
renderVisibleItems();
</script>
使用 requestAnimationFrame
在执行动画时,requestAnimationFrame 是比 setTimeout 更好的选择,因为它会在浏览器下一次重绘之前调用指定的回调函数。
<style>
#move_box {
width: 50px;
height: 50px;
background: red;
position: relative;
top: 0;
}
</style>
<button>执行动画</button>
<div id="move_box"></div>
<script>
let position = 0;
let isAnimating = false; // 用于标记动画是否正在运行
const oBox = document.querySelector('#move_box');
document.querySelector('button').addEventListener('click', function() {
if (!isAnimating) { // 如果动画未运行,则开始动画
position = 0; // 重置位置
isAnimating = true; // 标记动画正在运行
requestAnimationFrame(animate); // 开始动画
}
})
function animate() {
position += 1; // 每帧移动1像素
oBox.style.left = position + 'px'; // 更新方块位置
if (position < 300) { // 限制移动距离
requestAnimationFrame(animate); // 请求下一帧动画
} else {
isAnimating = false; // 动画结束,取消标记
}
}
</script>
减少重排和重绘
避免频繁修改样式
尽量将样式修改集中在一起,以减少浏览器的重绘和重排。
const oDiv = document.getoDivById("div");
oDiv.style.color = "red";
oDiv.style.fontSize = "18px";
oDiv.style.margin = "8px";
使用 transform 和 opacity
使用 transform 和 opacity 进行动画操作,这些属性不会触发重排和重绘,只会触发最终的合成阶段,性能更好。
<style>
html,
body {
height: 100%;
}
body {
margin: 0;
display: flex;
justify-content: center;
align-items: center;
}
div {
width: 100px;
height: 100px;
background: red;
transition: transform .3s, opacity 3s;
}
div:hover {
transform: scale(2);
opacity: .8;
}
</style>
<div></div>
使用服务端渲染(SSR)
对于 SEO 和首屏加载速度要求较高的应用,可以使用服务端渲染(SSR)。SSR 的核心思想是将页面的初始 HTML 在服务器端生成,然后直接发送给客户端,客户端接收到完整的 HTML 后直接渲染页面,从而大大减少了首屏渲染时间。
SSR 最快的理解方式就是,和曾经的 JSP、html 嵌入 PHP、ASP 都差不多
SSR 的优势
- 首屏加载速度快:服务器直接返回完整的 HTML,客户端无需等待 JavaScript 加载和执行即可渲染页面。
- SEO 友好:搜索引擎可以直接抓取服务器返回的 HTML 内容,无需等待 JavaScript 执行,从而提高 SEO 效果。
- 减少客户端负担:服务器端完成 HTML 的生成,客户端只需进行简单的渲染,减轻了客户端的计算压力。
SSR 的实现
这里为了简化,可以直接使用 Nuxt.js 官方提供的简单例子,下面附赠创建项目和运行命令行
npx create-nuxt-app nuxt-test
cd nuxt-test
npm run build
npm run start
SSR 的注意事项
- 服务器性能:SSR 会增加服务器的负担,因为服务器需要动态生成 HTML。因此,需要确保服务器有足够的资源来处理请求。
- 数据预取:在服务器端渲染页面时,可以使用 asyncData 或 fetch 方法预取数据,确保页面加载时数据已经准备好。
- 缓存策略:对于静态内容,可以使用缓存策略来减少服务器的重复计算,提高性能。
使用静态站点生成(SSG)
上面 SSR 运行的时候 SSR 和 SSG 同一个选项,这里不重复了,说一下两者的区别,方便快速理解和记忆
SSR 和 SSG 对比
特点 | SSR(服务器端渲染) | SSG(静态站点生成) |
---|---|---|
生成时机 | 每次请求时动态生成 | 构建时静态生成 |
性能 | 需要服务器处理,性能稍低 | 直接返回静态文件,性能更高 |
适用场景 | 动态内容频繁更新 | 静态内容不经常更新 |
SEO | SEO友好 | SEO友好 |
首屏加载 | 首屏加载快 | 首屏加载快 |
资源消耗 | 需要服务器资源 | 不需要服务器资源,适合CDN部署 |
更新频率 | 适合高频率更新 | 适合低频率更新 |
- SSR:适合需要动态生成内容的场景,如新闻网站、电商平台等。每次请求都会动态生成HTML内容,适合内容频繁更新的场景。
- SSG:适合静态内容不经常更新的场景,如博客、文档等。在构建时生成静态HTML文件,适合内容更新不频繁的场景,性能更高,适合大规模部署。
SSG 的注意事项
- 数据更新:如果数据经常更新,需要考虑如何重新生成静态页面。Next.js 提供了 revalidate 选项,可以在指定的时间间隔内重新生成页面。
- 构建时间:SSG 的构建时间可能会比较长,尤其是在数据量较大的情况下。需要合理优化构建过程。
使用缓存策略
缓存是提高性能的关键手段之一。通过合理配置缓存策略,可以减少重复请求,加快资源加载速度。
通过设置 HTTP 缓存头(如 Cache-Control),可以让浏览器缓存静态资源。
在服务器端配置缓存头:
# 设置 static 路径
location /static/ {
# 设置缓存时间为1年
expires 1y;
add_header Cache-Control "public";
}
end
恳请诸位同仁、挚友、兄弟姐妹不吝赐教,多多指点!