内容简介:微信小程序开发了云开发的功能, 提供了数据库、云函数、储存空间的服务。在此基础上,基本可以用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 复制代码
最近微信关闭了改名的通道, 暂时改不了名字和头像了...
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Head First Rails
David Griffiths / O'Reilly Media / 2008-12-30 / USD 49.99
Figure its about time that you hop on the Ruby on Rails bandwagon? You've heard that it'll increase your productivity exponentially, and allow you to created full fledged web applications with minimal......一起来看看 《Head First Rails》 这本书的介绍吧!