<
微信小程序总结
>

没有上一篇咯
下一篇

HardCandy-Jekyll

2018-09-27 学习工作总结——微信小程序

​ 从之前写完这个 HardCandy-Jekyll 主题以后就很少管理我的博客了。

ps:感谢给我提 issue 的小伙伴,也感谢给我 starfork 我的主题仓库的小伙伴。我会继续努力专注前端的。

​ 这几天,正好有空,领导也让我自己写一篇总结性的文章总结一下最近的开发遇到的问题与解决方法,一不小心写成了博客的样子,着实尴尬。删删减减,留下了精华部分,然后交了上去。博客版本和上交版本还是不太一样的。

​ 6月份开始找实习,6月25日正式上岗 web前端 。不多不少,忙忙碌碌下来,正好3个月过去了。3个月里,有时手忙脚乱,有时却也会闲着思考人生。作为一个前端小学生,怎么样才能够在这个行业内长久的立足下去,免不了的就是学习新技术啊。

​ 进公司几个月以来,写了一个半(1.5)微信小程序,写了一个汽车商城后台管理平台,以及各种各样零碎的东西。说实话,工作后才发现,像以前会一点DOM操作简直弱爆了,能够熟练的掌握一些数据处理的相关技术才是前端基础。

​ 关于小程序,原来有接触过,但写实从来没有写过一点小程序的代码。不过,从零学起,看着官方文档一天便可以上手。然而,上手以后的坑是避免不了的。

PS:由于 jekyll的源码和 微信小程序 或 vue 等框架的 “” 有冲突,具体的 wxml的代码可能会被认为是页面的源码而出现代码缺失。具体完整的代码需前往博客底部的,会有源码下载地址。

  1. 小程序的请求地址
  2. 数据处理
  3. 往页面data中动态插入值
  4. 自己写左滑删除组件
  5. 页面悬浮球
  6. 自己写的验证码输入框
  7. 循环请求地址的异步问题

小程序的坑坑洼洼

​ 关于小程序的坑,偶遇过程都是隐性的,在你的不知不觉中就会显现出来。

小程序的请求地址

​ 小程序是个前端开发框架,不可避免的会与后台进行数据的交互。关于请求地址,小程序官方为了数据的安全性,给了一个硬性的要求 ——

1、开发可以使用http,但是必须在开发工具中开启不校验,开发完成后手机微信端查看效果必须点击右上角打开调试

Markdown

2、上线要求,必须为 https

关于请求地址的公共部分统一存放方式

​ 在根目录下的 app.js 里添加公共变量:

App({
    https: "https://**.***.**"
})

​ 在其他需要请求的页面的 js 内引入这个变量

var app = getApp();

wx.request({
  url: app.https + "/***",
  method: "POST",
  data: {
  },
  header: {
    'Content-Type': 'application/json'
  },
  success: function (res) {
    console.log(res,data);
  },
  fail: function () {
    wx.showToast({
      title: '网络连接失败',
      duration: 2000,
      icon: 'none'
    })
  }
})

数据处理

​ 拿到数据后,对数据的处理有一些心得。

随机数函数

randomNum: function (num1, num2){
    return Math.random() * (num2 - num1) + num1;
},

时间戳与时间的转换

时间转时间戳:

一大巨坑,ios的时间如果有横岗 则无法转为标准时间,则无法转为时间戳,需要将 ‘ - ‘ 替换成 ‘ / ‘

不行: 2018-09-26 可以: 2018/09/26

// 如果借口请求拿到时间 2018-09-26 14:34:07 , 现在需要将它转成时间戳
Page({
    data{
    	time: '2018-09-26 14:34:07',
    }
    timeToTimestamp:function(){
		var that = this;
    	// 强行用 '/' 取代字符串中的 '-'
        var newTime = that.data.time.replace(new RegExp(/-/gm), "/");
    	// 获取标准时间
    	var standard = new Date(newTime);
    	// 时间转时间戳
    	var timestamp = standard.getTime();
    	
    	// timestamp 就是时间戳
    	console.log(timestamp);
    },
})

当前时间转时间戳:

Page({
	onLoad: function (options) {
        // 获取当前时间的标准时间
        var nowTime = new Date();
        // 时间转时间戳
    	var timestamp = nowTime.getTime();
        
        // timestamp 就是时间戳
    	console.log(timestamp);
	},
})

时间戳转时间:

