1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > 为女朋友写一个小程序(四)— —前端小程序的设计与实现

为女朋友写一个小程序(四)— —前端小程序的设计与实现

时间:2020-05-24 00:25:30

相关推荐

为女朋友写一个小程序(四)— —前端小程序的设计与实现

为女朋友写一个小程序(一)— —目的与需求

为女朋友写一个小程序(二)— —数据库设计

为女朋友写一个小程序(三)— —基于springboot的服务器端接口设计与实现

为女朋友写一个小程序(四)— —前端小程序的设计与实现(本文)

为女朋友写一个小程序(五)— —如何用docker简化部署

为女朋友写一个小程序(六)— —结合docker实现devOps

为女朋友写一个小程序(七)— —优化引进redis(未编码,未写)

为女朋友写一个小程序(八)— —基于moongodb实现即时通讯(未编码,未写)

后半年一直出差,几乎没时间书写博客,趁现在空档期,把

拖了许久的文章继续写完吧…还是要保持写文章的习惯呀,做过的项目很久没回顾回顾起来确实也需要一定时间…记忆力这东西

一、前端实现结果展示

(以首页,任务页,商城列表,兑换列表为例)

二、技术选型方案

1、为什么选择微信小程序?

因为开发的时候想到小程序是那个时候的风口,把玩一下小程序是一个技术人的乐趣。

对于这样一个简单的程序适合于寄生于一个平台,以平台为入口进行开发,可以节省许多其他不必要的环节(如使用原生需要考虑如何被下载,应用上下架的问题)。

小程序其实也是基于B/S结构,其开发的使用的自身的框架,但是其实说白了跟使用HTML+JS+CSS开发区别其实不是很大,因为之前在工作中有过前端开发的基础,尚于对前端框架的使用,熟悉JS,入门起来可以比较快速。

所以考虑、了解了几天之后决定使用小程序作为该程序的前端交互。

2、使用的技术栈是什么?

技术栈使用的是:wxss+weUI

使用wxss这是没办法,开发小程序是腾讯限死一定要使用这样的框架(不像我司、支持我司开发的框架,同时支持普通的HTML+CSS,原生等),开发起来具有一定的局限性,且要开发一定要先过一次小程序开发文档,需要消耗一定的时间,但是因为之前玩过VUE这样MVVC前端框架,一通百通,所以接触起来也不困难。

使用weUI是因为之前用VUE开发的时候已经有很多开源的UI框架,最初的版本也是自己用原生的wxss的组件去画,但是因为前端基础还是比较薄弱,所以找到了小程序的UI框架,weUI,其界面简洁,语法简单,真是居家旅行,外包必备的一个好框架。

3、根据技术栈如何进行技术储备?

3.1、认识前端开发

如果你对于前段开发还是不熟悉的话,那做起来肯定会比较吃力,博主提过,之前是玩过VUE开发,参考文章,所以具备一定的前端开发能力是必要的,最好是在玩过VUE等这样的MVVC框架之后,接触小程序就相当简单了,因为其思想都是差不多的,开发“全家桶”也是差不多,只是语法不同罢了。

3.2、认识微信小程序

对小程序的开发首先肯定要对小程序进行了解,了解的时候主要还是以官方文档为主,无论如何一定要过一遍官方文档,里面会提及许许多多的细节,是你在设计方案的时候想不到的,如:接服务器端时,服务器端一定是要使用https协议,且服务器端地址展示出来一定要是一个域名,否则无法使用其原生接口发起请求…

3.3、认识weUI

weUI是一个UI框架官方文档,UI框架的入手过官方文档帮助其实不大,像我就直接下demo,了解一下如何接入该框架,然后根据我设计的界面找到响应的组件,然后demo代码直接copy上,然后再进行调整,这样对一个小项目来说是最快的。

3.4、IDE选择

小程序IDE是我见过的最烂的IDE没有之一,除了一个好处就是支持预览与远程调试。但是对于经常使用webStom的开发者来说非常不习惯,快捷键极少,习惯难以切换,最开始一段时间开发起来是比较慢和吃力。

