帐号管理小程序——开发总结

从零开始写小程序,项目地址:GItHub博客地址

1. 开发前期

想法:

最初的想法是想做一个管理手机号码所绑定的APP,通过自动更换手机绑定的过程,减少换手机号码锁带来的迁移成本。打算通过爬虫区自动去尝试常用应用的帐号是否绑定该手机号码,但是这样的效率太差了,并且那些网站都有做相应的保护,难以完成。

后面了解到了中国移动,开发了一个名为移动认证的应用,正在减弱密码的使用,使用一个帐号九可以登录所有的应用,同样增加了用户的迁移成本。

后面呢,随着前端开发的火热,以及微信应用的爆发。了解和接触前端开发似乎成为一个开发者必不可少的能力,抱着多多接触新技术和增长能力的想法,打算开发一个微信小程序作为前端的入门。

互联网的发展,使我们增添了许多获取信息的渠道,同样使得网络安全以及帐号安全成为一个主要问题。纷繁复杂的应用,和许多密码,通过人工记忆许多无规则的密码显然是不够合理的。使用同一个密码容易使得帐号盗号撞库发生。

综上所述,开发一个以可以记录,保存,备份,加密的帐号管理软件成为了一个较好的方式。

参考:

在得到想法以后,参考了几个同样具有帐号管理的微信小程序,作为出发点。

准备:

2. 开发过程

2.1 框架

使用的开发框架:

  • 使用WEUI来制作UI界面
  • 微信的Markdown解析wxParse
  1. 该程序是使用WEUI来制作,大大降低了UI开发的难度,通过参考WEUI的代码,搭建出小程序的基本界面
  2. 实现每一项功能,实现数据存储
  3. 挖掘小程序的API,实现更多的功能,同时不断提高用户的体验
  4. 重构应用

其他开发框架:

  • zanui-weapp
  • TouchUI,能实现一套代码开发微信小程序和HTML页面
  • 微信小程序-腾讯云解决方案,简化了小程序后端开发的难度
  • MpVue,一款让小程序支持组件化开发的框架,通过预编译的手段让开发者可以选择自己喜欢的开发风格去开发小程序。框架的细节优化,Promise,Async Functions的引入都是为了能让开发小程序项目变得更加简单,高效。
  • Wepy,是一个使用 Vue.js 开发小程序的前端框架。框架基于 Vue.js 核心,mpvue 修改了 Vue.js 的 runtime 和 compiler 实现,使其可以运行在小程序环境中,从而为小程序开发引入了整套 Vue.js 开发体验。

开发框架总结:

从前端发展出来的微信小程序框架,可以看出,构建一个程序的方法有很多,重要的你的想法和逻辑,一个好的框架和完善的文档,能够让你快速的完成开发过程。

到目前的移动端(iOS,Android)同样可以通过前端来构建,比如目前火热的Flutter,可以使用一套代码构建出安卓和iOS端的APP,大大地减少了开发的难度和成本。缺点在于无法做到原生端那么的流畅和精美。但对于简单的开发需求完全可以胜任。

比如Alibaba的Wexx,框架可以构建三端的应用(iOS,Android,WEB)

从前端、移动端、WEB端的各种生态系统里,相同的是业务逻辑和想法,不同的只是标准。正所谓天下合久必分,分久必合。一个良好的生态环境和强大的技术能够实现一统三端,为相同的目标实现三套不同的标准可谓是形态所迫,希望未来简单的编码过程交付于AI完全可以。你只要专注与业务设计和想法逻辑即可。

所以务实基础,才是构建大厦的根本,才能让你在不断变化的世界里游刃有余。

未来存在你与机器的区分之处,就是你的创造力和想象力

2.2 逻辑&问题

