【微信小程序】 自定义组件

 

 创建微信小程序组件

在小程序中创建组件:

1、项目根目录中创建【components】目录,存放自定义组件

2、进入components目录,给组件创建一个组件目录

3、右键组件目录,选择【创建Components】

4、之后回车,会自动生成组件对应的4个文件, 后缀名分别为.js, .json, .wxml和.wxss

 

一、全局组件和局部组件 

 

局部页面引用组件:

在【页面的.json】配置文件中引用组件的方式,叫做“局部引用”。

示例代码如下:

{
  "usingComponents": {
    "test1": "/components/test1/test1"
  }
}

  

全局引用组件:

在【app.json】中配置组件信息

"usingComponents": {
  "test1": "/components/test1/test1"
}

  

使用组件:

<!-- 全局可以在任意页面中使用引入的组件, 局部组件只允许在引入的页面中使用 -->
<test1></test1>

  

组件和页面的区别

从表面来看,组件和页面都是由.js、 json、 .wxml 和.WXSS这四个文件组成的。

但是,组件和页面的js与.json文件有明显的不同:

● 组件的 json文件中需要声明"component": true属性
● 组件的 js文件中调用的是Component()函数
● 组件的事件处理函数需要定义到methods节点中

 

二、组件之间的样式隔离问题:

一、组件样式隔离

默认情况下,自定义组件的样式只对当前组件生效,不会影响到组件之外的UI结构

● 组件A的样式不会影响组件C的样式
● 组件A的样式不会影响小程序页面的样式
● 小程序页面的样式不会影响组件A和C的样式

好处:

① 防止外界的样式影响组件内部的样式

② 防止组件的样式破坏外界的样式

二、组件样式隔离的注意点

● app.wxss中的全局样式对组件无效

● 只有class选择器会有样式隔离效果,
    id选择器、属性选择器、标签选择器不受样式隔离的影响

建议:在组件和引用组件的页面中建议使用class选择器,不要使用id、属性、标签选择器!

 

 

三、修改组件的样式隔离选项

默认情况下,自定义组件的样式隔离特性能够防止组件内外样式互相干扰的问题。

但有时,我们希望在外界能够控制组件内部的样式,

此时,可以通过stylelsolation 修改组件的样式隔离选项,

用法如下:

// components/test1/test1.js
Component({
  options: {
    styleIsolation: 'isolated'
  }
})

// components/test1/test1.json
{
  "component": true,
  "usingComponents": {},
  "styleIsolation": "isolated"
}

  

四、 stylelsolation的可选值

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

apply-shared
  表示页面WXSs样式将影响到自定义组件,但自定义组件wxSS中指定的样式不会影响页面
shared   表示页面WxSs样式将影响到自定义组件,   自定义组件Wxss中指定的样式也会影响页面和其他设置了apply-shared或shared的自定义组件

  

三、Data属性 和 Method方法

和页面一样设置

/**
 * 组件的初始数据
 */
data: {
  count: 0
},

方法是独立的一个对象存放

  /**
   * 组件的方法列表
   * 在这里编写自定义方法,和事件处理函数
   * 自定义方法推荐使用下划线前缀标记
   * 如:
   *  addCount() // 事件执行方法
   *  _showCount() // 自定义方法
   */
  methods: {
    addCount() {
      this.setData({ count: this.data.count + 1})
      this._showCount()
    },
    _showCount() {
      wx.showToast({ 
        icon: 'none',
        title: `count值:${this.data.count}`
      })
    }
  }

 

四、Properties属性

  /**
   * 组件的属性列表
   * 用来接收外部页面或者其他组件传递进来的属性
   */
  properties: {
    // 和vue一样的写法
    max: { 
      type: Number,
      value: 10 // 默认值
    }
    /**
     * 简化写法,不设置默认值
     * max: Number 
     */
  },

  

组件使用时可以设置值

<test1 max="{{5}}"/>

  

使用propertis数据

等于max值时无法继续叠加

addCount() {
  if (this.data.count === this.properties.max) return
  this.setData({ count: this.data.count + 1})
  this._showCount()
},

  

五、data和properties的区别

在小程序的组件中,properties 属性和data数据的用法相同,它们都是可读可写的,只不过:

● data更倾向于存储组件的私有数据

● properties更倾向于存储外界传递到组件中的数据

两者的地址一致:

this.data === this.properties // true

给properties属性赋值

