首页 > 上网技巧 > 电脑小技巧 > 写一个微信小程序自定义公共组件

写一个微信小程序自定义公共组件

时间:2018-03-02 10:01 作者:QQ地带 我要评论

微信小程序已经推出一段时间了,官方也提供很多组件,但是有些业务场景,官方组件难免有些捉襟见肘,这时候,就需要自己开发一个自定义组件了。但是微信小程序其实是做了很多限制的,本文记录了开发(踩坑)一个公共登录弹出框组件的过程,仅供参考。
 
1. 一个组件的组成部分
一个微信小程序有四种格式的文件*.wxml、*.wxss、*.js和*.json,*.json其实是小程序和页面的配置文件,开发组件的时候,就不需要这个了。我这里开发一个登陆弹出框组件,我把组件放在component文件夹下:
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Wechat-APP/
├─app.js
├─app.json
├─app.wxss
├─component/
│ └─login-pannel/
│   ├─login-pannel.js
│   ├─login-pannel.wxml
│   └─login-pannel.wxss
├─image/
├─pages/
│ ├─index/
│ ├─merchant-detail/
│ └─merchant-list/
└─utils/
可见,组件也是由三个部分组成。
 
1.1. wxml
首先新建组件的模版,/component/login-pannel/login-pannel.wxml,同时,给定义的模版一个唯一标识<template name="loginPannel"></template>,代码如下:
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template name="loginPannel">
  <view class="login-pannel" hidden="{{ isHide }}" >
    <view catchtap="__lgpanel_close" class="close-btn"></view>
    <view class="form-area">
      <view class="login-inline phone {{ phoneClass }}">
        <input placeholder="请填写手机号码" maxlength="11" type="number" bindinput="__lgpanel_phoneInput" bindfocus="__lgpanel_phoneFocus" bindblur="__lgpanel_phoneBlur" />
        <text catchtap="__lgpanel_sendCode" class="verify-btn">{{ verifyText }}</text>
      </view>
      <view class="login-inline vcode {{ vcodeClass }}">
        <input placeholder="请填写验证码" maxlength="4" type="number" bindinput="__lgpanel_vcodeInput" bindfocus="__lgpanel_vcodeFocus" bindblur="__lgpanel_vcodeBlur" />
      </view>
    </view>
    <text catchtap="__lgpanel_login" class="login-btn">注册/登录</text>
  </view>
</template>
官方提供了模版的概念,同时又给出了两种引用方式:import 和 include。include 方式是将你的模版原封不动的“粘贴”到引用的地方,而我们这里开发的是一个交互组件,势必会碰到数据绑定和事件绑定,所以我们最终引用的时候,使用 import 的形式来使用组件。
 
1.2. wxss
定义组件的样式,/component/login-pannel/login-pannel.wxss。样式没什么好说的,这里略过。
 
1.3. js
首先定义组件的构造函数,构造函数主要做两件事:
 
把组件的数据“注入”到页面的data对象中;
把组件的事件“合并”到页面对象上。
小程序文档中,关于数据和事件,是这么描述的:
 
如bindtap,当用户点击该组件的时候会在该页面对应的Page中找到相应的事件处理函数。
WXML 中的动态数据均来自对应 Page 的 data。
模板拥有自己的作用域,只能使用 data 传入的数据。
这样,想实现模版的数据绑定,只能想办法把组件上的数据“追加”到页面的 data 对象上去;同理,想实现组件的事件绑定,则需要把组件的事件也“传给”页面函数Page。同时,为了防止影响到其它组件,还要给数据和事件都限定了“命名空间”:
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
let _compData = {
  '__lgpanel__.isHide': true,
  '__lgpanel__.phoneNum': '',
  '__lgpanel__.verifyCode': '',
  '__lgpanel__.verifyText': '获取验证码',
  '__lgpanel__.phoneClass': '',
  '__lgpanel__.vcodeClass': ''
}
let _compEvent = {
  __lgpanel_timer: null,
  __lgpanel_countDown: 60,
  __lgpanel_close: function () {
    clearInterval(this.__lgpanel_timer)
    this.__lgpanel_countDown = 60
    this.setData(_compData)
  },
  __lgpanel_sendCode: function () {
    if (this.__lgpanel_countDown < 60) {
      return
    }
    this.setData({
      '__lgpanel__.verifyText': this.__lgpanel_countDown + 's'
    })
    this.__lgpanel_timer = setInterval(() => {
      this.__lgpanel_countDown--
      if (this.__lgpanel_countDown <= 0) {
        clearInterval(this.__lgpanel_timer)
        this.__lgpanel_countDown = 60
        this.setData({
          '__lgpanel__.verifyText': '重新获取'
        })
        return
      }
      this.setData({
        '__lgpanel__.verifyText': this.__lgpanel_countDown + 's'
      })
    }, 1000)
    typeof this.loginPannel._configs.sendCode == "function" &&
      this.loginPannel._configs.sendCode()
  }
}
// 小程序最新版把原型链干掉了。。。换种写法
let loginPannel = {
  show: function(data) {
    this.__page.setData({'__lgpanel__.isHide': false})
    if (data) {
      Object.assign(this._configs, data)
    }
  }
}
function LoginPannel () {
  // 定义组件的一些回调
  this._configs = {
    sendCode: null,
    closeCode: null,
    login: null
  }
  // 拿到当前页面对象
  let pages = getCurrentPages()
  let curPage = pages[pages.length - 1]
  // 把组件的事件“合并到”页面对象上
  Object.assign(curPage, _compEvent)
  this.__page = curPage
  Object.assign(curPage, loginPannel) // 小程序最新版把原型链干掉了。。。换种写法
  // 附加到page上,方便访问
  curPage.loginPannel = this
  // 把组件的数据“注入”到页面的data对象中
  curPage.setData(_compData)
  return this
}
同时,在组件的构造函数的原型链上追加“启动”函数,(微信小程序最新版把原型链干掉了。。。)在构造函数中,把启动方法合并到LoginPannel对象上去,同时把LoginPannel暴露出去:
 
