Skip to content

自定义组件 ​

自定义组件 ​

创建自定义组件 ​

在项目根目录创建 components 文件夹,每个组件由 4 个文件组成:.js、.json、.wxml、.wxss。

components/
├── header/
│   ├── header.js
│   ├── header.json
│   ├── header.wxml
│   └── header.wxss
└── footer/
    ├── footer.js
    ├── footer.json
    ├── footer.wxml
    └── footer.wxss
components/
├── header/
│   ├── header.js
│   ├── header.json
│   ├── header.wxml
│   └── header.wxss
└── footer/
    ├── footer.js
    ├── footer.json
    ├── footer.wxml
    └── footer.wxss

组件需要在 .json 文件中声明为组件:

json
{
  "component": true
}
{
  "component": true
}

注册组件 ​

全局注册:在 app.json 中配置 usingComponents。

json
{
  "usingComponents": {
    "my-header": "/components/header/header"
  }
}
{
  "usingComponents": {
    "my-header": "/components/header/header"
  }
}

页面局部注册:在页面的 .json 文件中配置。

json
{
  "usingComponents": {
    "my-header": "/components/header/header"
  }
}
{
  "usingComponents": {
    "my-header": "/components/header/header"
  }
}

使用组件 ​

html
<!-- 页面 wxml -->
<my-header title="首页" />
<!-- 页面 wxml -->
<my-header title="首页" />

组件 JS 定义 ​

js
Component({
  properties: {
    title: {
      type: String,
      value: '默认标题'
    }
  },
  data: {
    count: 0
  },
  methods: {
    handleTap() {
      this.setData({
        count: this.data.count + 1
      })
      // 触发自定义事件,向父组件传递数据
      this.triggerEvent('change', { count: this.data.count })
    }
  }
})
Component({
  properties: {
    title: {
      type: String,
      value: '默认标题'
    }
  },
  data: {
    count: 0
  },
  methods: {
    handleTap() {
      this.setData({
        count: this.data.count + 1
      })
      // 触发自定义事件,向父组件传递数据
      this.triggerEvent('change', { count: this.data.count })
    }
  }
})
html
<!-- 组件 wxml -->
<view class="header">
  <text>{{title}}</text>
  <button bindtap="handleTap">点击</button>
</view>
<!-- 组件 wxml -->
<view class="header">
  <text>{{title}}</text>
  <button bindtap="handleTap">点击</button>
</view>

组件间通信与数据传递 ​

方式说明
属性绑定父组件通过 properties 向子组件传递数据
事件触发子组件通过 triggerEvent 向父组件传递数据
获取组件实例父组件通过 this.selectComponent(id) 获取子组件实例

父 -> 子:属性绑定 ​

html
<!-- 父组件 wxml -->
<my-child prop-a="{{dataA}}" prop-b="{{dataB}}" />
<!-- 父组件 wxml -->
<my-child prop-a="{{dataA}}" prop-b="{{dataB}}" />

子 -> 父:事件触发 ​

js
// 子组件触发事件
this.triggerEvent('myevent', { value: 'hello' }, { bubbles: true })

// 父组件监听事件
// <my-child bind:myevent="onMyEvent" />

// 父组件处理事件
onMyEvent(e) {
  console.log(e.detail.value) // 'hello'
}
// 子组件触发事件
this.triggerEvent('myevent', { value: 'hello' }, { bubbles: true })

// 父组件监听事件
// <my-child bind:myevent="onMyEvent" />

// 父组件处理事件
onMyEvent(e) {
  console.log(e.detail.value) // 'hello'
}

获取组件实例 ​

js
// 父组件中获取子组件实例
const child = this.selectComponent('#child-id')
child.setData({ name: '张三' })
// 父组件中获取子组件实例
const child = this.selectComponent('#child-id')
child.setData({ name: '张三' })

组件生命周期 ​

