小程序的组件化开发

577 阅读4分钟

1. 组件化开发的介绍

image.png

  • 组件化思想的应用:
    • 尽可能的将页面拆分成一个个小的、可复用的组件

    • 这样让代码更加方便组织和管理,并且扩展性也更强

2. 创建自定义组件

  • 自定义组件的步骤:

    • 首先需要在 json 文件中进行自定义组件声明(将component 字段设为 true 可将这一组文件设为自定义组件)
    • 在wxml中编写属于组件的模板
    • 在wxss中编写属于组件自己的相关样式
    • 在js文件中, 可以定义数据或组件内部的相关逻辑
  • 注意的细节:

    • 自定义组件也是可以引用自定义组件的,引用方法类似于页面引用自定义组件的方式(使用usingComponents 字段)

    • 自定义组件和页面所在项目根目录名 不能以“wx-”为前缀,否则会报错

    • app.json的usingComponents声明某个组件,那么所有页面和组件可以直接使用该组件

3. 组件的样式细节

3.1 组件内的样式 对 外部样式 的影响

  • 组件内的class样式,只对组件wxml内的节点生效, 对于引用组件的Page页面不生效

  • 组件内不能使用id选择器、属性选择器、标签选择器

3.2 外部样式 对 组件内样式 的影响

  • 外部使用class的样式,只对外部 wxml 的 class 生效,对组件内是不生效的

  • 外部使用了id选择器、属性选择器不会对组件内产生影响

  • 外部使用了标签选择器,会对组件内产生影响

3.3 如何让class可以相互影响

  • 在Component对象中,可以传入一个options属性,其中options属性中有一个styleIsolation(隔离)属性。

  • styleIsolation有三个取值:

    • isolated 表示启用样式隔离,在自定义组件内外,使用 class 指定的样式将不会相互影响(默认取值)

    • apply-shared 表示页面 wxss 样式将影响到自定义组件,但自定义组件 wxss 中指定的样式不会影响页面

    • shared 表示页面 wxss 样式将影响到自定义组件,自定义组件 wxss 中指定的样式也会影响页面和其他设置

    Component({
      options: {
        styleIsolation: "shared"
      }
    })
    

4. 组件的数据传递

image.png

4.1 properties

  • 支持的类型:

    • String、Number、Boolean、Object、Array、null(不限制类型)

4.2 externalClasses

  • 在Component对象中,定义externalClasses属性

    externalClasses: ["info"],
    
  • 在组件内的wxml中使用externalClasses属性中的class

    <section-info info="cba" title="黄金时代" content="在我一生中最好的黄金时代, 我想吃, 我想爱"/>
    
  • 在页面中传入对应的class,并且给这个class设置样式

    .cba {
      background-color: #00f;
    }
    

4.3 自定义事件

  • 子组件

    Component({
     properties: {
       title: {
         type: String,
         value: "默认标题"
       },
       content: {
         type: String,
         value: "默认内容"
       }
     },
     externalClasses: ["info"],
    
     methods: {
       onTitleTap() {
         console.log("title被点击了~");
         // 发射自定义事件
         this.triggerEvent("titleclick", "aaa")
       }
     }
    })
    
  • 父组件

    <section-info 
      info="abc" 
      title="我与地坛" 
      content="要是有些事情我没说, 别以为是我忘记了"
      bind:titleclick="onSectionTitleClick"
    />
    
    onSectionTitleClick(event) {
      console.log("区域title发生了点击", event.detail);
    },
    

5. 调用子组件的方法

  • 在父组件里调用 this.selectComponent ,获取子组件的实例对象。

    • 调用时需要传入一个匹配选择器 selector,如:this.selectComponent(".my-component")