后面我直接用webStom打开整个工程,在webStom进行编码,然后在小程序IDE进行调试,效率提升了不止一倍。我建议大家也可以这样玩。

三、前端代码的实现

1、整体开发架构规划,模块划分

小程序开发时,项目标准结构腾讯已经帮我们规划好了,这边开发是根据一些对象功能定义不同而简单划分出各个模块。小程序项目整体架构如下图所示:

1.1、小程序全局对象

整个“小程序”在项目中就是一个全局对象,所有的逻辑定义都在该全局对象中的,这个大家可以细看官方文档。这边主要使用到全局对象中(app.js)的东西是globalData用来装一些全局使用的变量,还有启动时一些操作、如获取高度、宽度,自动登录等操作。关于app.json就不解析,这个是关于布局亦可细看官方文档。

1.2、页面展示与交互逻辑模块

页面展示、交互逻辑这块曾经重构过一次、最开始的版本是所有页面都在/page目录下,到后面页面层次一多起来,维护起来看起来非常复杂,所以下了决心重构了一次,使用目录的层级体现页面的层级。

页面展示、交互逻辑模块这一块就是小程序说的MVVC结构,中规中举,下面给出登录页面的代码,简单展示一下MVVC结构。

展示页面login.wxml

<view class='login-wrapper' style='height:{{viewHeight}}px;width:{{viewWidth}}px'><view class="login-icon"><image class="login-img" src="../../static/images/icon-logo.png"></image></view><view class="login-from"><!--账号--><view class="inputView"><image class="nameImage" src="../../static/images/icon-account.png"></image><label class="loginLab">账号</label><input class="inputText" value="{{account}}" placeholder="请输入账号" maxlength="11" bindinput="handleInputAccount" /></view><view class="line"></view><!--密码--><view class="inputView"><image class="keyImage" src="../../static/images/icon-password.png"></image><label class="loginLab">密码</label><input class="inputText" password="true" value="{{password}}" maxlength="20" placeholder="请输入密码" bindinput="handleInputPassword" /></view><!--按钮--><view class="loginBtnView"><button type="primary" bindtap="handleTapLogin">登录</button></view></view><view class="weui-footer weui-footer_fixed-bottom"><view class="weui-footer__text">粤ICP备18035307号</view></view></view>

类CSS的wxss,为wxml穿衣服login.wxss

