大家好,这是小程序系列的第十五篇文章,在这一个阶段,我们的目标是 由简单入手,逐渐的可以较为深入的了解组件化开发,从本文开始,将记录分享lin-ui的源码分析,期望通过对lin-ui源码的学习能加深组件化开发的能力:
1.《微信小程序-进阶篇》package.json必须掌握的字段知识(二)
2.《微信小程序-进阶篇》Lin-ui组件库的安装与引入
3.《微信小程序-进阶篇》Lin-ui组件库源码分析-Button组件(一)
——
求关注,求收藏,求点赞,这是一个系列文章,建议专栏收藏,肯定分享的都是跟小程序相关的,旨在能提高在小程序中的能力,谢谢~
上一篇我们介绍了Button组件,我们从文件结构的角度大致预览了一遍Button组件的实现,了解了button组的文件结构以及属性中样式属性的实现,本篇主要的内容为Button组件的功能属性以及事件相关知识~
本文难度属于:中级,通过本文你可以了解到 Lin-ui的Button组件中功能属性以及事件在其背后是如何运转的一些知识,本文主要内容参考以下思维导图:
这类属性的实现难度往往要 大于 单纯的 样式属性,它除了 样式进行变化,还会涉及要一些功能的变动,接下来我们一个一个来学习lin-ui中的Button的功能属性:
loading属性
一共有两部分实现,第一部分为 css,这部分主要承担的职能为 样式 以及 动画 的实现,第二部分为 逻辑代码,主要实现的功能是当value值为 true/false 的时候,dom元素 显示/隐藏,先看第一部分,以下就是Lin-ui中CSS部分的源码
// Loading
&-loading{
opacity: 0.6;
display: inline-block;
vertical-align: middle;
width: 24rpx;
height: 24rpx;
background: transparent;
border-radius: 50%;
border: 4rpx solid @btn-loading-color;
border-color: @btn-loading-color @btn-loading-color @btn-loading-color transparent;
animation: btn-spin 0.6s linear;
animation-iteration-count: infinite;
}
@keyframes btn-spin {
0% {
transform: rotate(0);
}
100% {
transform: rotate(360deg);
}
}
我们可以看到 loading
的动画就是执行了一个名为 btn-spin
的 animation,而这个 animation 的动画效果为 做360度的旋转,持续时间为0.6s;
逻辑代码则就是一个 wx:if
的判断
<view wx:if="{{loading}}" class="l-btn-loading {{'margin-' + size}} {{ plain ?'l-btn-loading-' + type : '' }}">view>
我们可以看到当loading的值为true的时候,显示这个带有动画的DOM,为false的时候则销毁;
思考
当看到这里的时候,其实我个人感觉 loading这一块应该单独拆分成一个组件,因为loading效果个人觉得这是一个非常常见的效果,不应该仅仅只在button中出现,至少包括请求时的遮罩层上的loading动画;
因此比较合适的做法是 为了保证组件库中动画的一致性,loading组件,或者至少说loading的动画应该是 统一管理 的,而使用的时候应该类似以下这种,通过参数将显示不同的loading动画
<l-loading animate="{{type}}">l-loading>
当然,这个仅仅是针对button组件阅读的思考,可能会作者有别的考量没有这么实现,或者说这种做法有别的弊端是博主没有考虑到的;
disabled属性
同样有两部分组成,一部分 css,这部分包括了当添加disabled样式时按钮显示的状态,另外一部分则为 逻辑代码,这部分 其实和loading的实现接近,也就是 disabled
为true的时候触发效果,先看css代码
// Disabled
&-disabled{
opacity: 0.8;
}
css的代码一共只有以上者一条,就是说当处于 disabled
状态时 button
整体的透明度改成0.8,接着看判断代码
view>
它在模版中写了一个 三元表达式,通过判断 disabled
的值是否为 true 来决定是否将 l-btn-disabled
这个class类名添加到DOM上;
这个属性的作用为 是否禁用禁用 hover 效果,实现机制与 disabled
几乎完全一致,简单的说就是hover的样式单独写在了 btn-hover
和 l-hover-class
里,当 disabled
或者 disabledHover
为true的时候,将这个类名去掉,相当于去掉了带有hover效果的样式
<view class="l-btn "
hover-class="{{disabled?'':disabledHover?'':'btn-hover l-hover-class'}}"
>
view>
Button的插槽一共 只有一个,而且还是默认的,Lin-Ui中直接将它放在了icon组件的后面,如下代码所示
<view class="l-btn {{ 'l-btn-' + size }} {{ 'l-btn-' + type }} {{ 'l-btn-' + shape }} {{plain?'l-btn-plain':''}} {{ disabled ? 'l-btn-disabled' : ''}} l-class "
hover-class="{{disabled?'':disabledHover?'':'btn-hover l-hover-class'}}"
hover-stop-propagation="{{hoverStopPropagation}}"
hover-start-time="{{hoverStartTime}}"
hover-stay-time="{{hoverStayTime}}"
style="{{width?'min-width:'+width+'rpx;':''}} {{height?'height:'+height+'rpx;'+'line-height:'+height+'rpx;':''}} {{size=='long'?'border-radius:0;':''}} {{'background-color:'+bgColor}}">
<view wx:if="{{loading}}" class="l-btn-loading {{'margin-' + size}} {{ plain ?'l-btn-loading-' + type : '' }}"></view>
<l-icon l-class="l-icon-class" class="{{'margin-' + size}}" wx:if="{{icon}}" name="{{icon}}" color="{{iconColor}}" size="{{iconSize}}" />
<slot/>
</view>
这样也就实现了当输入文字的时候,文字出现的位置在loading动画图表的后方
<l-button loading="{{true}}">测试按钮</l-button>
我们可以做一个实验,调整插槽的位置,我们将插槽的位置调整到loading前面去,如下代码
<view class="l-btn {{ 'l-btn-' + size }} {{ 'l-btn-' + type }} {{ 'l-btn-' + shape }} {{plain?'l-btn-plain':''}} {{ disabled ? 'l-btn-disabled' : ''}} l-class "
hover-class="{{disabled?'':disabledHover?'':'btn-hover l-hover-class'}}"
hover-stop-propagation="{{hoverStopPropagation}}"
hover-start-time="{{hoverStartTime}}"
hover-stay-time="{{hoverStayTime}}"
style="{{width?'min-width:'+width+'rpx;':''}} {{height?'height:'+height+'rpx;'+'line-height:'+height+'rpx;':''}} {{size=='long'?'border-radius:0;':''}} {{'background-color:'+bgColor}}">
<slot/>
<view wx:if="{{loading}}" class="l-btn-loading {{'margin-' + size}} {{ plain ?'l-btn-loading-' + type : '' }}">view>
<l-icon l-class="l-icon-class" class="{{'margin-' + size}}" wx:if="{{icon}}" name="{{icon}}" color="{{iconColor}}" size="{{iconSize}}" />
view>
那么按钮的文字应该是在loading的动画前面,结果也确实如我们所料
lin-ui中Button组件的事件 常规 的只有1个,叫做:handleTap
,它被绑定在按钮的顶层元素上,位置如下
<label for="{{name}}" mut-bind:tap="handleTap" class="l-label-class">
// 其它代码
</label>
绑定的方式则通过 mut-bind
来绑定(如果不清楚mut-bind
,bind
,catch
等关键词代表的含义,可以查看博主的另外一篇博文:《微信小程序-基础篇》小程序中的事件与冒泡),handleTap
实现代码如下,也是非常简单的:
methods: {
// button点击事件
handleTap() {
if (this.data.disabled || this.data.loading) return false;
this.triggerEvent('lintap', {}, {
bubbles: true,
composed: true
});
},
// 开放能力事件回调
openTypeEvent(data) {
this.triggerEvent(data.type, data.detail, {});
}
}
代码首先判断了当前组件的状态,如果当前组件处于disabled或者loading状态时,按钮不可点击,接着就是触发了 lintap
这个 自定义事件 了,这里我们知道了如果想要触发点击事件,比如通过如下方式触发
<l-button bind:lintap="handleClick"><l-button>
既然知道了事件名是在这里写入的,那么我们理所当然的也可以修改,比如我们将自定义事件名改为tTap
,如下
<l-button bind:tTap="handleClick"><l-button>
methods: {
// button点击事件
handleTap() {
if (this.data.disabled || this.data.loading) return false;
this.triggerEvent('tTap', {}, {
bubbles: true,
composed: true
});
},
// 开放能力事件回调
openTypeEvent(data) {
this.triggerEvent(data.type, data.detail, {});
}
}
点击后如期打印了结果,符合我们的猜想
看到这里,可能有小伙伴会问,官网上的时间不是挺多的吗,怎么只介绍了一个 lintap
,其实剩下的那些事件都属于开放能力,lin-ui也只是做了一层转发,实际上都是小程序官方提供的能力,源码中我们也没有看到对对应的事件有任何其他操作;
本文主要讲述了Lin-UI的几个功能属性的实现,其实还是挺简单的,说到底就是 通过三元表达式判断了一下样式或者DOM是否显示,接着我们看了下Button中的 插槽,它仅有一个插槽用作文字的录入,最后是事件,我们通过 分析了事件的代码并且尝试修改了自定义事件名;
(PS:都已经看到这里了,点个赞,求个关注吧,万分感谢~)