小程序中跨页面组件共享数据的实现方法与对比
小程序中跨页面/组件共享数据的实现方法与对比
在小程序开发中,实现不同页面或组件之间的数据共享是常见需求。以下是几种主要实现方式的详细总结与对比分析:
一、常用数据共享方法
全局变量(getApp()
)、本地缓存(wx.setStorage
)、事件总线(Event Bus
)、状态管理库(如Redux
/MobX
适配方案)、页面间传参(跳转带参数的方法)、组件之间的传值。
1. 全局变量(getApp()
)深度解析
一、基本概念与核心机制
getApp()
是小程序提供的基础API,用于获取小程序全局唯一的App实例。其核心特点包括:
- 单例模式:整个小程序生命周期内只有一个App实例
- 全局可访问:任何页面/组件都能通过
getApp()
访问 - 内存存储:数据保存在运行内存中,关闭小程序即销毁
- 无响应式:数据变更不会自动触发视图更新
二、使用方法详解
1. 初始化全局数据
// app.js
App({// 必须定义在globalData对象内globalData: {userToken: null,systemInfo: {},config: {apiBaseUrl: 'https://api.example.com'}},// 可以添加自定义方法getSystemInfo() {return wx.getSystemInfoSync()}
})
2. 数据读写操作
// 在任何页面/组件中
const app = getApp()// 读取数据
console.log(app.globalData.userToken)// 修改数据(直接赋值)
app.globalData.userToken = 'new_token_123'// 调用全局方法
const sysInfo = app.getSystemInfo()
三、高级应用技巧
1. 数据监听方案
虽然原生不支持响应式,但可以通过以下方式实现监听:
// app.js中扩展监听方法
App({// ...其他配置watchMap: new Map(),watch(key, callback) {if (!this.watchMap.has(key)) {this.watchMap.set(key, new Set())}this.watchMap.get(key).add(callback)},trigger(key, value) {const callbacks = this.watchMap.get(key)callbacks?.forEach(cb => cb(value))}
})// 使用示例
const app = getApp()
app.watch('userToken', (newVal) => {console.log('token变更:', newVal)
})// 修改数据时触发
app.globalData.userToken = 'new'
app.trigger('userToken', 'new')
2. 类型安全增强(TypeScript)
// types/app.d.ts
declare type GlobalData = {userToken: string | nullsystemInfo: WechatMiniprogram.SystemInfoconfig: {apiBaseUrl: string}
}declare type CustomAppOption = {globalData: GlobalDatagetSystemInfo(): WechatMiniprogram.SystemInfowatch(key: string, callback: (value: any) => void): voidtrigger(key: string, value: any): void
}// 使用时有完整类型提示
const app = getApp() as CustomAppOption
四、性能优化建议
-
数据分层存储
// 按业务模块划分 globalData: {auth: { /* 认证相关 */ },settings: { /* 配置相关 */ },temp: { /* 临时数据 */ } }
-
避免大数据存储
• 单个属性值不超过1MB
• 复杂对象建议拆分为多个属性 -
及时清理机制
// 页面卸载时清理 Page({onUnload() {const app = getApp()delete app.globalData.tempData} })
五、与其它方案的对比优势
特性 | 全局变量 | 本地存储 | 状态管理库 |
---|---|---|---|
数据生命周期 | 内存级 | 持久化 | 可配置 |
读写速度 | 最快 | 慢(需I/O) | 中等 |
数据共享范围 | 全应用 | 全应用 | 可灵活控制 |
响应式支持 | 需手动实现 | 无 | 内置支持 |
适合场景 | 高频访问临时数据 | 需持久化数据 | 复杂状态管理 |
六、常见问题解决方案
1. 数据污染问题
现象:多个页面修改同一数据导致状态混乱
解决方案:
// 使用冻结保护重要数据
Object.freeze(app.globalData.config)// 或者使用代理拦截
const protectedData = new Proxy(app.globalData, {set(target, key, value) {if (key === 'criticalData') {throw new Error('此数据不可直接修改')}return Reflect.set(target, key, value)}
})
2. 跨页面同步问题
推荐模式:
// 在app.js中定义统一更新方法
App({// ...updateUserInfo(userInfo) {this.globalData.userInfo = userInfothis._broadcast('userInfoUpdate', userInfo)},_broadcast(event, data) {const pages = getCurrentPages()pages.forEach(page => {page.onGlobalDataUpdate?.(event, data)})}
})// 页面中实现监听方法
Page({onGlobalDataUpdate(event, data) {if (event === 'userInfoUpdate') {this.setData({ userInfo: data })}}
})
七、最佳实践建议
-
命名规范
• 全局方法:动词+名词
(如updateUserProfile
)
• 数据属性:名词+类型
(如themeConfig
) -
安全边界
// 对敏感操作添加验证 App({setGlobalData(key, value) {if (!this._validate(key, value)) returnthis.globalData[key] = value} })
-
调试支持
// 开发环境暴露到全局 if (process.env.NODE_ENV === 'development') {wx._app = getApp() }
-
性能监控
// 记录数据变更历史 App({dataHistory: [],setGlobalData(key, value) {this.dataHistory.push({key, value,time: Date.now(),page: getCurrentPages().pop()?.route})// 保持合理的历史记录长度if (this.dataHistory.length > 50) {this.dataHistory.shift()}} })
全局变量方案虽然简单,但通过合理的架构设计和技术增强,完全可以满足中小型小程序的全局状态管理需求,在性能和开发效率之间取得良好平衡。
2. 本地缓存(wx.setStorage
)深度解析
一、核心特性与底层机制
-
持久化存储:
• 数据写入设备本地文件系统
• 关闭小程序后仍保留(直到主动清除或用户清理缓存)
• 受微信客户端存储策略管理 -
存储限制:
wx.getStorageInfoSync() // 可获取使用情况
• 单条数据上限:1MB
• 总容量上限:10MB
• 超出限制会触发fail
回调 -
数据安全:
• 自动加密存储(iOS使用Keychain,Android使用SharedPreferences)
• 同一微信用户下多设备间不同步
二、API体系详解
1. 基础读写操作
// 异步写入(推荐)
wx.setStorage({key: 'user_profile',data: { name: '张三', age: 25 },encrypt: true, // 是否加密存储(v2.17.0+)success() {console.log('写入成功')}
})// 同步写入(可能阻塞渲染)
wx.setStorageSync('last_login', Date.now())// 异步读取
wx.getStorage({key: 'user_profile',success(res) {console.log(res.data)}
})// 同步读取
const data = wx.getStorageSync('last_login')
2. 高级操作
// 批量操作(v2.10.0+)
wx.batchStorage({operations: [{ type: 'set', key: 'token', data: 'abc123' },{ type: 'remove', key: 'temp_data' }]
})// 模糊删除(需自行实现)
const keys = wx.getStorageInfoSync().keys
keys.filter(k => k.startsWith('temp_')).forEach(k => {wx.removeStorageSync(k)
})
三、性能优化策略
-
数据序列化优化:
// 反例:直接存储复杂对象 wx.setStorageSync('big_obj', largeObject) // 正例:手动序列化 wx.setStorageSync('compressed', {data: JSON.stringify(largeObject),_isString: true })
-
读写时机控制:
// 在onHide时保存,避免阻塞页面交互 Page({onHide() {wx.setStorage({key: 'page_state',data: this.data})} })
-
缓存策略示例:
const CACHE_TTL = 3600000 // 1小时function getWithCache(key) {const cached = wx.getStorageSync(key)if (cached?.timestamp && Date.now() - cached.timestamp < CACHE_TTL) {return cached.data}return null }
四、企业级实践方案
-
封装存储层:
class StorageManager {constructor() {this.prefix = 'app_'}set(key, value, ttl) {const data = {value,_meta: {createdAt: Date.now(),expiresAt: ttl ? Date.now() + ttl : null}}wx.setStorageSync(this.prefix + key, data)}get(key) {const data = wx.getStorageSync(this.prefix + key)if (!data) return nullif (data._meta?.expiresAt && data._meta.expiresAt < Date.now()) {this.remove(key)return null}return data.value} }
-
TypeScript增强:
interface StorageMeta {createdAt: numberexpiresAt?: number }declare namespace wx {interface StorageOption {encrypt?: booleanttl?: number // 自定义扩展字段} }
五、常见问题解决方案
-
数据版本冲突:
// 存储时添加版本号 wx.setStorageSync('user_data', {_version: 'v2.1',data: currentData })// 读取时校验 function migrateData(oldVer, newVer) {// 数据迁移逻辑 }
-
加密数据场景:
// 配合wx.getUserInfo的加密数据 wx.setStorage({key: 'encrypted_data',data: {iv: encryptedData.iv,encrypted: encryptedData.encryptedData},encrypt: true })
-
多Tab同步问题:
// 监听storage事件 wx.onStorageChange((res) => {console.log('数据变更:', res.key, res.newValue) })
六、与全局变量对比实践
场景 | 推荐方案 | 原因 |
---|---|---|
用户登录凭证 | 本地缓存 + 内存缓存 | 持久化保证不丢失,内存缓存提高访问速度 |
页面间临时传参 | 全局变量 | 避免不必要的I/O操作 |
应用配置信息 | 本地缓存 | 首次加载后缓存,减少网络请求 |
大数据量(<1MB) | 分割存储 | 拆分为多个key存储,避免超出单条限制 |
七、调试技巧
-
查看所有缓存:
// 控制台快速查看 console.log(wx.getStorageInfoSync())
-
模拟器操作:
开发者工具 -> 存储 -> 可可视化查看/编辑
-
真机调试:
// 通过wx.getStorageInfo获取存储列表 wx.getStorageInfo({success(res) {console.debug('当前使用:', res.currentSize, 'KB')} })
八、安全注意事项
-
敏感信息处理:
// 避免直接存储敏感信息 const safeStorage = {setSensitive(key, value) {const encrypted = crypto.encrypt(value)wx.setStorageSync(key, encrypted)} }
-
清理策略:
// 启动时清理过期数据 App({onLaunch() {this.cleanExpiredStorage()},cleanExpiredStorage() {const { keys } = wx.getStorageInfoSync()keys.forEach(key => {const data = wx.getStorageSync(key)if (data?._meta?.expiresAt < Date.now()) {wx.removeStorageSync(key)}})} })
本地缓存作为小程序持久化存储的核心方案,合理运用可以显著提升用户体验。建议根据数据特性采用分层存储策略,关键数据建议实现「内存+持久化」双缓存机制,同时注意及时清理过期数据避免存储空间浪费。
3. 事件总线(Event Bus)深度解析
一、核心概念与实现原理
-
发布-订阅模式:
• 基于观察者模式实现
• 组件间完全解耦,通过事件标识通信
• 典型流程:发布者emit → 事件中心分发 → 订阅者on接收 -
小程序中的特殊考量:
// 需要手动维护组件生命周期 Page({onUnload() {eventBus.off('update', this.handleUpdate)} })
二、完整实现方案
基础实现(支持一次性和常规监听)
// eventBus.js
class EventBus {constructor() {this.events = new Map()}$on(event, callback, { once = false } = {}) {if (!this.events.has(event)) {this.events.set(event, new Set())}const wrapper = once ? (...args) => {callback(...args)this.$off(event, wrapper)} : callbackthis.events.get(event).add(wrapper)return () => this.$off(event, wrapper) // 返回取消函数}$emit(event, ...args) {const callbacks = this.events.get(event)callbacks?.forEach(cb => {try {cb(...args)} catch (e) {console.error(`Event ${event} handler error:`, e)}})}$off(event, callback) {if (!callback) {this.events.delete(event)} else {const callbacks = this.events.get(event)callbacks?.delete(callback)}}
}export default new EventBus()
TypeScript增强版
// eventBus.ts
type EventCallback<T = any> = (data?: T) => voidclass EventBus {private events: Map<string, Set<EventCallback>>constructor() {this.events = new Map()}$on<T>(event: string, callback: EventCallback<T>): () => void {// ...同JS实现}$emit<T>(event: string, payload?: T): void {// ...同JS实现}
}
三、高级应用场景
-
带命名空间的事件:
// 事件名格式:namespace:event eventBus.$on('user:updated', (data) => {console.log('用户数据更新', data) })// 批量取消命名空间事件 eventBus.$off(/^user:/)
-
事件竞态控制:
let lastEmitTime = 0 function throttleEmit(event, data, delay = 300) {const now = Date.now()if (now - lastEmitTime > delay) {eventBus.$emit(event, data)lastEmitTime = now} }
-
跨页面通信:
// 页面A发布 eventBus.$emit('global:refresh', { from: 'pageA' })// 页面B监听(需考虑生命周期) Page({onLoad() {this._unsubscribe = eventBus.$on('global:refresh', this.handleRefresh)},onUnload() {this._unsubscribe?.()} })
四、性能优化方案
-
事件池管理:
const MAX_LISTENERS = 20 class SafeEventBus extends EventBus {$on(event, callback) {if (this.events.get(event)?.size >= MAX_LISTENERS) {console.warn(`Event ${event} exceeds max listeners`)return () => {}}return super.$on(event, callback)} }
-
内存泄漏防护:
// 自动绑定组件实例 function autoBind(component, event, handler) {const boundHandler = handler.bind(component)component._eventHandlers = component._eventHandlers || []component._eventHandlers.push({ event, handler: boundHandler })return eventBus.$on(event, boundHandler) }// 组件销毁时自动解绑 Page({onUnload() {this._eventHandlers?.forEach(({ event, handler }) => {eventBus.$off(event, handler)})} })
五、与原生事件系统对比
特性 | 事件总线 | wx.event | 组件自定义事件 |
---|---|---|---|
通信范围 | 全局 | 页面内 | 父子组件间 |
生命周期管理 | 需手动 | 自动 | 自动 |
事件类型支持 | 自定义 | 固定类型 | 自定义 |
性能开销 | 中等 | 低 | 低 |
适合场景 | 跨组件/跨页面通信 | 原生组件事件处理 | 组件树内部通信 |
六、调试与监控
-
事件追踪:
// 开发环境增强 if (__DEV__) {const originalEmit = eventBus.$emiteventBus.$emit = function(event, ...args) {console.log(`[EventBus] ${event}`, args)return originalEmit.call(this, event, ...args)} }
-
性能分析:
const eventMetrics = {} function wrapWithMetrics(event, handler) {return function(...args) {const start = performance.now()handler(...args)const duration = performance.now() - starteventMetrics[event] = (eventMetrics[event] || 0) + duration} }
七、最佳实践建议
-
命名规范:
• 全局事件:模块:动作
如cart:item-added
• 局部事件:组件名-动作
如search-bar:submit
-
错误处理:
eventBus.$on('data:fetch', async (params) => {try {const data = await fetchData(params)eventBus.$emit('data:success', data)} catch (err) {eventBus.$emit('data:error', err)} })
-
混合使用策略:
// 简单状态用全局变量,复杂交互用事件总线 const app = getApp() eventBus.$on('user:login', (user) => {app.globalData.user = userwx.setStorageSync('user', user) })
事件总线作为松耦合通信方案,特别适合以下场景:
• 非父子关系的远距离组件通信
• 需要跨多个页面的状态同步
• 临时性的事件通知(如Toast提示)
通过合理的封装和生命周期管理,可以构建出既灵活又可靠的事件通信系统。建议在大型项目中配合TypeScript使用,以获得更好的类型安全和代码提示。
4. 状态管理库(如Redux
/MobX
适配方案)这里只讲MobX
在小程序中使用 MobX 进行状态管理可以帮助你更高效地管理全局或页面级的状态。以下是详细的步骤和示例代码:
一. 安装 MobX 及相关依赖
小程序默认不支持直接使用 npm,但可以通过构建工具(如 webpack
、gulp
)或使用 miniprogram-npm
工具安装 MobX。推荐使用 mobx-miniprogram
和 mobx-miniprogram-bindings
(专为小程序优化的版本)。
npm install mobx-miniprogram mobx-miniprogram-bindings --save
构建 npm 后,在小程序开发者工具中点击「工具」→「构建 npm」。
二. 创建 MobX Store
创建一个全局状态管理 Store,例如 stores/counterStore.js
:
// stores/counterStore.js
import { observable, action } from 'mobx-miniprogram';export const counterStore = observable({// 可观察状态count: 0,// Action 更新状态increment: action(function () {this.count++;}),decrement: action(function () {this.count--;}),// 计算属性get doubleCount() {return this.count * 2;}
});
三. 在 App 中挂载 Store(可选)
在 app.js
中挂载全局 Store,方便全局访问:
// app.js
import { counterStore } from './stores/counterStore';App({globalData: {store: {counter: counterStore}}
});
四. 在页面/组件中连接 MobX
使用 mobx-miniprogram-bindings
提供的 createStoreBindings
方法将 Store 绑定到页面或组件。
在页面中使用
// pages/index/index.js
import { createStoreBindings } from 'mobx-miniprogram-bindings';
import { counterStore } from '../../stores/counterStore';Page({onLoad() {// 绑定 Store 到页面实例this.storeBindings = createStoreBindings(this, {store: counterStore,fields: ['count', 'doubleCount'], // 需要监听的状态字段actions: ['increment', 'decrement'] // 需要绑定的 actions});},onUnload() {// 页面卸载时清理绑定this.storeBindings.destroy();},// 自定义方法(如按钮点击事件)handleIncrement() {this.increment(); // 直接调用 Store 中的 action}
});
在组件中使用
// components/my-component.js
import { createStoreBindings } from 'mobx-miniprogram-bindings';
import { counterStore } from '../../stores/counterStore';Component({lifetimes: {attached() {this.storeBindings = createStoreBindings(this, {store: counterStore,fields: ['count'],actions: ['increment']});},detached() {this.storeBindings.destroy();}}
});
五. 在 WXML 中绑定状态和事件
在页面的 WXML 文件中直接使用 Store 的状态和触发 Action:
<!-- pages/index/index.wxml -->
<view>当前计数:{{count}}</view>
<view>双倍计数:{{doubleCount}}</view>
<button bindtap="handleIncrement">增加</button>
<button bindtap="decrement">减少</button>
六. 使用计算属性和监听器
MobX 的 computed
和 reaction
可以帮助你处理复杂逻辑:
import { computed, reaction } from 'mobx-miniprogram';const store = observable({count: 0,// 计算属性get squared() {return this.count ** 2;}
});// 监听 count 变化
const disposer = reaction(() => store.count,(count) => {console.log('Count changed:', count);}
);// 在页面卸载时调用 disposer() 停止监听
注意事项
- 避免直接修改状态:始终通过
actions
修改状态,保证状态变更可追踪。 - 清理绑定:在页面/组件卸载时调用
this.storeBindings.destroy()
,防止内存泄漏。 - 性能优化:使用
fields
精确指定需要监听的状态,避免不必要的更新。
通过以上步骤,你可以在小程序中高效地使用 MobX 管理状态。如果需要更复杂的场景(如多 Store 管理),可以参考 MobX 官方文档进行扩展。
5. 页面间传参深度解析
一、核心传参方式与实现
1. URL参数直传(最常用)
// 发起页面
wx.navigateTo({url: '/pages/detail?id=123&name=张三&data=' + encodeURIComponent(JSON.stringify({score: 90}))
})// 接收页面
Page({onLoad(options) {const id = options.id // "123"(字符串类型)const name = decodeURIComponent(options.name) // "张三"const data = JSON.parse(decodeURIComponent(options.data)) // {score: 90}}
})
特点:
• 适用场景:简单基础类型数据传递
• 参数限制:
• 单个参数长度≤1KB
• 总URL长度≤2MB(iOS/Android差异)
• 编码要求:必须进行URI编码
2. 复杂对象处理方案
// 发送方:使用Base64编码
const complexData = { list: [1,2,3], time: Date.now() }
const encoded = btoa(JSON.stringify(complexData))
wx.navigateTo({url: `/pages/detail?payload=${encoded}`
})// 接收方解码
const decoded = JSON.parse(atob(options.payload))
注意事项:
• 需处理特殊字符(+/=)
• 数据量较大时建议分页传ID+接口请求
二、高级传参技巧
1. 页面栈传参(跨多级页面)
// 获取页面栈实例
const pages = getCurrentPages()
const prevPage = pages[pages.length - 2]// 直接操作上级页面数据
if(prevPage) {prevPage.setData({ returnData: { status: 'modified' }})
}
适用场景:
• 多级页面回传数据
• 需要反向修改上级页面状态
2. 事件总线传参
// 发送页面(B)
eventBus.$emit('page-return', { code: 200 })// 原页面(A)监听
Page({onShow() {this._handler = eventBus.$on('page-return', this.handleReturn)},onHide() {this._handler?.()},handleReturn(data) {console.log('收到返回数据:', data)}
})
优势:
• 支持异步数据回传
• 突破URL长度限制
三、数据安全与性能优化
1. 敏感数据处理策略
// 加密传输流程
const crypto = require('./crypto-utils')// 发送前加密
const encrypted = crypto.encrypt({token: 'secret_token',timestamp: Date.now()
})wx.navigateTo({url: `/pages/auth?c=${encodeURIComponent(encrypted)}`
})// 接收方解密
const rawData = crypto.decrypt(options.c)
2. 性能优化方案
// 大文件传递方案
async function sendLargeFile(filePath) {const fileID = await uploadFileToCloud(filePath) // 上传至云存储wx.navigateTo({url: `/pages/preview?fileID=${fileID}`})
}// 接收方从云端下载
Page({onLoad({ fileID }) {downloadFileFromCloud(fileID).then(localPath => {this.setData({ filePath: localPath })})}
})
四、不同场景选型建议
场景 | 推荐方案 | 理由 |
---|---|---|
简单参数传递 | URL参数 | 实现简单,无需额外处理 |
复杂对象(<1KB) | Base64编码URL参数 | 平衡开发效率与数据容量 |
敏感数据 | 加密传输+临时存储 | 避免URL暴露敏感信息 |
大文件/大数据量 | 云存储ID传递 | 突破URL长度限制,保证传输可靠性 |
需要页面返回值的场景 | 事件总线+页面栈操作 | 灵活处理异步返回操作 |
多页面共享数据 | 全局状态管理 | 避免重复传递,保证数据一致性 |
五、常见问题解决方案
1. 数据类型转换错误
// 安全转换函数
function safeParse(param, defaultValue) {try {return JSON.parse(decodeURIComponent(param))} catch (e) {console.error('参数解析失败:', e)return defaultValue}
}// 使用示例
const config = safeParse(options.config, { size: 10 })
2. 页面回传数据丢失
// 可靠回传模式
Page({onUnload() {// 页面被销毁前强制回传if(this.data.needReturn) {eventBus.$emit('page-close', this.data)}}
})
六、调试与监控技巧
1. 参数追踪工具
// 包装导航方法
const originalNavigateTo = wx.navigateTo
wx.navigateTo = function(params) {console.log('[导航追踪]', params.url)return originalNavigateTo(params)
}
2. 性能监控埋点
const startTime = Date.now()
wx.navigateTo({url: '/pages/detail',complete() {const cost = Date.now() - startTimeanalytics.log('navigate_cost', cost) }
})
七、最佳实践总结
-
参数规范:
• 定义统一参数前缀:p_
表示基本参数,e_
表示加密参数
• 示例:p_id=123&e_data=xxxx
-
生命周期管理:
Page({onLoad(options) {this._initParams(options) // 初始化参数},onUnload() {this._cleanParams() // 清理敏感参数} })
-
文档维护:
## 页面参数规范 | 参数名 | 类型 | 必填 | 说明 | |--------|--------|------|----------------------| | id | string | 是 | 项目ID | | mode | enum | 否 | 预览模式(edit/view) |
通过合理选择传参方式并遵守最佳实践,可以有效提升小程序页面间通信的可靠性和可维护性。建议在复杂项目中建立统一的参数管理模块,处理编解码、类型校验和安全传输等公共逻辑。
6. 组件之间的传值
本章并不是很详细,若想了解更加详细的 组件传值 可以去往别的文章:
微信小程序组件间通信与传值的全面解析_小程序子组件向父组件传值-CSDN博客
下面是一个大致了解图片:
组件之间的传值属于组件通信的一种核心方式,但组件通信的范畴更广,传值只是其中一种常见形式。以下是详细解析:
一. 组件通信的本质
组件通信指的是 不同组件之间传递数据或交互行为 的过程,目的是实现组件间的协作。传值是实现这一目标的直接手段,但通信方式不局限于简单的数据传递。
二. 组件传值的主要方式(以微信小程序为例)
(1) 父传子:通过 Properties
- 父组件 通过属性(
properties
)向子组件传递数据。 - 子组件 在
properties
中声明接收的数据类型和默认值。
示例:
javascript
复制
// 子组件定义 properties
Component({properties: {title: {type: String,value: '默认标题'}}
});// 父组件传递数据
<child-component title="来自父组件的标题" />
(2) 子传父:通过自定义事件
- 子组件 使用
triggerEvent
触发事件并传递数据。 - 父组件 监听事件并处理数据。
示例:
javascript
复制
// 子组件触发事件
Component({methods: {onTap() {this.triggerEvent('update', { value: 123 });}}
});// 父组件监听事件
<child-component bind:update="handleUpdate" />// 父组件处理事件
Page({handleUpdate(e) {console.log(e.detail.value); // 123}
});
(3) 兄弟组件通信
- 通过 共同的父组件中转数据,即父组件接收一个子组件的数据,再通过
properties
传递给另一个子组件。 - 或使用 全局状态管理(如 MobX、Vuex)共享数据。
二、方法对比分析
方法 | 数据范围 | 持久性 | 响应式 | 复杂度 | 适用场景 |
---|---|---|---|---|---|
全局变量 | 全应用 | 内存 | 无 | 低 | 简单配置、低频修改的数据 |
本地缓存 | 全应用 | 持久 | 无 | 中 | 需要持久化的用户偏好设置 |
事件总线 | 任意组件间 | 内存 | 事件 | 中 | 松散耦合的组件间通信 |
状态管理库 | 全应用 | 可配 | 自动 | 高 | 复杂状态管理、多组件共享数据 |
页面间传参 | 页面跳转时 | 临时 | 无 | 低 | 简单页面跳转参数传递 |
三、高级方案:自定义数据共享层
对于企业级项目,推荐封装统一的数据管理层:
// services/dataCenter.js
// 定义一个名为 DataCenter 的类,用于管理数据存储和监听机制
class DataCenter {// 构造函数,初始化数据存储和监听器constructor() {this._data = {} // 用于存储数据,键值对形式this._listeners = {} // 用于存储监听器,键对应监听的属性,值是监听回调函数数组}// 设置数据的方法set(key, value) {this._data[key] = value // 将数据存储到 _data 对象中this._notify(key) // 触发通知机制,告知监听器数据已更新}// 获取数据的方法get(key) {return this._data[key] // 返回指定键的数据}// 监听数据变化的方法watch(key, callback) {// 如果当前键没有对应的监听器数组,则初始化为空数组if(!this._listeners[key]) {this._listeners[key] = []}// 将回调函数添加到监听器数组中this._listeners[key].push(callback)// 返回一个取消监听的函数return () => { /* 返回取消监听函数 */ }}// 内部通知方法,用于通知监听器数据已更新_notify(key) {// 获取当前键对应的监听器数组,如果没有则为空数组(this._listeners[key] || []).forEach(cb => cb(this._data[key]))}
}// 导出一个 DataCenter 实例作为模块的默认导出
export default new DataCenter()
优势:
- 统一管理所有共享数据
- 支持响应式更新
- 提供类型安全(配合TypeScript)
- 可扩展持久化策略
四、选择建议
- 简单项目:全局变量 + 页面传参
- 中等复杂度:全局变量 + 事件总线
- 大型应用:状态管理库 + 自定义数据层
- 需要持久化:配合本地缓存使用
- 需要实时同步:考虑结合云开发数据库
五、注意事项
- 内存管理:及时清理不再使用的数据和事件监听
- 性能优化:避免频繁触发大规模数据更新
- 数据安全:敏感信息不应存储在全局变量中
- 类型提示:使用TypeScript增强代码可维护性
- 测试覆盖:共享数据变更应有完善的测试用例
enter 的类,用于管理数据存储和监听机制
class DataCenter {
// 构造函数,初始化数据存储和监听器
constructor() {
this._data = {} // 用于存储数据,键值对形式
this._listeners = {} // 用于存储监听器,键对应监听的属性,值是监听回调函数数组
}
// 设置数据的方法
set(key, value) {
this._data[key] = value // 将数据存储到 _data 对象中
this._notify(key) // 触发通知机制,告知监听器数据已更新
}
// 获取数据的方法
get(key) {
return this._data[key] // 返回指定键的数据
}
// 监听数据变化的方法
watch(key, callback) {
// 如果当前键没有对应的监听器数组,则初始化为空数组
if(!this._listeners[key]) {
this._listeners[key] = []
}
// 将回调函数添加到监听器数组中
this._listeners[key].push(callback)
// 返回一个取消监听的函数
return () => { /* 返回取消监听函数 */ }
}
// 内部通知方法,用于通知监听器数据已更新
_notify(key) {
// 获取当前键对应的监听器数组,如果没有则为空数组
(this._listeners[key] || []).forEach(cb => cb(this._data[key]))
}
}
// 导出一个 DataCenter 实例作为模块的默认导出
export default new DataCenter()
**优势**:
1. 统一管理所有共享数据
2. 支持响应式更新
3. 提供类型安全(配合TypeScript)
4. 可扩展持久化策略## 四、选择建议1. **简单项目**:全局变量 + 页面传参
2. **中等复杂度**:全局变量 + 事件总线
3. **大型应用**:状态管理库 + 自定义数据层
4. **需要持久化**:配合本地缓存使用
5. **需要实时同步**:考虑结合云开发数据库## 五、注意事项1. **内存管理**:及时清理不再使用的数据和事件监听
2. **性能优化**:避免频繁触发大规模数据更新
3. **数据安全**:敏感信息不应存储在全局变量中
4. **类型提示**:使用TypeScript增强代码可维护性
5. **测试覆盖**:共享数据变更应有完善的测试用例通过合理选择数据共享方案,可以显著提高小程序的可维护性和开发效率,同时避免不必要的性能开销。
相关文章:
小程序中跨页面组件共享数据的实现方法与对比
小程序中跨页面/组件共享数据的实现方法与对比 在小程序开发中,实现不同页面或组件之间的数据共享是常见需求。以下是几种主要实现方式的详细总结与对比分析: 一、常用数据共享方法 全局变量(getApp())、本地缓存(w…...
ngx_http_core_merge_srv_conf
定义在 src\http\ngx_http_core_module.c static char * ngx_http_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) {ngx_http_core_srv_conf_t *prev parent;ngx_http_core_srv_conf_t *conf child;ngx_str_t name;ngx_http_server_name_t…...
如何在中科方德llinux系统上离线安装salt-minion
1,我的系统是什么 国产操作系统 中科方德 NFSChina Server release 4.0.240701 (RTM4-G320) 2,首先准备好两个安装包 salt-minion-2015.8.8-2.el7.noarch.rpm和salt-2015.8.8-2.el7.noarch.rpm 后者这个是前者的依赖项。 所以先安装salt-2015.8.8-2.e…...
RAG系统实战:当检索为空时,如何实现生成模块的优雅降级(Fallback)?
目录 RAG系统实战:当检索为空时,如何实现生成模块的优雅降级(Fallback)? 一、为什么需要优雅降级(Fallback)? 二、常用的优雅降级策略 策略一:预设后备提示࿰…...
输电线路航空标志球:低空飞行的安全路标 / 恒峰智慧科技
在现代社会,随着航空业的快速发展,低空飞行活动日益频繁。为了确保飞行安全,避免飞机与高压电线等障碍物发生碰撞,输电线路航空标志球应运而生。这种装置被广泛应用于高压输电线路上,尤其是超高压和跨江输电线…...
【SPP】蓝牙 SDP 协议在SPP中的互操作性解析
在蓝牙通信体系中,服务发现协议(SDP, Service Discovery Protocol)扮演着 "服务目录" 的核心角色。对于串口通信协议(SPP, Serial Port Profile)而言,SDP 服务记录是设备间建立串口连接的基础&am…...
本地部署vanna ai+通过http请求调用vanna
本地部署vanna ai ① 准备python环境,推荐最新的python12、13版本 ② 安装vanna库 我这里安装的python环境是python312 进入目录python312/Scripts,在该目录下的命令行窗口中输入以下命令:pip jinstall vanna pip install vanna③ 配置向…...
seq2seq
理解 transformer 中的 encoder decoder 详细的 transformer 教程见:【极速版 – 大模型入门到进阶】Transformer 文章目录 🌊 Encoder: 给一排向量输出另外一排向量🌊 Encoder vs. Decoder: multi-head attention vs. masked multi-head at…...
C++ ---- 虚继承
一、什么是虚继承 虚继承就是子类中只有一份间接父类的数据。用于解决多继承中的父类为非虚继承时出现的二义性问题,即菱形继承问题。继承方式需要加上virtual关键字。 二、虚继承的特性 以菱形继承为例: 1.不使用虚继承 根据输出的大小和关系图&…...
COMSOL多层圆片随机堆积三维模型
构建多层圆片随机堆积三维模型可用于材料、化工、土木、生物医学等多领域的研究,如复合材料设计、催化剂载体、颗粒物堆积研究等。本案例介绍在COMSOL内建立三维圆片堆积模型。 三维圆片堆积模型可采用CAD纤维密堆积3D插件建立,参数设置如图所示&#…...
PHP 开发API接口签名验证
就安全来说,所有客户端和服务器端的通信内容应该都要通过加密通道(HTTPS)传输,明文的HTTP通道将会是man-in-the- middle及其各种变种攻击的温床。所谓man-in-the-middle攻击简单讲就是指恶意的黑客可以在客户端和服务器端的明文通信通道上做手 脚&#x…...
Web开发-JavaEE应用ORM框架SQL预编译JDBCMyBatisHibernateMaven
知识点: 0、安全开发-JavaEE-构建工具-Maven 1、安全开发-JavaEE-ORM框架-JDBC 2、安全开发-JavaEE-ORM框架-Mybatis 3、安全开发-JavaEE-ORM框架-Hibernate 4、安全开发-JavaEE-ORM框架-SQL注入&预编译 一、演示案例-WEB开发-JavaEE-构建工具-Maven IDEA配置m…...
软考-数据库系统工程师第四版pdf
软考-数据库系统工程师第四版pdf git中的文件相对没有那么清楚,网盘的有高清版 github下载 这里我给出仓库地址 链接: https://github.com/yaodada123/ruankao-pdf https://github.com/yaodada123/ruankao-pdf gitee下载 https://gitee.com/yao-hengchao/ruank…...
扫描仪+文档pdf编辑器+pdf格式转换器
小扫描仪是一款集“扫描仪文档pdf编辑器pdf格式转换器”于一体的多功能扫描软件,软件功能丰富,而且目前是免费,功能包括扫描、编辑、转换三部分。 扫描:扫描的功能包括文档扫描、身份证扫描、护照扫描、书籍扫描、OCR和二维码。 扫…...
【stm32--HAL库DMA+USART+空闲中断不定长收发数据】
串口通信-Hal库实现不定长度收发,DMAUSART DMA串口STM32CUBEMX配置(工程创建)基础配置时钟配置工程配置 代码编写现象 DMA 在正式配置之前,我们先来一起简单了解一下DMA。DMA(Direct Memory Access,直接内…...
5G-A技术
最近的iOS 18.4 推送了 新功能,最引人注目的便是这个5G-A的这个功能,那什么是5G-A呢 ? 目前北京 四环内 还是有能显示出5G-A标志的。 5G-A 🌐 一句话概括: 5G-A 更快的速度 更低的延迟 更强的AI能力 更智能的网…...
Vue 组件 - 动态组件
Vue 渐进式JavaScript 框架 基于Vue2的学习笔记 - Vue 组件 - 动态组件 目录 动态组件 选项卡页面示例 更简单写法 增加输入框 弥补措施 总结 动态组件 选项卡页面示例 功能:选项卡功能,设置导航点击哪个显示相应页面。 设置三个全局组件&#…...
ffmpeg滤镜使用
ffmpeg实现画中画效果 FFmpeg中,可以通过overlay将多个视频流、多个多媒体采集设备、多个视频文件合并到一个界面中,生成画中画的效果 FFmpeg 滤镜 overlay 基本参数 x和y x坐标和Y坐标 eof action 遇到 eof表示时的处理方式,默认为重复。…...
【MVC简介-产生原因、演变历史、核心思想、组成部分、使用场景】
MVC简介 产生原因: MVC(Model-View-Controller)模式诞生于20世纪70年代,由Trygve Reenskaug在施乐帕克研究中心(Xerox PARC)为Smalltalk语言设计,目的是解决图形用户界面(GUI&…...
基于大模型的房间隔缺损手术全流程预测与方案优化研究报告
目录 一、引言 1.1 研究背景与意义 1.2 研究目的与目标 1.3 研究方法与创新点 二、房间隔缺损概述 2.1 房间隔缺损定义与分类 2.2 发病机制与病理生理 2.3 流行病学特征 三、大模型在房间隔缺损预测中的应用原理 3.1 大模型技术简介 3.2 数据收集与预处理 3.3 模型…...
什么是 CSSD?
文章目录 一、什么是 CSSD?CSSD 的职责 二、CSSD 是如何工作的?三、CSSD 为什么会重启节点?情况一:网络和存储都断联(失联)情况二:收到其他节点对自己的踢出通知(外部 fencing&#…...
uniapp APP端在线升级(简版)
设计思路: 1.版本比较:应用程序检查其当前版本与远程服务器上可用的最新版本 2. 更新状态指示:如果应用程序是不是最新的版本,则页面提示下载最新版本。 3.下载启动:通过plus.downloader.createDownload()启动新应用…...
2024年蓝桥杯Java B组省赛真题超详解析-分布式队列
问题:你需要回答在某个时刻,队列中有多少个元素具有可见性 方案:跟踪每个副节点已经同步到主节点队列的元素数量,并找出所有副节点中同步到的最少元素数量,这个数量即为所有副节点都已经同步的元素数量。 解析&#…...
Vue3入门
环境准备: node.js vscode or webstorm 哪个熟悉用哪个 这两个都是傻瓜式安装 浏览器直接搜索 下载即可 安装: 安装完node.js之后 按住快捷键 winR 打开命令提示符输入node 将显示版本信息 接着我们通过 vite 构建vue3工程 优点: 轻量快速的热重载(HMR…...
向量库(Vector Database)概述
向量库(Vector Database)概述 1. 核心概念 向量 高维空间中的数值数组,通常由模型(如BERT、ResNet)将非结构化数据(文本、图像等)转换为嵌入向量。 向量相似性 衡量方法:余弦相…...
Oracle迁移达梦遇中断?试试SQLark的断点续迁功能!
在企业级数据迁移项目中,如果迁移单表数据量超过亿行、占用空间超过100GB时,一旦遇到网络中断或迁移报错,往往需要整表重新迁移,导致效率低下,严重影响项目进度。针对这一痛点,SQLark 支持对 Oracle→DM 的…...
上海某海外视频平台Android高级工程师视频一面
问的问题比较细,有很多小细节在里面,平时真不一定会注意到,做一个备忘: 1.Object类里面有哪些方法? Object 类是 Java 中所有类的根类,它定义了一些基本方法,供所有类继承和重写1. 常用方法 1…...
基于yolov11的汽车损伤检测系统python源码+onnx模型+评估指标曲线+精美GUI界面
【算法介绍】 基于YOLOv11的汽车损伤检测系统是一种先进的计算机视觉技术,旨在快速准确地识别汽车的各种损伤类型。该系统利用YOLOv11模型的强大性能,实现了对车辆损伤的精确检测与分类。 该系统能够识别的损伤类型包括裂纹(crackÿ…...
华为IP(3)
DHCP Relay报文格式 DHCP Relay主要负责转发DHCP客户端与DHCP服务器之间的DHCP报文,所以DHCP Relay的报文格式只是把DHCP的报文部分字段做了相应的修改,报文格式没有发生变化 hops:表示当前DHCP报文经过DHCP中继的数目,该字段由…...
面试问题总结:qt工程师/c++工程师
C 语言相关问题答案 面试问题总结:qt工程师/c工程师 C 语言相关问题答案 目录基础语法与特性内存管理预处理与编译 C 相关问题答案面向对象编程模板与泛型编程STL 标准模板库 Qt 相关问题答案Qt 基础与信号槽机制Qt 界面设计与布局管理Qt 多线程与并发编程 目录 基础…...
【TS学习】(15)分布式条件特性
在 TypeScript 中,分布式条件类型(Distributive Conditional Types) 是一种特殊的行为,发生在条件类型作用于裸类型参数(Naked Type Parameter) 时。这种特性使得条件类型可以“分布”到联合类型的每个成员…...
四款高效数据报表工具 让数据分析更简单
概述 在数字化时代,企业和组织越来越依赖数据驱动决策,报表软件成为提高数据可视化能力、优化业务管理的关键工具。本文将为大家介绍四款功能强大的报表软件,帮助不同需求的企业找到合适的解决方案。 一、山海鲸报表 山海鲸报表是一款零代…...
QT 非空指针 软件奔溃
在用QT的实际项目中,出现如下现象: 运行软件再关闭软件,然后再运行软件会崩溃。等待5~10分钟,再运行软件,又正常,百思不得其解,后面找到原因是在头文件里定义指针变量时没有赋初nullptr&#x…...
图漾相机——C#语言属性设置
文章目录 前言1.示例程序说明2.SDK API功能介绍2.1 ListDevice2.2 Open2.3 OpenDeviceByIP2.4 Close2.5 DeviceStreamEnable2.6 DeviceStreamFormatDump2.7 DeviceStreamFormatConfig2.8 DeviceReadCurrentEnumData2.9 DeviceReadCalibData2.10 DeviceStreamOn2.11 DeviceStrea…...
WPF中viewmodel单例模式
1、单例模式介绍 单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。它常用于需要全局唯一访问点的场景,如配置管理、日志记录、数据库连接等。 2、WPF 中 ViewModel 的单例实现 在 WPF 中&#…...
AI比人脑更强,因为被植入思维模型【36】时光机理论思维
giszz的理解:据说是软银孙正义提出的一种思维模型,他利用同一时间内的地区差,通过引入技术、思维,在同一地区,形成了时间差。所谓商业模式,有时就是打空间差、时间差,信息差。 一、定义 时光机…...
SQL Server:用户权限
创建 & 删除 1. 创建用户命令整理 创建 admin2 用户 -- 在 master 数据库创建登录名 USE master; BEGINCREATE LOGIN [admin2] WITH PASSWORDNCljslrl0620!, DEFAULT_DATABASE[master], CHECK_EXPIRATIONOFF, CHECK_POLICYON; END;-- 在 db03 数据库创建用户并添加到相应…...
Qt之QTextEdit控制文本滚动, 停止滚动, 开始滚动, 鼠标控制滚动
对工作台文本框进行控制。含以下内容。详细说明在源码中可查看 至最底部停止滚动开始滚动 源码分两部分. .h文件和.cpp文件 MyTextEdit.h #ifndef MYTEXTEDIT_H #define MYTEXTEDIT_H#include <QObject> #include <QTextEdit> #include <QScrollBar> #includ…...
策略模式与元数据映射模式融合 JSR 380 验证规范实现枚举范围校验
类文件 Target({ElementType.METHOD,ElementType.FIELD,ElementType.ANNOTATION_TYPE,ElementType.CONSTRUCTOR,ElementType.PARAMETER,ElementType.TYPE_USE }) Retention(RetentionPolicy.RUNTIME) Documented Constraint(validatedBy {InEnumValidator.class, InEnumColle…...
9对象树(3)
目录 创建自定义的类,最主要的目的,是自定义一个析构函数,在析构函数中,完成打印.方便咱们看到最终的自动销毁对象的效果!!! 写完一个函数的声名之后, 按下 altenter, 在按下enter就可以自动的在对应的 cpp 文件中添加函数的定义了 内置类型,析构不会明…...
深入 OpenPDF:高级 PDF 生成与操作技巧
1 引言 1.1 项目背景 在许多企业级应用中,生成和操作 PDF 文档是一个常见的需求。PDF(Portable Document Format)因其格式统一、易于打印和分发而被广泛使用。本文将介绍如何使用 OpenPDF 库在 Java 项目中生成和操作 PDF 文档。 1.2 技术选型理由 OpenPDF:OpenPDF 是一…...
电脑屏幕亮度随心控,在Windows上自由调整屏幕亮度的方法
调整电脑屏幕的亮度对于保护视力和适应不同环境光线条件非常重要。无论是在白天强光下还是夜晚昏暗环境中,合适的屏幕亮度都能让您的眼睛更加舒适。本文中简鹿办公小编将向您介绍几种在 Windows 系统中调整屏幕亮度的方法。 方法一:使用快捷键 大多数笔…...
Navicat导出mysql数据库表结构说明到excel、word,单表导出方式记录
目前只找到一张一张表导出的方式 使用information_schema传入表名查询 字段名根据需要自行删减,一般保留序号、字段名、类型、说明就行 SELECT COLUMNS.ORDINAL_POSITION AS 序号, COLUMNS.COLUMN_NAME AS 字段名, COLUMNS.COLUMN_TYPE AS 类型(长度), COLUMNS.N…...
【C++笔记】C++常见二叉树OJ和拓扑排序
【C笔记】C常见二叉树OJ和拓扑排序 🔥个人主页:大白的编程日记 🔥专栏:C笔记 文章目录 【C笔记】C常见二叉树OJ和拓扑排序前言一.二叉树OJ1.1 根据二叉树创建字符串1.2 二叉树的层序遍历1.3 二叉树的最近公共祖先1.4 将二叉搜索…...
ARM-----数据处理、异常处理、模式切换
实列一: 1. 异常向量表 area reset, code, readonly code32 entry area reset, code, readonly:定义一个名为reset的代码区域,只读。 code32:指示编译器生成32位ARM指令。 entry:标记程序的入口点。 2. 程序入口…...
mapbox基础,使用geojson加载line线图层,实现铁路黑白间隔效果
👨⚕️ 主页: gis分享者 👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象1.2 ☘️mapboxgl.Map style属性1.3 ☘️line线图层样式二、🍀使用geojson加载…...
Python FastAPI + Celery + RabbitMQ 分布式图片水印处理系统
FastAPI 服务器Celery 任务队列RabbitMQ 作为消息代理定时任务处理 首先创建项目结构: c:\Users\Administrator\Desktop\meitu\ ├── app/ │ ├── __init__.py │ ├── main.py │ ├── celery_app.py │ ├── tasks.py │ └── config.py…...
阶段项目:Windows 服务器的组建与管理
项目概述 公司简介 创鑫公司是一家新成立的小型 IT 公司 公司决定组建部署一个小型的企业网络 员工人数不到20人 使用一台独立的 Windows 服务器提供各种网络服务 网络拓扑 设计需求 权限部分 权限部分要求 公司的网络管理员对办公计算机和服务器分别进行独立管理ÿ…...
【408】26考研-王道计算机408
王道408考研全套视频资料: 讲义01.26考研王道计算机【C语言督学营】02.【408领学班】26考研王道计算机B站独家03.26考研王道计算机【组成原理领学班】04.26王道计算机【计算机网络领学班】05.26考研王道计算机【数据结构领学班】06.26王道计算机【操作系统领学班】…...
数据分析问题思考路径
一、思考问题 1. 确认问题 因为背景: 因为5月1日的营业额突然下滑了10%,而历史从未出现过类似的跌幅 我想目的: 我想知道本次下滑的原因以此避免再出现这样的异常情况 现在思路: 现在能想到是原因是节假日和产品环节转化异常 最后感谢: 想请你帮我取数分析一下,…...