wx小程序(3) - 自定义组件及参数传输

2,682 阅读6分钟

组件化的项目开发中,组件应当划分为三个层次:组件、模块、页面 微信小程序已经为开发者封装好了基础组件,页面文件(pages)也有了详细的规定 而模块就需要自行开发,并且要和页面文件区分开,这就涉及到自定义组件

**开发者可以将页面内的功能模块抽象成自定义组件,以便在不同的页面中重复使用;也可以将复杂的页面拆分成多个低耦合的模块,有助于代码维护。**自定义组件在使用时与基础组件非常相似。

一、基本用法

在根目录下创建一个 component 目录,用于存放自定义组件 组件也是由 json、wxml、wxss、js 四个文件组成

其中 wxml 部分没有什么特殊的地方,和页面的写法一致 wxss 也是只对组件生效,而且 app.wxss 中的样式也不会对自定义组件生效

在组件wxss中不应使用ID选择器、属性选择器和标签名选择器

#a { } /* 在组件中不能使用 */
[a] { } /* 在组件中不能使用 */
button { } /* 在组件中不能使用 */
.a > .b { } /* 除非 .a 是 view 组件节点,否则不一定会生效 */

最关键的地方在于,需要在 json 中添加配置项:将 component 字段设为 true,这样才能注册这个自定义组件

{
  "component": true, 
  "usingComponents": {}
}

usingComponents字段组件里面可以引入其他组件,在页面引用组件同样也需要在json设置这个字段

{
  "navigationBarTitleText": "登录",
  "usingComponents": {
    "toast": "../component/Toast/toast"
  }
}

然后就能在登录页面的 wxml 中直接使用该组件

二、组件间的参数传递

  • 组件的属性列表 小程序的页面 pages 需要使用 Page() 来注册,而组件则需要 Component() 构造器

如果组件需要接受一个外部数据,比如一个列表组件的数据源 data,可以这么配置:

这里的** properties** 类似于 vue 中的 props,表示该对象下的属性将从外部传入 properties 可设置 type、value、observer 三个属性

其中 type 用于指定字段类型(Number,String,Array,Object,Function) value 表示字段的默认值,observer 用于定义该字段的监听函数

在页面上调用组件的时候,直接给 data 赋值就好:<toast text="登录成功"></toast>

PS:开发中应当避免使用 data 为前缀的属性名(如 data-text),这会被识别为 dataset 中的参数

  • 组件事件触发回调
// toast.wxml
<view class="toast" style="{{ pos }}: 60rpx">
  <view class='text' catchtap='_tapEvent'>{{text}}</view>
</view>
// toast.js
methods: {
    // 私有方法:以下划线为前缀命名
    _tapEvent() {
      //触发tap回调(子组件调用父组件的方法)
      this.triggerEvent("tapEvent")
    }
}

// 然后在login调用组件的时候用tapEvent绑定_tapEvent方法
// login.wxml
<toast text="登录成功" pos="top" bind:tapEvent="_tapEvent"></toast>
// login.js
methods: {
    //tab事件
    _tapEvent() {
      console.log('你点击了一下');
    }
}

整个的流程是 我们在点击toast组件的时候,执行了catchtap='_tapEvent'方法,这个方法用了this.triggerEvent("tapEvent"),这种是类似广播或者是说模拟事件,触发了在login调用toast组件里面绑定的事件bind:tapEvent并执行了login.js里面定义的方法_tapEvent

  • 组件插槽(具名插槽) 小程序的组件也可以使用 来扩展内容 然后父组件引入这个组件的时候,可以在组件中插入节点,节点内容会渲染到 节点的位置

组件默认只能有一个 ,如果需要添加多个插槽,首先需要在组件 js 中声明,这就是具名插槽。比较建议使用这个,因为很容易辨识你加入的节点会渲染指到那个里面