6. 自定义组件tab-control案例

  • wxml

    <view class="tab-control">
      <block wx:for="{{ titles }}" wx:key="*this">
        <view 
          class="item {{index === currentIndex ? 'active': ''}}"
          bindtap="onItemTap"
          data-index="{{index}}"
        >
          <text class="title">{{ item }}</text>
        </view>
      </block>
    </view>
    
  • wxss

    .tab-control {
      display: flex;
      height: 40px;
      line-height: 40px;
      text-align: center;
    }
    
    .tab-control .item {
      flex: 1;
    }
    
    .tab-control .item.active {
      color: #ff8189;
    }
    
    .tab-control .item.active .title {
      border-bottom: 3px solid #ff8189;
      padding: 5px;
    }
    
  • js

    Component({
      properties: {
        titles: {
          type: Array,
          value: []
        }
      },
    
      data: {
        currentIndex: 0
      },
    
      methods: {
        onItemTap(event) {
          const currentIndex = event.currentTarget.dataset.index
          this.setData({ currentIndex })
    
          // 自定义事件
          this.triggerEvent("indexchange", currentIndex)
        },
        test(index) {
          console.log("tab control test function exec");
          this.setData({
            currentIndex: index
          })
        }
      }
    })
    
  • 父组件js

    onTabIndexChange(event) {
      const index = event.detail
      console.log("点击了", this.data.digitalTitles[index]);
    },
    onExecTCMethod() {
      // 1.获取对应的组件实例对象
      const tabControl = this.selectComponent(".tab-control")
    
      // 2.调用组件实例的方法
      tabControl.test(2)
    }
    

7. 插槽的使用

  • 单个插槽

    <view class="my-slot">
      <view class="header">Header</view>
      <view class="content">
        <!-- 小程序中插槽是不支持默认值的 -->
        <slot></slot>
      </view>
      <view class="default">默认值</view>
      <view class="footer">Footer</view>
    </view>
    
    • 默认值(设置wxss)
    .default {
      display: none;
    }
    
    .content:empty + .default {
      display: block;
    }
    
  • 多个插槽

    • 子组件
    <view class="mul-slot">
      <view class="left">
        <slot name="left"></slot>
      </view>
      <view class="center">
        <slot name="center"></slot>
      </view>
      <view class="right">
        <slot name="right"></slot>
      </view>
    </view>
    
    Component({
      options: {
        // 必须在此处设置
        multipleSlots: true
      }
    })
    
    • 父组件
    <mul-slot>
      <button slot="left" size="mini">left</button>
      <view slot="center">哈哈哈</view>
      <button slot="right" size="mini">right</button>
    </mul-slot>
    

8. Behaviors混入

behaviors 是用于组件间代码共享的特性,类似于一些编程语言中的 “mixins”

  • 每个 behavior 可以包含一组属性、数据、生命周期函数和方法

    export const counterBehavior = Behavior({
      data: {
        counter: 100
      },
      methods: {
        increment() {
          this.setData({ counter: this.data.counter + 1 })
        },
        decrement() {
          this.setData({ counter: this.data.counter - 1 })
        }
      }
    })
    
  • 组件引用它时,它的属性、数据和方法会被合并到组件中,生命周期函数也会在对应时机被调用

    import { counterBehavior } from "../../behaviors/counter"
    
    Component({
      behaviors: [counterBehavior]
    })
    
  • 每个组件可以引用多个 behavior ,behavior 也可以引用其它 behavior

9. 生命周期函数

  • 组件的生命周期函数

    • 最重要的生命周期是 created attached detached ,包含一个组件实例生命流程的最主要时间点
    • 组件的的生命周期推荐在 lifetimes 字段内进行声明 image.png
  • 页面的生命周期函数

    • 在 pageLifetimes 定义段中定义 image.png
    Component({
      lifetimes: {
        created() {
          console.log("组件被创建created");
        },
        attached() {
          console.log("组件被添加到组件树中attached");
        },
        detached() {
          console.log("组件从组件树中被移除detached");
        }
      },
      pageLifetimes: {
        show() {
          console.log("page show");
        },
        hide() {
          console.log("page hide");
        }
      }
    })
    

10. Component选项

image.png image.png