微信小程序開發03-這是一個組件

編寫組件

基本結構

接上文:微信小程序開發02-小程序基本介紹css

咱們今天先來實現這個彈出層:html

以前這個組件是一個容器類組件,彈出層可設置載入的html結構,而後再設置各類事件便可,這種組件有一個特色:git

① 只提供Header部分以及容器部分github

② 容器部分的HTML結構由業務層提供web

③ 容器部分對應樣式由業務層提供小程序

咱們若是要在小程序中實現這類組件,意味着咱們須要往小程序中動態插入WXML結構,咱們這裏先作個demo,試試往動態插入WXML是否是可行微信小程序

1 this.setData({'wxml': `
2   <my-component>
3   <view>動態插入的節點</view> 
4   </my-component>
5 `});

小程序對應設置的數據進行了轉義,因此並不能動態解析,若是站在性能角度思考,不進行動態解析也不是錯誤的;另外一方面,一旦小程序能動態解析wxml,那麼可能會涌出各類花式用法,控制力會減低,那麼咱們這裏如何解決這個問題呢?微信

我想的是,直接將業務級wxml結構放到頁面裏面,隱藏起來,須要使用彈出層的時候,直接將之裝載進去,咱們來看看是否可行,咱們將咱們須要展現的結構放到一個模板當中:dom

1 <template name="searchbox">
2   <my-component>
3       <view>動態組件部分</view> 
4   </my-component>
5 </template>

而後,咱們在咱們主界面中載入模板:xss

 1 <import src="mod.searchbox.wxml"/>
 2 <view>
 3   <my-component>
 4     <!-- 這部份內容將被放置在組件 <slot> 的位置上 -->
 5      <view>這裏是插入到組件slot中的內容</view> 
 6   </my-component>
 7 </view>
 8 <view>
 9   <template is="searchbox" />
10 </view>

主體結構放到頁面中,咱們傳入數據模型或者控制顯示便可,看起來是可行的,因而咱們先實現咱們基本的樣式,由於業務模塊的樣子應該由業務提供,因此對應樣式寫到index.wxss裏面:

 1 .btn-primary {
 2     background-color: #00b358;
 3     color: #fff;
 4     border: 0 none;
 5 }
 6 .btn, .btn-primary, .btn-secondary, .btn-sub {
 7     line-height: 88rpx;
 8     height: 88rpx;
 9     padding: 0 20rpx;
10     display: inline-block;
11     vertical-align: middle;
12     text-align: center;
13     border-radius: 8rpx;
14     cursor: pointer;
15     font-size: 32rpx;
16     -webkit-box-sizing: border-box;
17     box-sizing: border-box;
18 }
19 .full-width {
20     width: 100%;
21     -webkit-box-sizing: border-box;
22     box-sizing: border-box;
23 }
24 
25 
26 .c-row {
27     width: auto;
28     display: -webkit-box;
29     -webkit-box-orient: horizontal;
30     -webkit-box-direction: normal;
31     -webkit-box-pack: justify;
32     -webkit-box-align: stretch;
33     -webkit-box-lines: single;
34     display: -webkit-flex;
35     -webkit-flex-direction: row;
36     -webkit-justify-content: space-between;
37     -webkit-align-items: strecth;
38     -webkit-align-content: flex-start;
39     -webkit-flex-wrap: nowrap;
40     padding: 20rpx 40rpx;
41 }
42 
43 .c-span3 {
44     width: 25%;
45     -webkit-box-flex: 3;
46     -webkit-flex: 3 3 auto;
47 }
48 
49 .c-span9 {
50     width: 75%;
51     -webkit-box-flex: 9;
52     -webkit-flex: 9 9 auto;
53 }
54 
55 .search-line {
56     position: relative;
57     height: 96rpx;
58     line-height: 96rpx;
59     font-size: 30rpx;
60     font-weight: 600;
61     border-bottom: 1rpx solid #e6e6e6;
62 }
63 
64 .search-line::after {
65     content: "";
66     display: inline-block;
67     vertical-align: middle;
68     width: 20rpx;
69     height: 20rpx;
70     border-top: 4rpx solid #00b358;
71     border-right: 4rpx solid #00b358;
72     position: absolute;
73     right: 60rpx;
74     top: 50%;
75     margin-top: -4rpx;
76     -webkit-transform: rotate(45deg) translateY(-50%);
77     transform: rotate(45deg) translateY(-50%);
78     -webkit-box-sizing: border-box;
79     box-sizing: border-box;
80 }
81 
82 .search-line-txt {
83     text-align: right;
84     padding-right: 60rpx;
85     overflow: hidden;
86     text-overflow: ellipsis;
87     white-space: nowrap;
88 }
View Code
 1 <template name="searchbox">
 2   <view class="c-row search-line" data-flag="start">
 3       <view class="c-span3">
 4           出發</view>
 5       <view class="c-span9 js-start search-line-txt">
 6           請選擇出發地</view>
 7   </view>
 8   <view class="c-row search-line" data-flag="arrive">
 9       <view class="c-span3">
