本学期的系统分析课程要求我们做一个小项目,我们以微信小程序为框架进行了项目的前端搭建,在UI上以开源组件库vant-ui为基础进行了设计,其中用到了许多该开源库的设计,对于项目前端起到了很大的帮助。
组件库的使用教程在 https://youzan.github.io/vant-weapp/#/intro ,介绍说明比较详细且简单,因此这里不再赘述,这里希望对于框架的组成和实现方式进行初步的窥探。
以Popup 弹出层为例,这个组件的实现功能如下图所示,可以实现在页面的各个位置以不同的方式弹出提示框选择框的效果
使用方法:
"usingComponents": {
"van-popup": "path/to/vant-weapp/dist/popup/index"
}
<van-popup show="{{ show }}" bind:close="onClose">内容</van-popup>
Page({
data: {
show: false
},
onClose() {
this.setData({ show: false });
}
});
<van-popup
show="{{ show }}"
position="top"
overlay="{{ false }}"
bind:close="onClose"
>
内容
van-popup>
API
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
show | 是否显示弹出层 | Boolean | false |
z-index | z-index 层级 | Number | 100 |
overlay | 是否显示背景蒙层 | Boolean | true |
position | 可选值为 top bottom right left | String | - |
duration | 动画时长,单位为毫秒 | Number / Object | 300 |
custom-style | 自定义弹出层样式 | String | `` |
overlay-style | 自定义背景蒙层样式 | String | `` |
close-on-click-overlay | 点击蒙层是否关闭 | Popup | Boolean true |
safe-area-inset-bottom | 是否为iPhoneX留出底部安全距离 | Boolean | true |
safe-area-inset-top | 是否留出顶部安全距离(状态栏高度 + 导航栏高度) | Boolean | false |
根据上面的例子可以看出,这个框架实现的功能以及可调用的接口还是比价丰富的,那么首先的问题是组件库是怎么知道调用哪个组件?
"van-popup": "path/to/vant-weapp/dist/popup/index"
,这样wxml在解析对应的组件时,遇到van-popup就会调用对应的index的插入到wxml。下面会说明插入的index这个组件的组成成分。知道了对应的调用组件之后,组建内部是如何实现对应的效果的?
index内部文件组成:
@import '../common/index.wxss'
里面的css,不介绍van-overlay
,比较简单,不介绍{
"component": true,
"usingComponents": {
"van-overlay": "../overlay/index"
}
}
<wxs src="../wxs/utils.wxs" module="utils" />
<van-overlay
wx:if="{{ inited && overlay }}"
mask
show="{{ show }}"
z-index="{{ zIndex }}"
custom-style="{{ overlayStyle }}"
duration="{{ duration }}"
bind:click="onClickOverlay"
/>
<view
wx:if="{{ inited }}"
class="custom-class {{ classes }} {{ utils.bem('popup', [position, { safe: isIPhoneX && safeAreaInsetBottom }]) }}"
style="z-index: {{ zIndex }}; -webkit-transition-duration:{{ currentDuration }}ms; transition-duration:{{ currentDuration }}ms; {{ display ? '' : 'display: none;' }} {{ customStyle }}"
bind:transitionend="onTransitionEnd"
>
<view wx:if="{{ safeAreaInsetTop }}" class="van-popup__safe-top" style="padding-top: {{ statusBarHeight }}px;">view>
<slot />
view>
van-overlay
是这个组件库的另一个组件,这里用来实现遮罩层的效果。
这里的双括号{{}}
里面的变量都由下面的index.js
来控制,留意到这个wxml除了被index.js
控制,还引入了/wxs/utils.wxs
,前往utils.wxs
查看,可以看见如下代码:
var bem = require('./bem.wxs').bem;
var memoize = require('./memoize.wxs').memoize;
function isSrc(url) {
return url.indexOf('http') === 0 || url.indexOf('data:image') === 0 || url.indexOf('//') === 0;
}
module.exports = {
bem: memoize(bem),
isSrc: isSrc,
memoize: memoize
};
这里有个判断url属性的函数isSrc()
,还引用了bem
和memioze
的函数,其中bem
是一个命名格式,将输入统一成bem的风格,memioze
用来处理缓存,返回一个装有对应函数的缓存。
function memoize(fn) {
var cache = {};
return function() {
var key = serializer(arguments);
if (cache[key] === undefined) {
cache[key] = call(fn, arguments);
}
return cache[key];
};
}
memioze
用于将bem
函数装入utils.wxs
的bem
里面,这个bem
在index.wxml
里面被调用,如下面,用于处理class的名字的处理拼接。
class="custom-class {{ classes }} {{ utils.bem('popup', [position, { safe: isIPhoneX && safeAreaInsetBottom }]) }}"
总的来说,这个文件用于将API和调用转换为对应的class name,以此实现特定的css呈现效果。
import { VantComponent } from '../common/component';
import { transition } from '../mixins/transition';
import { safeArea } from '../mixins/safe-area';
VantComponent({
classes: [
'enter-class',
'enter-active-class',
'enter-to-class',
'leave-class',
'leave-active-class',
'leave-to-class'
],
mixins: [transition(false), safeArea()],
props: {
transition: {
type: String,
observer: 'observeClass'
},
customStyle: String,
overlayStyle: String,
zIndex: {
type: Number,
value: 100
},
overlay: {
type: Boolean,
value: true
},
closeOnClickOverlay: {
type: Boolean,
value: true
},
position: {
type: String,
value: 'center',
observer: 'observeClass'
}
},
created() {
this.observeClass();
},
methods: {
onClickOverlay() {
this.$emit('click-overlay');
if (this.data.closeOnClickOverlay) {
this.$emit('close');
}
},
observeClass() {
const { transition, position } = this.data;
this.updateClasses(transition || position);
if (transition === 'none') {
this.set({ duration: 0 });
}
}
}
});
首先是VantComponent
,这个是一个函数声明,没有包含任何的具体实现。
declare function VantComponent<Data, Props, Watch, Methods, Computed>(
vantOptions?: VantComponentOptions<Data, Props, Watch, Methods, Computed, CombinedComponentInstance<Data, Props, Watch, Methods, Computed>>
): void;
与之类似,transition
和safeArea
也只是函数的格式声明,没有任何的实现代码,具体的实现都在这个index.js
文件。
接着上面的说明,VantComponent
在这个index.js
进行了具体的定义,定义了classes
mixins
props
created()
和methods
,这里可以很明显的看出vue
的风格,具体的原理可以查看vue
对应的教程。这个函数实现了将本框架的特定字诸如vant-popup
用基于vue
的微信前端框架来解释和实现。
总的来说,vant-ui
是一个比较优秀的开源前端组件库,可以实现绝大多数的普通需求,用户在使用的时候也可以通过修改源码来实现自己的特定需求。如果说有什么不足之处,那就是css在设计的时候使用的单位都是px,没有一处是使用rpx这个单位,这个会造成组件在不同终端的显示的大小不能随终端而自我调整,希望可以在接下来的更新能有所修改。