【前端】懒加载(组件/路由/图片等)+预加载 汇总
目录
- 懒加载
- 组件
- 路由
- 图片
- 其他场景
- 预加载
按需加载=懒加载+预加载
懒加载
组件
组件频繁使用时不建议懒加载,只懒加载低频组件!
- vue
defineAsyncComponent+suspense【vue2不支持】+ #default/fallback
#default 插槽:渲染真正的内容(比如异步组件)
#fallback 插槽:在内容加载期间显示的占位内容(比如 loading 文案或动画)
<Suspense><template #default><MyComponent /></template><template #fallback>Loading...</template>
</Suspense>// 异步组件(默认内置 Suspense 支持)搭配 <Suspense> 使用
const MyComponent = defineAsyncComponent(() => import('./MyComponent.vue'))
- React
React.lazy 和 Suspense
const MyComponent = React.lazy(() => import('./MyComponent'))<Suspense fallback={<div>Loading...</div>}><MyComponent />
</Suspense>
路由
//vue
// Vue Router 中使用动态 import 实现懒加载
const routes = [{path: '/about',component: () => import('./views/About.vue')}
]//react routerv6+
const About = React.lazy(() => import('./pages/About'))
//注意:每个使用 React.lazy()的路由组件都要手动包一层 <Suspense>。
<Route path="/about" element={<Suspense fallback={<div>Loading...</div>}><About /></Suspense>
} />
图片
- HTML 原生方式(推荐,所有框架都通用)
<img src="image.jpg" loading="lazy" alt="示例图">
- vue/react
//vue
//插件 vue-lazyload
// main.ts
import VueLazyLoad from 'vue3-lazyload'
app.use(VueLazyLoad)
// 使用
<img v-lazy="imageUrl" />//react
//1. 原生 loading="lazy"
//2. IntersectionObserver 手写懒加载(更高级)暂略
其他场景
类型 | Vue | React |
---|---|---|
第三方组件 | defineAsyncComponent() | React.lazy() |
Markdown/Code 高亮插件 | 异步加载 PrismJS 等库 | 同上 |
地图库(如高德) | <script async> 动态加载 SDK | useEffect + createElement('script') |
iframe/视频等 | 设置 loading="lazy" + 原生IntersectionObserver | loading="lazy"/preload="none" // 不预加载视频 /第三方库 react-intersection-observer /原生IntersectionObserver |
- IntersectionObserver
原生 IntersectionObserver 是 JavaScript 原生浏览器 API,不属于 React、Vue、jQuery 等框架,vue/react都没有自带或者封装它。用来监听某个元素是否进入或离开视口(或另一个指定元素),常用于:
懒加载图片、视频、iframe
无限滚动
进入视口时动画播放
页面曝光率埋点
⚠️注意
不能监听 display: none 的元素
滚动容器若是自定义元素,需要设置 overflow: auto/scroll
IE11 不支持,需要用 IntersectionObserver polyfill
const target = document.querySelector('#myElement');
/*
第一个参数是回调函数,每当被观察的元素进入或离开视口时触发。
entries: 是一个数组,包含每个被观察元素的状态(IntersectionObserverEntry)。
observer: 当前的 IntersectionObserver 实例本身。第二个参数配置
root: 观察区域。null 表示是浏览器视口(viewport)。
threshold: 触发比例阈值,取值 0 ~ 1。0.1 表示当目标元素有 10% 可见时就触发回调。
*/
const observer = new IntersectionObserver((entries, observer) => {entries.forEach(entry => {if (entry.isIntersecting) {//元素已经进入观察区域(例如视口)console.log('元素进入视口');observer.unobserve(entry.target); // 一次性监听//观察器停止监听这个目标}});
}, {root: null, // 默认为视口threshold: 0.1, // 元素进入视口 10% 就触发
});observer.observe(target);//开始观察一个 DOM 元素 target,只要它进入视口就会执行前面的回调。//多个元素
// 选中所有需要懒加载的图片
const images = document.querySelectorAll('.lazy');
// 遍历每个元素,添加观察
images.forEach(img => observer.observe(img));
预加载
- 应用场景
场景 | 推荐方式 |
---|---|
当前页面关键 JS/CSS/font | <link rel="preload"> 或 webpackPreload |
下个页面可能需要的组件 | webpackPrefetch / 动态 import() |
静态图片 | <link preload> 或 JS 创建 Image |
提前连接 CDN 或后端 API 域名 | dns-prefetch / preconnect |
用户 hover/点击前加载 | 手动 import()/fetch() |
- html文件
类型 | 技术/方式 | 说明 |
---|---|---|
HTML 资源级别 | <link rel="preload"> | 提前加载关键资源,支持 js/css/font/image/video 等。 |
<link rel="prefetch"> | 低优先级加载未来可能用到的资源(如下一页)。 | |
<link rel="dns-prefetch"> | 提前进行 DNS 查询,加快第三方资源访问。 | |
<link rel="preconnect"> | 提前进行 TCP + TLS 握手。 | |
<link rel="prerender"> | 预渲染整个页面(较少使用,Chrome 支持有限)。 |
- JS文件
安装Webpack 的“魔法注释”写法,用于控制异步模块的加载优先级
这类注释只在 Webpack 构建时生效,属于 Webpack 的“魔法注释”
生成的 HTML 中会自动插入<link rel="preload"> 或 <link rel="prefetch">
标签
import(/* webpackPrefetch: true */ './HeavyComponent');
import(/* webpackPreload: true */ './CriticalComponent');
webpackPrefetch: 低优先级预加载,浏览器空闲时加载(推荐用于未来页面组件)
webpackPreload: 高优先级并行加载,立即加载(推荐用于当前页面关键模块)
- vue/react懒加载+预加载组合,使加载更聪明
const Page = () => import(/* webpackPrefetch: true */ './Page.vue');const LazyPage = React.lazy(() => import(/* webpackPrefetch: true */ './Page'));// 可搭配使用:使用 prefetch 手动提前加载,否则空闲才加载
import('./Page');
- 图片
<link rel="preload" as="image" href="/images/banner.jpg">
const img = new Image();
img.src = '/images/banner.jpg';
- 组件:vue/react也是通过提前触发 import() 方式实现(参考上方 webpackPrefetch),也可以组合搭配上懒加载
- 视频
<video preload="auto" src="video.mp4" />
<!--
none 不预加载任何数据
metadata 只加载元数据
auto 浏览器自己决定(尽量预加载)
-->
- 路由也是用路由预加载确实可以通过 Webpack 的魔法注释来实现搭配懒加载
- 字体 也是
<link rel="preload" as="font" href="font.woff2" type="font/woff2" crossorigin="anonymous">
- 页面可见性触发
结合 IntersectionObserver,提前加载资源
const observer = new IntersectionObserver((entries) => {entries.forEach(entry => {if (entry.isIntersecting) {fetch('/api/data.json'); // 或 import()observer.unobserve(entry.target);}});
});
observer.observe(document.querySelector('#future-content'));