// 时间戳转时间
timestampToTime: function (timestamp) {
    var date = new Date(timestamp);//时间戳为10位需*1000,时间戳为13位的话不需乘1000
    var Y = date.getFullYear() + '-';
    var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
    var D = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + ' ';
    // var D = date.getDate() + ' ';
    var h = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':';
    var m = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) + ':';
    var s = (date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds());
    return Y + M + D + h + m + s;
    // return Y + M + D;
}

数组按数组中的对象的key值大小排序

Page({
    data:{
        test:[
            { id: 2, name: '徐金琦', age: '22' },
            { id: 55, name: '徐金琦', age: '22' },
            { id: 3, name: '徐金琦', age: '22' },
            { id: 34, name: '徐金琦', age: '22' }
        ]
    },
    onLoad:function(){
    	var that = this;
        // 按key值id排序
        var test = that.data.test.sort(that.sortKey);
        console.log(test)
    },
    
    // 按 key 值排序的函数
    sortKey:function(a, b) {
        // 其中的 id 即为对应的 key 的名称
        return a.id > b.id ? 1 : -1;
    }
})

// 结果:
//    test:[
//        { id: 2, name: '徐金琦', age: '22' },
//        { id: 3, name: '徐金琦', age: '22' },
//        { id: 34, name: '徐金琦', age: '22' },
//        { id: 55, name: '徐金琦', age: '22' }
//    ]

// 写法二:
onLoad:function(){
    var that = this;
    // 按key值id排序
    var test = that.data.test.sort(function(a, b) {
        // 其中的 id 即为对应的 key 的名称
        return a.id > b.id ? 1 : -1;
    });
    console.log(test)
},

往页面data中动态插入值

​ 假设需要插入的数据为某一个数组的某个索引下的值

Page({
    data:{
        test:[
            {id:'',name:'徐金琦',age:'22'},            
            {id:'',name:'徐金琦',age:'22'}
        ]
    },
    onLoad:function(){
    	var that = this;
		var that = this;
        for (var i = 0; i < that.data.test.length; i++) {
            // 声明一个动态字符串代表 某个索引下的 id ********
            var id = 'test[' + i + '].id';
            // *****************************************
            that.setData({
            	[id]: i
            })
        }
    },
})

// 结果:
//    test:[
//        {id:0,name:'徐金琦',age:'22'},            
//        {id:1,name:'徐金琦',age:'22'}
//    ]

自己写左滑删除组件

​ 在小程序应用中,有场景需要用到左滑出现删除按钮,类似微信的删除聊天的操作逻辑。

​ 可是找遍的微信的官方组件也没能找到这方面的资料,所以只好在网上查找资料,自己写一个类似的组件。

先看效果:

Markdown

wxml:

<view class="body">
  <view class="touchItem " data-index="" bindtouchstart="touchstart" bindtouchmove="touchmove" bindtouchend="touchend" wx:for="" wx:key="">
    <view class="contentArea">
      <text class="title"></text>
    </view>
    <view class="del" catchtap="ignore" data-index="">删除</view>
  </view>
</view>

wxss:

page{
  box-sizing: border-box;
  padding-top: 20rpx;
  background:rgba(244,249,255,1);
  height: 100%;
  width: 100%;
}
.body{
  height: 100%;
  width: 100%;
  overflow-y: scroll;
}
.touchItem {
  font-size: 14px;
  display: flex;
  justify-content: space-between;
  height: 120rpx;
  width: 100%;
  margin-bottom: 20rpx;
  border-bottom: 1px solid #eee;
  overflow-x: hidden;
  background-color: white;
}
.contentArea {
  width: 100%;
  padding: 10px 20rpx;
  line-height: 22px;
  margin-right:0;
  -webkit-transition: all 0.4s;
  transition: all 0.4s;
  -webkit-transform: translateX(70px);
  transform: translateX(70px);
  margin-left: -70px
}
.title{
  font-size: 30rpx;
  color: #333;
  display: block;
  float: left;
}
.del {
  background-color: #FD5454;
  width: 70px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: #fff;
  -webkit-transform: translateX(70px);
  transform: translateX(70px);
  -webkit-transition: all 0.4s;
  transition: all 0.4s;
}

/* 左滑后的样式 */
.touch-move-active .contentArea,
.touch-move-active .del {
  -webkit-transform: translateX(0);
  transform: translateX(0);
}

js:

Page({

    /**
    * 页面的初始数据
    */
    data: {
        test: [
            { title: '这是测试' },
            { title: '这是测试' },
            { title: '这是测试' },
            { title: '这是测试' }
        ]
    },

    //手指触摸动作开始 记录起点X坐标
    touchstart: function (e) {
        //开始触摸时 重置所有删除
        this.data.test.forEach(function (v, i) {
            if (v.isTouchMove)//只操作为true的
                v.isTouchMove = false;
        })
        this.setData({
            startX: e.changedTouches[0].clientX,
            startY: e.changedTouches[0].clientY,
            test: this.data.test
        })
    },
    //滑动事件处理
    touchmove: function (e) {
        var that = this,
        index = e.currentTarget.dataset.index, //当前索引
        startX = that.data.startX, //开始X坐标
        startY = that.data.startY, //开始Y坐标
        touchMoveX = e.changedTouches[0].clientX, //滑动变化坐标
        touchMoveY = e.changedTouches[0].clientY, //滑动变化坐标
        //获取滑动角度
        angle = that.angle({ X: startX, Y: startY }, { X: touchMoveX, Y: touchMoveY });
        
        that.data.test.forEach(function (v, i) {
            v.isTouchMove = false
            //滑动超过30度角 return 
            if (Math.abs(angle) > 30) return;
            if (i == index) {
                if (touchMoveX > startX) //右滑
                    v.isTouchMove = false
                else //左滑
                    v.isTouchMove = true
            }
        })
        //更新数据
        that.setData({
        test: that.data.test
        })
    },
    // 触碰结束
    touchend: function () {
        var that = this;
        for (var i = 0; i < that.data.test.length; i++) {
            var showDel = 'test[' + i + '].showDel'
            if (that.data.test[i].isTouchMove) {
                that.setData({
                [showDel]: true
                })
            } else {
                that.setData({
                    [showDel]: false
                })
            }
        }
    },
    /**
    * 计算滑动角度
    * @param {Object} start 起点坐标
    * @param {Object} end 终点坐标
    */
    angle: function (start, end) {
        var _X = end.X - start.X,
        	_Y = end.Y - start.Y;
        //返回角度 /Math.atan()返回数字的反正切值
        return 360 * Math.atan(_Y / _X) / (2 * Math.PI);
    },

    //删除通知
    ignore: function (e) {
        var that = this;
        var delIndex = e.currentTarget.dataset.index;
        console.log(delIndex)
    },

})

​ 以上代码,复制直接用即可。实际使用,只需将 data 中的 test 数组修改为自己接口所得的数据。然后,在页面上直接绑定数据即可。

关于ios页面会上下滑动可能会干扰左右滑的解决办法

在页面 json 中添加下面的这句话禁用页面滚动

"disableScroll": true

在页面最底层套一个大的 view,并为 page 设置宽高均为 100%,这个 view 的宽高也为 100%,然后为 view 设置

overflow-y: scroll;

页面悬浮球

​ 同样的,由于小程序的右上角被微信的组件所占用(现在具体能不能改,我还真不知道)。可是当页面上已经存在了相对和谐的布局以后,突然觉得必须要给页面添加一个搜索或者其他什么按钮时,你看会觉得放在那里都不合适。就在这时候,可能添加一个悬浮的小球,会给页面一种不一样的感觉。既不会打破页面的和谐,也可以让用户自己随意拖动到页面的某个地方并悬停在屏幕的边缘处。

​ 或许用户并不太喜欢这个按钮,后期我会再次修改这个悬浮球的相应逻辑,给用户更好的操作体验。暂时的想法是 让悬浮球隐藏在页面边缘,露出一点点边缘,用户可点击,则弹出。

先看效果:

Markdown

关于代码:

wxml:

<view class="btn" style="bottom:px;right:px;" bindtouchmove="ballMoveEvent" bindtouchend="ballMoveEnd">悬浮</view>

wxss:

.btn{
  height: 50px;
  width: 50px;
  border-radius: 50%;
  box-sizing: border-box;
  text-align: center;
  line-height: 50px;
  font-size: 30rpx;
  color: white;
  position: fixed;
  bottom: 350rpx;
  right: 5px;
  /* right: calc( 100% - 60px ); */  
  background-color: #6d75f4;  
  box-shadow:  1px 1px 2px rgba(0,0,0,0.2);   
  z-index: 100;
}

js:

onLoad: function (options) {
    var that = this;
    // 获取屏幕宽高
    wx.getSystemInfo({
        success: function (res) {
            that.setData({
                screenHeight: res.windowHeight,
                screenWidth: res.windowWidth,
            });
        }
    });
},
ballMoveEvent: function (e) {
    var that = this;
    // console.log('我被拖动了....');
    var touchs = e.touches[0];
    var pageX = touchs.pageX;
    var pageY = touchs.pageY;
    // console.log('pageX: ' + pageX)
    // console.log('pageY: ' + pageY)
    
    //防止坐标越界,view宽高的一般
    if (pageX < 30) return;
    if (pageX > that.data.screenWidth - 30) return;
    if (that.data.screenHeight - pageY <= 30) return;
    if (pageY <= 30) return;
    //这里用right和bottom.所以需要将pageX pageY转换
    var x = that.data.screenWidth - pageX - 30;
    var y = that.data.screenHeight - pageY - 30;
    // console.log('x: ' + x)
    // console.log('y: ' + y)
    that.setData({
        ballBottom: y,
        ballRight: x
    });
},
ballMoveEnd: function () {
    var that = this;
    console.log("我停止接触了")
    if (that.data.ballRight >= (that.data.screenWidth / 2) - 25) {
        that.setData({
        	ballRight: that.data.screenWidth - 55
        });
    } else {
        that.setData({
            ballRight: 5
        });
    }
},

​ 复制上述代码并在相应的地方粘贴便可以看到悬浮球的效果。关于修改悬浮球的大小,单位为 px,修改完大小后,记得在 js 中也修改相关的悬停位置, 单位同样也是 px

自己写的验证码输入框

业务场景:当用户点击确定后,向后台请求并收到一条带有验证码的短信。此时输入验证码即可进行下一步。

思路:

​ 想要在弹出框中输入验证码,且验证码位置无光标显示,以保证美观。

​ 所以,将 输入框 用定位移出到屏幕外,当弹出验证码输入的弹出框时,自动将输入框变为 有焦点 状态。在弹出框内预留六个框,每个框绑定一个输入框内输入的数值。每个框绑定一个点击事件,点击框后自动时屏幕外的输入框 有焦点

先看效果:

Markdown

关于代码:

wxml:

<!-- 点击按钮,弹出输入框 -->
<view class="btn" bindtap="showSureCode">点击输入验证码</view>

<!-- 验证码弹窗 -->
<view class="back" hidden=''> 
  <view class="sureCode">
    <text class="sureCode-title">输入验证码</text>
    <view class="inputLine">
      <view class="inputBlock" bindtap="inputFocus"></view>
      <view class="inputBlock" bindtap="inputFocus"></view>
      <view class="inputBlock" bindtap="inputFocus"></view>
      <view class="inputBlock" bindtap="inputFocus"></view>
      <view class="inputBlock" bindtap="inputFocus"></view>
      <view class="inputBlock" bindtap="inputFocus"></view>
    </view>
    <text class="cancel" bindtap="cancel">取 消</text>
    <text class="sure" bindtap="sure">确 认</text>
  </view>
  <input class="ainput" type="number" bindinput="input" focus="" maxlength='6'></input>
</view>

wxss:

/* 按钮 */
.btn{
  width:600rpx;
  height:100rpx;
  background: linear-gradient(to right, #b663f9 , #6d75f4);   
  border-radius:10rpx;
  color: white;
  line-height: 100rpx;
  text-align: center;
  margin: 70rpx auto;
  font-size: 30rpx;
}
/* 弹窗 */
.back{
  height: 100%;
  width: 100%;
  position: fixed;
  bottom: 0;
  left: 0;
  background-color: rgba(0,0,0,.3);
}
.sureCode{
  height: 400rpx;
  width: 650rpx;
  background-color: white;
  border-radius: 10rpx;
  position: relative;
  left: calc( 50% - 325rpx );
  top: 30%;
  box-sizing: border-box;
  padding: 30rpx 40rpx 0;
}
.sureCode-title{
  display: block;
  font-size:36rpx;
  color:rgba(51,51,51,1);
  text-align: center;
  margin-bottom: 20rpx;
}

.ainput{
  height: 100rpx;
  width: 100%;
  position: absolute;
  background-color: white;
  top: 0;
  left: -10000px;
  text-align: center;
}
.inputLine{
  margin-top: 100rpx;
}
.inputBlock{
  height: 55rpx;
  width: 55rpx;
  float: left;
  margin-right: 48rpx;
  box-sizing: border-box;
  border-bottom: 1px solid #b264f8;
  line-height: 55rpx;
  text-align: center;
  font-size: 40rpx;
  color: #303030;
}
.inputLine view:nth-child(6){
  margin: 0;
}
.cancel{
  display: block;
  height: 100rpx;
  width: 50%;
  position: absolute;
  bottom: 0;
  left: 0;
  line-height: 100rpx;
  text-align: center;
  font-size: 28rpx;
  color: #333333;
}
.sure{
  display: block;
  height: 100rpx;
  width: 50%;
  position: absolute;
  bottom: 0;
  right: 0;
  line-height: 100rpx;
  text-align: center;
  font-size: 28rpx;
  color: #FD5454;
}

js:

data: {
    sureCode: true,
    inputFocus: false,
    sureCodeNum: [],
},
// 弹出验证框
showSureCode:function(){
    var that = this;
    that.setData({
        sureCode: false
    })
},
// 输入框的焦点问题
inputFocus: function () {
    var that = this;
    that.setData({
        inputFocus: true
    })
},
// 输入框输入值后的数据处理
input: function (e) {
    var that = this;
    var value = e.detail.value;
    var sureCodeNum = [];
    console.log(value);
    for (var i = 0; i < value.length; i++) {
        sureCodeNum[i] = value.substr(i, 1);
    }
    that.setData({
        sureCodeNum: sureCodeNum
    })
},
// 取消输入
cancel: function () {
    var that = this;
    that.setData({
        sureCode: true
    })
},
// 确认输入
sure:function(){
    var that = this;
    wx.showLoading({
        title: '验证中',
    })
    setTimeout(function(){
        wx.hideLoading();
        that.setData({
            sureCode: true
        })
    },1000)
},

具体业务场景根据需要添加具体的样式和逻辑,基础模板就参考上述代码。同样复制粘贴即可看效果。

循环请求地址的异步问题

业务场景:当请求接口时,拿到了一串数组,数组里的每一个对象对应一个 id 或者其他的什么身份标识。此时页面要求显示的不是 id 而是这个 id 所对应的名字。而名字需要调用另一个接口去获取。

思路:

​ 通过 递归 的方式,而不能用 for 循环请求。

原因:

​ 由于小程序的请求为异步请求,异步请求就存在一个程序并行的问题。而 for 循环在执行过程中,请求发送便会执行下一次的循环。当网络环境不佳时,则必然会存在多个请求并存的问题,导致请求数据的顺序并非是自己想要的那种样子。

​ 而递归则不同,递归的意义就在于当发送一次请求之后,我们可以在请求完成以后再次调用这个 递归函数 ,并将 i + 1 ,在函数内判断,当 i 满足某个条件时,结束这个递归函数。这样的请求就不会将数据顺序打乱。

demo:

// 请求所得数据
// [{ id: 2, age: '22' },
//  { id: 55, age: '22' },
//  { id: 3, age: '22' },
//  { id: 34, age: '22' }]

// 页面需要的数据
// [{ id: 2, name:'徐金琦' , age: '22' },
//  { id: 55, name:'徐金琦', age: '22' },
//  { id: 3, name:'徐金琦', age: '22' },
//  { id: 34, name:'徐金琦', age: '22' }]


data:{
    test: [],
},
forTest:function(){
    var that = this;
    // 第一次请求拿到所有数据与对应id
	wx.request({
        url: 'https://**.**/forTest', 
        data: {
            a:'**',
            b:'**'
        },
        header: {
        	'content-type': 'application/json'
        },
        success (res) {
        	console.log(res.data)
            var test = res.data;
            that.setData({
                test: test
            })
            // 触发 递归函数 
            that.forName(0)
        }
        fail (res) {
        	console.log(res.data)
        }
    })

},
forName:function(i){
	var that = this;
    // 备份第一次请求所得数据,防止之后操作对原始数据的影响
    var testBak = that.test.slice();
    // 第二次使用对应 id 逐个请求
    wx.request({
        url: 'https://**.**/forName', 
        data: {
            id: testBak[i].id
        },
        header: {
        	'content-type': 'application/json'
        },
        success (res) {
        	console.log(res.data)
            var key = 'name';
            var value = res.data;
            testBak[i][key] = value;
            that.setData({
                test: testBak
            })
            
            // 判断再次触发递归的条件,并将 i 自增 1
            if(++i < testBak.length){
                that.forName(i);
            } else {
                console.log(that.data.test)
            }
        },
        fail (res) {
            console.log(res.data)
        }
    })
},

暂时就想到这些,日后再补。


最后,以上部分源码下载:

wechat-applet

最最后,打个广告,我写的完整的小程序,暂时不更新版:

Markdown


同样的,欢迎提意见。。。

Top
Foot