最近学习小程序,用了五天工做之余从0开始,到一个简单的App完成,有点心得,记录下来。html
最近在学习算法动态规划那部分,有点感悟,不要想着怎么样让总体结果最优,由于在系统足够复杂的状况下太消耗时间。若是按贪婪算法的思路,让每一步都是当下最优,也许结果并非最优,但也足够优秀了,关键时间很快。
因此此次写项目改了思路,不像以前设计的很完整后再作,此次只先作最核心的功能,其余功能暂时用简单技术替代,先作出东西来,再慢慢优化。
坑仍是只有走过了才印象深入。vue
前阵子刚学习了心理学皮毛,里面有个心理学词典的内容,我以为很受用,就像作个词典类的App,让我能时常回顾温习,运气好的话还能够帮助到别人。
因此个人核心需求就是:作个“心理学辞典”,可以浏览各个词条,并推荐一些文章供扩展阅读。
花了点时间大体构思了下,我要作的事情有:程序员
技术选型算法
开发阶段数据库
由于需求简单,原型也不须要高保真,手绘就搞定了。一个搜索框,一个标签列表,一个结果列表。一个词典的内容并不会不少,能够在列表项中直接作展开。
由于词典数据量有限,选择一次加载完全部的项目(包含_id,name,tags),点击后查询详情缓存结果,再展开。
搜索有两种模式,普通搜索和标签搜索。为了更好展现,这里设计成,tag搜索也当作条件,只是在前面加个“#”号。
具体交互效果可查看小程序。 json
交互稿肯定后,去网络上找了几个对眼的设计稿,参考了下,在psd上大体设计了下(设计和PS技术太渣 ( ̄. ̄)|||)小程序
App的第一阶段作纯展现,就涉及一张表后端
小程序的语法很简单,和Vue有点类似,发展到现在有基于React
的Taro
,基于Vue
的mpvue
/wepy
,还有uni-app
。可选择的挺多的,可是我任然选择用小程序自带的语法来写,毕竟在用其余语法时,明白其原先的样子老是有好处的。确实在实际过程当中发现小程序自身语法设计不足与不一样之处。 数组
数据库方面,小程序最近推出云开发
的服务,这东西相似Leancloud
,Bmob
这些后端云服务,并且这些第三方的服务对微信支持的也不错。此次项目选用云开发
,毕竟是一家的,集成的应该会更好一些。不过实际使用过程当中发现这玩意不足的地方不少,大项目慎用。 promise
由于选用的技术都是没有使用的技术,这里就要用些策略了,我是这样作的。
数据源主要来自《武志红的心理学课》,在课程末尾老师有把讲到的关键词都罗列出来了。可是有个问题,老师给的那些资料里,都是以图片展现的,若是一个个手动录入也太不像程序员了。因而找了个Mac下好用的OCR工具iText
,App Store里能够下载到。如此一来静态文本数据就不是问题了,可是要怎么入库呢?
云开发
里有个经过CSV或JSON上传数据的功能。那么咱们就作个JSON文件吧。
又有个问题,在content
字段里存的是比较复杂的文本数据,JSON文件处理字符串麻烦。因此我借用yaml,把数据卸载yml文件里,而后写个函数把结果翻译成JSON
这里记录几个开发过程当中遇到的几个问题
由于云开发有对取数据作限制,客户端请求数据最多每次20条,云函数每次100条。由于个人需求是取出全部词条的基本数据,因此须要作点工做,我拿官方的代码稍做修改:
// 云函数目录/db_get_many_record/index.js const cloud = require('wx-server-sdk') cloud.init() const db = cloud.database() const MAX_LIMIT = 100 // 云函数一次最多获取100条数据 /** * 取出多条数据 * wx.cloud.callFunction({ name: 'db_get_many_record', data: { collection: 'vocabulary', field: { name: true, tags: true }, } }).then().catch() */ exports.main = async (event, context) => { const { collection = "", where, field } = event let commond = db.collection(collection) if (collection === ""){ return; } if(where){ commond = commond.where(where) } // 先取出集合记录总数 const { total } = await commond.count() // 计算需分几回取 const batchTimes = Math.ceil(total / 100) // 承载全部读操做的 promise 的数组 const tasks = [] if (field) { commond = commond.field(field) } for (let i = 0; i < batchTimes; i++) { const promise = commond.skip(i * MAX_LIMIT).limit(MAX_LIMIT).get() tasks.push(promise) } // 等待全部 return (await Promise.all(tasks)).reduce((acc, cur) => { return { data: acc.data.concat(cur.data), errMsg: acc.errMsg, } }) }
为了减小请求的次数,提示性能,缓存数据是必要的。我利用app.globalData
,和微信提供的Storage
能力。
原本是想用Map类型来作数据存储的,可是Storage不支持这种数据类型。
内容是分段落的,可是小程序自己是不支持html标签的,但还好有个rich-text
的组件能够用。
如何把本身收集的内容翻译成html的内容?写个脚本遍历下。固然后期会写个可视化的界面来管理内容。
const fs = require('fs'); const readYaml = require('read-yaml'); readYaml('./data.yml', function(err, data) { if (err) throw err; // 处理content内容 let result = data.map(item => { let _content = item.content.split(/[\r\n]/).map(item => { return '<p style="margin-bottom: .8em;">' + item + '</p>' }) item.content = _content.join("") return item }); fs.writeFileSync('./result.json', JSON.stringify(result)); });
const db = wx.cloud.database() const _ = db.command const xxCollection = db.collection('collection') /////////////////////////////////////// 查询记录 /////////////////////////////////////// xxCollection.doc('id').get() // ID查询 db.collection('collection').doc('id').get().then(res => {}).catch(error => {}) // 条件查询 db.collection('collection').where({}).get() db.collection('collection').where({name:_.eq('xxx')}).get() // _.eq neq lt lte gt gte in nin and or // 限制返回字段 db.collection('collection').where({}).field({name: true}).get() // 排序 db.collection('collection').where({}).orderBy('field', 'asc|desc').get() // 分页 db.collection('collection').where({}).skip(0).limit(10).get() // 统计 db.collection('collection').where({}).count() /////////////////////////////////////// 新增记录 /////////////////////////////////////// db.collection('collection').add({ data:{ update_at: db.serverDate(), location: db.Geo.Point(113, 23) } }) /////////////////////////////////////// 更新数据 /////////////////////////////////////// db.collection('collection').where({}).update({}) db.collection('collection').doc('id').set({}) // 替换记录 db.collection('collection').doc('id').update({ data:{ // _.inc(自增) _.set(更新对象使用) _.push _.pop _.shift _.unshift(数组) } }) /////////////////////////////////////// 删除数据 /////////////////////////////////////// db.collection('collection').doc('id').remove() db.collection('collection').where({}).remove() // 目前只能在云函数里操做 //////////////////////////////////////// 文件 ///////////////////////////////////////// wx.chooseImage({ success: chooseResult => { // 将图片上传至云存储空间 wx.cloud.uploadFile({ // 指定上传到的云路径 cloudPath: 'my-photo.png', // 指定要上传的文件的小程序临时文件路径 filePath: chooseResult.tempFilePaths[0] }) }, }) wx.cloud.downloadFile({ fileID: '', }) wx.cloud.deleteFile({ fileID: '', }) // 换取临时连接 wx.cloud.getTempFileURL({ fileList: [], }) /////////////////////////////////////// 云函数 //////////////////////////////////////// wx.cloud.callFunction({ name: 'methodName', data: { a: 12, }, success: res => {}, fail: error => {}, complete: () => {} }) wx.cloud.callFunction({ name: 'methodName', data: { a: 12, } }).then(res => {}).catch(error => {}) // error: errCode errMsg /////////////////////////////////////// 错误参考 /////////////////////////////////////// error => { let message; switch (error.errCode) { case -1: message = '通用错误' break case -401001: message = '无权限使用 API' break case -401002: message = 'API 传入参数错误' break case -401003: message = 'API 传入参数类型错误' break case -402001: message = '检测到循环引用' break case -403001: message = '上传的文件超出大小上限' break case -404001: case -404002: case -404003: case -404004: case -404005: case -404006: case -404007: case -404008: case -404009: message = '云函数调用失败' break case -404010: message = '云函数执行失败' break case -601004: message = '无权限使用 API' break case -501001: message = '云端系统错误' break case -501002: message = '云端响应超时' break case -501003: message = '请求次数超出环境配额' break case -501004: message = '请求并发数超出环境配额' break case -501005: message = '环境信息异常' break case -501009: message = '操做的资源对象非法或不存在' break case -502001: message = '数据库请求失败' break case -502002: message = '非法的数据库指令' break case -502003: message = '无权限操做数据库' break case -502005: message = '集合不存在' break case -503001: message = '云文件请求失败' break case -503002: message = '无权限访问云文件' break case -503003: message = '文件不存在' break case -504001: message = '云函数调用失败' break case -504002: message = '云函数执行失败' break default: message = error.errMsg break } }