使用流程:

  1. 通过唯一的密码 或者 指纹登录验证后进入软件的主页面(数据存储、页面跳转、验证逻辑、页面布局)
  2. 主页显示基础的帐号分类,提供全局搜索的功能(数据预先加载&分类、控件重叠居中等)
  3. 点击分类显示帐号(同时在标题栏显示帐号的数量、右下角可以方便地添加帐号)
  4. 点击帐号(显示帐号的具体信息、页面的复用&传值、分享帐号)
  5. 点击设置(帐号导出&导入,导出的信息通过AES加密、修改密码&密钥、常见问题解答、用户帮助等)

开发逻辑:

1. 验证页面

用户打开程序,首先加载的是名为index的页面,所以需要设计验证页面为index,验证通过才能跳转到主页面。

在此之前需要判断用户是否第一次使用该软件(通过是否获取到登录密码为准,因为程序必要要用登录密码才能使用),第一次使用则需要设置密码。

获取密码通过程序的入口app.js来设置,并保存到全局变量APP中的globalData,通过getApp()来获取APP全局变量,这个同时也是页面传值的通道。

用到了wx.getStorageSync(key) || wx.getStorageAsync(key),需要注意避免获取为空的异常。

1
2
3
4
5
// 获取加密信息, 虽然是一个字典,但是数据是空的,自然旧获取不到key
var secret = wx.getStorageSync('secret') || {
"key": "",
"iv": ""
}

优化点1:这里使用了大量的同步API来获取缓存中的数据(主要是帐号),是导致程序启动缓慢的主要原因,可以考虑懒加载来提高程序的运行速度,或者将帐号数据保存到服务器中,这就要考虑到服务器安全和用户信任的问题了。

对于全局变量用于保存用户的默认设置,同样需要从缓存中读取到内存中。

验证页面布局:

采用居中和流式布局(flex)

顶部:

  1. 使用一个标签来标识用户的状态(输入密码、设置密码、再输一次、密码错误)
  2. 6个圆点表示用户输入的密码个数(填充、未填充),根据密码的长度来做相应的显示

优点1:微信小程序的数据和视图是绑定在一起的,数据的变化会自动更新视图。类似iOS里面的viewModel,通过数据来布局

