
前端常见问题解决方案
约 2882 字大约 10 分钟
2024-07-20
1. 图片懒加载
为什么要做图片的懒加载
实现图片懒加载的好处包括:
- 减少初始加载时间:页面加载更快,因为浏览器不需要立即加载所有图片资源。
- 减少服务器负载:服务器不需要一次性处理所有图片资源的请求,可以根据用户的滚动行为逐步处理。
- 节省用户数据:对于使用按流量计费的用户来说,只加载用户实际看到的图片可以节省他们的数据使用量。
- 提高用户体验:用户可以更快地看到页面的初始内容,而不必等待所有的媒体资源都加载完成。
什么是图片懒加载
图片懒加载(Lazy Loading)是一种在网页开发中常用的性能优化技术。通常,在网页加载时,浏览器会尝试加载页面中的所有资源,包括图片、JavaScript 文件、CSS 文件等。如果页面中有很多图片,特别是那些位于页面底部或不立即可见的图片,它们仍然会被加载,这会消耗额外的带宽,并且增加页面的加载时间。
懒加载的原理
图片懒加载的核心思想是推迟这些不立即可见的图片的加载时间,直到它们接近或进入浏览器的视口(即用户可以看到的部分)。换句话说,只有当用户滚动页面,图片即将出现在屏幕上时,才开始加载这些图片。
实现方案
实现图片懒加载的方法有多种,包括:
- 使用原生的 loading="lazy" 属性:这是最简单的方法,只需在 img 标签上添加这个属性即可。
- JavaScript 监听滚动事件:通过监听滚动事件来判断图片是否进入视口,并动态加载图片。
- 使用 Intersection Observer API:这是一个现代的 API,能更高效地检测元素是否进入视口。
- 使用第三方库:例如 lazysizes、lozad.js、vue-lazyload 等,它们提供了更多功能和更好的兼容性。
1. loading="lazy"
loading="lazy"指示浏览器应当如何加载该图像。允许的值:
- eager 立即加载图像,不管它是否在可视视口(visible viewport)之外(默认值)。
- lazy:延迟加载图像,直到它和视口接近到一个计算得到的距离(由浏览器定义)。目的是在需要图像之前,避免加载图像所需要的网络和存储带宽。这通常会提高大多数典型用场景中内容的性能。
<img src="./example.jpg" loading="lazy" alt="loading lazy" />
缺点: 1. 控制性有限:原生的懒加载属性不允许开发者自定义懒加载的行为。例如,你不能指定图片应该在距离视口多远时开始加载,这在使用 JavaScript 实现懒加载时是可以做到的。 2. 没有回退机制:在不支持 loading="lazy" 属性的浏览器上,没有内置的回退机制。如果你想要在所有用户体验上保持一致,你可能需要写额外的代码来检测属性的支持情况并实现一个回退方案。 3. 预加载策略不明确:浏览器可能会根据自己的判断来决定何时开始加载懒加载的图片,这可能不符合开发者的预期。例如,一些浏览器可能会更积极地预加载图片,而其他浏览器可能会更保守。 4. 无法控制缓存:使用原生懒加载时,你没有控制浏览器缓存行为的能力。这可能会导致在用户滚动页面时,已经加载过的图片在用户再次访问时重新加载。 5. 不支持背景图片:loading="lazy" 属性仅适用于 <img>
和 <iframe>
标签,不适用于通过 CSS 设置的背景图片。 6. 不适用于动态内容:如果你的网站使用了大量的动态内容加载(如无限滚动),原生懒加载可能不足以处理这种情况,因为它主要设计用于静态的、在页面初次加载时就存在的图片。
2. JavaScript 监听滚动事件
- 通过 offsetTop 来计算是否在可视区域内: 可视区域高度是 document.documentElement.clientHeight ,而可视区域的位置是在滚动条滚动位置 scrollTop 到 scrollTop+document.documentElement.clientHeight 之间。因此通过 image.offsetTop <= document.documentElement.clientHeight + document.documentElement.scrollTop 判断图片是否可以在可视区域内。 2. 监听到 scroll 事件,调用目标元素的 getBoundingClientRect()方法,得到它对应于视口左上角的坐标,再判断是否在视口之内。再动态修改 src 属性加载图片。
<body>
<style>
img {
display: block;
margin-bottom: 50px;
height: 200px;
}
</style>
<img src="images/placeholder.jpg" data-src="images/1.png" />
<img src="images/placeholder.jpg" data-src="images/2.png" />
<img src="images/placeholder.jpg" data-src="images/3.png" />
<img src="images/placeholder.jpg" data-src="images/4.png" />
<img src="images/placeholder.jpg" data-src="images/5.png" />
<img src="images/placeholder.jpg" data-src="images/6.png" />
<img src="images/placeholder.jpg" data-src="images/7.png" />
<img src="images/placeholder.jpg" data-src="images/8.png" />
<img src="images/placeholder.jpg" data-src="images/9.png" />
<img src="images/placeholder.jpg" data-src="images/10.png" />
<img src="images/placeholder.jpg" data-src="images/11.png" />
<img src="images/placeholder.jpg" data-src="images/12.png" />
<script>
function throttle(fn, delay, atleast) {
var timeout = null;
var startTime = new Date();
return function () {
var curTime = new Date();
clearTimeout(timeout);
if (curTime - startTime >= atleast) {
fn();
startTime = curTime;
} else {
timeout = setTimeout(fn, delay);
}
};
}
function lazyload() {
var images = document.querySelectorAll("[data-src]");
var len = images.length;
var n = 0; //存储图片加载到的位置,避免每次都从第一张图片开始遍历
return function () {
var seeHeight = document.documentElement.clientHeight;
var scrollTop =
document.documentElement.scrollTop || document.body.scrollTop;
for (var i = n; i < len; i++) {
if (images[i].offsetTop < seeHeight + scrollTop) {
if (
images[i].getAttribute("src") !==
images[i].getAttribute("data-src")
) {
images[i].src = images[i].getAttribute("data-src");
}
n = n + 1;
}
}
};
}
var loadImages = lazyload();
loadImages(); //初始化首页的页面图片
// window.addEventListener('scroll', loadImages, false); //会被高频触发,这非常影响浏览器的性能
window.addEventListener("scroll", throttle(loadImages, 500, 1000), false); //设置500ms 的延迟,和 1000ms 的间隔 避免高频防抖
</script>
</body>
3. Intersection Observer API
IntersectionObserver API,可以自动"观察"元素是否可见。由于可见(visible)的本质是,目标元素与视口产生一个交叉区,所以这个 API 叫做 交叉观察器。 IntersectionObserver 在懒加载、虚拟滚动、曝光统计、上拉刷新等场景中,均能提供高效的解决方案。因为传统的 观察元素是否可见方案,都离不开 Element.getBoundingClientRect 等 DOM 方法,而这些方法均运行在浏览器主线程,一旦方案设计有缺陷,去频繁的触发调用,便会造成一定的性能问题。
<img data-src="image.jpg" alt="test image" />
<script type="text/javascript">
const config = {
rootMargin: "0px 0px 50px 0px",
threshold: 0,
};
const preloadImage = (imagEl) => {
if (imagEl.getAttribute("src") !== imagEl.getAttribute("data-src")) {
imagEl.src = imagEl.getAttribute("data-src");
}
};
let observer = new intersectionObserver(function (entries, self) {
entries.forEach((entry) => {
if (entry.isIntersecting) {
// 将 data-src 改到 src
preloadImage(entry.target);
// 停止对它监听
self.unobserve(entry.target);
}
});
}, config);
const imgs = document.querySelectorAll("[data-src]");
imgs.forEach((img) => {
observer.observe(img);
});
</script>
2. 无限滚动
为什么要做无限滚动
设想有这样一种场景, 浏览器页面高度有限,数据量很大,为了渲染性能,不希望一下全部渲染;同时没有分页的交互。 此时就需要无限滚动来解决这个问题。
原理篇
Infinite Scroll,即无限滚动,是一种在网页滚动到底部时自动加载更多内容的技术。它通常与 AJAX 技术结合使用,通过异步加载数据来减少页面跳转,提高用户体验。在 Infinite Scroll 的实现过程中,开发者需要监听滚动事件,当用户滚动到页面底部时,触发加载更多数据的操作。加载完成后,将数据追加到页面中,从而实现无缝滚动的效果。
实现方案
具体可参考 element UI 中 v-infinite-scroll 指令实现。
// 注意防抖和节流
// 判断是否触发加载的关键代码
let container,shouldTrigger
const distance = 50
const scrollBottom = container.scrollTop + container.clientHeight;
shouldTrigger = container.scrollHeight - scrollBottom <= distance;
if(shouldTrigger) {
// 加载函数
cb()
}
进阶篇: 无限滚动虚拟列表
3. 响应式布局
大屏适配
移动端适配
移动端兼容性问题解决方案
表格大数据渲染
主题切换(换肤)
首屏快速加载
菜单、按钮、数据权限
登录认证鉴权控制
这一部分在网上有一篇总结的比较好的文章, 进行复制参考。
用户体验
1. 用户体验度量模型
UES 模型、适合网站的 PLUSE、谷歌的 HEART、蚂蚁的 PTECH、K- MUX 模型和 GSM 模型,它们各有切入点,也各有其适应场景。
各个模型可从一下三方面进行设计:
- 用户感受
- 用户行为
- 系统表现
2. 前端性能指标
提示
白屏时间 = 地址栏输入网址后回车 - 浏览器出现第一个元素
首屏时间 = 地址栏输入网址后回车 - 浏览器第一屏渲染完成
根据白屏和首屏的定义,我们可以用 FP 和 FCP 来计算白屏和首屏。
白屏结束时间 = FP 事件触发时间
首屏结束时间 = FCP 事件触发时间
FP (First Paint) 首次绘制:标记浏览器渲染任何在视觉上不同于导航前屏幕内容之内容的时间点。
FCP (First Contentful Paint) 首次内容绘制:标记浏览器渲染来自 DOM 第一位内容的时间点,该内容可能是文本、图像、SVG 甚至 元素。
LCP (Largest Contentful Paint) 最大内容渲染:衡量 viewport 内可见的最大内容元素的渲染时间。元素包括 img、video、div 及其他块级元素。
用户体验性能指标: FCP FID CLS
谷歌参考
vscode 工作空间设置
VS Code 的设置共分为五层,其权重由小到大依次为:
默认设置 < 用户设置<远程设置< 工作区设置<文件夹设置。
- 默认设置:VS Code 的默认值不可编辑修改,当没有提供其它设置的值时候或者提供的是无效值的时候,VS Code 提供的默认设置生效;
- 用户设置:此设置对所有项目生效,如果提供了有效设置会覆盖比它权重低的设置;
- 远程设置:如果使用了 VS Code 的远程开发功能将会显示此 Tab,此设置对该远程机器生效,如果提供了有效设置会覆盖比它权重低的设置;
- 工作区设置:默认情况下每打开一个文件夹就是一个工作区,如果提供了有效设置会覆盖比它权重低的设置;
- 文件夹设置:如果启用了多根工作区就会展示此设置,可以为工作区下面的每个文件夹添加独立设置,如果提供了有效设置会覆盖比它权重低的设置,这个设置也是权限最高的设置因为就在当前文件夹下。
版权所有
版权归属:tuyongtao1