前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >前端-微信小程序开发(3):这是一个组件

前端-微信小程序开发(3):这是一个组件

作者头像
grain先森
发布2019-03-29 11:38:33
4710
发布2019-03-29 11:38:33
举报
文章被收录于专栏:grain先森

作者:叶小钗  www.cnblogs.com/yexiaochai/p/9382862.html

编写组件

基本结构

接上文:

微信小程序开发02-小程序基本介绍

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

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

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

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

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

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

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

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

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

<template name="searchbox">   <my-component>       <view>动态组件部分</view>    </my-component> </template>

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

<import src="mod.searchbox.wxml"/> <view>    <my-component>      <!-- 这部分内容将被放置在组件 <slot> 的位置上 -->       <view>这里是插入到组件slot中的内容</view>    </my-component> </view> <view>    <template is="searchbox" /> </view>

主体结构放到页面中,我们传入数据模型或者控制显示即可,看起来是可行的,于是我们先实现我们基本的样式,因为业务模块的样子应该由业务提供,所以对应样式写到index.wxss里面:

.btn-primary {     background-color: #00b358;     color: #fff;     border: 0 none; } .btn, .btn-primary, .btn-secondary, .btn-sub {     line-height: 88rpx;     height: 88rpx;     padding: 0 20rpx;     display: inline-block;     vertical-align: middle;     text-align: center;     border-radius: 8rpx;     cursor: pointer;     font-size: 32rpx;     -webkit-box-sizing: border-box;     box-sizing: border-box; } .full-width {     width: 100%;     -webkit-box-sizing: border-box;     box-sizing: border-box; } .c-row {     width: auto;     display: -webkit-box;     -webkit-box-orient: horizontal;     -webkit-box-direction: normal;     -webkit-box-pack: justify;     -webkit-box-align: stretch;     -webkit-box-lines: single;     display: -webkit-flex;     -webkit-flex-direction: row;     -webkit-justify-content: space-between;     -webkit-align-items: strecth;     -webkit-align-content: flex-start;     -webkit-flex-wrap: nowrap;     padding: 20rpx 40rpx; } .c-span3 {     width: 25%;     -webkit-box-flex: 3;     -webkit-flex: 3 3 auto; } .c-span9 {     width: 75%;     -webkit-box-flex: 9;     -webkit-flex: 9 9 auto; } .search-line {     position: relative;     height: 96rpx;     line-height: 96rpx;     font-size: 30rpx;     font-weight: 600;     border-bottom: 1rpx solid #e6e6e6; } .search-line::after {     content: "";     display: inline-block;     vertical-align: middle;     width: 20rpx;     height: 20rpx;     border-top: 4rpx solid #00b358;     border-right: 4rpx solid #00b358;     position: absolute;     right: 60rpx;     top: 50%;     margin-top: -4rpx;     -webkit-transform: rotate(45deg) translateY(-50%);     transform: rotate(45deg) translateY(-50%);     -webkit-box-sizing: border-box;     box-sizing: border-box; } .search-line-txt {     text-align: right;     padding-right: 60rpx;     overflow: hidden;     text-overflow: ellipsis;     white-space: nowrap; }

<template name="searchbox">   <view class="c-row search-line" data-flag="start">       <view class="c-span3">           出发</view>       <view class="c-span9 js-start search-line-txt">           请选择出发地</view>   </view>   <view class="c-row search-line" data-flag="arrive">       <view class="c-span3">           到达</view>       <view class="c-span9 js-arrive search-line-txt">           请选择到达地</view>   </view>   <view class="c-row " data-flag="arrive">       <span class="btn-primary full-width js_search_list">查询</span>   </view> </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; }

let LayerView = require('behavior-layer-view') Component({   behaviors: [LayerView],   data: {     myData: {}   },   attached: function () { },   methods: {   } })

可以看到,这个遮盖层mask没有什么意义,而且一般来说mask也不会单独存在,一般是一个组件(比如弹出层的loading)会包含一个遮盖层,所以我们这里要改造下Mask的结构,让他可以装载组件,我们从js组件逻辑来说是mask应该是loading的一个实例,但是我们站在标签角度来说,他们两个应该是独立的:

<view class="cm-overlay">   <slot></slot> </view>

我们这里实现一个loading的组件(PS:CSS3动画稍微要做点兼容调试):

loading样式