// 为properties的数据重新赋值
this.setData({ max: this.properties.max + 1 })

  

六、数据监听器

监听器功能再页面不支持

/**
 * 组件的数据监听器
 */
observers: {
  // 可以同时监听多个属性, 入参的参数对应属性个数,表示属性更新后的值
  'attr1, attr2': function(newValue1, newValue2) {
    // todo ...
  }
},

 

演示案例:

组件HTML代码:

<!--components/test2/test2.wxml-->
<text>components/test2/test2.wxml</text>

<view>
  {{n1}} + {{n2}} = {{sum}}
</view>
<button size="mini" type="primary" bindtap="addN1">n1增加</button>
<button size="mini" type="primary" bindtap="addN2">n2增加</button>

组件JS代码:

// components/test2/test2.js
Component({
  /**
   * 组件的数据监听器
   */
  observers: {
    'n1, n2': function(n1Value, n2Value) {
      this.setData({ sum: n1Value + n2Value })
    }
  },
  /**
   * 组件的属性列表
   */
  properties: { },

  /**
   * 组件的初始数据
   */
  data: { n1: 0, n2: 0, sum: 0 },

  /**
   * 组件的方法列表
   */
  methods: {
    addN1() { this.setData({ n1: this.data.n1 + 1 }) },
    addN2() { this.setData({ n2: this.data.n2 + 1 }) }
  }
}) 

 

演示案例2:

一个RGB颜色案例,HTML代码

<!--components/rgbView/rgbView.wxml-->
<text>components/rgbView/rgbView.wxml</text>

<view class="colorBox" style="background-color: rgb({{fullColor}});">
  颜色值:{{fullColor}}
</view>
<button size="mini" bindtap="changeR" >R</button> |
<button size="mini" bindtap="changeG" >G</button> |
<button size="mini" bindtap="changeB" >B</button>
<view>
  {{rgb.r}}, {{rgb.g}}, {{rgb.b}}
</view>

  

JS代码:

// components/rgbView/rgbView.js
Component({
  observers: {
    // 监听,然后重新赋值
    'rgb.**': function(newRgb) {
      this.setData({ 
        fullColor: `${newRgb.r}, ${newRgb.g}, ${newRgb.b}`
      })
    }
  },

  /**
   * 组件的属性列表
   */
  properties: { },

  /**
   * 组件的初始数据
   */
  data: {
    rgb: { r: 0, g: 0, b: 0 },
    fullColor: '0, 0, 0'
  },

  /**
   * 组件的方法列表
   */
  methods: {
    changeR() {
      let rValue = this.data.rgb.r + 5 
      rValue = rValue > 255 ? 255 : rValue
      this.setData({ 'rgb.r': rValue })
    },
    changeG() {
      let gValue = this.data.rgb.g + 5 
      gValue = gValue > 255 ? 255 : gValue
      this.setData({ 'rgb.g': gValue })
    },
    changeB() {
      let bValue = this.data.rgb.b + 5 
      bValue = bValue > 255 ? 255 : bValue
      this.setData({ 'rgb.b': bValue })
    }
  }
})

RGB显示板样式:

/* components/rgbView/rgbView.wxss */
.colorBox {
  line-height: 200rpx;
  font-size: 24rpx;
  color: white;
  text-shadow: 0rpx 0rpx 2rpx black;
  text-align: center;
}

  

七、纯数据字段

1.什么是纯数据字段

概念:纯数据字段指的是那些不用于界面渲染的data字段。
应用场景:例如有些情况下,某些data中的字段既不会展示在界面上,也不会传递给其他组件,仅仅在当前
组件内部使用。带有这种特性的data字段适合被设置为纯数据字段。

好处:纯数据字段有助于提升页面更新的性能。

2.使用规则

在Component构造器的options节点中,指定pureDataPattern为一个正则表达式,

字段名符合这个正则表达式的字段将成为纯数据字段,示例代码如下:

