小程序组件化方案01

缘由

此方案采用wux开源版本,本章节,主要概览框架和核心基类。

一、框架整体介绍

1
2
3
4
5
6
7
8
9
10
Components
|____button -- 带有滑动效果的button
| |____button.js -- 组件代码(统一实例自component.js)
| |____button.wxml -- 模板代码
|____styles -- 样式文件
| |____animate.wxss -- 所有关于动画的复用样式
| |____widget -- 存放所有组件的样式
| | |____button.wxss -- button组件的样式文件
|____component.js -- 统一使用的component类
|____wux.js -- 导出Components文件夹下,所写的组件

上述树状图就是整体目录和架构。

二、component.js基类文件,代码解读

2-1、基类构造函数

1
2
3
4
5
6
constructor(options = {}) {
Object.assign(this, {
options,
})
this.__init()
}

接受一个options(一般为字典对象)参数,然后将参数再转为{options}字典,赋值给this,最终变为this.options

2-2、整体初始化

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
__init() {
//获取当前页面
this.page = getCurrentPages()[getCurrentPages().length - 1]
//生成新的setData函数,bind当前页面作为上下文
const setData = this.page.setData.bind(this.page)
// 检查版本库是否高于或等于 1.5.0,setData 方法才有回调函数,否则采用 setTimeout 模拟
const checkSDKVersion = () => {
let has = false
try {
const res = wx.getSystemInfoSync()
const SDKVersion = res.SDKVersion.split('.')
has = Number(SDKVersion[0]) > 1 || Number(SDKVersion[1]) >= 5
} catch (e) {}
return has
}
// 重写 setData 方法,支持回调,兼容低版本
this.setData = (obj = {}, cb = () => ({})) => {
const fn = () => {
if (typeof cb === 'function') {
cb()
}
}
if (checkSDKVersion()) {
setData(obj, fn)
} else {
setData(obj)
setTimeout(fn, 0)
}
}
this.__initState()
}

上述代码中的注释,已经大体说明该函数的作用,getCurrentPages()为小程序提供的获取页面堆栈的接口。checkSDKVersion为版本判断函数,以便兼容setData函数。在小程序api1.5.0版本中,才有setData的回调函数,所以需要做兼容。最后调用__initState函数,完成属性和方法的绑定。

2-3、绑定组件的属性,将options中非函数部分setData

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
__initData() {
const scope = this.options.scope
const data = this.options.data
this._data = {}
// 筛选非函数类型,更改参数中函数的 this 指向
if (!this.isEmptyObject(data)) {
for (let key in data) {
if (data.hasOwnProperty(key)) {
if (typeof data[key] === `function`) {
data[key] = data[key].bind(this)
} else {
this._data[key] = data[key]
}
}
}
}
// 将数据同步到 page.data 上面方便渲染组件
this.page.setData({
[`${scope}`]: this._data,
})
}

上面的代码可能仍对读者,产生部分困惑,主要是options.data中的函数,被弄到哪里去了。其实这是该组件库,做的不太好的一个地方。component基类,其实不处理options.data中的函数。我们需要自己定义组件的时候,在options.methods中,处理options.data里面的函数。

2-4、组件方法设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
__initMethods() {
const scope = this.options.scope
const methods = this.options.methods
// 筛选函数类型
if (!this.isEmptyObject(methods)) {
for (let key in methods) {
if (methods.hasOwnProperty(key) && typeof methods[key] === `function`) {
this[key] = methods[key] = methods[key].bind(this)
// // 将 methods 内的方法重命名并挂载到 page 上面,否则 template 内找不到事件
this.page[`${scope}.${key}`] = methods[key]
// 将方法名同步至 page.data 上面,方便在模板内使用 {{ method }} 方式绑定事件
this.setData({
[`${scope}.${key}`]: `${scope}.${key}`,
})
}
}
}
}

获取options中的methods项,挂载到pagepage.data上。这里可能存在二个迷惑点。

  • 挂载到page上是什么意思?

    挂载到page上是方便wxml上的bindtap等方法可以找到对应函数

  • 挂载到page.data上是什么意思?

    挂载到this.setData上面,是方便template可以直接使用

简而言之,上述两个是协同起来,一起工作的。

2-5、共用函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 设置元素显示
*/
setVisible(className = `wux-animate--fade-in`) {
this.setData({
[`${this.options.scope}.animateCss`]: className,
[`${this.options.scope}.visible`]: !0,
})
}
/**
* 设置元素隐藏
*/
setHidden(className = `wux-animate--fade-out`, timer = 300) {
this.setData({
[`${this.options.scope}.animateCss`]: className,
})
setTimeout(() => {
this.setData({
[`${this.options.scope}.visible`]: !1,
})
}, timer)
}

上述代码为所有组件共用的函数,有元素的显示和元素的隐藏。

坚持原创技术分享,您的支持将鼓励我继续创作!