作為從事前端開發的(de)你(ni)肯定(ding)見過(guo)不少(shao)的(de)彈框(kuang)組件,你(ni)可曾(ceng)有想過(guo)要自(zi)己實現一個彈框(kuang)組件庫,又或(huo)者想完全定(ding)制(zhi)化的(de)使用各種標準UI框(kuang)架中的(de)彈框(kuang)組件呢(ni)?
今(jin)天這(zhe)篇文(wen)章將(jiang)會帶著你解析這(zhe)一系列疑問(wen),以(yi)vant-weapp組(zu)件(jian)庫為例,從開發標準的彈窗(chuang)組(zu)件(jian)使用(yong)到高度(du)定制復合自我審美的彈窗(chuang),再到完全研究清楚vant-weapp框架(jia)彈窗(chuang)組(zu)件(jian)部分源碼。
vant-weapp組(zu)件(jian)(jian)(jian)庫(ku)是(shi)有贊團隊開發的 一款靈(ling)活簡(jian)潔且美觀(guan)的小程序UI組(zu)件(jian)(jian)(jian)庫(ku) ,此(ci)文將以這個組(zu)件(jian)(jian)(jian)庫(ku)的用(yong)法(fa)為標準,下文提及(ji)的彈框組(zu)件(jian)(jian)(jian)均(jun)指的是(shi)此(ci)組(zu)件(jian)(jian)(jian)庫(ku)中的彈框。
vant-weapp中彈(dan)框主要分為(wei)**兩大(da)類:彈(dan)出層Popup和(he)(he)對(dui)話框Dialog,**彈(dan)出層一(yi)般是帶(dai)有(you)背景遮罩層和(he)(he)內容展示區域用(yong)于在不(bu)跳轉頁面情況下(xia)進行詳(xiang)情的(de)展示作(zuo)用(yong),對(dui)話框多數用(yong)于帶(dai)有(you)詳(xiang)情展示的(de)同時還(huan)帶(dai)有(you)希望用(yong)戶(hu)確認等操作(zuo)。如(ru)下(xia)圖所(suo)示,圖左為(wei)典(dian)型(xing)的(de)Dialog,圖右為(wei)典(dian)型(xing)的(de)Popup。
在使(shi)用彈(dan)框組件(jian)(jian)(jian)之前(qian)記得在小程序的(de)app.json文(wen)件(jian)(jian)(jian)中先注冊組件(jian)(jian)(jian),詳細介(jie)紹見 快速上手(shou) ,例如注冊van-popup組件(jian)(jian)(jian)代碼如下:
// app.json
"usingComponents": {
"van-popup": "path/to/@vant/weapp/dist/popup/index"
}
復制代碼
在項目中實際使用如下:
在本文(wen)后續分析van-dialog源碼(ma)中會發現在dialog的(de)index.json中也定義過(guo)van-popup組(zu)件(jian)(jian)(jian),但是我們要直接實行van-popup組(zu)件(jian)(jian)(jian)必須在小(xiao)程序的(de)配置文(wen)件(jian)(jian)(jian)app.json中按照(zhao)上(shang)圖方式進(jin)行定義,微信(xin)小(xiao)程序官網(wang)說明(ming)過(guo) 自定義組(zu)件(jian)(jian)(jian)內(nei)部的(de)引入組(zu)件(jian)(jian)(jian)只在該(gai)組(zu)件(jian)(jian)(jian)內(nei)生效
注冊完(wan)組(zu)(zu)件(jian)之后,就可以(yi)直接在小程(cheng)序頁面(mian)中(zhong)使用這里注冊的自定義組(zu)(zu)件(jian),組(zu)(zu)件(jian)名稱為這里 key ,例如:。
最常見(jian)的用法就(jiu)是(shi)直接使用van-popup組(zu)件(jian)(jian),通過(guo)組(zu)件(jian)(jian)的show屬(shu)性來控制其是(shi)否展示(shi),組(zu)件(jian)(jian)內部嵌套的其他組(zu)件(jian)(jian)或標簽(qian)是(shi)popup組(zu)件(jian)(jian)的內容,如(ru)下(xia)所示(shi):
// wxml
<button bindtap="showPopup">展示彈出層</button>
<van-popup
show="{{ show }}"
position="top"
bind:close="onClose"
closeable
>內容</van-popup>
// js
Page({
data: {
show: false
},
showPopup() {
this.setData({ show: true });
},
onClose() {
this.setData({ show: false });
}
});
復制代碼
van-popup組(zu)件可以通過position屬性(xing)的五(wu)個值: center、top、right、bottom、left 來快(kuai)捷的控制是(shi)從哪個位置(zhi)彈出(chu),例如:上例中的彈框從上往下彈出(chu)
可(ke)以通過round屬性(xing)來(lai)控制彈(dan)窗內(nei)容是否(fou)顯(xian)示(shi)圓角,closeable可(ke)以決定是否(fou)顯(xian)示(shi)關閉(bi)彈(dan)框的圖標按鈕(niu),例如:上例中的彈(dan)窗將(jiang)不(bu)顯(xian)示(shi)圓角,同時顯(xian)示(shi)關閉(bi)按鈕(niu)
各種基本的彈窗形式如下(xia):
對話框則是在popup彈出層的(de)基礎上添加了額外(wai)的(de)內置的(de)標題,快速確定按鈕(niu)等組(zu)件,用(yong)(yong)于消息提示、消息確認等場景,下面看看其常見用(yong)(yong)法。
最常規的用法(fa)就是(shi)直接(jie)使用van-dialog組(zu)(zu)件,通過組(zu)(zu)件的show屬性來控制其是(shi)否展示(shi),組(zu)(zu)件內部嵌套的其他(ta)組(zu)(zu)件或標簽是(shi)dialog組(zu)(zu)件的內容,如下所示(shi):
// wxml
<van-dialog
title="標題"
message="代碼是寫出來給人看的,附帶能在機器上運行"
show="{{ show }}"
confirm-button-open-type="getUserInfo"
bind:close="onClose"
bind:getuserinfo="getUserInfo"
>
<image src="//img.yzcdn.cn/1.jpg" />
</van-dialog>
// js
Page({
data: {
show: true
},
getUserInfo(event) {
console.log(event.detail);
},
onClose() {
this.setData({ close: false });
}
});
復制代碼
直接(jie)使(shi)用van-dialog組(zu)件(jian),通過組(zu)件(jian)的(de)show屬性(xing)來控制其是(shi)否(fou)展示,組(zu)件(jian)內部嵌套的(de)其他(ta)組(zu)件(jian)或(huo)標簽是(shi)dialog組(zu)件(jian)的(de)內容,不(bu)使(shi)用use-title-slot且不(bu)傳遞title屬性(xing),如下所示:
// wxml
<van-dialog
show="{{ show }}"
confirm-button-open-type="getUserInfo"
bind:close="onClose"
bind:getuserinfo="getUserInfo"
>
<view class="message">代碼是寫出來給人看的,附帶能在機器上運行</view>
</van-dialog>
// js
Page({
data: {
show: true
},
getUserInfo(event) {
console.log(event.detail);
},
onClose() {
this.setData({ close: false });
}
});
復制代碼
上述兩種用法中的use-slot屬性表示使用默認的slot(即van-dialog嵌套的wxml內容,比如此處的
最常規的(de)(de)另一種用(yong)法就是直(zhi)接使用(yong) Dialog、Dialog.alert、Dialog.confirm 的(de)(de)方法快速打(da)開彈窗組(zu)(zu)件(jian),關(guan)閉彈框組(zu)(zu)件(jian)則通過 Dialog.close ,取消(xiao)彈框的(de)(de)加載狀(zhuang)態則使用(yong) Dialog.stopLoading,組(zu)(zu)件(jian)內部嵌套的(de)(de)其他(ta)組(zu)(zu)件(jian)或標簽(qian)是dialog組(zu)(zu)件(jian)的(de)(de)內容(rong),如下所示:
// wxml
<van-dialog id="van-dialog">
import Dialog from 'path/to/@vant/weapp/dist/dialog/dialog';
// js
Dialog.alert({
title: "標題"
message: '代碼是寫出來給人看的,附帶能在機器上運行'
}).then(() => {
// on close
});
復制代碼
這里使(shi)用(yong)函數(shu)調(diao)用(yong)一定要(yao)注意在使(shi)用(yong)van-dialog的頁面的wxml中一定需要(yao)寫這個來(lai)使(shi)用(yong)組(zu)件(jian),下文在分析(xi)dialog的源碼中會講到(dao)(賣個關子),或者你可以先猜一猜:blush::blush:
上面三種(zhong)van-dialog的(de)(de)常規(gui)使用(yong)方(fang)法的(de)(de)效果如下:
下面將會(hui)提供幾(ji)個作者(zhe)在(zai)實(shi)戰(zhan)中寫出的Dialog對話框組件的實(shi)戰(zhan)用法
<van-dialog
id="van-dialog"
show="{{ dialogShow }}"
message="資質原件拍照或掃描可以不加蓋公章,復印件需蓋章\n\n如是三證合一,則無需提供稅務登記證、組織機構代碼證"
message-align="left"
confirm-button-text="知道了"
confirm-button-color="#EE712F"
use-title-slot
>
<view slot="title" class=" merchant-dialog__title">
<view class="merchant-dialog__title-text">**前,請準備以下資料</view>
<van-icon name="cross" size="40rpx" class="merchant-dialog__title-icon" bindtap="closeDialog" />
</view>
</van-dialog>
// 樣式部分的代碼此處省略
復制代碼
觸發彈框顯示
handleButtonClick1: function () {
this.setData({
dialogShow: true
})
},
復制代碼
此例子(zi)如(ru)要(yao)使用了如(ru)下特性:
use-title-slot confirm-button-text、confirm-button-color van-icon
對應的效(xiao)果如下:
<van-dialog
id="van-dialog-2"
use-slot
use-title-slot
>
<view slot="title" style="padding-bottom: 10px;">
<van-icon name="close" color="#fff" size="30" bindtap="closeDialog2" />
</view>
<image class="image" src="//tva1.sinaimg.cn/large/0082zybply1gbylbcwm44j30rs13bdsg.jpg" mode="aspectFit"></image>
</van-dialog>
復制代碼
通過觸發彈框顯示
handleButtonClick2: function () {
Dialog({
selector: '#van-dialog-2',
showConfirmButton: false,
closeOnClickOverlay: false,
className: 'dialog2',
width: '260px'
})
},
復制代碼
此例(li)子(zi)如(ru)要(yao)使用了(le)如(ru)下特性:
對應(ying)效(xiao)果如下:
<van-dialog
id="van-dialog-3"
use-title-slot
>
<view slot="title" style="color: #000;">提示</view>
<view>
<view>為了給你推薦更合適的漫展~</view>
<view>請開啟定位權限~</view>
</view>
</van-dialog>
復制代碼
通(tong)過觸發彈(dan)框(kuang)顯示
handleButtonClick3: function () {
Dialog({
selector: '#van-dialog-3',
showCancelButton: true,
cancelButtonTrext: '取消',
confirmButtonText: '去設置',
cancelButtonColor: '#C46B85',
confirmButtonColor: '#C46B85',
message: '為了給你推薦更合適的漫展~\n請開啟定位權限~',
confirmButtonOpenType: 'openSetting',
width: '260px',
className: 'dialog3'
})
},
復制代碼
外部樣式類
.dialog-index--dialog3 {
--dialog-background-color: rgba(255,255,255,0.8);
--popup-background-color: rgba(255,255,255,0.8);
--button-default-background-color: transparent;
color: #666;
}
復制代碼
此例(li)子(zi)如(ru)要使用了如(ru)下特性:
cancelButtonColor、confirmButtonColor --dialog-background-color
對應效果如下:
如果你仔(zi)細看過(guo)上面(mian)中的(de)(de)(de)三(san)種(zhong)自(zi)定義方式的(de)(de)(de)實現(xian)代碼應(ying)該也可以(yi)根(gen)據(ju)UI需求實現(xian)自(zi)己的(de)(de)(de)彈(dan)窗交(jiao)互效果;這里(li)我(wo)已經基于前(qian)面(mian)提(ti)到(dao)的(de)(de)(de)三(san)種(zhong)用法來開發了幾個(ge)實際場景中的(de)(de)(de)彈(dan)框組件:
這部分的可以直(zhi)接去看源碼
也可以掃(sao)碼(ma)這個(ge)小程序二維(wei)碼(ma)查看效果
在看完上面幾(ji)種炫酷的(de)彈(dan)框(kuang)(kuang)效果后,我們還是(shi)按照慣例研(yan)究(jiu)下(xia)如(ru)此強(qiang)大的(de)彈(dan)框(kuang)(kuang)組件的(de)源碼。在研(yan)究(jiu)彈(dan)框(kuang)(kuang)部(bu)分源碼之前有必有分析一下(xia)一套完整UI框(kuang)(kuang)架(jia)所需(xu)要注意的(de)框(kuang)(kuang)架(jia)級別的(de)整體架(jia)構
處理樣式是所有UI框架比不可(ke)忽略的核心邏輯之(zhi)一,在(zai)vant-weapp中對樣式的處理主(zhu)(zhu)要分為以下三部分;源碼對應結構如下圖所示,使用less的mixins復用實現主(zhu)(zhu)題變量控制、公共樣式抽離(li)等。
在var.less文件定義了(le)框架所用(yong)到的(de)全部的(de)樣式控制相關的(de)變量,其中與(yu)彈框相關的(de)部分源(yuan)碼(ma)如下:
// Dialog @dialog-width: 320px; @dialog-small-screen-width: 90%; @dialog-font-size: @font-size-lg; @dialog-border-radius: 16px; @dialog-background-color: @white; @dialog-header-font-weight: @font-weight-bold; @dialog-header-line-height: 24px; @dialog-header-padding-top: @padding-lg; @dialog-header-isolated-padding: @padding-lg 0; @dialog-message-padding: @padding-lg; @dialog-message-font-size: @font-size-md; @dialog-message-line-height: 20px; @dialog-message-max-height: 60vh; @dialog-has-title-message-text-color: @gray-7; @dialog-has-title-message-padding-top: @padding-sm; 復制代碼
源碼:
此文件中(zhong)的最終(zhong)會(hui)轉換(huan)成 css變(bian)量 ,并(bing)非像antd、iview等網(wang)頁端框架(jia)中(zhong)的樣(yang)式處理那樣(yang)編(bian)譯(yi)成變(bian)量指向的值。根據(ju)css變(bian)量作(zuo)用域的特性,可以在(zai)自(zi)定義組件的外部(bu)樣(yang)式類中(zhong)局(ju)部(bu)覆蓋樣(yang)式變(bian)量來改變(bian)組件內部(bu)的樣(yang)式。
像清(qing)除浮動、文字省略、1像素邊框等通(tong)用的(de)樣式類(lei)的(de)處理在mixin文件(jian)夾下
.clearfix() {
&::after {
display: table;
clear: both;
content: '';
}
}
復制代碼
使用常(chang)見的after偽類來(lai)實現清除浮(fu)動
.multi-ellipsis(@lines) {
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: @lines;
/* autoprefixer: ignore next */
-webkit-box-orient: vertical;
}
.ellipsis() {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
復制代碼
使用(yong)less的函(han)數封裝了兩個處理文(wen)字(zi)省(sheng)略方(fang)法:單行省(sheng)略、多行省(sheng)略
微(wei)信小程序官方(fang)提供(gong)了(le) Component構造方(fang)法 注冊自(zi)定(ding)義組件,為了(le)結合typescript給(gei)自(zi)定(ding)義組件提供(gong)更靈活(huo)強大(da)的(de)組件注冊器(qi)對(dui)Component進行(xing)了(le)下面(mian)的(de)功能封裝處理
function VantComponent<Data, Props, Methods>(
vantOptions: VantComponentOptions<
Data,
Props,
Methods,
CombinedComponentInstance<Data, Props, Methods>
> = {}
): void {
const options: any = {};
mapKeys(vantOptions, options, {
data: 'data',
props: 'properties',
mixins: 'behaviors',
methods: 'methods',
beforeCreate: 'created',
created: 'attached',
mounted: 'ready',
relations: 'relations',
destroyed: 'detached',
classes: 'externalClasses'
});
const { relation } = vantOptions;
if (relation) {
makeRelation(options, vantOptions, relation);
}
// 給所有組件添加默認外部樣式類custom-class
options.externalClasses = options.externalClasses || [];
options.externalClasses.push('custom-class');
// 給所有組件添加默認behaviors
options.behaviors = options.behaviors || [];
options.behaviors.push(basic);
// map field to form-field behavior
if (vantOptions.field) {
options.behaviors.push('wx://form-field');
}
// 默認啟用多slot支持、組件中允許全局樣式修改
options.options = {
multipleSlots: true,
addGlobalClass: true
};
// 最終使用官網構造方法構造組件
Component(options);
}
復制代碼
源碼:
behaviors 是微信小程序官(guan)方用(yong)(yong)于組件復用(yong)(yong) data、methods 等屬性方法的(de)(de)一種方式,和vue中的(de)(de) mixins 小作用(yong)(yong)一致,vant-weapp中定義的(de)(de) mixins 如下(xia)圖所示:
其中(zhong)basic是所有(you)自定義組(zu)件(jian)(jian)都復用的(de)一個(ge)(ge)mxin,給所有(you)自定義的(de)組(zu)件(jian)(jian)提供(gong)了三(san)個(ge)(ge)方法: $emit、 set 和 getRect 。
源碼如下:
// basic.ts
export const basic = Behavior({
methods: {
$emit(...args) {
this.triggerEvent(...args);
},
set(data: object, callback: Function) {
this.setData(data, callback);
return new Promise(resolve => wx.nextTick(resolve));
},
getRect(selector: string, all: boolean) {
return new Promise(resolve => {
wx.createSelectorQuery()
.in(this)[all ? 'selectAll' : 'select'](selector)
.boundingClientRect(rect => {
if (all && Array.isArray(rect) && rect.length) {
resolve(rect);
}
if (!all && rect) {
resolve(rect);
}
})
.exec();
});
}
}
});
復制代碼
源碼:
其實(shi)生命(ming)周期如(ru)何命(ming)名到不是很(hen)重要(yao),vant-weapp對(dui)命(ming)名進行了轉換主(zhu)要(yao)基于以下兩(liang)個(ge)原因:
function mapKeys(source: object, target: object, map: object) {
Object.keys(map).forEach(key => {
if (source[key]) {
target[map[key]] = source[key];
}
});
}
mapKeys(vantOptions, options, {
data: 'data',
props: 'properties',
mixins: 'behaviors',
methods: 'methods',
beforeCreate: 'created',
created: 'attached',
mounted: 'ready',
relations: 'relations',
destroyed: 'detached',
classes: 'externalClasses'
});
復制代碼
源碼:
通過(guo) mapKeys 方法(fa)對 VantComponent 中傳入的生命(ming)周期函數進行了轉(zhuan)換,轉(zhuan)換名生命(ming)周期名稱與微信小程序一致
微信(xin)小程序自定義組(zu)(zu)件(jian)(jian)默認(ren)樣(yang)式作用域的范圍(wei)是為當前組(zu)(zu)件(jian)(jian),也就是說組(zu)(zu)件(jian)(jian)文(wen)件(jian)(jian)夾下的wxss中的樣(yang)式只(zhi)對該文(wen)件(jian)(jian)夾下的wxml生效(xiao)(除去標(biao)簽名、ID選擇(ze)器)
這種以組件(jian)(jian)為單位進行樣式(shi)(shi)隔離(li)的(de)模式(shi)(shi)類似(si)于React框(kuang)架(jia)中處理的(de)組件(jian)(jian)樣式(shi)(shi)的(de)庫
要在組(zu)件之前共(gong)享(xiang)樣(yang)(yang)式或(huo)者讓自定義(yi)組(zu)件接受外(wai)部樣(yang)(yang)式,可行(xing)方案(an)有(you)如下幾種:
| styleIsolation屬性配置 |
使用(yong)(yong)(yong)vant-weapp組(zu)件(jian)庫(ku)的使用(yong)(yong)(yong)者最(zui)佳的自定義組(zu)件(jian)樣式(shi)的方式(shi)是: 采用(yong)(yong)(yong)外部樣式(shi)類(lei)+CSS變量,在無相關(guan)CSS變量時才用(yong)(yong)(yong)自己(ji)的樣式(shi)+ !important 確保樣式(shi)優先級(ji) ,在自定義組(zu)件(jian)中使用(yong)(yong)(yong)vant-weapp的組(zu)件(jian)時候(hou)的注意事項(xiang)參照 樣式(shi)覆(fu)蓋 。
自(zi)(zi)定義(yi)組件(jian)(jian)(jian)(jian)(jian)通信(xin)主要包(bao)括(kuo) 組件(jian)(jian)(jian)(jian)(jian)參(can)數傳(chuan)遞 和 事(shi)(shi)(shi)件(jian)(jian)(jian)(jian)(jian)監聽 ,這兩個(ge)功(gong)能(neng)都是微信(xin)小(xiao)程序官網(wang)提(ti)供的(de);參(can)數傳(chuan)遞是由父傳(chuan)到子的(de)單(dan)向傳(chuan)遞,而事(shi)(shi)(shi)件(jian)(jian)(jian)(jian)(jian)監聽則(ze)是相應原生事(shi)(shi)(shi)件(jian)(jian)(jian)(jian)(jian)或(huo)者自(zi)(zi)定義(yi)事(shi)(shi)(shi)件(jian)(jian)(jian)(jian)(jian)。自(zi)(zi)定義(yi)事(shi)(shi)(shi)件(jian)(jian)(jian)(jian)(jian)用于對組件(jian)(jian)(jian)(jian)(jian)的(de)事(shi)(shi)(shi)件(jian)(jian)(jian)(jian)(jian)進行封裝,自(zi)(zi)定義(yi)事(shi)(shi)(shi)件(jian)(jian)(jian)(jian)(jian)機制(zhi)如下(xia):
這里在van-dialog組(zu)件(jian)(jian)使(shi)用位置監聽bindclick事(shi)件(jian)(jian),最終這個(ge)事(shi)件(jian)(jian)會在van-dialog組(zu)件(jian)(jian)內部的(de)button的(de)tap時(shi)被(bei)觸發,后面源(yuan)碼分(fen)析中的(de)自定義(yi)組(zu)件(jian)(jian)的(de)自定義(yi)事(shi)件(jian)(jian)全部采用的(de)此種模(mo)式。
popup組件部分源碼結構如(ru)下:
組件(jian)的命名規范(fan)與微信小程序自(zi)定義組件(jian)的規范(fan)相符合(README.md為組件(jian)的使用(yong)說(shuo)明(ming)文檔,用(yong)于生成官網(wang)的組件(jian)文檔說(shuo)明(ming))。
popup組(zu)件(jian)的配(pei)置文件(jian)標識當(dang)前的index為組(zu)件(jian),通過 using-components 引入了 van-icon 和 van-overlay 組(zu)件(jian),在對應(ying)的wxml中可以(yi)直接使用(yong)。
彈出層組件(jian)主要分類 遮蓋(gai)(gai)層 和(he) 內容層 ,內容層嵌套在遮蓋(gai)(gai)層內部來(lai)確(que)保視覺上覆蓋(gai)(gai)在遮蓋(gai)(gai)層之上。
遮蓋層通過overlay、overlayStyle等組(zu)件(jian)屬性來(lai)控(kong)制(zhi)其是否顯示以及遮蓋層的(de)樣式等,遮蓋的(de)事(shi)件(jian)有 onClickOverlay ,通過$emit觸發組(zu)件(jian)的(de)自(zi)定義事(shi)件(jian)close。
onClickOverlay() {
this.$emit('click-overlay');
if (this.data.closeOnClickOverlay) {
this.$emit('close');
}
}
復制代碼
通過closable屬(shu)性決定是否(fou)顯示默認(ren)的關(guan)閉(bi)按(an)鈕,也(ye)可以通過關(guan)閉(bi)圖標相(xiang)關(guan)屬(shu)性配置更改按(an)鈕樣式,關(guan)閉(bi)按(an)鈕的事件(jian)有onClickCloseIcon,通過$emit觸發(fa)組件(jian)的自定義事件(jian)close。
onClickCloseIcon() {
this.$emit('close');
},
復制代碼
接受(shou)一個(ge)默認的slot,其(qi)位置根據(ju)傳入的 position 參(can)數不(bu)同有 top、right、bottom、left、center 五種,根據(ju)這五種位置參(can)數有對(dui)應的五種不(bu)同的彈出位置和動畫
使(shi)用transform來實現動畫效果,根據 position 參(can)數的五種情況有五種默認動畫
// popup/index.less
.van-bottom-enter,
.van-bottom-leave-to {
transform: translate3d(0, 100%, 0);
}
.van-top-enter,
.van-top-leave-to {
transform: translate3d(0, -100%, 0);
}
.van-left-enter,
.van-left-leave-to {
transform: translate3d(-100%, -50%, 0);
}
.van-right-enter,
.van-right-leave-to {
transform: translate3d(100%, -50%, 0);
}
復制代碼
同時(shi)暴露了(le)外部樣式類(lei)可(ke)以用來自定(ding)義動(dong)畫,這里動(dong)畫階(jie)段劃(hua)分(fen)和vue相同,分(fen)類(lei): enter、enter-active、enter-to、leave、leave-active、leave-to
// popup/index.ts
VantComponent({
classes: [
'enter-class',
'enter-active-class',
'enter-to-class',
'leave-class',
'leave-active-class',
'leave-to-class'
],
...
}
復制代碼
dialog組件(jian)部分(fen)源(yuan)碼結(jie)構如下(xia):
結構同(tong)popup組(zu)件(jian),不同(tong)點在于index.json使(shi)用(yong)了 van-popup、van-button 組(zu)件(jian),以及多了dialog.ts這個暴露API函數(shu)調(diao)用(yong)方法的文件(jian)。
dialog組件整體基于popup組件,在其(qi)默認slot中添(tian)加了頂部標題的(de)slot和(he)按鈕(niu)組元素,大致結構(gou)如下
源碼結構:
// dialog/index.wxml
<van-popup
show="{{ show }}"
...
>
<view
wx:if="{{ title || useTitleSlot }}"
class="van-dialog__header {{ message || useSlot ? '' : 'van-dialog--isolated' }}"
>
<slot wx:if="{{ useTitleSlot }}" name="title" />
<block wx:elif="{{ title }}"> {{ title }}</block>
</view>
<slot wx:if="{{ useSlot }}" />
<view
wx:elif="{{ message }}"
class="van-dialog__message {{ title ? 'van-dialog__message--has-title' : '' }} {{ messageAlign ? 'van-dialog__message--' + messageAlign : '' }}"
>
<text class="van-dialog__message-text">{{ message }}</text>
</view>
<view class="van-hairline--top van-dialog__footer">
<van-button
wx:if="{{ showCancelButton }}"
...
>
{{ cancelButtonText }}
</van-button>
<van-button
wx:if="{{ showConfirmButton }}"
...
>
{{ confirmButtonText }}
</van-button>
</view>
</van-popup>
復制代碼
在前(qian)面中(zhong)通(tong)過Dialog函(han)數(shu)調用(yong)來打開彈出框組件(jian),實現(xian)函(han)數(shu)式調用(yong)的核心思(si)路(lu)主要是: 通(tong)過selectComponent(selector)方(fang)法查找(zhao)(類似于查找(zhao)DOM、Vue中(zhong)查找(zhao)組件(jian)實例(li))對(dui)頁面中(zhong)定義渲染好的dialog組件(jian),手動更新其(qi)組件(jian)實例(li)的數(shu)據(ju)。 ** Dialog方(fang)法定義如(ru)下:
const Dialog: Dialog = options => {
options = {
...Dialog.currentOptions,
...options
};
return new Promise((resolve, reject) => {
const context = options.context || getContext();
const dialog = context.selectComponent(options.selector);
delete options.context;
delete options.selector;
if (dialog) {
dialog.setData({
onCancel: reject,
onConfirm: resolve,
...options
});
queue.push(dialog);
} else {
console.warn('未找到 van-dialog 節點,請確認 selector 及 context 是否正確');
}
});
};
復制代碼
**
函數式調(diao)用時候根(gen)據傳入(ru)的options配置去(qu)更新找(zhao)到的組(zu)件實(shi)例上的屬性
由微信小程序(xu)自定義組(zu)件限制不能更新slot,slot需要用組(zu)件嵌套來傳入(ru)
函數式調(diao)用中的(de)options會有(you)默認值強制覆蓋掉van-dialog組件屬(shu)性(xing)(xing)處(chu)傳入(ru)的(de)非id等其他(ta)屬(shu)性(xing)(xing),即函數調(diao)用的(de)時通過組件傳入(ru)的(de)屬(shu)性(xing)(xing)無效
**
確認彈窗
Dialog.confirm({
selector: '#van-dialog',
title: '提示',
message: '這里放置提示內容'
})
復制代碼
Dialog.confirm = options =>
Dialog({
showCancelButton: true,
...options
});
復制代碼
調用Dialog時候默認執行定了顯(xian)示取消(xiao)按鈕,其他無區(qu)別(bie)
關閉彈窗
Dialog.close() 復制代碼
Dialog.close = () => {
queue.forEach(dialog => {
dialog.close();
});
queue = [];
};
復制代碼
遍歷內部緩存(cun)的所(suo)有調用Dialog方(fang)法找到的van-dialog組件實例(li),執行(xing)其close方(fang)法
更改對話框(kuang)默(mo)認配(pei)置
Dialog.setDefaultOptions(options) 復制代碼
Object.assign(Dialog.currentOptions, options); 復制代碼
通過Object.assign將(jiang)傳入的(de)默認配(pei)置合并到內部Dialog.currentOptions配(pei)置上
恢復對話框默認配置
Dialog.resetDefaultOptions() 復制代碼
Dialog.resetDefaultOptions = () => {
Dialog.currentOptions = { ...Dialog.defaultOptions };
};
復制代碼
恢復(fu)Dialog.currentOptions配置為Dialog.defaultOptions
本文(wen)講解了vant-weapp組(zu)件庫中的(de)彈(dan)框組(zu)件的(de)基本用法(fa)、進階用法(fa)、定制(zhi)主題(ti)、自(zi)定義內(nei)容等用法(fa),同時還更(geng)進一步的(de)研究(jiu)了vant-weapp組(zu)件中的(de)popup組(zu)件、dialog組(zu)件的(de)實(shi)現(xian)。也只有徹底(di)弄(nong)懂了UI框架(jia)的(de)封裝(zhuang)思路(lu)我們才能更(geng)進一步的(de)修改框架(jia)來定制(zhi)化更(geng)復雜更(geng)貼合項目要求的(de)各種(zhong)組(zu)件,本文(wen)按照 由實(shi)用到進階再到研究(jiu)源(yuan)碼(ma)(ma) 的(de)思路(lu)為各位研究(jiu)框架(jia)源(yuan)碼(ma)(ma)提供另一種(zhong)方法(fa)。