• 小程序-心理学辞典应用开发的心得


    小程序-心理学辞典应用开发的心得

    前言

    最近学习小程序,用了五天工作之余从0开始,到一个简单的App完成,有点心得,记录下来。

    想法

    最近在学习算法动态规划那部分,有点感悟,不要想着怎么样让整体结果最优,因为在系统足够复杂的情况下太消耗时间。如果按贪婪算法的思路,让每一步都是当下最优,也许结果并不是最优,但也足够优秀了,关键时间很快。
    所以这次写项目改了思路,不像之前设计的很完整后再做,这次只先做最核心的功能,其他功能暂时用简单技术替代,先做出东西来,再慢慢优化。
    坑还是只有走过了才印象深刻。

    预备工作

    前阵子刚学习了心理学皮毛,里面有个心理学词典的内容,我觉得很受用,就像做个词典类的App,让我能时常回顾温习,运气好的话还可以帮助到别人。
    所以我的核心需求就是:做个“心理学辞典”,能够浏览各个词条,并推荐一些文章供扩展阅读。
    花了点时间大致构思了下,我要做的事情有:

    • 设计原型
    • 设计界面
    • 设计数据库
    • 技术选型

      • 浏览一遍小程序文档(不用深入,只需要知道它有哪些能力,在大脑建个索引,需要时能快速查找)
      • 数据库方案
    • 制作测试数据
    • 开发阶段

      • 界面制作
      • 数据
      • 优化
    • 总结归纳

    进入开发

    设计原型

    因为需求简单,原型也不需要高保真,手绘就搞定了。一个搜索框,一个标签列表,一个结果列表。一个词典的内容并不会很多,可以在列表项中直接做展开。
    因为词典数据量有限,选择一次加载完所有的项目(包含_id,name,tags),点击后查询详情缓存结果,再展开。
    搜索有两种模式,普通搜索和标签搜索。为了更好展示,这里设计成,tag搜索也当做条件,只是在前面加个“#”号。
    具体交互效果可查看小程序。
    小程序二维码

    界面设计

    交互稿确定后,去网络上找了几个对眼的设计稿,参考了下,在psd上大致设计了下(设计和PS技术太渣 ( ̄. ̄)|||)

    数据库设计

    App的第一阶段做纯展示,就涉及一张表

    • vocabulary(_id, name, tags, content, remark, links, create_at, update_at)

    技术选型

    小程序的语法很简单,和Vue有点相似,发展到如今有基于ReactTaro,基于Vuempvue/wepy,还有uni-app。可选择的挺多的,但是我任然选择用小程序自带的语法来写,毕竟在用其他语法时,明白其原先的样子总是有好处的。确实在实际过程中发现小程序自身语法设计不足与不同之处。

    数据库方面,小程序最近推出云开发的服务,这东西类似LeancloudBmob这些后端云服务,而且这些第三方的服务对微信支持的也不错。这次项目选用云开发,毕竟是一家的,集成的应该会更好一些。不过实际使用过程中发现这玩意不足的地方很多,大项目慎用。

    因为选用的技术都是没有使用的技术,这里就要用些策略了,我是这样做的。

    • 先把文档大致看下,主要了解下大体情况,了解小程序和云开发的能力,在脑海里建立个索引,知道哪些内容文档里有,在要用到的时候就能快速查找。
    • 遇到云开发增删改查的问题,我是把常用的方法和场景都罗列到自己的笔记本里,为了更方便的查找

    制作测试数据

    数据源主要来自《武志红的心理学课》,在课程末尾老师有把讲到的关键词都罗列出来了。但是有个问题,老师给的那些资料里,都是以图片展示的,如果一个个手动录入也太不像程序员了。于是找了个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(/[
    ]/).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
      }
    }

    总结

    • 虽然初看小程序的语法和vue有点像,实际做起来,和想象的差太多,很多功能不支持,做起来不是那么顺手
    • 云开发的能力也很有限,解决一点简单数据可以,稍微复杂点功能要自己用代码实现
  • 相关阅读:
    内存溢出
    接手新业务
    pjb fabu
    中文手册
    人背的时候,做啥都失败
    帮助开发人员学习
    python中的__dict__,__getattr__,__setattr__
    NetCore在Docker中发布及运行
    ELK基础配置
    IdentityServer4 手动验签及日志记录
  • 原文地址:https://www.cnblogs.com/10manongit/p/12693470.html
Copyright © 2020-2023  润新知