原生小程序 - 组件间通信

294 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第17天,点击查看活动详情

小程序从v1.6.3开始, 小程序开始支持自定义组件开发

有了组件化的思想,我们在之后的开发中尽可能的将页面拆分成一个个小的、可复用的组件,

让我们的代码更加方便组织和管理,并且扩展性也更强

image.png

类似于页面,自定义组件由 json wxml wxss js 4个文件组成

和页面不同的时候,在组件的json文件中需要将component选项设置为true,以表示其是一个组件

组件和页面都可以使用自定义组件和第三方组件的,只不过和内置组件可以直接使用不一样

如果需要在组件和页面中使用自定义组件或第三方组件,需要将自定义组件和第三方组件先在usingComponents中进行注册

  • 自定义组件也是可以引用自定义组件的,引用方法类似于页面引用自定义组件的方式(使用usingComponents 字段)
  • 自定义组件和页面所在项目根目录名 不能以“wx-”为前缀,否则会报错
  • 如果在app.json的usingComponents声明某个组件,那么所有页面和组件可以直接使用该组件,即该组件会被识别为全局组件

组件 --- /components/Cpn

wxml

<text class="cpn">Cpn</text>

使用组件的页面

{
  "usingComponents": {
    "Cpn": "/components/Cpn/Cpn"
  }
}
<Cpn />

组件样式

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

  1. 组件内的class样式,只对组件wxml内的节点生效, 对于引用组件的Page页面不生效
  2. 组件内不能使用id选择器、属性选择器、标签选择器,不是不能用,而是无法进行样式隔离,会对组件外样式产生污染

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

  1. 外部使用class的样式,只对外部wxml的class生效,对组件内是不生效的
  2. 外部使用了id选择器、属性选择器不会对组件内产生影响
  3. 外部使用了标签选择器,会对组件内产生影响

所以推荐在小程序中尽可能的使用类选择器来设置样式,以最大程度减少样式之间的相互影响

styleIsolation

在Conponent构造函数的配置对象中,可以传入一个options属性,其中options属性中有一个styleIsolation(隔离)属性

styleIsolation属性中存在如下三个值:

合法值说明
isolated表示启用样式隔离,在自定义组件内外,使用 class 指定的样式将不会相互影响(默认取值)
apply-shared表示页面 wxss 样式将影响到自定义组件,但自定义组件 wxss 中指定的样式不会影响页面
shared表示页面 wxss 样式将影响到自定义组件,自定义组件 wxss 中指定的样式也会影响页面和其他设置

组件间通信

image.png

父传子

大部分情况下,组件只负责布局和样式,内容是由使用组件的对象决定的

所以,我们经常需要从外部传递数据给我们的组件,让我们的组件来进行展示

我们可以使用使用properties属性来接收调用者向组件内部传入的值

properties支持以下数据类型

  1. String、Number、Boolean

  2. Object、Array、null(不限制类型)

组件调用者

<Cpn propTitle="外部传入的数据" />

组件

<view>{{ title }}</view>
<view>{{ propTitle }}</view>
Component({
  properties: {
    propTitle: {
      type: String,
      // 小程序中使用value属性来设置默认值
      // 在小程序中设置引用类型值的默认值设置不需要使用函数
      // 直接设置即可 例如 value: [], 不需要通过 value: () => []进行设置
      value: '默认值' 
    }
  },

  data: {
    title: 'data中定义的数据'
  }
})

传递样式

有时候,我们不希望将样式在组件内固定不变,而是外部可以决定样式

这个时候,我们可以使用externalClasses属性给组件传入样式

  1. 在Component对象中,定义externalClasses属性
  2. 在组件内的wxml中使用externalClasses属性中的class
  3. 在页面中传入对应的class,并且给这个class设置样式

组件调用者

<Cpn foo="foo" />
.foo {
  background-color: skyblue;
}

组件

<view class="foo">Cpn</view>
Component({
  externalClasses: ['foo']
})

子传父

子组件

<view bind:tap="handleTap">Cpn</view>
Component({
  // 组件中的事件不是写在配置对象顶层的
  // 而是写在methods选项下边的
  methods: {
    handleTap() {
      // triggerEvent 的作用和vue中的$emit的作用是一致的
      this.triggerEvent('cpnClick', {
        name: 'Klaus',
        age: 23
      })
    }
  }
})

父组件

<!-- 事件名是大小写敏感的 -->
<Cpn bind:cpnClick="handleClick" />
Page({
  handleClick(e) {
    console.log(e.detail) // => {name: "Klaus", age: 23}
  }
})

页面调用组件方法

Page({
  onShow() {
    // 通过小程序内置方法 this.selectComponent 可以获取对应的组件实例
    // selectComponent功能类似于 querySelector
    // 小程序同样提供方法selectAllComponents 可以获取所有符合条件的组件实例
    // selectAllComponents功能类似于 querySelectorAll
    
    // 通过selectComponent和selectAllComponents只能获取自定义组件或第三方组件
    // 无法通过这两个方法去获取原生组件实例
    const cpnCpn = this.selectComponent('.cpn')

    // 获取组件后,就可以直接调用组件中的属性和方法
    cpnCpn.foo()
  }
})

使用外部npm包

  1. 在控制台中使用传统的方式去下载和依赖对应的npm包

  2. 使用工具选项标下的构建npm选项,将node_modules中的依赖包构建到

    小程序中的miniprogram_npm

  3. 和传统使用npm的方式一样去使用对应的npm依赖包

如果更新或修改了对应的依赖包,就需要重新构建npm,以更新对应的miniprogram_npm文件夹