// components/rgbView/rgbView.js
Component({
  options: {
    // 自定义正则规则来匹配纯数据字段,演示为首字符下划线的属性
    pureDataPattern: /^_/
  }
}

把之前的JS代码的rgb属性名改为_rgb使用即可:

// components/rgbView/rgbView.js
Component({
  options: {
    // 自定义正则规则来匹配纯数据字段,演示为首字符下划线的属性
    pureDataPattern: /^_/
  },
  observers: {
    // 监听,然后重新赋值
    '_rgb.**': function(newRgb) {
      this.setData({ 
        fullColor: `${newRgb.r}, ${newRgb.g}, ${newRgb.b}`
      })
    }
  },

  /**
   * 组件的属性列表
   */
  properties: { },

  /**
   * 组件的初始数据
   */
  data: {
    _rgb: { r: 0, g: 0, b: 0 },
    fullColor: '0, 0, 0'
  },

  /**
   * 组件的方法列表
   */
  methods: {
    changeR() {
      let rValue = this.data._rgb.r + 5 
      rValue = rValue > 255 ? 255 : rValue
      this.setData({ '_rgb.r': rValue })
    },
    changeG() {
      let gValue = this.data._rgb.g + 5 
      gValue = gValue > 255 ? 255 : gValue
      this.setData({ '_rgb.g': gValue })
    },
    changeB() {
      let bValue = this.data._rgb.b + 5 
      bValue = bValue > 255 ? 255 : bValue
      this.setData({ '_rgb.b': bValue })
    }
  }
})

  

八、组件的生命周期函数

全部的钩子函数

// components/rgbView/rgbView.js
Component({
  lifetimes: {
    // 创建完成后执行
    created() {},
    // 组件实例和页面完成dom绑定后执行,等同vue的mounted
    attached() {},
    // 组件再页面的视图渲染完成
    ready() {},
    // 组件被更新后执行(组件实例移动到节点树的新位置)
    moved() {},
    // 组件从页面的dom树中移除时触发
    detached() {},
    // 组件发生异常触发
    error() {}
  }
})

主要的三个函数

2.组件主要的生命周期函数
在小程序组件中,最重要的生命周期函数有3个,分别是created、attached、 detached。 它们各自的特点
如下:

① 组件实例刚被创建好的时候,
  created生命周期函数会被触发 ● 此时还不能调 用setData ● 通常在这个生命周期函数中, 只应该用于给组件的this添加一些自定义的属性字段 ② 在组件完全初始化完毕、
  进入页面节点树后,attached 生命周期函数会被触发
  此时,this.data 已被初始化完毕 ● 这个生命周期很有 用,绝大多数初始化的工作可以在这个时机进行( 例如发请求获取初始数据)
③ 在组件离开页面节点树后,detached生命周期函数会被触发 ● 退出一个页面时,会触发页面内每个自定义组件的detached生命周期函数 ● 此时适合做一些清理性质的工作

  

九、所属页面的生命周期函数

1.什么是组件所在页面的生命周期
有时,自定义组件的行为依赖于页面状态的变化,此时就需要用到组件所在页面的生命周期。

// components/rgbView/rgbView.js
Component({
  pageLifetimes: {
    // 所引用的页面被展示时执行
    show() {},
    // 所引用的页面被隐藏时执行
    hide() {},
    // 所引用的页面被尺寸变化时执行, 可以获取size大小
    resize(size) {},
  }
})

 

演示案例,在颜色组件中编写一个随机颜色生成的方法:

  /**
   * 组件的方法列表
   */
  methods: {
    /**
     * 生成随机色彩
     */
    _randomColor() {
      this.setData({
        _rgb: {
          r: Math.floor(Math.random() * 256),
          g: Math.floor(Math.random() * 256),
          b: Math.floor(Math.random() * 256)
        }
      })
    },
  }

  

在页面渲染展示的周期时刻调用:

  pageLifetimes: {
    // 所引用的页面被展示时执行
    show() {
      // 在引用页面展示时触发
      this._randomColor()
    }
  }

  

十、Slot 组件插槽

单个插槽

编写一个组件

<!--components/test4/test4.wxml-->
<text>components/test4/test4.wxml</text>

<view>
  <view>这是组件的内部结构</view>
  <slot></slot>
</view>

使用组件:

<test4>
  <text>这是嵌入组件插槽的部分</text>
</test4>

  

多个插槽

<view>
  <view>这是组件的内部结构1</view>
  <slot name="before"></slot>
  <view>这是组件的内部结构2</view>
  <slot name="after"></slot>
  <view>这是组件的内部结构3</view>
</view>

微信新版本需要追加JS配置项:

黑马教程演示没有,说明在老版本可以直接配置

// components/test4/test4.js
Component({
  options: {
    // 开启多插槽支持
    multipleSlots: true,
  }
}

使用时需要指明名称:

<test4>
  <text slot="before">这是嵌入组件插槽的部分1</text>
  <text slot="after">这是嵌入组件插槽的部分2</text>
</test4>

  

十一、组件之间通信问题

1.父子组件之间通信的3种方式

①属性绑定
    ●用于父组件向子组件的指定属 性设置数据,仅能设置JSON兼容的数据

②事件绑定
    ● 用于子组件向父组件传递数据,可以传递任意数据

③获取组件实例
    ● 父组件 还可以通过this.selectComponent()获取子组件实例对象
    ● 这样就可以直 接访问子组件的任意数据和方法

  

 一、父组件(页面) 向 子组件传递数据:

<father counter="{{val1}}" />
<view> father组件 counter值: {{val1}}</view>  

Father组件接参:

// components/father/father.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    counter: {
      type: Number
    }
  }
})

 