<view class='wx_dialog_container' hidden="{{!isShow}}">
    <view class='wx-mask'></view>
    <view class='wx-dialog'>
        <slot name="title"></slot>
        <view class='wx-dialog-content'>{{ content }}</view>
        <view class='wx-dialog-footer'>
          <view class='wx-dialog-btn' catchtap='_cancelEvent'>{{ cancelText }}</view>
          <view class='wx-dialog-btn' catchtap='_confirmEvent'>{{ confirmText }}</view>
        </view>
    </view>
</view>

<dialog id="dialog" title='提示' content="{{quickData}}" bind:cancelEvent="_cancelEvent"
   bind:confirmEvent="_confirmEvent">
    <view class='wx-dialog-title' slot="title">温馨提示</view>
  </dialog>

这样使用插槽之后title就可以随意在父组件进行修改,甚至设置其他元素,输入框、按钮、icon都是可以的

三、事件传参

在view中 ,我们用bindtap绑定了showDia方法,并且自定义data-name属性(使用过JQ的人会知道这种方法跟JQ的自定义属性取值是一样的)

<view class="float-item" wx:for="{{quick}}" wx:key="*this" data-name="{{item}}" bindtap='showDia'>
        <view class="icon"></view>
        {{ item }}
</view>
// 然后通过e.currentTarget.dataset可以拿到自定义的值
showDia(e) {
    console.log(e.currentTarget.dataset.name)
 },

这时候大家就可以看到上面说的,避免使用data作为前缀的属性名的原因了, ,data作为前缀的属性名会被dataset识别,混搅到自定义属性取值的使用

这里还有注意两点: 1、data-名称 不能有大写字母,如果需要,可以通过 - (中划线)来连接单词,编译的时候小程序会将第二个单词首字母自动大写。data-* 属性中不可以存放对象。 2、注意打印结果中target和currentTarget的区别。 - target 触发事件的源组件。 - currentTarget 事件绑定的当前组件。

四、URL 传参

如果需要页面跳转,那么就可以使用 navigateTo() 方法或者使用navigator标签的url属性 跳转页面,这样可以在 url 后面接 query 参数

wx.navigateTo({
   url: '../login/login?source=index'
})
// 或者
<navigator url="{{item.url + '?name=' + item.name}}"></navigator>

然后在 Page 页面的生命周期函数 onLoad 中可以接收到这些参数

onLoad: function (options) {
    console.log(options)
 }

这种方式只能通过 navigateTo方法或者navigator 标签发送,onLoad 接收 所以只能用于跳转到非 tabbar 的页面

五、简易动画

因为做组件的时候需要一下动画效果,所以顺便说下小程序封装好的动画函数wx.createAnimation()的使用 它创建一个动画实例animation,调用实例的方法来描述动画,最后通过动画实例的export方法导出动画数据传递给组件的animation属性。 注意: export 方法每次调用后会清掉之前的动画操作

// 首先我们需要在data里面定义一个参数来存放动画
data: {
    animationData: {}
 },
// 然后把参数用animation属性绑定到组件上,在哪使用就绑定在哪
<view class="toast" animation="{{animationData}}"></view>
methods: {
    // 公有方法
    show: function() {
      // 创建一个动画实例,可配置参数(持续时间,运动曲线,延时,设置transform-origin)
      var animation = wx.createAnimation({
        duration: 500,
        timingFunction: 'ease-out',
        // delay: 1000,
        // transformOrigin: 50% 50%
      })
      // animation有rotate旋转、scale缩放、translate偏移、skew倾斜等方法
      animation.translateY(100).rotate(360).scale(2, 2).step()
      this.setData({
        // 清掉前面动画操作
        animationData: animation.export()
      })
    }
}

然后可以在父组件调用toast里面的方法

// 调用前需要用获取toast,才可以使用toast组件里面的方法
onReady: function () {
      this.toast = this.selectComponent("#toast");
},
// 然后定义showToast
showToast() {
      this.toast.show()
},