专注小程序 / APP 开发,用代码改变生活

获取方案
全国分站 主站首页 北京分站 石家庄分站 唐山分站 保定分站 廊坊分站 沧州分站 郑州分站 邯郸分站

小程序组件开发实战指南

小程序开发 2026-11-07 0 阅读 0 点赞 原创

小程序的页面由组件构成。官方提供的基础组件能满足大部分展示需求,但当业务开始出现重复的卡片、弹窗、表单块时,把这些结构抽成自定义组件,是让代码保持可控的关键一步。这篇文章从实际项目出发,讲清楚自定义组件的定义、通信、样式隔离与复用方式。

小程序组件开发实战指南

什么时候该抽组件

不是所有结构都值得封装。我的判断标准有三条:

  • 同一段结构在三个以上页面出现,且未来可能继续增加;
  • 结构内部有独立的状态逻辑,比如展开收起、倒计时、分页加载;
  • 需要被不同业务方复用,接口应当稳定。

只出现一两次的布局,直接写在页面里更省事。过早抽象会增加理解成本,尤其是给后来接手的人。

定义一个自定义组件

以常见的商品卡片为例。在项目里新建 components/goods-card 目录,包含四个文件:js、json、wxml、wxss。json 中必须声明 component: true,否则它会被当成普通页面处理。

{
  "component": true,
  "usingComponents": {}
}

组件逻辑写在 Component() 中,而不是 Page()。属性、数据、方法分开声明,阅读时一目了然:

Component({
  properties: {
    goods: {
      type: Object,
      value: {}
    },
    showPrice: {
      type: Boolean,
      value: true
    }
  },
  data: {
    pressed: false
  },
  methods: {
    onTapCard() {
      this.triggerEvent('select', { id: this.data.goods.id })
    },
    onTouchStart() {
      this.setData({ pressed: true })
    },
    onTouchEnd() {
      this.setData({ pressed: false })
    }
  }
})

wxml 中使用属性绑定,注意属性名在模板里要写成短横线形式:

<view class="card {{pressed ? 'card--pressed' : ''}}"
      bindtap="onTapCard"
      bindtouchstart="onTouchStart"
      bindtouchend="onTouchEnd">
  <image class="card__cover" src="{{goods.cover}}" mode="aspectFill" />
  <view class="card__body">
    <text class="card__title">{{goods.title}}</text>
    <text wx:if="{{showPrice}}" class="card__price">¥{{goods.price}}</text>
  </view>
</view>

父子通信的三种方式

属性向下传递

父组件通过 properties 传数据,子组件只读。这里有一个容易忽略的点:对象类型的属性如果直接修改内部字段,不会触发视图更新,也不符合单向数据流。需要变更时,应该通过事件通知父级,由父级 setData 后再传下来。

事件向上通知

triggerEvent 是子传父的标准做法。事件名建议用动词,如 select、change、submit,避免和基础事件混淆。父组件监听时写 bind:select 或 bindselect,前者可读性更好。

通过 selectComponent 主动调用

父组件可以用 this.selectComponent('#card') 拿到子组件实例,调用其方法。这种方式适合命令式操作,比如让弹窗组件执行 open()。但不要滥用,否则组件之间会形成隐式依赖,测试和重构都会变难。

样式隔离与外部类

自定义组件默认开启样式隔离,组件内的 class 不会影响外部,外部样式也进不来。这既是保护也是限制。如果需要让父级控制部分样式,在组件 json 中声明:

{
  "component": true,
  "styleIsolation": "apply-shared"
}

或者更精细地使用 externalClasses,把指定类名暴露出去:

Component({
  externalClasses: ['custom-class']
})

模板里写 class="card custom-class",父组件传入 custom-class="my-card" 即可覆盖部分外观。我倾向于只暴露一两个类名,而不是完全放开样式隔离,这样组件的视觉边界仍然清晰。

把组件放进 npm 或私有库

当组件数量超过十个,复制粘贴就开始出问题。做法有两种:一是把通用组件抽成独立 npm 包,通过 npm install 引入,配合开发者工具的“构建 npm”;二是放在项目内的 components 目录,用统一的 usingComponents 路径管理。

个人项目我更推荐第二种,简单直接。多项目复用时再考虑发包,注意在 package.json 中声明 miniprogram 字段指向组件目录,否则构建后路径会出错。

性能上的几个细节

  • 属性传大对象时,尽量在父级做裁剪,只传组件真正需要的字段,减少 setData 的数据量;
  • 长列表中的组件开启 virtualHost,可以去掉组件外层多余的节点,减少渲染层级;
  • 频繁更新的数据(如倒计时)放在组件内部维护,避免父级整体刷新;
  • 组件销毁时清理定时器和事件监听,在 lifetimes.detached 中处理。
Component({
  lifetimes: {
    attached() {
      this.timer = setInterval(() => {
        this.setData({ now: Date.now() })
      }, 1000)
    },
    detached() {
      clearInterval(this.timer)
    }
  }
})

组件开发的核心不是写得多,而是边界划得准。属性只暴露必要的数据,事件只传递必要的信息,样式只开放必要的入口。做到这三点,组件才能在不同页面之间稳定复用,而不是每接一个页面就要改一次源码。

小程序开发架构:从分层到落地小程序开发入门:从零到上线

评论 (0)

还没有评论,快来抢沙发吧~

好想法,值得被认真交付

从小程序、APP 到全栈网站,一站式把想法变成可落地的产品

微信咨询

微信扫码,直接沟通需求