js
Component({
  lifetimes: {
    created() {
      // 组件实例刚被创建,不能使用 setData
    },
    attached() {
      // 组件进入页面节点树,可以 setData
    },
    ready() {
      // 组件在视图层布局完成后触发
    },
    moved() {
      // 组件被移动到节点树另一个位置
    },
    detached() {
      // 组件从页面节点树移除
    }
  }
})
Component({
  lifetimes: {
    created() {
      // 组件实例刚被创建,不能使用 setData
    },
    attached() {
      // 组件进入页面节点树,可以 setData
    },
    ready() {
      // 组件在视图层布局完成后触发
    },
    moved() {
      // 组件被移动到节点树另一个位置
    },
    detached() {
      // 组件从页面节点树移除
    }
  }
})

也可以使用旧版生命周期写法:

js
Component({
  created() {},
  attached() {},
  ready() {}
})
Component({
  created() {},
  attached() {},
  ready() {}
})

组件所在页面的生命周期 ​

js
Component({
  pageLifetimes: {
    show() {
      // 页面被展示时触发
    },
    hide() {
      // 页面被隐藏时触发
    },
    resize(size) {
      // 页面尺寸变化时触发
    }
  }
})
Component({
  pageLifetimes: {
    show() {
      // 页面被展示时触发
    },
    hide() {
      // 页面被隐藏时触发
    },
    resize(size) {
      // 页面尺寸变化时触发
    }
  }
})

组件插槽(slot) ​

单个插槽 ​

html
<!-- 组件 wxml -->
<view>
  <slot name="header" />
  <view>默认内容</view>
  <slot name="footer" />
</view>
<!-- 组件 wxml -->
<view>
  <slot name="header" />
  <view>默认内容</view>
  <slot name="footer" />
</view>
html
<!-- 使用组件 -->
<my-comp>
  <view slot="header">头部内容</view>
  <view slot="footer">底部内容</view>
</my-comp>
<!-- 使用组件 -->
<my-comp>
  <view slot="header">头部内容</view>
  <view slot="footer">底部内容</view>
</my-comp>

启用多个插槽 ​

js
// 组件 js
Component({
  options: {
    multipleSlots: true
  }
})
// 组件 js
Component({
  options: {
    multipleSlots: true
  }
})

组件样式隔离 ​

默认情况下,组件的样式只对当前组件生效,不会影响外部。

js
Component({
  options: {
    styleIsolation: 'isolated' // 默认值
  }
})
Component({
  options: {
    styleIsolation: 'isolated' // 默认值
  }
})
值说明
isolated组件内外样式互不影响(默认)
apply-shared页面样式会影响到组件
shared组件和页面样式互相影响

数据监听器(observers) ​

js
Component({
  properties: {
    a: Number,
    b: Number
  },
  observers: {
    'a, b': function (a, b) {
      // 当 a 或 b 变化时触发
      this.setData({
        sum: a + b
      })
    },
    'obj.**': function (obj) {
      // 监听对象深层属性变化
      console.log(obj)
    }
  }
})
Component({
  properties: {
    a: Number,
    b: Number
  },
  observers: {
    'a, b': function (a, b) {
      // 当 a 或 b 变化时触发
      this.setData({
        sum: a + b
      })
    },
    'obj.**': function (obj) {
      // 监听对象深层属性变化
      console.log(obj)
    }
  }
})

组件间关系(relations) ​

js
// 父组件
Component({
  relations: {
    './child': {
      type: 'child',
      linked(target) {
        // 每次子组件被插入时触发
      }
    }
  }
})

// 子组件
Component({
  relations: {
    './parent': {
      type: 'parent',
      linked(target) {
        // 每次被插入到父组件时触发
      }
    }
  }
})
// 父组件
Component({
  relations: {
    './child': {
      type: 'child',
      linked(target) {
        // 每次子组件被插入时触发
      }
    }
  }
})

// 子组件
Component({
  relations: {
    './parent': {
      type: 'parent',
      linked(target) {
        // 每次被插入到父组件时触发
      }
    }
  }
})