按需挂载核心是“只在真正进入视野时才加载”,推荐用intersection observer监听可视区域,结合动态组件与异步导入实现触发即加载、加载完即渲染。

组件按需挂载,核心是“只在真正进入视野时才加载”,而不是一打开页面就全量渲染。这能显著减少首屏资源压力、提升滚动流畅度和内存使用效率。关键不在于“要不要挂载”,而在于“什么时候挂载更合理”。
用 Intersection Observer 监听可视区域
这是目前最推荐、性能最好、兼容性足够(现代浏览器全覆盖)的方式。它不依赖 scroll 事件节流,原生支持被动监听,开销极低。- 创建一个观察器实例,传入回调函数和配置项(如 threshold: 0 表示元素哪怕露出 1px 就触发)
- 在 mounted 钩子中调用 observe() 开始监听目标元素的 ref
- 回调中判断 isIntersecting 为 true 时,再执行组件加载逻辑(比如设置 v-if 为 true 或动态 import)
示例片段:
脚本中控制加载时机
export default {
data() {
return {
loadedComponent: null,
observer: null
}
},
mounted() {
this.observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
this.loadComponent()
this.observer.unobserve(this.$refs.targetEl)
}
},
{ threshold: 0.1 }
)
this.observer.observe(this.$refs.targetEl)
},
methods: {
async loadComponent() {
const mod = await import('./HeavyChart.vue')
this.loadedComponent = mod.default
}
},
beforeUnmount() {
if (this.observer) this.observer.disconnect()
}
}
结合动态组件 + 异步导入
Vue 的 `- 不要在 components 选项里提前写死
() => import(...)—— 那只是注册了异步组件,但 Vue 仍可能在初始渲染时预取 - 更稳妥的做法是:把 import() 放进方法或计算属性中,由 Intersection Observer 或用户操作(如点击、Tab 切换)触发
- 加载成功后,将模块 default 导出赋给 :is 绑定的数据变量
避免常见误区
- v-if 控制挂载 ≠ 按需挂载:仅靠 v-if 切换,若组件已在 components 中静态声明(哪怕是异步形式),Webpack 仍可能打包进初始 chunk - scroll 事件监听不推荐:频繁触发、需手动节流、无法准确判断是否真正可见(比如被遮挡、opacity:0、visibility:hidden) - keep-alive 要慎用:对已加载的懒组件加 keep-alive 可缓存状态,但若组件内部持有大量数据或定时器,反而增加内存负担适合这类场景
- 长列表中的复杂卡片(图表、富编辑器、视频播放器) - 页面底部的“相关推荐”、“评论区”等非首屏区块 - Tab 切换时,仅激活 tab 对应的组件才加载(而非全部预加载) - 折叠面板展开时,才加载其内部重型子组件不复杂但容易忽略。真正起效的关键,是把“加载动作”从初始化阶段,挪到可感知的用户行为或可视信号之后。











