← 返回题目列表

小程序自定义组件的 properties、observers、behaviors 和生命周期如何理解?

高频 困难 第 17 / 32 题 更新于 2026/07/29
小程序自定义组件propertiesbehaviors

简化版

自定义组件用 properties 接收外部数据,用 data 管内部状态,用 methods 定义方法,用 observers 监听属性或数据变化,用 lifetimespageLifetimes 管组件与页面生命周期,用 behaviors 复用组件逻辑。核心是分清“外部传入”和“内部状态”,避免直接修改 props 式数据。

详细版

组件基础结构:

Component({
  properties: {
    value: String
  },
  data: {
    innerValue: ''
  },
  lifetimes: {
    attached() {},
    detached() {}
  },
  observers: {
    value(v) {
      this.setData({ innerValue: v })
    }
  },
  methods: {
    onTap() {
      this.triggerEvent('change', { value: this.data.innerValue })
    }
  }
})

behaviors 类似 mixin,可复用属性、数据、方法和生命周期,但命名冲突和隐式来源要控制。组件对外通信常用 triggerEvent,不要让组件直接修改父页面数据。

完整版教学

一、自定义组件解决的是复用和边界

小程序页面复杂后,会出现大量重复 UI 和交互:商品卡片、弹窗、筛选栏、输入框、导航栏。自定义组件的作用是把结构、样式、逻辑封装在一起,通过属性和事件与外部交互。

父页面
  -> properties 传入数据
  -> 组件内部渲染和交互
  -> triggerEvent 把结果通知父页面

这个边界和 Vue/React 组件思想类似:父级负责业务数据,组件负责局部表现和交互。面试回答时不要只背字段名,要讲“输入、内部状态、输出”三段。

如果组件直接操作父页面数据,短期省事,长期会让依赖关系变乱。复用组件最重要的是对外接口稳定。

二、properties 是外部输入,data 是内部状态

properties 用来接收父级传入的数据,data 用来维护组件自己的状态。例如一个弹窗组件,visible 可以来自外部,动画状态、临时输入值可以放内部。

Component({
  properties: {
    visible: Boolean,
    title: String
  },
  data: {
    loading: false
  }
})
类型来源是否建议组件内部随意改示例
properties父级传入不建议visiblevalue
data组件内部可以通过 setDataloadingactiveIndex
externalClasses外部样式注入不属于数据自定义样式类

如果组件需要实现类似受控输入,可以把外部 value 同步到内部 innerValue,用户输入后通过 triggerEvent 通知父级更新。这样数据流比较清楚。

三、observers 用来响应数据变化

observers 可以监听 propertiesdata 的变化,适合做派生状态同步、格式化、联动计算。它不是业务流程入口,也不适合放重逻辑。

Component({
  properties: {
    start: Number,
    end: Number
  },
  observers: {
    'start,end'(start, end) {
      this.setData({ duration: end - start })
    }
  }
})

假设 start=10end=25,observer 计算出 duration=15。这类派生字段比较适合 observer,因为它由输入数据决定。

但 observer 里如果继续频繁 setData 大对象,可能引发额外渲染成本。更糟的是 observer A 改字段 B,observer B 又改字段 A,就可能形成难排查的循环。

易错点:observer 是“响应变化”的工具,不是“替代所有业务逻辑”的工具。计算要轻,依赖要清楚。

四、lifetimes 和 pageLifetimes 分别管不同生命周期

组件自身生命周期用 lifetimes,常见有 createdattachedreadydetached。组件所在页面生命周期影响组件时,可以用 pageLifetimes,例如页面显示、隐藏、尺寸变化。

Component({
  lifetimes: {
    attached() {
      this.init()
    },
    detached() {
      this.dispose()
    }
  },
  pageLifetimes: {
    show() {},
    hide() {},
    resize(size) {}
  }
})

如果组件里注册了定时器、事件监听、长连接或全局订阅,应在 detached 中释放。假设一个页面列表里创建 100 个组件,每个组件都注册一个全局监听却不清理,页面反复进入 10 次后可能积累 1000 个监听器。

生命周期回答要带资源管理意识:初始化在哪里做,什么时候能访问节点,什么时候清理副作用。

五、methods 和 triggerEvent 定义组件输出

组件内部方法写在 methods 中。组件要通知父级时,使用 triggerEvent 触发自定义事件,由父级在 WXML 中绑定。

<price-input value="{{price}}" bind:change="onPriceChange" />
// component
this.triggerEvent('change', { value: 1299 })

// page
Page({
  onPriceChange(e) {
    this.setData({ price: e.detail.value })
  }
})

事件 detail 是组件输出的主要载体。这样父级可以决定是否接受变更、是否校验、是否请求接口。组件不需要知道父级业务细节。

这也是面试中“父子通信”的高级版:父传子靠 properties,子传父靠 triggerEvent,复杂跨层通信再考虑状态管理或事件总线,但不能一上来就乱用全局对象。

六、behaviors 是复用能力,也会带来隐式复杂度

behaviors 可以复用组件的 properties、data、methods、生命周期等能力。它适合抽取多个组件共同的逻辑,例如表单校验、主题能力、埋点能力。

const trackBehavior = Behavior({
  methods: {
    track(name) {
      console.log('track', name)
    }
  }
})

Component({
  behaviors: [trackBehavior],
  methods: {
    onTap() {
      this.track('tap')
    }
  }
})

它的风险是来源不直观。一个方法到底来自组件自身还是 behavior,读代码时需要跳转。多个 behavior 出现同名字段时,还要理解合并和覆盖规则。

复用方式优点风险
组件组合边界清晰层级可能增加
behavior复用逻辑方便隐式来源、命名冲突
工具函数简单直接无生命周期能力

工程上不要把 behavior 做成“万能混入”。可复用但要小而清晰,并保持命名规范。

七、常见误区与追问

  • 误区:properties 和 data 没区别。 properties 是外部输入,data 是内部状态,数据流边界不同。
  • 误区:组件可以直接改父页面数据。 组件应通过 triggerEvent 通知父级,由父级决定如何更新。
  • 误区:observers 适合写复杂业务流程。 observers 适合轻量派生和同步,重逻辑会增加循环和性能风险。
  • 追问:attached 和 ready 有什么区别? attached 表示组件进入节点树,ready 更适合需要布局完成后的操作。
  • 追问:behaviors 和工具函数怎么选? 需要属性、生命周期、方法混入时用 behavior;纯计算逻辑优先工具函数。
  • 追问:组件资源在哪里清理? 定时器、订阅、全局监听等应在 detached 中释放。

八、加强记忆

自定义组件按“输入、状态、输出、复用、清理”记:properties 是输入,data 是状态,triggerEvent 是输出,behaviors 是复用,detached 管清理。再用 observers 做轻量派生,用生命周期安排初始化和销毁,组件题就能答得像工程经验而不是字段背诵。