一文理清页面/组件通信与 Store 全局状态管理

【小程序实战】告别繁琐传递!一文理清页面/组件通信与 Store 全局状态管理

📢 前言 : 大家好,今天集中攻克了微信小程序开发中的两座大山:页面与组件的通信 以及 全局状态管理(Store)

在刚接触小程序时,我们通常习惯把所有逻辑都写在 Page 里;但随着项目变大,组件化是必经之路。而组件一旦多起来,数据怎么互相传递就成了头疼的问题。今天这篇笔记,就来总结一下我的学习成果,并分享几个避坑经验,希望能帮到正在学习小程序的你!


一、 页面与组件的"窃窃私语":基础通信方式

在引入复杂的 Store 之前,我们必须先掌握原生的页面与组件通信方式。核心可以总结为三招:

1. 父传子:properties (属性绑定)

这是最基础的单向数据流。页面(父)通过属性将数据传递给组件(子)。

页面(父)端:

xml 复制代码
<!-- index.wxml -->
<my-component my-name="{{userName}}"></my-component>

组件(子)端:

javascript 复制代码
// components/my-component/my-component.js
Component({
  properties: {
    myName: {
      type: String,
      value: '默认名字' // 默认值
    }
  }
})

2. 子传父:triggerEvent (事件绑定)

当组件内部发生了点击或数据改变,需要通知页面时,就需要用到自定义事件。

组件(子)端触发:

javascript 复制代码
// 当点击按钮时触发
handleTap() {
  this.triggerEvent('myevent', { age: 18 }) // 传递对象给父级
}

页面(父)端接收:

xml 复制代码
<!-- index.wxml 绑定事件 -->
<my-component bind:myevent="handleChildEvent"></my-component>
javascript 复制代码
// index.js 处理事件
handleChildEvent(e) {
  console.log('收到子组件的数据:', e.detail.age); // 输出 18
}

3. 父控子:selectComponent (获取组件实例)

有时候页面需要直接调用子组件里的方法,这时候可以通过给组件加 idclass,直接获取实例。

javascript 复制代码
// 父页面的 js 中
const child = this.selectComponent('#my-child-id');
child.someMethod(); // 直接调用子组件的方法
// ⚠️ 经验:虽然好用,但不建议滥用,容易造成父子组件强耦合。

二、 告别"回调地狱",拥抱 Store 全局状态管理

❓ 为什么需要 Store?

当遇到跨页面通信 ,或者兄弟组件通信 (比如 A 组件的数据,C 组件也要用)时,如果用原生方法,你需要:A组件 -> 传给父页面 -> 传给B组件 -> ...。这种**"属性层层透传"**简直是噩梦!

这时候,Store(全局状态管理) 就闪亮登场了!在原生小程序中,我们通常使用 mobx-miniprogrammobx-miniprogram-bindings

1. 定义 Store (数据仓库)

首先创建一个 store.js,用来存放全局共享的数据和修改数据的方法。

javascript 复制代码
import { observable, action } from 'mobx-miniprogram';

export const store = observable({
  // 1. 数据字段 (State)
  numA: 1,
  numB: 2,

  // 2. 计算属性 (Getters)
  get sum() {
    return this.numA + this.numB;
  },

  // 3. 修改数据的方法 (Actions)
  updateNumA: action(function (step) {
    this.numA += step;
  })
});

2. 在 Page 中使用 Store

在页面中使用,需要用到 createStoreBindings

javascript 复制代码
import { createStoreBindings } from 'mobx-miniprogram-bindings';
import { store } from '../../store/store';

Page({
  onLoad() {
    // 绑定 Store
    this.storeBindings = createStoreBindings(this, {
      store,
      fields: ['numA', 'numB', 'sum'], // 需要的数据
      actions: ['updateNumA'] // 需要的方法
    })
  },
  
  onUnload() {
    // ⚠️ 重点:页面卸载时一定要解绑,防止内存泄漏!
    this.storeBindings.destroyStoreBindings();
  },

  btnHandler() {
    this.updateNumA(1); // 直接调用 store 中的 action
  }
})

3. 在 Component 中使用 Store

在组件中使用更加优雅,官方提供了一个 behavior

javascript 复制代码
import { storeBindingsBehavior } from 'mobx-miniprogram-bindings';
import { store } from '../../store/store';

Component({
  behaviors: [storeBindingsBehavior], // 引入 behavior
  storeBindings: {
    store,
    fields: {
      numA: () => store.numA, // 映射数据
      sum: 'sum'
    },
    actions: {
      updateNumA: 'updateNumA'
    }
  }
})

三、 💡 学习心得与"避坑"经验分享

经过今天的折腾,我对这两种方式有了更深的体会,总结了以下几条经验:

  1. 别把什么都塞进 Store 里! Store 确实"真香",但千万别把什么数据都往里面丢。

    • 适合放 Store 的: 用户登录信息(Token、头像)、购物车数据、全局主题配置等(跨页面高度共享的数据)。
    • 适合放页面/组件内部(data)的: 表单的输入内容、弹窗的显示隐藏状态(isModalShow)、局部的 Loading 状态。保持局部状态的纯粹,代码才好维护。
  2. 时刻警惕内存泄漏 在 Page 中使用 createStoreBindings 时,必须、一定、千万要onUnload 生命周期里调用 destroyStoreBindings() 进行清理。如果你发现从小程序某个页面返回后,页面变卡或者数据出现诡异的重叠,大概率是忘记解绑了。

  3. 组件通信尽量保持"单向数据流" 即使有了 selectComponent,我们在开发组件时也应尽量遵循:父组件通过 properties 传值,子组件通过 triggerEvent 汇报。把子组件当成一个"黑盒",这样写出来的组件复用性最高,不会因为换了个父页面就报错。


如果这篇文章对你有帮助,点个赞支持一下吧!你的鼓励是我持续分享的动力!


相关推荐
codingWhat2 小时前
手撸一个「能打」的 React Table 组件
前端·javascript·react.js
HelloReader2 小时前
Tauri 应用安全从开发到发布的威胁防御指南
前端
bluceli2 小时前
WebAssembly实战指南:将高性能计算带入浏览器
前端·webassembly
yuki_uix2 小时前
Object.entries:优雅处理 Object 的瑞士军刀
前端·javascript
奇迹_h6 小时前
打造你的HTML5打地鼠游戏:零基础入门实践
前端
SuperEugene6 小时前
Vue生态精选篇:Element Plus 的“企业后台常用组件”用法扫盲
前端·vue.js·面试
Neptune16 小时前
JavaScript回归基本功之---类型判断--typeof篇
前端·javascript·面试
贾铭6 小时前
如何实现一个网页版的剪映(三)使用fabric.js绘制时间轴
前端·后端
子兮曰7 小时前
后端字段又改了?我撸了一个 BFF 数据适配器,从此再也不怕接口“屎山”!
前端·javascript·架构