二、子组件向父组件传递数据

3.事件绑定
事件绑定用于实现子向父传值,可以传递任何类型的数据。使用步骤如下:

① 在父组件的js中,定义一个函数,这个函数即将通过自定义事件的形式,传递给子组件

② 在父组件的wxml中,通过自定义事件的形式,将步骤1中定义的函数引用,传递给子组件

③ 在子组件的js中,通过调用this.triggerEvent(自定义事件名称',{/*参数对象*/ }) ,将数据发送到父组件

④ 在父组件的js中,通过e.detail获取到子组件传递过来的数据

  

1、Father组件先追加事件和展示

<!--components/father/father.wxml-->
<text>components/father/father.wxml</text>

<view>展示Father组件 counter值:{{counter}}</view>
<button bindtap="addCounter">+1</button>

JS代码,使用事件触发器配置事件和传递值:

  /**
   * 组件的方法列表
   */
  methods: {
    addCounter() {
      this.setData({ counter: this.properties.counter + 1}) 
      // 给父组件进行同步
      this.triggerEvent('sync', { value: this.properties.counter})
    }
  }

页面使用组件时,绑定对应的接收方法:

<father counter="{{val1}}" bind:sync="syncCounter" />
<view> father组件 counter值: {{val1}}</view>

方法编写:

  syncCounter(event) {
    // 接收从子组件发送过来的参数
    this.setData({ val1: event.detail.value })
  },

  

三、直接获取组件实例实现调用

可在父组件里调用this.selectComponent("id或class选择器"),获取子组件的实例对象,

从而直接访问子组件的任意数据和方法。

调用时需要传入一-个选择器,例如this.selectComponent(".my-component")。

<father 
  counter="{{val1}}" 
  bind:sync="syncCounter"
  class="cf"
  id="componentFather" />
<view> father组件 counter值: {{val1}}</view>
<button bindtap="getComponentFather"> 测试</button>

JS代码:

现版本拿不到组件的方法,data属性可以获取

  /**
   * 直接获取组件实例
   */
  getComponentFather() {
    // 或者id选择器 this.selectComponent('#componentFather')
    // 或者类选择器 this.selectComponent('.cf')
    const father = this.selectComponent('.cf')
    console.log(father)
    console.log(father.data.counter)
    // 拿不到组件方法? 清空缓存,刷新页面再尝试
    father.addCounter()
  },

 

十二、behaviors

1.什么是behaviors
behaviors是小程序中,用于实现组件间代码共享的特性,类似于Vue.js中的"mixins"

2. behaviors的工作方式
每个behavior可以包含一组属性、 数据、生命周期函数和方法。

组件引用它时,它的属性、数据和方法会被合并到组件中。

每个组件可以引用多个behavior, behavior 也可以引用其它behavior。

 

3.创建behavior

调用Behavior(Object object)方法即可创建一一个共享 的behavior实例对象,供所有的组件使用:

// /behaviors/bhvirs1.js
module.exports = Behavior({
  // 属性
  properties: {},
  // 数据
  data: { msg: 'Hello Behaviors!'},
  // 方法
  methods: {
    
  }
})

  

4.导入并使用behavior

在组件中,使用require()方法导入需要的behavior,

挂载后即可访问behavior中的数据或方法,

示例代码如下:

// components/father/father.js
const b1 = require('../../behaviors/bhvirs1')

Component({
  behaviors: [b1],
})

  

组件可以展示Behaviors中的值

<view>展示Behvior数据 {{msg}}</view>

  

对应的所有设置属性:

 

冲突问题,见官方文档描述

https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/behaviors.html

  

 

posted @ 2022-05-04 23:14  emdzz  阅读(913)  评论(0编辑  收藏  举报