内容简介:微信小程序开发了云开发的功能, 提供了数据库、云函数、储存空间的服务。在此基础上,基本可以用js一把捞一个相对完整的服务,并且省下了发布、部署、运维的繁琐。语言同构、js一把捞, 对前端开发非常友好, 使用js就可以完成整个业务流程。环境整合的非常好, 不用操心各种环境配置、数据库、存储空间、部署、运维的东西,减轻了开发复旦
微信小程序开发了云开发的功能, 提供了数据库、云函数、储存空间的服务。在此基础上,基本可以用js一把捞一个相对完整的服务,并且省下了发布、部署、运维的繁琐。
优点
语言同构、js一把捞, 对前端开发非常友好, 使用js就可以完成整个业务流程。
环境整合的非常好, 不用操心各种环境配置、数据库、存储空间、部署、运维的东西,减轻了开发复旦
自带登录和账号体系,方便接入
缺点
过多的回调、异步导致代码难看切难以维护、难以调试。
云函数的框架过于简陋, 处理复杂业务的时候对代码组织能力要求很高
数据库多表联查的时候很忧伤, 严重影响性能
没有公共代码块, 无法共享通用代码, 难以整合组织代码
产品需求
经过考虑决定做一个小号的微博小程序, 梳理的功能如下
后台部分
根据官方的api文档, 小程序的云函数也是可以通过http调用的,这就给一个独立的后台提供了技术接口。 我们可以通过http调用小程序云函数来读取和操作数据库来实现产品后台的功能,是否可以通过htpps上传图片还待探索
用户部分
用户端的就是一个小号的微博了。 其中,微信提供了一个存储空间,可以方便的进行文件的存储,账号部分可以直接通过微信的鉴权机制,使用openid作为用户id。
原型
设计
不多说, 上设计稿。
开发环境
其实上面的都不重要,坑都在这里
1.更新开发工具, 摆脱回调
这里要分两部分说
- 小程序部分 在最新的微信开发 工具 中, 开发了增强编译的功能, 已经支持async语法了。使用async的好处这里就不在列举了。为了可以使用async来调用微信原生的api。可以引入一个工具, 将回调式的转化为async方式
function promisify (api) { return (options, ...params) => { return new Promise((resolve, reject) => { api(Object.assign({}, options, { success: resolve, fail: reject }), ...params); }); } } 复制代码
// 这样就可以简化微信原生的api const getSetting = promisify(wx.getSetting) const res = await getSetting() 复制代码
但是async 语法也有不好的地方在于异常捕获写起来很难看, 越是复杂的逻辑, 用户捕获异常的catch就越不好处理, 这里可以使用await-to-js来进一步优化
import to from 'await-to-js' // 使用node风格的异常捕获 const [err, res] = await to(getSetting()) 复制代码
- 云函数部分 云函数是一个纯净的node环境, 使用的时候要注意它不支持import语法,在导入的时候需要使用
require
例如const to = require('await-to-js').default;
2.使用字体图标代替切图
小程序推荐使用rpx作为尺寸单位, 切图适配的时候, 小图标可能会在缩放的时候失真,推荐使用字体图标。将制作好的字体图标放入小程序的css文件中就好了,我这里是用的阿里icon的图标库, 将base64的css样式直接放在全局的css中
3.开发工具。
微信开发工具自带了编译和无法替代的云开发工具, 目前是没有办法摆脱它了。 但是在编辑代码的时候还是可以用其他工具的。可以用vscode作为代码编辑攻击, 安装minapp等插件。微信开发工具作为预览和调试的工具就好
4.npm支持,debuger
在云函数中是支持直接使用npm包的, 但是每一个云函数的入口对应一个文件夹, 每一个文件夹需要单独npm。为了可以在本地断点调试云函数,我们必须在云函数下的每一个文件夹install一下。这操作是真的难受。。。
在开发工具中, 右键云函数文件夹可以开启本地调试, 然后就可以在浏览器中打断点了, 同时, console页面也可以看到云函数的console信息, 注意右边可以切换请求方式。
代码组织
先放一下数据库的方案
前文说过, 云函数每一个文件夹视为一个入口文件,并且需要单独install一下。 我们最少需要4张表,最起码的增删改查就需要16个接口, 一个接口一个入口显然不可能, 所以我们可能需要自己做一下路由的设计。
考虑到代码的复用, 我将每一个入口作为一个功能块, 将代码的功能拆分成 service层、router层、controller层. 在小程序调用云函数的时候传入action字符串作为路由的参数。将对数据库的操作细化,作为service便于复用。 在controller里调用service来处理复杂逻辑。
1.根据数据库操作编写service
// user/user.js // UserBase 确定每个集合的数据结构 class UserBase { constructor(data) { this.openId = data.openId this.avatarUrl = data.avatarUrl this.city = data.city this.country = data.country this.gender = data.gender this.language = data.language this.nickName = data.nickName this.province = data.province this.signature = null // 签名 this.watchList = [] // 关注列表 this.releaseList = [] // 发布列表 this.createTime = db.serverDate() this.userStatus = 0 // 用户状态 0 正常 1 限制登录 this.inviteUser = null } } 复制代码
// 这里用来实现对数据库的操作 class User { // 获取用户信息 async getUserInfo(openId) { const [err, {data}] = await to(db.collection('user') .where({ openId: openId }) .get()) if (err) return Promise.reject(err.errMsg) if (Array.isArray(data) && data.length) return data[0] else return null } // 创建新用户 async createUser (userInfo) { let [err, res] = await to(db.collection('user').add({ data: new UserBase(userInfo)})) if (err) return Promise.reject(err.errMsg) return res._id } } 复制代码
2.根据需求调用service的方法, 实现业务
Controller可以通过继承也可以单独引入进来运行
// user/userController.js 这里调用service来处理复杂逻辑 // 根据情况也会调用其他的模块, 这个时候就用callFunction // 云函数入口文件 const User = require('./user.js') const cloud = require('wx-server-sdk') cloud.init({ env: 'prod-4ygqk'}) const to = require('await-to-js').default; class UserController extends User { // 处理登录 async handelLogin(event) { const { OPENID } = event const [err, res] = await to(this.getUserInfo(OPENID)) if (err) return {err, res} if (res) return {err, res} if (!event.loginInfo) { return {err: '注册用户失败,请先调用wx.userInfo接口', res} } event.loginInfo.openId = OPENID return this.handelCreateUser(event) } // 注册用户 async handelCreateUser(event) { const { loginInfo, OPENID } = event const [err, res] = await to(this.createUser(loginInfo)) if (err) return {err, res} const [_err, _res] = await to(this.getUserInfo(OPENID)) if (_err) return {err, res: _res} return {err, res: _res} } // 通过openId查询用户 async queryUserByOpenid(event) { const { targetUser, OPENID} = event // 如果没有传openId进来, 就查询当前用户 const seatchOpenId = targetUser || OPENID const [err, res] = await to(this.getUserInfo(seatchOpenId)) if (err) return {err, res} // 查询是否关注过该用户 if (res && targetUser) { const [error,{result}] = await to(cloud.callFunction({ name: 'attention', data: { action: 'queryIsAttention', currentUser: OPENID, targetUser: targetUser } })) if (error) return {err: error, res: result} res.isAttention = result.res } return { res: res, err: null } } } module.exports = UserController 复制代码
3.根据路由指派控制器
这里我们将openId作为用户识别码挂载在event上, 供控制器使用 在Controller需要调用其他模块的Controller时, 也将所需的参数挂载在event上。 这里的event实际上就是这次请求的上下文
// 云函数入口文件 user/index.js const cloud = require('wx-server-sdk') cloud.init({ env: 'prod-4ygqk'}) const UserController = require('./userController.js') // 云函数入口函数 exports.main = async (event, context) => { const wxContext = cloud.getWXContext() const { OPENID, APPID } = wxContext event.OPENID = OPENID const user = new UserController() const { action } = event switch (action) { case 'login': { return user.handelLogin(event) }; case 'createUser': { return user.handelCreateUser(event) }; case 'queryUserByOpenid': { return user.queryUserByOpenid(event) }; } } 复制代码
4.其他复杂情况
- 需要调用其他模块的
async queryArticlebyId(event) { const { OPENID, id } = event // 查询对应的文章内容 let [err, res] = await to(article.queryArticlebyId(id)) // 有错误返回错误, 没有查到返回空 if (err) return {err, res} if (!res) return {err, res} // 查询文章用户, 调用user模块 const [error, { result }] = await to(cloud.callFunction({ name: 'user', data: { action: 'queryUserByOpenid', currentUser: event.OPENID, targetUser: res.openId } })) // 调用userInfo错误 if (error) return {err: error, res} // userInfo自己的错误 if (result.err) return {err: result.err, res: res} res.user = result.res // 查询用户是否已经收藏, 调用收藏模块 const [collectionErr, collectionRes] = await to(article.queryIsCollection(OPENID, id)) res.isCollection = collectionRes return {res, err: null} } 复制代码
-
需要异步循环的
异步迭代一直是一个问题。一方面涉及到并发和效率, 另一方面又涉及到异常处理。 如果按照线性的async方式查询会严重的影响到速度。 所以必须使用Promise.all的方式。 以下代码缺少异常处理
async queryArticleAll(event) { const { userInfo, size = 10, page, sort = 'desc', orderBy = 'createTime' } = event let ArticleList = await article.queryArticleAll({ size, page, sort, orderBy }) if (ArticleList.length === 0) { return [] } const funList = ArticleList.map(async article => { // 查询文章的作者 const [err,{result}] = await to(cloud.callFunction({ name: 'userInfo', data: { action: 'queryUserByOpenid', currentUser: event.OPENID, targetUser: article.openId } })) let user = result.res || null article.user = user return article }) return {res: await Promise.all(funList), err: null} } 复制代码
最后
其他就没有啥子东西可写了。后面使用http调用云函数的以后再继续尝试。 在调试云函数的时候几乎花掉了全部的耐心....最后贴代码和地址
https://github.com/xanggang/picture-miniapp 复制代码
最近微信关闭了改名的通道, 暂时改不了名字和头像了...
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- ABP开发框架前后端开发系列---(14)基于Winform的ABP快速开发框架
- Java开发人员的Flex开发
- Java开发人员的Flex开发
- 行为驱动开发让开发做正确事
- 让开发者专注于应用开发,OpenCenter 3.0 开发者预览版发布
- 让开发者专注于应用开发,OpenCenter 3.0 开发者预览版发布
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。