.spinner {   width: 140rpx;   height: 140rpx;   position: fixed;   align-items: center;   display: flex;   top: 50%;   left: 50%;   margin-left: -70rpx;   margin-top: -70rpx; } .container1 > view, .container2 > view, .container3 > view {   width: 24rpx;   height: 24rpx;   background-color: #00b358;   border-radius: 100%;   position: absolute;   -webkit-animation: bouncedelay 1.2s infinite ease-in-out;   animation: bouncedelay 1.2s infinite ease-in-out;   -webkit-animation-fill-mode: both;   animation-fill-mode: both; } .spinner .spinner-container {   position: absolute;   width: 66%;   height: 66%;   top: 10%;   left: 10%; } .container2 {   -webkit-transform: rotateZ(45deg);   transform: rotateZ(45deg); } .container3 {   -webkit-transform: rotateZ(90deg);   transform: rotateZ(90deg); } .circle1 { top: 0; left: 0; } .circle2 { top: 0; right: 0; } .circle3 { right: 0; bottom: 0; } .circle4 { left: 0; bottom: 0; } .container2 .circle1 {   -webkit-animation-delay: -1.1s;   animation-delay: -1.1s; } .container3 .circle1 {   -webkit-animation-delay: -1.0s;   animation-delay: -1.0s; } .container1 .circle2 {   -webkit-animation-delay: -0.9s;   animation-delay: -0.9s; } .container2 .circle2 {   -webkit-animation-delay: -0.8s;   animation-delay: -0.8s; } .container3 .circle2 {   -webkit-animation-delay: -0.7s;   animation-delay: -0.7s; } .container1 .circle3 {   -webkit-animation-delay: -0.6s;   animation-delay: -0.6s; } .container2 .circle3 {   -webkit-animation-delay: -0.5s;   animation-delay: -0.5s; } .container3 .circle3 {   -webkit-animation-delay: -0.4s;   animation-delay: -0.4s; } .container1 .circle4 {   -webkit-animation-delay: -0.3s;   animation-delay: -0.3s; } .container2 .circle4 {   -webkit-animation-delay: -0.2s;   animation-delay: -0.2s; } .container3 .circle4 {   -webkit-animation-delay: -0.1s;   animation-delay: -0.1s; } @-webkit-keyframes bouncedelay {   0%, 80%, 100% { -webkit-transform: scale(0.0) }   40% { -webkit-transform: scale(1.0) } } @keyframes bouncedelay {   0%, 80%, 100% {     transform: scale(0.0);     -webkit-transform: scale(0.0);   } 40% {     transform: scale(1.0);     -webkit-transform: scale(1.0);   } }

<ui-mask z-index="{{maskzIndex}}" ></ui-mask> <view class="spinner" style="z-index: {{meIndex}}">   <view class="spinner-container container1">     <view class="circle1"></view>     <view class="circle2"></view>     <view class="circle3"></view>     <view class="circle4"></view>   </view>   <view class="spinner-container container2">     <view class="circle1"></view>     <view class="circle2"></view>     <view class="circle3"></view>     <view class="circle4"></view>   </view>   <view class="spinner-container container3">     <view class="circle1"></view>     <view class="circle2"></view>     <view class="circle3"></view>     <view class="circle4"></view>   </view> </view>

const util = require('../utils/util.js'); let LayerView = require('behavior-layer-view'); Component({   behaviors: [LayerView],   data: {     maskzIndex: util.getBiggerzIndex(),     meIndex: util.getBiggerzIndex()   },   attached: function () {     console.log('loading')   },   methods: {   } })

index调用情况:

<import src="./mod.searchbox.wxml" /> <view>   <template is="searchbox" />   <ui-loading></ui-loading> </view>

我们后续将完整的项目代码放到github上去,这里便继续代码了

添加事件

于是,我们开始添加事件了,这里添加一个点击遮盖层关闭整个组件的功能,这里有个问题是,我们点击遮盖层事实上关闭的是遮盖以及loading两个标签,而我们这里的isShow属性便派上了用处,我们现在page中设置下属性:

<ui-loading is-show="{{isLoadingShow}}"></ui-loading>

onShow: function() {   this.setData({     isLoadingShow: ''   }); },

然后我们改造mask以及loading添加事件:

<view class="cm-overlay" style="z-index: {{zIndex}}; display: {{isShow}}" bindtap="onTap"> </view>

let LayerView = require('behavior-layer-view') Component({   behaviors: [LayerView],   data: {     myData: {}   },   attached: function () {     console.log('mask')   },   methods: {     onTap: function() {       this.triggerEvent('customevent', {}, {})     }   } })

<ui-mask z-index="{{maskzIndex}}" is-show="{{isShow}}" bindcustomevent="onMaskEvent"></ui-mask> <view class="spinner" style="z-index: {{meIndex}}; display: {{isShow}}; ">   <view class="spinner-container container1">     <view class="circle1"></view>     <view class="circle2"></view>     <view class="circle3"></view>     <view class="circle4"></view>   </view>   <view class="spinner-container container2">     <view class="circle1"></view>     <view class="circle2"></view>     <view class="circle3"></view>     <view class="circle4"></view>   </view>   <view class="spinner-container container3">     <view class="circle1"></view>     <view class="circle2"></view>     <view class="circle3"></view>     <view class="circle4"></view>   </view> </view>

const util = require('../utils/util.js'); let LayerView = require('behavior-layer-view'); Component({   behaviors: [LayerView],   data: {     maskzIndex: util.getBiggerzIndex(),     meIndex: util.getBiggerzIndex()   },   attached: function () {      console.log('loading')   },   methods: {     onMaskEvent: function (e) {       console.log(e);       this.setData({         isShow: 'none'       });     }   } })

这个时候,当我们点击遮盖层的时候,我们整个组件便关闭了。

总结

我们今天花了很多功夫写一个loading,发现小程序中的组件事实上是标签,我们没法使用js获取到我们“组件”的实例,所以使用上有很大的区别,但是什么都不能阻碍我们写通用组件的决心,于是我们明天来写一些通用的组件库,并且形成一个小程序的体系,这里想的是有:

① 消息框

② toast提示

③ 日历组件

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018.09.12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云开发 CloudBase
云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档