Svelte memory management stores的subscribe与存储性能咨询
Svelte Stores 内存管理相关问题解答
1. subscribe 运行机制,以及和引用机制的差异
Svelte store的核心是一套极简的发布-订阅实现,和JS引擎层面的引用机制没有直接关联,不要把两者混为一谈。
它的运行逻辑非常直白:
- 所有符合store规范的对象,内部都会维护一个订阅者回调集合。当你调用
store.subscribe(回调函数)时,store会做两个操作:首先立刻把当前存储的值推给你传入的回调执行一次,其次把这个回调存入内部的订阅者集合,最后返回一个用于解绑的unsubscribe函数。 - 后续你通过
set、update方法修改store的值时,store会遍历整个订阅者集合,把新值挨个传给所有注册的回调执行,触发所有订阅方的更新。 - 当你调用subscribe返回的解绑函数时,对应的回调会从内部订阅者集合里被移除,后续值更新就不会再触发这个回调。
注意:Svelte组件里用
$store语法糖访问store时,框架会在组件挂载时自动完成订阅,组件销毁时自动解绑,不需要手动处理。但如果是在组件外、或者在定时器/事件回调里手动调用subscribe,一定要记得在不需要的时候调用unsubscribe解绑——因为store的内部集合会一直持有回调的引用,不解绑的话回调以及回调引用的所有变量都不会被GC回收,直接造成内存泄漏。
你可以通过下面这个极简的自定义store实现,直观看到subscribe的逻辑:
function createWritable(initialValue) { let currentValue = initialValue const subscribers = new Set() return { subscribe(cb) { cb(currentValue) // 订阅即推送当前值 subscribers.add(cb) // 返回解绑函数 return () => subscribers.delete(cb) }, set(newValue) { if (newValue === currentValue) return // Svelte内置store会做等值判断,相同值不触发更新 currentValue = newValue subscribers.forEach(cb => cb(currentValue)) }, update(updater) { this.set(updater(currentValue)) } } }
2. 上传数据是否适合存入store,是否存在性能负面影响
这个问题没有绝对答案,核心看你存的是什么类型的上传相关数据:
- 完全可以存、不会有性能问题的场景:如果存的是小体积的上传状态,比如上传进度百分比、上传状态(待上传/上传中/成功/失败)、文件基础元信息(文件名、大小、类型、上传后服务端返回的访问地址),这类数据体积极小,哪怕频繁更新、有多个订阅方,也不会造成性能负担,放在全局store里做跨组件状态共享非常合适。
- 不建议存、可能引发性能问题的场景:如果要存的是大体积的原始上传数据,比如GB级的视频File对象、全量文件切片缓存、转码后的超大base64字符串、原始高清图片Bitmap数据,这类数据本身内存占用极高,存入store后会被store长期持有引用,只要store不销毁就无法被GC回收,很容易造成页面内存占用过高;如果这类大对象触发store更新,所有订阅方都会收到这个大对象的引用,要是订阅逻辑里存在深拷贝、全量遍历这类操作,还很容易造成主线程卡顿。
如果确实有跨组件共享大体积上传对象的需求,也不是完全不能存,只要在上传流程结束(成功/失败/用户取消)、相关页面组件销毁时,主动把store里对应的值设为null,断开对大对象的引用,让GC可以正常回收内存,就不会造成长期的性能问题。
内容的提问来源于stack exchange,提问作者frederik
相关产品推荐
相关产品推荐