1
2
3
4
5
6
7
8
9
10
11
/* LoginPannel.prototype.show = function(data) {
  this.__page.setData({'__lgpanel__.isHide': false})
  if (data) {
    Object.assign(this._configs, data)
  }
} */
module.exports = {
  LoginPannel
}
到这里,一个组件就写好了。
 
2. 使用自定义组件
由于微信小程序限制太多,使用组件的时候,需要引用三个文件
 
2.1. 引用 wxml
在要使用组件的页面的wxml文件中引用组件,同时使用模版,传入担任“命名空间”的对象:
 
1
2
<import src="../../component/login-pannel/login-pannel.wxml"/>
<template is="loginPannel" data="{{ ...__lgpanel__ }}"/>
2.2. 引用 wxss
如果你的组件需要在多个页面中使用,那么在app.wxss中引入一次组件样式即可;如果只需要在摸个特定的页面使用,只需在该页面的样式文件中引入组件样式。
 
1
@import "./component/login-pannel/login-pannel.wxss";
2.3. 引用 js
如果你的组件需要在多个页面中使用,你需要在app.js中引用,同时传给App():
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { LoginPannel } from './component/login-pannel/login-pannel'
App({
  LoginPannel,
  _getUserInfo: function (cb) {
    var that = this
    if (this.globalData.userInfo) {
      typeof cb == "function" &&
             cb(this.globalData.userInfo)
    } else {
      //调用登录接口
      wx.login({
        success: function () {
          wx.getUserInfo({
            success: function (res) {
              that.globalData.userInfo = res.userInfo
              typeof cb == "function" &&
                     cb(that.globalData.userInfo)
            }
          })
        }
      })
    }
  }
})
然后,在使用的页面的生命周期函数onLoad中“注册”该组件:
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
onLoad: function () {
  // 调用应用实例的方法获取全局数据
  let app = getApp()
  // “注册”组件
  new app.LoginPannel()
  app._getUserInfo((userInfo) => {
    //更新数据
    this.setData({
      userInfo:userInfo
    })
  })
}
然后,在这个页面中就可以愉快的使用组件啦:
 
1
2
3
4
5
6
7
8
openLoginPannel: function() {
  this.loginPannel.show({
    sendCode: function () {
      console.log('index - ', 'sendCode')
    },
    login: this.onLogin
  })
}
如果你只是要在特定的页面使用组件,只要在当前页面引用组件即可:
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { LoginPannel } from '../../component/login-pannel/login-pannel'
let pageData = {
  LoginPannel,
  data: {
    motto: '打开登录框',
    userInfo: {}
  },
  openLoginPannel: function() {
    this.loginPannel.show({
      sendCode: function () {
        console.log('index - ', 'sendCode')
      }
    })
  },
  onLoad: function () {
    new this.LoginPannel()
  }
}
Page(pageData);
到此,自定义的组件就写好啦:
 
image
 
PS:由于用到了ES6特性,需要在小程序调试工具中勾选“开启ES6转ES5”。
 
3. 写在最后
小程序官方还是限制太多,我并不看好。但是对于初创公司还是有点意义的,这时候遇到自定义组件的需求,只好带着镣铐跳舞。本文只是给出一个写一个微信小程序自定义组件的思路,所谓条条大路通罗马,肯定还有更合适的方案,能力有限,仅供参考。
 
2017-06-16更新:
本文是我在小程序刚刚推出没多久后,调研的时候写的,后来我和项目上一致认为小程序不适合我们公司项目的使用场景,所以调研也就自动结束了。但是过去了这么长时间,新的小程序开发工具不一定能正常跑起来,所以也仅供参考,提供个思路。

标签: 微信小程序
顶一下
(0)
0%
踩一下
(0)
0%

Google提供的广告