/*登录图片*/.login-wrapper {background-color: white}.login-icon {text-align: center;background-color: #fff}.login-img {width: 250px;height: 250px;}/*表单内容*/.login-from {flex: auto;}.inputView {background-color: #fff;line-height: 44px;}/*输入框*/.nameImage, .keyImage {margin-left: 22px;width: 14px;height: 14px;}.loginLab {margin: 15px 15px 15px 10px;color: #545454;font-size: 14px;}.inputText {flex: block;float: right;text-align: right;margin-right: 22px;margin-top: 11px;color: #ccc;font-size: 14px;}.line {width: 100%;height: 1px;background-color: #ccc;margin-top: 1px;}/*按钮*/.loginBtnView {width: 100%;height: auto;background-color: #f2f2f2;margin-top: 0px;margin-bottom: 0px;padding-bottom: 0px;}.loginBtn {width: 80%;margin-top: 35px;}

主要的交互逻辑,控制层login.js

// pages/login/login.jslet userLoginObj = require('../../request/user/login.js')Page({/*** 页面的初始数据*/data: {account:'',password:'',viewHeight: 0,viewWidth: 0,requestBuilder: {},userDao:{},router: {}},//登录控制handleTapLogin(){if (this.validate()) { //数据校验userLoginObj.data = { account: this.data.account,password:this.data.password}let that = this wx.request(this.data.requestBuilder(userLoginObj,(res)=>{if(res.data.status){console.log('登录成功')//存储帐号与密码let security = { account: that.data.account, password: that.data.password}that.data.userDao.setSecurity(security)//存储用户信息that.data.userDao.setUser(res.data.data)console.log('页面跳转')that.data.router.toTapTargetTargetList()// that.data.router.toTapShopRewardList()// that.data.router.toTapShopExchangeDetailList()// that.data.router.toTapSupervisionRewardList()// that.data.router.toTapSupervisionRewardAdd()//that.data.router.toTapSupervisionTargetList()}else{//失败了wx.showToast({title: res.data.message,icon:'none'})return}}))}else{}},//form校验validate(){if(this.data.account==''){wx.showToast({title: '账户不能为空',icon: 'none'})return false}if(this.data.password==''){wx.showToast({title: '密码不能为空',icon: 'none'})return false}return true},handleInputAccount(even){this.setData({account:even.detail.value})},handleInputPassword(even){this.setData({password:even.detail.value})},onShow(){let account = this.data.userDao.getAccount()let password = this.data.userDao.getPassword()if (account != null && password != null) {this.setData({account:account,password:password})this.handleTapLogin()} },onLoad(){let app = getApp()//定义高度与宽度this.setData({viewHeight: app.globalData.viewHeight,viewWidth: app.globalData.viewWidth,requestBuilder: app.globalData.requestBuilder,userDao: app.globalData.userDao,router: app.globalData.router})}})

login.json没有对页面定义什么内容,故不做展示

1.3、请求、与服务器端交互模块

在上述的login.js中大家应该也看到与服务器端请求逻辑,这里不外乎也是使用wx.request(obj)进行请求,这里我用了类VUE axios的思想设计,把每个请求都定义成一个对象,称为请求对象。再由一个工厂类,对请求对象进行封装一层,成为wx.request(obj)要求的标准对象,这样设计的一个考虑,就是为了把每个不同请求进行解耦。

为了更好说明刚刚的设计理念,以登录接口为例进行代码展示,先来看看请求对象工厂类

requestObjBuilder.js

let config = require('../../config/config.js')let userDao = require('../../store/user-dao.js')let router = require('../../router/router.js')module.exports=function(baseRequestObj,success,fail,complete){//复制一个传递进来的请求对象baseRequestObj = JSON.parse(JSON.stringify(baseRequestObj))//定义请求头baseRequestObj.header.KIKI_AUTH_TOKEN = userDao.getToken()//解耦域名基础路径baseRequestObj.url = config.BASE_SERVICE_PATH + baseRequestObj.url//定义全局错误策略,与成功策略let baseSuccess = (res)=>{if (res.statusCode!=200){wx.showToast({title: '请求失败了:' + res.statusCode,icon: 'none',duration: 2000})}else{//res.data!=undefined 下载接口是没有data的if (res.data!=undefined && res.data.code == 401){//尚未登录router.toLogin()}else{if (typeof success === "function") {success(res)} }}}baseRequestObj.success = baseSuccessif (typeof fail === "function"){baseRequestObj.fail = fail}else{let defaultfail = (err)=>{console.log(err)wx.showToast({title: '服务器挂了:' + err.errMsg,icon:'none',duration:2000})}baseRequestObj.fail = defaultfail}if (typeof complete === "function")plete = completereturn baseRequestObj}

再来看看设计的请求对象是怎样的

login.js

let requestObj = {//请求实体url: '/user/login',//请求地址data:null,//请求的参数header:{'content-type': 'application/json' // 默认值},//请求头method: "POST"//请求方法}module.exports = requestObj

把requestBuilder注入data中,当我需要对用户进行登录时,我可以使用以下方法进行登录,成功的解耦

wx.request(this.data.requestBuilder(userLoginObj,(res)=>{if(res.data.status){console.log('登录成功')//存储帐号与密码let security = { account: that.data.account, password: that.data.password}that.data.userDao.setSecurity(security)//存储用户信息that.data.userDao.setUser(res.data.data)console.log('页面跳转')that.data.router.toTapTargetTargetList()// that.data.router.toTapShopRewardList()// that.data.router.toTapShopExchangeDetailList()// that.data.router.toTapSupervisionRewardList()// that.data.router.toTapSupervisionRewardAdd()//that.data.router.toTapSupervisionTargetList()}else{//失败了wx.showToast({title: res.data.message,icon:'none'})return}}))}else{}

1.4、页面之间的路由跳转模块

页面之间的路由跳转小程序是提供了标准的接口,参考导航,但是我感觉这个处理不是很优雅,因为这样需要在不同页面里面写入其他页面的地址,所以我干脆定义一个全局对象,使用方法进行跳转,见代码:

router.js

const loginPath = '/pages/login/login'const tapTargetTargetList = '/pages/tap-target/target-list/target-list'const tapShopRewardList = '/pages/tap-shop/reward-list/reward-list'const tapTargetTargetDetail = '/pages/tap-target/target-detail/target-detail'const tapSupervisionTargetDetail = '/pages/tap-supervision/target-detail/target-detail'const tapTargetTargetComplete = '/pages/tap-target/target-complete/target-complete'const tapSupervisionReviewedList = '/pages/tap-supervision/reviewed-list/reviewed-list'const tapSupervisionTargetList = '/pages/tap-supervision/target-list/target-list'const tapSupervisionRewardList = '/pages/tap-supervision/reward-list/reward-list'const tapSupervisionRewardAdd = '/pages/tap-supervision/reward-add/reward-add'const persionPath = '/pages/tap-persion/persion/persion'const tapPersionReviewedList = '/pages/tap-persion/reviewed-list/reviewed-list'const toTapPersionExchangeList = '/pages/tap-persion/exchange-list/exchange-list'const tapSupervisionExchangeList = '/pages/tap-supervision/exchange-list/exchange-list'const rewardPath = '/pages/reward/reward'const tapShopExchangeDetail = '/pages/tap-shop/exchange-detail/exchange-detail'const tapPersionExchangeDetail = '/pages/tap-shop/exchange-detail/exchange-detail'let router = {toLogin() {wx.reLaunch({url: loginPath,})},toTapTargetTargetList() {wx.switchTab({url: tapTargetTargetList})},toTapShopRewardList() {wx.switchTab({url: tapShopRewardList})},toTapTargetTargetComplete(params) {console.log(params)let url = tapTargetTargetComplete;if (params instanceof Array) {if (params.length > 0) {url = url + '?'let key = null;let value = null;for ({key, value} of params) {url = url + key + '=' + value + '&'}url.substring(0, url.length - 1)}}wx.navigateTo({url: url,})},toTapSupervisionReviewedList() {wx.navigateTo({url: tapSupervisionReviewedList,})},toTapShopExchangeDetail(params) {let url = tapShopExchangeDetail;if (params instanceof Array) {if (params.length > 0) {url = url + '?'let key = null;let value = null;for ({key, value} of params) {url = url + key + '=' + value + '&'}url.substring(0, url.length - 1)}}wx.navigateTo({url: url,})},toTapPersionExchangeDetail(params) {let url = tapPersionExchangeDetail;if (params instanceof Array) {if (params.length > 0) {url = url + '?'let key = null;let value = null;for ({key, value} of params) {url = url + key + '=' + value + '&'}url.substring(0, url.length - 1)}}wx.navigateTo({url: url,})},toTapSupervisionTargetList() {wx.navigateTo({url: tapSupervisionTargetList,})},toTapSupervisionRewardAdd() {wx.navigateTo({url: tapSupervisionRewardAdd,})},toTapTargetTargetDetail(params) {console.log(params)let url = tapTargetTargetDetail;if (params instanceof Array) {if (params.length > 0) {url = url + '?'let key = null;let value = null;for ({key, value} of params) {url = url + key + '=' + value + '&'}url.substring(0, url.length - 1)}}wx.navigateTo({url: url,})},toTapSupervisionTargetDetail(params) {console.log(params)let url = tapSupervisionTargetDetail;if (params instanceof Array) {if (params.length > 0) {url = url + '?'let key = null;let value = null;for ({key, value} of params) {url = url + key + '=' + value + '&'}url.substring(0, url.length - 1)}}wx.navigateTo({url: url,})},toTapPersionReviewedList() {wx.navigateTo({url: tapPersionReviewedList,})},toTapPersionExchangeList() {wx.navigateTo({url: toTapPersionExchangeList,})},toTapSupervisionExchangeList() {wx.navigateTo({url: tapSupervisionExchangeList,})},toTapSupervisionRewardList() {wx.navigateTo({url: tapSupervisionRewardList,})},}module.exports = router

其他页面需要跳转时,使用以下的方式

console.log('页面跳转')that.data.router.toTapTargetTargetList()

1.5、其他模块

1.5.1、全局配置

主要定义了服务器的基础路径,没有其他的东西

1.5.2、页面之间的交互

这里是一个比较有趣的问题,就是比如你在添加任务的页面完成一个任务添加时,需要通知任务列表去刷新,拉取刚刚任务。小程序在页面切换的时候是不会主动刷新的,切过去是你上一次点击看到的内容,所以需要一个通知机制来做主动刷新操作,所以就设计了这个模块。

其主要也是通过globalData的字段来体现,当页面被调到栈顶的时候,主动监测一下是否需要刷新,要的话就先刷新数据再展示,否则还是展示之前内容。下面看看代码

targetTargetListInteractive.js

module.exports={isReload(){return getApp().globalData.isTargetTargetListReload;},setReload(){getApp().globalData.isTargetTargetListReload=true;},resetReload(){getApp().globalData.isTargetTargetListReload = false;},isPartRefresh(){return getApp().globalData.targetTargetListPartRefresh.length>0},setPartRefresh(target){getApp().globalData.targetTargetListPartRefresh.push(target)},resetPartRefresh(){let array = getApp().globalData.targetTargetListPartRefreshgetApp().globalData.targetTargetListPartRefresh=[]return array}}

看看任务列表页如果监听这个对象的机制

target-list.js

.....onShow(){if (supervisionTargetListInteractive.isReload()) {//判断是否整个页面刷新//获取数据wx.pageScrollTo({scrollTop: 0,duration: 0})this.resetPage()this.loadTable()supervisionTargetListInteractive.resetReload()} else {//判断是否局部刷新if (supervisionTargetListInteractive.isPartRefresh()) {let newTargets = supervisionTargetListInteractive.resetPartRefresh()for (let target of newTargets) {for (let i = 0; i < this.data.targets.length; i++) {if (this.data.targets[i].id == target.id) {this.setData({['targets[' + i + ']']: target})}}}}}}......

而在添加任务页面成功添加任务之后,需要调一下这个方法让任务列表刷新

//让任务列表刷新targetListInteractive.setReload()

1.5.5、缓存模块

缓存模块主要是使用wx.setStorageSync()方法来进行本地缓存,主要是缓存用户数据。在开发的过程中,因为作者的手机网络比较慢,下载图片会很卡,每次读商城都要卡等一段时间,所以使用到了本地缓存,只要资源被下载过,就会缓存,不会二次下载。

store.js

let storeDownloadObj = require('../request/store/download-file')let requestBuilder = require('../request/factory/requestObjBuilder')let storeDao = {setStore(storeId,path){let stores = wx.getStorageSync('store')||{};stores[storeId]=pathwx.setStorageSync('store', stores)},getStore(storeId){let stores = wx.getStorageSync('store')||{};return stores[storeId];},//下载图片资源并存储downloadPicture(storeId){let that = thislet requetObj = JSON.parse(JSON.stringify(storeDownloadObj))requetObj.url = requetObj.url + "?storeId=" + storeId + ""wx.downloadFile(requestBuilder(requetObj, (res) => {console.log(res);console.log('下载资源:' + storeId + ' 成功,' + '临时目录:' + res.tempFilePath)//存储到本地wx.saveFile({tempFilePath: res.tempFilePath,success: function (res) {console.log('存储到本地成功')that.setStore(storeId, res.savedFilePath)}})}))}}module.exports = storeDao

1.5.6、工具

一些常用的工具对象,如时间转换等…

果然,唯有代码可以让我找回初心,那一个热爱编程的初生牛犊不怕虎的想进BAT男孩,hhhhh可是现在不是很想了,三个小时的回顾,1w+字,如果对您有帮助,希望给我一分鼓励~愿你我皆不忘初心!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。