在 HBuilderX 中封装组件,是基于 uni-app(使用 Vue 语法)进行组件开发。

封装一个通用组件的标准流程,分为 创建、编写、使用 三个主要阶段。


第一步:创建组件文件

HBuilderX 提供了便捷的向导,且 uni-app 有一个非常强大的特性叫 easycom(自动注册),只要遵循特定目录结构,甚至不需要手动引入即可使用。

  1. 在项目的根目录下找到 components 目录(如果没有则新建一个)。

  2. 右键点击 components -> 新建组件

  3. 勾选 “创建同名目录”(推荐,方便管理资源)。

  4. 输入组件名称(建议使用大驼峰或连字符,如 MyButtonmy-button)。

注意: 目录结构最好是 components/组件名/组件名.vue,这样 HBuilder 会自动识别并注册,你在页面里直接写标签就能用。


第二步:编写组件逻辑 (Implementation)

在 HBuilderX (uni-app) 中编写组件逻辑(第二步)是整个封装的核心。我们可以把它分为 “入门:能用就行”“进阶:好用且通用” 两个层次。

一、 入门篇:把组件“跑”起来

入门阶段的核心目标是:实现“父子通信”。你需要搞清楚三个问题:父组件传给子组件什么(Props)?子组件自己管什么(Data)?子组件怎么通知外面(Events)?

1. 简单的 Props(接收数据)

入门时,你只需要知道 props 是用来接收外面传进来的参数的。

// 入门写法:数组形式(简单,但不严谨)
props: ['title', 'imageUrl']
  • 应用场景:比如封装一个“商品卡片”,父组件把商品名和图片传进来,子组件负责展示。

2. 私有 Data(内部状态)

组件内部自己变化的数据,不要用 props,要用 data

  • 应用场景:比如一个“折叠面板”,它是展开还是收起,这个状态只有它自己关心,应该放在 data 里。

3. 基础的 $emit(向外喊话)

当组件内部发生了交互(比如用户点了个按钮),子组件不能直接修改父组件的数据,必须“喊话”。

// 子组件内部
methods: {
  onTap() {
    // 这里的 'buy' 是自定义事件名,父组件监听 @buy
    this.$emit('buy', { id: 123 });
  }
}

二、 进阶篇:让组件“强”起来

进阶阶段的核心目标是:健壮性、灵活性和开发者体验。我们要防止别人传错参数,支持双向绑定,还要能灵活控制样式和结构。

1. 严格的 Prop 验证(健壮性)

不要只用数组写 props,要用对象写法,规定类型、默认值,甚至自定义验证函数。

props: {
  // 1. 规定类型:如果传了 Number 会报错,方便排查 bug
  title: {
    type: String,
    required: true // 必填项
  },
  // 2. 默认值:如果父组件没传,就用这个
  theme: {
    type: String,
    default: 'light' 
  },
  // 3. 高级:自定义验证器
  score: {
    type: Number,
    validator: function (value) {
      // 分数必须在 0 到 100 之间
      return value >= 0 && value <= 100;
    }
  }
}

2. 实现 v-model 双向绑定(开发者体验)

如果你封装的是输入框、开关、选择器类组件,一定要支持 v-model,这样父组件用起来非常爽。

  • 原理:v-model 本质是接收一个名为 value 的 prop,并在变化时触发 input 事件。

// MyInput.vue
props: {
  value: String // 1. 接收 value
},
methods: {
  onInput(e) {
    // 2. 触发 input 事件,把新值传出去
    this.$emit('input', e.detail.value);
  }
}
  • 父组件使用<my-input v-model="searchText" /> (不需要写繁琐的监听事件)。

3. 多插槽与作用域插槽(灵活性)

入门只用一个 <slot>,进阶要学会具名插槽(Named Slots)。

  • 场景:封装一个“通用的弹窗组件”。

    • 标题栏可能想放文字,也可能想放图标 -> 使用具名插槽。

    • 底部按钮可能是“确定/取消”,也可能只有一个“知道了” -> 使用具名插槽。

<view class="modal">
  <view class="header">
    <slot name="header">默认标题</slot>
  </view>
  <view class="content">
    <slot></slot> </view>
</view>
<my-modal>
  <template v-slot:header>
    <text style="color:red">警告!</text>
  </template>
  <view>这是内容...</view>
</my-modal>

4. 生命周期的大坑(重要知识点)

这是新手进阶最容易卡住的地方。

  • 页面生命周期onLoad, onShow 等(仅在 pages 里的页面有效)。

  • 组件生命周期created, mounted 等(components 里的组件必须用这个)。

切记:在组件里写 onLoad 是不会执行的!初始化数据请求要写在 mountedcreated 里。

总结: 入门进阶对比,封装组件的“三要素”

我把入门和进阶整合成一个表格,放在一起做比较。

维度 入门做法 进阶做法 为什么进阶更好?
参数接收 props: ['color'] 对象写法,含 typedefault 防止父组件漏传参数导致报错,代码更健壮。
数据同步 父组件手动监听 @update 修改值 支持 v-model 父组件代码量减少一半,逻辑更清晰。
内容分发 单一 <slot> 具名插槽、作用域插槽 组件结构可被高度定制,复用性极高。
样式 写死 CSS 支持传入 custom-classstyle 父组件可以微调子组件的样式。
初始化 混淆 onLoadmounted 明确区分生命周期 避免代码写了却不执行的尴尬。

在设计组件时,只需要想清楚这三点就可以了:

要素 对应代码 作用
属性 (Props) props: {} 父 -> 子:你可以配置组件的哪些外观或数据?
事件 (Events) this.$emit() 子 -> 父:组件内部发生了什么需要告诉外面?
插槽 (Slots) <slot></slot> 内容分发:组件内部哪块区域的内容由父组件决定?

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