我们首先说一说什么是微信小程序和微信云开发(都是自己一个字一个字打的 = =)
微信小程序是一种不需要下载,进入微信就可以直接打开的软件,已经有成千上万的开发者加入了它的开发,一起推动了微信小程序的发展,它是一种创新,微信小程序应用也越来越多,已经超过了一百万个,覆盖了几乎所有的行业,每天活跃人数高达两个亿,它影响到了千千万万的程序员,微信小程序的发展给互联网带来了全新的生机,也带来了更多的就业机会。微信小程序能够实现消息通知、公众号关联、扫码等七大功能,用户使用起来得心应手。
微信推出小程序云开发的SDK是在2018年8月份,官方对目前用户的版本统计,大概99%的用户支持使用微信小程序云开发发布的小程序,微信小程序云开发不需要自建服务器,并且提供了一个免费的基础版本,而且不依托外部服务器就能对数据库进行一系列的操作,它在微信小程序的接口下,通过云开发安全拿到用户凭证,它以OPENID作为每个用户的身份证,请求方式如图2.1所示。
通过开发者服务器的中转,需要大量的接口去校验,而当使用云开发时,仅仅需要两步就解决了上述的问题。
微信云开发在本项目担任了重要角色,一系列的前后台交互都是通过云函数对云服务器的请求以及云服务器对前台JS的反馈,通过动态加载修改前端页面,而且,微信云开发是通过开发者创建的云函数对云开发服务器进行快捷操作的一系列开发过程,数据库的存储格式为JSON,处理方便快捷。目前支持四大基础功能:
支持云函数,将一些私有操作和数据库操作封装为函数,上传云端,在云端运行即可,它提供了安全的协议,开发者仅仅需要开发逻辑代码即可。
支持数据库,利用云函数操作更具有安全性,是一个支持JSON格式数据库。
支持云存储,小程序本身在上传时不允许超过2M,这就要求,大部分数据图片要放在云端,云存储方便了开发者,在小程序前端JS调用接口,直接实现上传和下载云端文件。
支持云调用,通过一些小程序开发的接口,完成一系列的调用和获取数据。
微信团队为微信小程序默认提供的开发框架为MINA。它封装了一系列的底层基础操作包括了数据安全、网络通讯、文件系统、任务管理。该开发框架提供了一整套的调用API使开发者可以迅速上手,方便开发各种基础功能,迅速构建一个轻量级应用,系统框架图如图2.2所示。
其中,逻辑层是逻辑处理的地方,JS脚本的集合其实就是逻辑层,微信小程序在逻辑层将数据进行某些处理后发给视图层,视图层接受数据来渲染画面。视图层由WXML和WXSS编写,视图层其实就是微信小程序所呈现的页面内容,视图层可以接收数据来渲染自己。数据层包含了各种各样的数据,是一个数据传输站。
在页面视图层,WXML是微信小程序提供的一套类似于HTML语言以及其基础组件。使用WXML搭建基础视图,通过WXSS控制页面展示的样式,通过EVENT事件组请求系统层。AppService逻辑层是中间站,通过异步单独记载。页面实时渲染以及页面之间的交互都通过这个逻辑层进行实现。当然,AppService使用JS编写逻辑,使其可以进行网络请求以及数据处理。每个小程序都有自己的逻辑层来进行通讯与交互。MINA框架还为页面提供了不同于HTML的绑定事件bindtap和bindlongtap等相关属性,通过绑定事件实现了同步交互数据。
微信开发者工具是微信为了方便开发者开发和调试小程序所开发的一款高效的集成开发环境,集中了开发、调试、预览、上传等功能。在本项目开发过程中,全程使用了微信开发者工具,它集成了许多便利的功能,并且可以迅速和云开发服务器交互,本项目主要采用了个人账号开发和两个测试号测试。
前端使用了微信开发的三种语言:WXML、WXSS和JavaScript,其中,WXML是一种类似于HTML的前端语言,是微信小程序的前端开发语言,它可以构建出完整的前端静态页面,而WXSS是微信小程序为了适配其前端的WXML,而定制了一套WXSS语言,其原理和CSS大致相同。WXSS用来修饰WXML的样式,并且可以使前端呈现动态效果,甚至可以结合各种脚本语言对各个元素进行动态格式化。
本项目云开发技术包含了云服务器、云数据库、云函数以及云存储四个部分,项目整体实现采用了微信小程序MINA框架,架构分为视图层、逻辑层、系统层三大部分。其中,服务器和数据库包含在上述的云开发技术中,前端使用ColorUI进行WXSS视图渲染,全局使用flex布局,以确保在不同型号大小的手机上均可以正确加载界面。所有的数据都是通过Python网络爬虫以及之后数据清洗处理获取。
本项目的后台使用了JavaScript语言交互,通过JavaScript使前端同云服务器进行数据交互,同时更新前端页面信息。本项目大量使用了JavaScript交互,使前后台具备的微信小程序交互自如。
该微信小程序项目的功能有哪些?
SECTION A部分,我们只聊一聊引导页的设计和实现。
之所以使用“拥抱健康”来引导的重要原因是,如果你不授权,对不起,你没有进入小程序的权利。
其次,我要重点说明的一点是,这次的项目前端框架使用的ColorUI
吹爆,非常优秀的前端框架,大大提高了我的开发速度
两天开发一个项目已不再是梦。
该页面掌控着授权的关键点。
说起JS,简单,撸就完了 welcome.js
const app = getApp()
Page({
data: {
remind: '加载中',
userInfo: null,
hasUserInfo: false,
canIUse: wx.canIUse('button.open-type.getUserInfo')
},
goToIndex: function() {
wx.switchTab({
url: '../../pages/home/home',
});
},
onLoad: function() {
if (app.globalData.userInfo) {
this.setData({
userInfo: app.globalData.userInfo,
hasUserInfo: true
})
} else if (this.data.canIUse) {
// 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
// 所以此处加入 callback 以防止这种情况
app.userInfoReadyCallback = res => {
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
} else {
// 在没有 open-type=getUserInfo 版本的兼容处理
wx.getUserInfo({
success: res => {
app.globalData.userInfo = res.userInfo
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
})
}
},
onShow: function() {
},
onReady: function() {
var that = this;
setTimeout(function () {
that.setData({
remind: ''
});
}, 500);
},
getUserInfo: function (e) {
var that = this;
this.setUserInfo(e.detail.userInfo);
if (getApp().globalData.openid == null){
wx.showToast({
icon: 'none',
title: '网络错误'
});
return
}
if (this.data.userInfo != null){
this.goToIndex(); //点击拥抱健康时,要经过此处
var timestamp = Date.parse(new Date());
timestamp = timestamp / 1000;
console.log("timestamp = ",timestamp)
wx.cloud.init()
wx.cloud.callFunction({
name: 'searchUser',
data:{
openid: getApp().globalData.openid
},
complete: r1 => {
console.log("searchUser res = ", r1);
var searchRes = r1;
if (r1.result.data.length == 0) {
wx.showToast({
icon: 'loading',
title: '欢迎您首次光临',
mask:true
});
wx.cloud.init()
wx.cloud.callFunction({
name: 'addUser',
data:{
user_id: timestamp,
user_name: getApp().globalData.userInfo.nickName,
user_signature: "这个家伙很懒,什么都没有留下",
user_age: 0,
user_email: "",
user_sex: 1,
user_address: "",
user_tel: "",
user_sicks: [false, false, false, false, false],
user_loginnum: 1,
user_readhelnum: 0,
user_readrecnum: 0,
user_buynum: 0,
user_friends: [],
user_isDoctor: false,
user_isManager:false,
user_images: getApp().globalData.userInfo.avatarUrl,
user_favorite:[],
user_eatHistory:[]
},success: r2 => {
getApp().globalData.user_id = timestamp
getApp().globalData.user_name = getApp().globalData.userInfo.nickName
getApp().globalData.user_isManager = false;
wx.showToast({
icon: 'loading',
title: '注册成功',
mask:true
})
}
});
} else {
wx.showToast({
icon: 'loading',
title: '欢迎回来',
mask:true
});
getApp().globalData.userData_id = searchRes.result.data[0]._id;
getApp().globalData.user_name = searchRes.result.data[0].user_name;
getApp().globalData.user_id = searchRes.result.data[0].user_id;
getApp().globalData.user_friends = searchRes.result.data[0].user_friends;
getApp().globalData.user_isManager = searchRes.result.data[0].user_isManager;
getApp().globalData.user_isDoctor = searchRes.result.data[0].user_isDoctor;
getApp().globalData.user_favorite = searchRes.result.data[0].user_favorite;
getApp().globalData.user_eatHistory = searchRes.result.data[0].user_eatHistory;
wx.cloud.init()
wx.cloud.callFunction({
name:'incLoginNum',
success: res => {
// console.log("自增 :",res)
wx.showToast({
icon: 'loading',
title: '登录成功',
mask:true
})
}
})
}
}
})
}
},
setUserInfo: function (userInfo) {
if (userInfo != null) {
app.globalData.userInfo = userInfo
this.setData({
userInfo: userInfo,
hasUserInfo: true
})
}
}
});
有些地方写的有些冗余,其实一个变量就能解决问题的,但是问题不大。
welcome.html
<!--start.wxml-->
<view class="Welcontainer">
<view class="Welremind-box" wx:if="{{remind}}">
<!-- <button open-type="getUserInfo" bindgetuserinfo="getUserInfo" hidden="true"></button> -->
<image class="Welremind-img" src="images/loading.gif"></image>
</view>
<block wx:else>
<image class="Weltitle" src="images/title8.png"></image>
<view class="Welcontent">
<view class="Welhd" style="transform:rotateZ({{0}}deg);">
<image class="Wellogo" src="{{userInfo.avatarUrl}}"></image>
<image class="Welwave" src="images/wave.png" mode="aspectFill"></image>
<image class="Welwave Welwave-bg" src="images/wave.png" mode="aspectFill"></image>
</view>
<view class="Welbd">
<image class="WelhealthTitle" src="images/confirm-word1.png"></image>
<view class="Welconfirm-btn" >
<!-- -->
<button open-type="getUserInfo" bindgetuserinfo="getUserInfo"style="color:white;background-color: rgba(0,0,0,0);">拥抱健康</button>
<!-- <text >拥抱健康</text> -->
</view>
</view>
</view>
</block>
</view>
welcome.wxss
page{
font-family: -apple-system-font, Helvetica Neue, Helvetica, sans-serif;
font-size: 10pt;
line-height: 150%;
min-height: 100%;
position: relative;
display: flex;
flex-direction: column;
align-items: stretch;
}
.Welcontainer {
position: relative;
flex: 1;
display: flex;
flex-direction: column;
background: #0094ff;
align-items: stretch;
padding: 0;
height: 100%;
overflow: hidden;
justify-content: space-between;
box-sizing: border-box;
}
.Welcontent{
flex: 1;
display: flex;
position: relative;
z-index: 10;
flex-direction: column;
align-items: stretch;
justify-content: center;
width: 100%;
height: 100%;
padding-bottom: 450rpx;
background: -webkit-gradient(linear, left top, left bottom, from(rgba(244,244,244,0)), color-stop(0.1, #f4f4f4), to(#f4f4f4));
opacity: 0;
transform: translate3d(0,100%,0);
animation: rise 3s cubic-bezier(0.19, 1, 0.22, 1) .25s forwards;
}
.Welremind-box {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-bottom: 300rpx;
}
.Welremind-img {
width: 250rpx;
height: 250rpx;
padding-bottom: 25rpx;
}
.Weltitle{
position: absolute;
top: 30rpx;
left: 50%;
width: 600rpx;
height: 200rpx;
margin-left: -300rpx;
opacity: 0;
animation: show 2.5s cubic-bezier(0.19, 1, 0.22, 1) .5s forwards;
}
.WelhealthTitle{
position: absolute;
top: 50rpx;
left: 50%;
width: 600rpx;
height: 200rpx;
margin-left: -300rpx;
opacity: 0;
animation: show 2.5s cubic-bezier(0.19, 1, 0.22, 1) .5s forwards;
}
.Welhd {
position: absolute;
top: 0;
left: 50%;
width: 1000rpx;
margin-left: -500rpx;
height: 200rpx;
transition: all .35s ease;
}
.Wellogo {
position: absolute;
z-index: 2;
left: 50%;
bottom: 200rpx;
width: 160rpx;
height: 160rpx;
margin-left: -80rpx;
border-radius: 160rpx;
animation: sway 10s ease-in-out infinite;
opacity: .95;
}
.Welwave {
position: absolute;
z-index: 3;
right: 0;
bottom: 0;
opacity: 0.725;
height: 260rpx;
width: 2250rpx;
animation: wave 10s linear infinite;
}
.Welwave-bg {
z-index: 1;
animation: wave-bg 10.25s linear infinite;
}
.Welbd {
position: relative;
flex: 1;
display: flex;
flex-direction: column;
align-items: stretch;
animation: bd-rise 2s cubic-bezier(0.23,1,0.32,1) .75s forwards;
opacity: 0;
}
.Welconfirm-btn {
font-size: 13pt;
line-height: 85rpx;
height: 85rpx;
background: #0094ff;
color: #fff;
text-align: center;
border-radius: 100rpx;
margin: 40% 20%;
}
.Welconfirm-btn:active {
opacity: .8;
}
.Welcontainer {
display: flex;
flex-direction: column;
align-items: center;
box-sizing: border-box;
}
@keyframes rise{
0% {opacity: 0;transform: translate3d(0,100%,0);}
50% {opacity: 1;}
100% {opacity: 1;transform: translate3d(0,450rpx,0);}
}
@keyframes bd-rise{
from {opacity: 0; transform: translate3d(0,60rpx,0); }
to {opacity: 1; transform: translate3d(0,0,0); }
}
@keyframes wave{
from {transform: translate3d(125rpx,0,0);}
to {transform: translate3d(1125rpx,0,0);}
}
@keyframes wave-bg{
from {transform: translate3d(375rpx,0,0);}
to {transform: translate3d(1375rpx,0,0);}
}
@keyframes sway{
0% {transform: translate3d(0,20rpx,0) rotate(-15deg); }
17% {transform: translate3d(0,0rpx,0) rotate(25deg); }
34% {transform: translate3d(0,-20rpx,0) rotate(-20deg); }
50% {transform: translate3d(0,-10rpx,0) rotate(15deg); }
67% {transform: translate3d(0,10rpx,0) rotate(-25deg); }
84% {transform: translate3d(0,15rpx,0) rotate(15deg); }
100% {transform: translate3d(0,20rpx,0) rotate(-15deg); }
}
@keyframes show{
0% {opacity: 0;}
100% {opacity: .95;}
}