10           到達</view>
11       <view class="c-span9 js-arrive search-line-txt">
12           請選擇到達地</view>
13   </view>
14   <view class="c-row " data-flag="arrive">
15       <span class="btn-primary full-width js_search_list">查詢</span>
16   </view>
17 </template>

如此一來,咱們基本的彈出層樣式就七七八八了,這裏能夠看出一些特色:小程序與平時咱們的樣式差距不大,稍微改點就能用,甚至能直接通用;另外一方面,咱們也須要思考一個問題:公共部分的CSS該怎麼處理?其實我這裏須要解決的不僅是公共的樣式部分,還須要解決公共的組件部分。

我這裏想的是將全部公共部分的CSS放到一個全局的文件global.wxss中,而後在每一個業務級頁面import便可,因此咱們這裏須要造成一個公共的WXSS庫,這個與純web映射起來便可,咱們這裏便不深刻。

公共組件庫

要提升開發效率的第一個前提就是要有足夠多的UI組件,小程序自己提供了一些定製化的組件,咱們仍然會用到的組件有:

① alert類彈出層

② loading類彈出層

③ 日曆組件

④ toast&message類提示彈出組件

⑤ 容器類組件

⑥ ......

以前的作法,是咱們將html實體和組件實現直接放到一塊兒,css放到全局global裏面去,如今小程序並不支持動態展現wxml,因此動態插入的方式行不通了,咱們須要將組件的wxml放到頁面裏面作預加載,這裏我想的是提供一個通用global.ui.wxml文件用以裝載全部的wxml實體,經常使用的組件咱們默認全局引入,咱們這裏先挑點軟柿子來捏,咱們先實現一個alert類彈出層組件。

咱們將原來彈出層類會用到的CSS所有翻譯爲WXSS,放入global.wxss中:

而後咱們每一個組件都會有一個固定的生命週期:建立->顯示->隱藏,這個生命週期是每一個組件都具備的特性,因此咱們這裏應該引入繼承概念實現組件,可是小程序官方提供的Components並無提供繼承概念,而是提供了behaviors概念,用以將組件間的公共部分處理掉,因此咱們這裏也使用behaviors,由於不能操做dom,咱們的組件抽象會變得相對簡單,不用記錄太多dom節點了,另外小程序的組件與咱們以前的「組件」從定義到使用上有很大的不一樣,以前咱們是以js做爲控制器,如今是以標籤wxml做爲控制器,根本沒有辦法在js中獲取實例,而小程序組件的生命週期並不包含顯示隱藏生命週期,因此他的組件和咱們覺得的組件有很大的不一樣

我思考了下爲何小程序中,js不能獲取組件的實例,這裏得出的結論是:

小程序中全部的WXML必須在頁面中進行預加載邏輯,不能動態插入DOM的方式插入WXML,因此小程序沒有提供組件實例給咱們控制
因此在小程序中想完成組件庫,那麼便只能把組件作標籤使用(並且是js不能獲取的標籤),而不是js組件,這樣會有效幫助咱們理解

咱們這裏嘗試實現一個遮蓋層的標籤(這裏開始不用組件這個詞,感受頗有歧義):

代碼很是簡單:

<view class="cm-overlay"></view>
.cm-overlay {
    background: rgba(0, 0, 0, 0.5);
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
}
 1 let LayerView = require('behavior-layer-view')
 2 
 3 Component({
 4   behaviors: [LayerView],
 5 
 6   data: {
 7     myData: {}
 8   },
 9   attached: function () { },
10   methods: {
11   }
12 })

能夠看到,這個遮蓋層mask沒有什麼意義,並且通常來講mask也不會單獨存在,通常是一個組件(好比彈出層的loading)會包含一個遮蓋層,因此咱們這裏要改造下Mask的結構,讓他能夠裝載組件,咱們從js組件邏輯來講是mask應該是loading的一個實例,可是咱們站在標籤角度來講,他們兩個應該是獨立的:

1 <view class="cm-overlay">
2   <slot></slot>
3 </view>

咱們這裏實現一個loading的組件(PS:CSS3動畫稍微要作點兼容調試):

  1 .spinner {
  2   width: 140rpx;
  3   height: 140rpx;
  4   position: fixed;
  5   align-items: center;
  6   display: flex;
  7   top: 50%;
  8   left: 50%;
  9   margin-left: -70rpx;
 10   margin-top: -70rpx;
 11 }
 12  
 13 .container1 > view, .container2 > view, .container3 > view {
 14   width: 24rpx;
 15   height: 24rpx;
 16   background-color: #00b358;
 17   border-radius: 100%;
 18   position: absolute;
 19   -webkit-animation: bouncedelay 1.2s infinite ease-in-out;
 20   animation: bouncedelay 1.2s infinite ease-in-out;
 21   -webkit-animation-fill-mode: both;
 22   animation-fill-mode: both;
 23 }
 24  
 25 .spinner .spinner-container {
 26   position: absolute;
 27   width: 66%;
 28   height: 66%;
 29   top: 10%;
 30   left: 10%;
 31 }
 32  
 33 .container2 {
 34   -webkit-transform: rotateZ(45deg);
 35   transform: rotateZ(45deg);
 36 }
 37  
 38 .container3 {
 39   -webkit-transform: rotateZ(90deg);
 40   transform: rotateZ(90deg);
 41 }
 42  
 43 .circle1 { top: 0; left: 0; }
 44 .circle2 { top: 0; right: 0; }
 45 .circle3 { right: 0; bottom: 0; }
 46 .circle4 { left: 0; bottom: 0; }
 47  
 48 .container2 .circle1 {
 49   -webkit-animation-delay: -1.1s;
 50   animation-delay: -1.1s;
 51 }
 52  
 53 .container3 .circle1 {
 54   -webkit-animation-delay: -1.0s;
 55   animation-delay: -1.0s;
 56 }
 57  
 58 .container1 .circle2 {
 59   -webkit-animation-delay: -0.9s;
 60   animation-delay: -0.9s;
 61 }
 62  
 63 .container2 .circle2 {
 64   -webkit-animation-delay: -0.8s;
 65   animation-delay: -0.8s;
 66 }
 67  
 68 .container3 .circle2 {
 69   -webkit-animation-delay: -0.7s;
 70   animation-delay: -0.7s;
 71 }
 72  
 73 .container1 .circle3 {
 74   -webkit-animation-delay: -0.6s;
 75   animation-delay: -0.6s;
 76 }
 77  
 78 .container2 .circle3 {
 79   -webkit-animation-delay: -0.5s;
 80   animation-delay: -0.5s;
 81 }
 82  
 83 .container3 .circle3 {
 84   -webkit-animation-delay: -0.4s;
 85   animation-delay: -0.4s;
 86 }
 87  
 88 .container1 .circle4 {
 89   -webkit-animation-delay: -0.3s;
 90   animation-delay: -0.3s;
 91 }
 92  
 93 .container2 .circle4 {
 94   -webkit-animation-delay: -0.2s;
 95   animation-delay: -0.2s;
 96 }
 97  
 98 .container3 .circle4 {
 99   -webkit-animation-delay: -0.1s;
100   animation-delay: -0.1s;
101 }
102 
103 @-webkit-keyframes bouncedelay {
104   0%, 80%, 100% { -webkit-transform: scale(0.0) }
105   40% { -webkit-transform: scale(1.0) }
106 }
107  
108 @keyframes bouncedelay {
109   0%, 80%, 100% {
110     transform: scale(0.0);
111     -webkit-transform: scale(0.0);
112   } 40% {
113     transform: scale(1.0);
114     -webkit-transform: scale(1.0);
115   }
116 }
loading樣式
 1 <ui-mask z-index="{{maskzIndex}}" ></ui-mask>
 2 <view class="spinner" style="z-index: {{meIndex}}">
 3   <view class="spinner-container container1">
 4     <view class="circle1"></view>
 5     <view class="circle2"></view>
 6     <view class="circle3"></view>
 7     <view class="circle4"></view>
 8   </view>
 9   <view class="spinner-container container2">
10     <view class="circle1"></view>
11     <view class="circle2"></view>
12     <view class="circle3"></view>
13     <view class="circle4"></view>
14   </view>
15   <view class="spinner-container container3">
16     <view class="circle1"></view>
17     <view class="circle2"></view>
18     <view class="circle3"></view>
19     <view class="circle4"></view>
20   </view>
21 </view>
 1 const util = require('../utils/util.js');
 2 let LayerView = require('behavior-layer-view');
 3 
 4 Component({
 5   behaviors: [LayerView],
 6 
 7   data: {
 8     maskzIndex: util.getBiggerzIndex(),
 9     meIndex: util.getBiggerzIndex()
10   },
11   attached: function () { 
12     console.log('loading')
13   },
14   methods: {
15   }
16 })

index調用狀況:

1 <import src="./mod.searchbox.wxml" />
2 
3 <view>
4   <template is="searchbox" />
5   <ui-loading></ui-loading>
6 </view>

咱們後續將完整的項目代碼放到github上去,這裏便繼續代碼了

添加事件

因而,咱們開始添加事件了,這裏添加一個點擊遮蓋層關閉整個組件的功能,這裏有個問題是,咱們點擊遮蓋層事實上關閉的是遮蓋以及loading兩個標籤,而咱們這裏的isShow屬性便派上了用處,咱們如今page中設置下屬性:

<ui-loading is-show="{{isLoadingShow}}"></ui-loading>
1 onShow: function() {
2   this.setData({
3     isLoadingShow: ''
4   });
5 },

而後咱們改造mask以及loading添加事件:

1 <view class="cm-overlay" style="z-index: {{zIndex}}; display: {{isShow}}" bindtap="onTap">
2 </view>
 1 let LayerView = require('behavior-layer-view')
 2 
 3 Component({
 4   behaviors: [LayerView],
 5 
 6   data: {
 7     myData: {}
 8   },
 9   attached: function () { 
10     console.log('mask')
11   },
12   methods: {
13     onTap: function() {
14       this.triggerEvent('customevent', {}, {})
15     }
16   }
17 })
 1 <ui-mask z-index="{{maskzIndex}}" is-show="{{isShow}}" bindcustomevent="onMaskEvent"></ui-mask>
 2 <view class="spinner" style="z-index: {{meIndex}}; display: {{isShow}}; ">
 3   <view class="spinner-container container1">
 4     <view class="circle1"></view>
 5     <view class="circle2"></view>
 6     <view class="circle3"></view>
 7     <view class="circle4"></view>
 8   </view>
 9   <view class="spinner-container container2">
10     <view class="circle1"></view>
11     <view class="circle2"></view>
12     <view class="circle3"></view>
13     <view class="circle4"></view>
14   </view>
15   <view class="spinner-container container3">
16     <view class="circle1"></view>
17     <view class="circle2"></view>
18     <view class="circle3"></view>
19     <view class="circle4"></view>
20   </view>
21 </view>
 1 const util = require('../utils/util.js');
 2 let LayerView = require('behavior-layer-view');
 3 
 4 Component({
 5   behaviors: [LayerView],
 6 
 7   data: {
 8     maskzIndex: util.getBiggerzIndex(),
 9     meIndex: util.getBiggerzIndex()
10   },
11   attached: function () { 
12     console.log('loading')
13   },
14   methods: {
15     onMaskEvent: function (e) {
16       console.log(e);
17       this.setData({
18         isShow: 'none'
19       });
20     }
21   }
22 })

這個時候,當咱們點擊遮蓋層的時候,咱們整個組件便關閉了。

總結

咱們今天花了不少功夫寫一個loading,發現小程序中的組件事實上是標籤,咱們無法使用js獲取到咱們「組件」的實例,因此使用上有很大的區別,可是什麼都不能阻礙咱們寫通用組件的決心,因而咱們明天來寫一些通用的組件庫,而且造成一個小程序的體系,這裏想的是有:

① 消息框

② toast提示

③ 日曆組件

④ 而後再作一個須要定位的氣泡組件

相關文章
相關標籤/搜索