zoukankan      html  css  js  c++  java
  • 微信小程序开发03-这是一个组件

    编写组件

    基本结构

    接上文:微信小程序开发02-小程序基本介绍

    我们今天先来实现这个弹出层:

    之前这个组件是一个容器类组件,弹出层可设置载入的html结构,然后再设置各种事件即可,这种组件有一个特点:

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

    ② 容器部分的HTML结构由业务层提供

    ③ 容器部分对应样式由业务层提供

    我们如果要在小程序中实现这类组件,意味着我们需要往小程序中动态插入WXML结构,我们这里先做个demo,试试往动态插入WXML是不是可行

    1 this.setData({'wxml': `
    2   <my-component>
    3   <view>动态插入的节点</view> 
    4   </my-component>
    5 `});

    小程序对应设置的数据进行了转义,所以并不能动态解析,如果站在性能角度思考,不进行动态解析也不是错误的;另一方面,一旦小程序能动态解析wxml,那么可能会涌出各种花式用法,控制力会减低,那么我们这里如何解决这个问题呢?

    我想的是,直接将业务级wxml结构放到页面里面,隐藏起来,需要使用弹出层的时候,直接将之装载进去,我们来看看是否可行,我们将我们需要展示的结构放到一个模板当中:

    1 <template name="searchbox">
    2   <my-component>
    3       <view>动态组件部分</view> 
    4   </my-component>
    5 </template>

    然后,我们在我们主界面中载入模板:

     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提示

    ③ 日历组件

    ④ 然后再做一个需要定位的气泡组件

  • 相关阅读:
    Vue+Element-UI+jQuery+Layer+Camera+Easing的简单应用
    Vue(vue-cli脚手架)中使用layer.js做非模态弹窗(二)
    unicable
    grep 搜索目录时,怎样排除某些目录?
    Mali Midgard架构解析
    如何从GFP确定最后申请的内存来自哪个zone?
    内存申请 GFP_KERNEL GFP_ATOMIC
    Linux内核中的page migration和compaction机制简介
    IOVA/IOMMU
    memory zone spanned/present/managed区别
  • 原文地址:https://www.cnblogs.com/yexiaochai/p/9382862.html
Copyright © 2011-2022 走看看