自定义组件解决什么

一个项目里,商品卡片、列表项、弹窗这些界面元素会在好几个页面重复出现。把它们写成自定义组件,一处改、处处生效,不用每页复制粘贴一遍。这就是自定义组件最直接的价值:复用。

但写组件不是把一段 WXML 抽出来就完了。好的组件要能接收外部参数、能响应内部状态、还能让外部往里面塞自己的内容。properties、observers、插槽,就是解决这三件事的三把钥匙。

项目小的时候,直接在页面里写其实更快。但同一个元素出现在三个页面以上,就值得抽成组件了。别为了抽而抽,抽得太早反而难维护。

写组件还有个好处:组件内部的状态自己管,页面不用记着每一处该怎么更新。比如一个开关组件,点开关合的动画、状态切换都封装在里面,页面只需要问它现在是开还是关。这种「黑盒化」能大幅减少页面里的胶水代码。

组件化的代价是要提前设计好接口。参数叫什么、事件抛什么、插槽留几个,都得想清楚。接口定得好,后面谁用都顺;接口定得含糊,用一次改一次,组件就成了负担。

组件化的目标不是「抽得多」,而是「改一处就够」。
本文核心知识点
  • properties外部往里传参数。
  • observers监听属性变化做联动。
  • slots让外部往组件里塞内容。
  • 通信组件和页面互相传值。
小程序自定义组件示意:蓝调渐变背景上拼装的组件积木块与连接节点的扁平科技插画
积木一块块拼起来,传参、监听、插槽三样配合,组件才真正好用。

properties:对外传参

组件长得像什么样,由外部通过 properties 传入。定义好属性名、类型和默认值,外部用的时候传不同的值,组件就呈现不同样子。

Component({
  properties: {
    title: { type: String, value: "默认标题" },
    count: { type: Number, value: 0 }
  }
});

页面里用的时候直接传:自定义标签上加 title="某个标题"。默认值要给合理的,外部不传也不至于渲染出奇怪的东西。类型也要标清楚,传错了组件内部才好察觉。

observers:监听属性变化

光接收参数还不够。有时候一个属性变了,派生出的别的状态也要跟着变,这时候就用 observers 监听。比如价格从分换算成元,监听 price 一变就重新算展示值。

能力说明
properties外部传入初始值
observers属性变化时联动计算
data组件内部自己的状态
observers: {
  "price": function (n) { this.setData({ yuan: (n / 100).toFixed(2) }); }
}

别在 observers 里死循环改自己监听的那个属性,不然会越转越停不下来。

slots:插槽让组件可扩展

组件内部结构是写死的,但有时候外部想在组件里插一段自己的内容。这就是插槽干的事:组件留个坑,外部往里填。

// 组件 WXML 里留一个坑位
<view class="body"><slot></slot></view>
  • 默认插槽:一个坑位用默认。
  • 命名插槽:多个坑位各有名字。
  • 有了插槽,组件才能既通用又灵活。

没有插槽,组件每要一种新布局就得改组件本身;有了插槽,外部自己往里填内容,组件不用动。

用插槽的时候注意,插槽里放的是外部页面的内容,样式默认按页面的来。如果想让插槽内容和组件内部风格统一,可以给插槽套一层组件自己的样式容器。这样外面塞什么进来,看起来都还是组件该有的样子。

组件内外通信

组件里发生了事情(比如被点了一下),要告诉外面的页面。做法是组件触发一个自定义事件,页面监听这个事件再处理。

this.triggerEvent("tapitem", { id: itemId });

页面这边在组件标签上 bind:tapitem 接收。这样组件只管抛事件,具体怎么处理是页面的事,组件不绑死业务。

除了向上抛事件,组件之间兄弟通信一般别直接互相调用,那样会把耦合搞乱。真要共享数据,放到页面层统一管,兄弟组件通过各自的 properties 拿、通过各自的事件报,页面做个中转就行。这样数据流是单向的,出问题好查。