中部

  1. 使用1~9的按钮作为密码输入控件,采用流式布局
  2. 因为使用wx:for来创建的数字输入控件,数据绑定的点击事件为numTap:,对于特殊的符号C,X,0区别对待`,设置不同的颜色和功能

底部:

  1. 功能按钮设置(重新输入、指纹识别等),为了避免用户忘记上一次的输入
  2. 采用flex布局,后期可以添加新的功能上去,而不用考虑布局

优点2:拥有自动约束的功能,能够为后面的布局自动适配。类似iOS中的storyboard

验证页面逻辑:

使用顶部标签来标记用户的状态,进而进行操作

流程:

  1. 页面加载,获取全局变量app.globalData中的userKey,判断用户是否为第一次使用程序(密码为空表示第一次使用程序),设置标签的状态为请设置密码,否则加载userKey,openFingerPrint状态到当前页面。同时如果用户设置了指纹识别,则自动弹出。
  2. 输入密码时,根据标签执行不同的方法。
  3. 请设置密码:将输入的密码pushpwd ,然后由settingPwd保存到userKey里,这里的pwd作为密码的输入容器,当pwd的长度为6时,切换到再输入一次状态下,执行confirmPwd,当pwd的长度为6时,验证两次输入的密码是否正确,正确:执行setStorageAndLogin保存用户的密码,并跳转到主页面wx.switchTab,关于跳转页面参考开发文档(有几种不同的跳转方式)。这里使用的是关闭当前页面,并跳转到tabbar错误:清空用户的输入,并振动confirmError。此时用户可以选择返回到上一次的状态,底部的工具按钮会显示clearStoreKey
  4. 用户不是第一次登录时,请输入密码:执行loginPwd,将pwduserKey比较叫醒相应的操作
  5. 清除按钮:请设置密码的状态下,需要清除userKey,其余状态只需清除pwd即可
  6. 删除按钮:请设置密码的状态下,需要pop userKey,其余状态只需pop pwd即可

重构:

这里用到了一些重构的方法:提炼函数(Extract Method)塑造模板函数(From Template Method)、过多的注释(Comments)

来自阅读笔记:重构改善既有代码的设计

2. 主页

主页布局:

分为一个主页背景&搜索框,帐号分类的九宫格

上部:

  1. 采用一个375rpx*200rpx相对像素的图片作为背景,背景上重叠一个半透明的搜索框

解释:

  1. rpx表示根据屏幕进行自动适配的像素点,因为点和像素是存在一个比例的。类似iOS里面的屏幕适配,采用的点和像素点之间的转换
  2. 微信小程序里面的布局是自动分布下来的,并不会发生重叠。所以需要设置z-index: -1;来调节显示的优先

下部:

  1. 一个分割栏,表示分类信息&一个开启随机壁纸的按钮
  2. 采用WEUI的九宫格控件来完成,自带navigator,只要放置好URL即可

主页逻辑:

从全局变量加载分类信息&用户设置

流程:

  1. 点击搜索框,输入帐号的(名称、帐号、备注)在所有帐号里面返回对应的集合。这里采用了util工具类,对于全局一些常用的功能和方法提取。搜索成功则跳转到对应的页面showAccount,否则弹出相应的提示。
  2. 点击九宫格中的分类,跳转到navigator,设置好的URL,然后将分类信息传递到页面showAccount

3.showAccount页面

一个简单的tableList,显示帐号的基本信息(图标、名称、帐号)。右下角提供一个添加帐号的按钮,长按可以删除帐号

showAccount逻辑:

流程:

  1. 因为该页面包含了从九宫格和搜索框过来的两种情况,需要根据页面传递过来参数来判断。搜索的跳转是不带参数的
  2. 每次页面显示都会更新导航栏标题上面,当前分类的帐号数量。是为了当用户移动了帐号的类别,就需要做出相应的改变,这里用到了页面反向传参
  3. 九宫格跳转:根据传递帐号的类型accType,通过util工具类,在所有帐号中返回该类型的帐号集合。为空就显示出empty的页面,并提示用户添加帐号。
  4. 搜索跳转:通过util工具类,返回包含关键词的帐号,使用到了search(key)来判断一个字符串中是否存在某个字符。因为这里是通过URL跳转的,URL只能是字符串。所以需要将返回的集合进行json化,才能传递。并没有像HTPP那样,可以通过response来传递数据。
  5. 长按删除:选择一个帐号长按,会触发showOperation,可以用来删除帐号。删除帐号要注意的是,需要记录帐号在当前集合中的行数,因为所有的帐号的缓存中并没有按照分类来保存,所有的帐号都是保存在一起的。所以显示的顺序和存储顺序并不一致,所以同样通过util工具类,返回对象的位置,并记录。接下来需要做的是:

    1. 删除页面例的帐号信息
    2. 删除缓存中的帐号信息,这里同样需要找到缓存中当前帐号的位置
    3. 更新页面信息(全局变量、帐号数量)
  6. 点击显示帐号:记录帐号在缓存中的位置,和当前页面的位置。因为用户可能修改帐号的信息,你需要同步更新(这里用到了逆向传值)。还有就是所有数据都是根据全局变量来做出改变的,并不会继续读取缓存中的数据,为了加快程序运行速度,所以需要同步更新缓存和全局变量。传递到新页面adding_account的参数有帐号的json信息accountJSON、帐号在缓存中的位置accountIndex、帐号的类型accType、帐号在原页面的位置tapIndex、页面类型pageType,因为该新页面提供了两种功能。

解释:

  1. 页面正向跳转传参,用到的是基本的URL,通过字符串来传递参数,和HTTP中的TCP类似

  2. 反向传参,通过获取页面栈

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var pages = getCurrentPages()
    var that = pages[pages.length - 2]
    // 然后就可一像操作上一页面的数据一样即可
    if (that.__route__ == "pages/showAccount/showaccount") {
    that.setData({
    accountList: beforeAccountList,
    // 这一句代码用于临界情况,该分类下只有一个帐号,将所有情况普通化
    emptyInfo: "暂无 " + this.data.accType + " 的帐号"
    })
    }
  3. 如何在对象数组中查找对象,数组对象的比较还包括了指针, 所以即使内容完全一样,也无法查找到,原因是因为对象数组中判断两个对象是否相同,这两个数据都必须是该对象数组中的。该对象的数据来源并不一致(即不是同一个数组),所以采用将对象json化,然后进行比较即可。需要注意:json转码中 & ? 等符号不能使用。

that & this 在回调方法中的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 解决方法一
* 复制一份当前的对象,如下:
* var that=this;//把this对象复制到临时变量that
*
* 在wx.request({}),有时候会需要获取页面初始化数据data的情况,
* 如果使用,this.data来获取,调试页面会报undefiend。
* 原因是,在javascript中,this代表着当前对象,会随着程序的执行过程中的上下文改变
* 在wx.request({}); 方法的回调函数中,对象已经发生改变,所以已经不是wx.request({});方法对象了,data属性也不存在了。
*
* 解决方法二
* 将回调函数换一种声明方式
* success: res=> {
* this.
* }
*/

4.adding_account页面

用于显示帐号的详细信息、修改帐号、添加帐号、分享帐号

页面布局:

  1. 帐号图标,可以通过右侧的选择图标,从自带的图标或者相册里面获取

  2. 分类选择器

  3. 帐号名称

  4. 帐号

  5. 密码位数,滑杆(0~16)

  6. 密码规则,用于自动生成密码

  7. 密码,通过右侧的生成密码按钮生成密码

  8. 备注

  9. 操作按钮区域(保存帐号、更新帐号、复制密码)

页面逻辑:

  1. 通过util类获取已存在的帐号分类,包括用户自定义添加的类

  2. 根据页面的类型pageType,显示不同的信息

  3. 显示帐号:将页面传递过来的帐号数据,保存到当前页面,通过控件显示value=''

  4. 更新帐号:当用户修改了帐号的信息,通过输入框失去焦点来获取实时的更新内容,保存在account里。然后用户点击更新帐号按钮即可更新完毕。saveAccount这里面处理的有:

    1. 判断信息是否为空
    2. 特殊符检查,避免json数据传递失败
    3. 判断页面状态,是更新帐号 or 添加帐号,进而进行不同的操作
    4. 同时为了减少图片的缓存问题,将帐号的icon进行判断(图片 || 已有类型)
    5. 根据按钮的类型,进行不同的操作modifyAccount。更新全局信息,上一页面的信息,缓存信息。
  5. 保存帐号:和更新帐号采用的是同一个方法,仅仅第五步不同,通过util工具,来添加对象,需要保证不能添加相同的帐号。所以同样需要对象json化,来保证。

总结:

随着开发的难度不断减小,得益于开发工具和完善的开发资料。人们对于应用的接入并不在乎是通过小程序,还是原生应用。对于那些使用频率小的应用,更是如此。

未来的开发方向不在由几大平台割裂,我们需要的不是同一个解决方案下的多套代码,我们需要的是开发过程中,通过开发人员不断完善和统一的开发平台。

或许我们可以通过AI和开发各个领域的高级开发工程师,来不断向这个目标靠近,开发出一个强大的AI开发平台,区分的仅仅是逻辑而不是标准。

核心问题:

  1. 处理边界情况
  2. 数据更新,页面传值、工具类封装
  3. 页面布局
  4. 压缩工程体积,用户体验,UI界面

开发问题:

  1. 控件布局
  2. 应用处理逻辑
  3. 开发工具的利用(主要是调试)

扩展和深入:

  1. 对于项目里面提供的常用帐号图标,来自与网页应用市场,然后通过编写python脚本来进行一键处理(背景透明化、大小设置)

  2. 使用工具将图片进行压缩,降低工程的体积

  3. 下一步,考虑使用服务器(腾讯云或者阿里云)

参考资料: