/** * Store 基类(规范 §131) * * 微信小程序原生开发无 Pinia/MobX,此处用极简的发布订阅实现响应式 Store: * - state:只读数据视图 * - setState(partial):浅合并 + 通知订阅者 * - subscribe(listener):页面 onLoad/onShow 订阅,onUnload/onHide 退订 * * 设计原则: * 1. Store 是单例,跨页面共享。 * 2. Store 只持有"跨页面需要"的状态;纯页面局部状态仍用 this.data + setData。 * 3. Store 内部不直接操作 wx.* UI(toast/modal),UI 副作用由调用方页面承担; * 但 AppStore 提供 toast/loading 的统一封装供页面调用。 * 4. Store 的 action 返回 Promise,错误由调用方 try/catch 并决定 UI 反馈。 */ const { STORAGE_KEYS } = require('../constants'); class BaseStore { constructor(initialState = {}) { this.state = { ...initialState }; /** @type {Array<(state: object, patch: object) => void>} */ this._listeners = []; } /** * 浅合并 patch 到 state,并通知所有订阅者 * @param {Partial} patch * @returns {object} 合并后的新 state(引用变化) */ setState(patch) { const next = { ...this.state, ...patch }; this.state = next; for (const fn of this._listeners) { try { fn(next, patch); } catch (e) { /* 单个监听者错误不影响其他 */ } } return next; } /** * 订阅 state 变更,返回退订函数 * @param {(state: object, patch: object) => void} listener * @returns {() => void} unsubscribe */ subscribe(listener) { if (typeof listener !== 'function') return () => {}; this._listeners.push(listener); return () => { const i = this._listeners.indexOf(listener); if (i >= 0) this._listeners.splice(i, 1); }; } /** * 把当前 state 的指定字段同步到页面 data(页面常用辅助) * @param {WechatMiniprogram.Page.Instance} page * @param {{ [storeKey: string]: string }} mapping storeKey -> pageDataKey * @returns {() => void} 退订函数 */ bindToPage(page, mapping = {}) { const apply = () => { const patch = {}; for (const storeKey of Object.keys(mapping)) { const pageKey = mapping[storeKey] || storeKey; // 跳过 undefined 值,避免触发"Setting data field X to undefined is invalid"警告 const value = this.state[storeKey]; if (value !== undefined) { patch[pageKey] = value; } } if (Object.keys(patch).length > 0 && typeof page.setData === 'function') { page.setData(patch); } }; apply(); // 立即同步一次 return this.subscribe(apply); } /** * 把关键字段持久化到 wx.storage(避免刷新/冷启动丢失) * @param {object} snapshot * @param {string} storageKey STORAGE_KEYS 中的键 */ persist(snapshot, storageKey) { try { wx.setStorageSync(storageKey, snapshot); } catch (e) { /* ignore */ } } /** * 从 wx.storage 还原(冷启动调用) * @param {string} storageKey STORAGE_KEYS 中的键 * @returns {object|null} */ static hydrate(storageKey) { try { const v = wx.getStorageSync(storageKey); return v && typeof v === 'object' ? v : null; } catch (e) { return null; } } } module.exports = BaseStore;