1、使用IntersectionObserver实现图片懒加载的原理是什么?
答:IntersectionObserver是浏览器提供的原生 API,用于监听一个元素与其父容器或视口(viewport) 的交叉状态(是否进入可视区域)。
懒加载的核心思路:
- 页面中图片或资源先只设置
占位(如灰色背景或小图),真实图片不加载; - 当图片即将进入可视区域时,
IntersectionObserver回调触发; - 回调里把
data-src等真实图片地址赋值给src - 浏览器开始加载真实图片:
- 初始化观察器
const observer = new IntersectionObserver( entries => { entries.forEach(entry => { if (entry.isIntersecting) { // 元素进入视口 const img = entry.target; img.src = img.dataset.src; // 替换真实图片 observer.unobserve(img); // 取消观察,避免重复触发 } }); }, { root: null, // 默认为 viewport rootMargin: '0px', threshold: 0.1 // 元素 10% 可见时触发 } );- 将要懒加载的图片加入观察
document.querySelectorAll('img[data-src]').forEach(img => { observer.observe(img); });- 图片进入可视区域 → 回调触发 → 加载真实图片
root:指定观察的容器(可选,默认为viewport)threshold:可见比例阈值,0~1,例如 0.1 表示 10% 可见时触发rootMargin:可视区域的边距,用于提前或延迟触发
- 优点:
- 原生
API:性能优于滚动事件监听(不用频繁触发scroll回调) - 懒加载性能优化:减少首屏资源、降低带宽消耗
- 灵活:可用于图片、组件、广告、表格行、无限滚动等
- 原生
2、DOM 操作非常频繁时如何优化性能?
答:浏览器渲染流程:JS 执行 → 修改 DOM → 样式计算(Reflow)→ 重绘(Repaint);频繁DOM操作 会导致 多次 Reflow/Repaint,开销大,性能下降,尤其是大列表或动画场景。目标是 减少DOM操作次数、批量更新、减少渲染开销。
- 批量操作
DOM/ 使用DocumentFragment- 将元素先加入
DocumentFragment,一次性append到DOM
- 将元素先加入
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const li = document.createElement('li');
li.textContent = `Item ${i}`;
fragment.appendChild(li);
}
document.querySelector('#list').appendChild(fragment);
- 使用虚拟
DOM/ 框架优化React / Vue通过虚拟DOM对比前后状态,只更新必要节点- 减少直接操作
真实DOM,降低渲染开销
- 避免触发多次重排
- 修改样式或布局属性时,避免读写交叉:
- 批量修改样式时可使用
classList或CSS 变量
// 不推荐
const height = el.offsetHeight;
el.style.width = '100px';
// 推荐:先修改后读取或缓存值
el.style.width = '100px';
const height = el.offsetHeight;
- 使用
requestAnimationFrame- 动画或滚动场景,频繁修改
DOM可用requestAnimationFrame批量执行,保证每帧只渲染一次
- 动画或滚动场景,频繁修改
- 使用
CSS3动画替代JS动画- 尽量使用
transform / opacity,避免触发布局重排; GPU加速渲染,减少JS操作和重绘;
- 尽量使用
- 虚拟列表 / 懒加载
- 对大量列表或表格,只渲染可视区域的
DOM - 滚动时动态渲染,减少一次性
DOM创建
- 对大量列表或表格,只渲染可视区域的
- 减少不必要的监听器
- 尽量 委托事件,而不是每个元素绑定事件
3、Shadow DOM 中的 CSS 隔离是如何实现的?你遇到过哪些坑?
答:Shadow DOM提供了原生的CSS隔离机制,shadow root内定义的样式不会影响外部,外部样式也不会渗透进来。它是通过浏览器在渲染时为选择器加作用域标记实现的。常见的坑有:全局样式无法生效、CSS框架不兼容、调试不方便,以及需要通过::part、CSS variables等方式暴露主题接口。Shadow DOM是Web Components的核心之一,它提供了一种样式作用域隔离的能力。
- 样式只在
Shadow DOM内生效Shadow root内定义的CSS规则,只会应用于shadow内部的元素,不会“泄露”到外部。- 外部样式(页面全局
CSS)默认也不会影响Shadow DOM内部。
- 选择器作用范围限制
Shadow内部的style标签只影响当前shadow root。- 全局的
.btn {}不会影响shadow内的<button>。
- 特性选择器实现隔离(浏览器内部机制)
- 浏览器在渲染时,会给
shadow DOM元素加上特殊的 “scoped attribute”(类似hash标记),使选择器只匹配到自己范围内的节点。 - 这就是为什么
shadow DOM内的CSS能天然隔离,而不像普通scoped CSS需要构建工具处理。
- 浏览器在渲染时,会给
常见的坑点
- 全局样式无法渗透进
Shadow DOM- 解决:可以用
CSS Custom Properties (变量)来“穿透”:
- 解决:可以用
- 无法用外部
CSS框架(如Tailwind、Bootstrap):因为框架的全局样式不会进入shadow。- 解决:在组件内手动引入需要的样式。或者用
CSS variables + :host机制,暴露出主题接口。
- 解决:在组件内手动引入需要的样式。或者用
- 调试困难
- 开发时用
DevTools看DOM,会看到“#shadow-root”分隔,层级比较绕。样式问题经常出现在 host和shadow内部元素之间的交互。
- 开发时用
- 样式穿透限制
Shadow DOM隔离太“严格”,有时你想自定义某个子组件样式,却发现改不了。- 解决:使用
::part和::theme暴露可控的CSS接口:
- 第三方库不兼容
-
一些 UI 库(基于全局 CSS)在 Shadow DOM 内直接用会失效。
-
比如用到
document.querySelector或者全局class选择器的库。
-