文章导航

小站更新:利用localStorage实现距离上次访问的时间显示

现在用户可以知道自己上次的访问时间,利用localStorage实现,那么,清缓存的操作就很可能一并清除localStorage,从行为上导致功能被初始化。

一分钟以内提示刚刚,大于一分钟&&一小时以内提示?分钟前,以此类推,直到?天。超过30天也依然按天算,比如59天前。

实现思路:

if (!window.localStorage.getItem(‘last-visited’)) {
this.notice = ‘这是你第一次访问本站’;
window.localStorage[‘last-visited’] = Date.now();
} else {
let last = Number(localStorage.getItem(‘last-visited’));
let now = Date.now();
window.localStorage[‘last-visited’] = now;
let diff = now – last;
if (diff < 60000) {
this.notice += ‘刚刚’;
} else if (diff >= 60000 && diff < 3600000) {
this.notice += Math.floor(diff / 60000) + ‘分钟前’;
} else if (diff >= 3600000 && diff < 24*3600000) {
this.notice += Math.floor(diff / 3600000) + ‘小时前’;
} else {
this.notice += Math.floor(diff / 24*3600000) + ‘天前’;
}
}
localStorage只能存储字符串形式的键值。虽然-号会直接进行隐式类型转换成数字类型,不过依然建议先将键值强制转换成数字类型。
其实本来是看见阮一峰老师在wangdoc上web api里有一个Intl.RelativeFormatTime可以实现相对时间,想用用这个api的。后来试了很久都发现要报错又不知道问题出在哪,后来从chrome控制台(这里点名下Firefox Dev,aurora通道升级的内核已经67了,居然控制台只报错,没说明错在哪?直接控制台用构造函数定义一个,然后发现是一个空对象。。)发现了,在MDN上找到问题,原来这个api有点新,对浏览器版本要求很高,很多人的浏览器访问都不一定能支持这个api,遂放弃。
之后想再在后端实现下记录用户访问次数和ip收集。

TCP三握四挥

今天做京东前端笔试题的时候看到了相关的问题,然后在网上看了一下一些博客的解释。https://blog.csdn.net/qq_38950316/article/details/81087809

说下我的理解。

为什么一定需要三次握手呢?就是因为我们无法保证第二次握手的时候,Customer端能收到来自Server端的确认消息。举个例子,你和一些人打篮球,你运着球,这个时候你发现一个队友站的位置非常好就想传球给他,然后你叫了他一声就马上把球扔过去了,结果你队友根本没听见,然后你就把队友给砸晕了。。。这个例子的目的是想表达二次握手这种思路太理想,根本没有考虑实际的情况。所以二次挥手会有死锁的可能性。

三次握手就避免了上述情况的发生,第二次握手的意思其实是S向C表示已经准备好,但是C是否准备好S并不知道(S不应该默认C准备好),第三次握手的目的就是C告诉S这边已经准备好。当S收到C的确认消息之后,表示CS都准备好了,所以连接就正式建立,开始发收数据。

为什么是四次挥手?举个例子,假设你是好学生,正在写作业,然后你作业还没写完的时候,你妈告诉你等会写完作业要跟她去探亲戚,所以你自然是先回应她一声你知道了,然后等你把作业写完的时候,你才再告诉她你写完作业可以去了。多出来一次挥手就是因为S端不会在接收到断开连接之后就马上停止发送数据,它需要把来自C端的请求处理完才能停。

挥手同样也是需要两边准备好的过程。接上边的例子,你作业写完了,然后你通知你妈,但是你妈不一定听见了,那这个时候很自然的,你猜她肯定没听见,这个时候肯定要再叫她一次(未必你说完了之后就穿着鞋子管都不管就走了?),直到她答应你才代表她听见了(我也不相信你妈听见之后也一言不发低着头穿鞋无视你的存在)。所以说最后两次挥手就是这么来的,很符合现实环境。

上面的例子顺带解释了wait 2个msl的存在理由,wait其实解决的是你妈回答了你,你却没听见的问题——你如果没听见你妈回复你,自然就会认为是她没听见,然后再叫她(妈!!!),然后你妈就又回答你(干什么啊?我知道了呀!)。直到你听见之后,这个循环就停了。这个例子的思路和wait并不完全一致,因为现实中妈妈不会专门去wait,但是大体思路已经能说清wait的存在理由。

我的第一个微信小程序–蜂巢账单

说正题之前,想多说点别的。不想看可以直接往下拉,到加粗的地方是正文。

感谢区块链天然追求自由的精神让我萌生了创建一个组织的想法,看到每个区块链项目的网站这么炫酷,我也一拍脑袋就打算学习网页开发,进而了解了前端,然后成为一名合格的前端工程师就变成了我实现最初想法的第一步。虽然我离最终的目标还存在天地之间的距离,但我相信滴水穿石,任何微不足道的积累都有意义。

最近在网上翻实习公司,前两天在知乎上搜知道创宇,发现一位叫余弦的在相关话题中很活跃,点进去仔细看了下,果然是位大牛,是个黑客,大学却并不是计算机专业,自学Flash和编程入了计算机行业,之后便是网页编程,从事了Web安全领域,大学期间提供了不少的解决方案和病毒专杀,大三大四获得学校机房管理权,最后就是两位知道创宇创始人拉他入伙。看到这里,我觉得我真的在眼睁睁看我的大学时光流逝,虽然今天的时代和他那时0几年有很大的不同,但我确实该为大学期间的无作为而反省。

跳过中间余弦大大各种出彩表现,说到今天。后来我关注了他的微信公众号lazy-thought,他已经离开了知道创宇两年多,而他今天在从事的领域是区块链生态安全,链圈很多人都知道的慢雾就是余弦及若干大牛创立的,而我知道慢雾是先于认识他,在链圈当下这种浮躁的大环境下,我一点儿都不看好慢雾,我觉得就是来分羹抢风头的,区块链都还没稳定下来就在考虑安全问题简直就是主次不分。当我看了余弦的好几篇文章之后,我才真正明白他眼光的超前性,而且他是很认真地在做这个事情,当然做这些事情其实报酬也不小,毕竟区块链创业公司都有钱。但我欣赏的并不是他现在和曾经的荣耀,而是他的思想和由此而散发出的气场,不停探索,保持敬畏,持续输出,这种气质你只能从理想主义的人身上看见,永远在改变,而创造世界未来的弄潮手永远都是这样的一批人。

说了这么多并没有特别想表达什么,每当看到这些很厉害的人都会点燃我心中的一把火,就想把想说的写出来,释放和鼓励下懒惰的自己:成功路上没有捷径。

说正题了。—————————————–

小程序的名字叫蜂巢账单,该账单的思想借鉴了区块链的广播特性,拥有账单明细意见这个特性,本质就是账单的每笔明细都需要达到一定的条件才能被视为有效,每位加入当前账单的用户都有权利确认或者拒绝这笔明细,目前我设置的条件为:意见为同意的数量超过当前账单用户总数的一半时,该笔明细状态转为确认。意见为拒绝的数量大于或等于当前账单用户总数的一半时,该笔明细状态转为拒绝,被拒绝的明细的消费额不被计入账单总消费额。如果我加入权重这个功能,就可以使账单从去中心化转型为弱中心化,更贴合实际需求。

账单的组成和普通账单没有多大不同,只是账单不再交由某一个人去负责记录,而是交给代码,用户只需要提交明细即可,之后按照上述的规则去确认明细,明细的状态被定下之后就不会再更改。所以该小程序去掉了记账人,每个人都有权确认明细的真实性。

上面所说的基本已经将该小程序的目标说得很清楚了,所以该APP本来就是一个为特殊情景而开发的作品,应用范围非常狭窄,熟人之间不会用,大规模的场景也不可能用一个小程序。满足的可能只是我个人对区块链美好的想象,最终下场可能只是一个练手作品。

虽说它的结局可能会很凄凉,但作为我的第一个微信小程序,它的意义很重要,为了实现各种功能,我对开发微信小程序有了更多了解,对整个小程序框架和结构有了更深刻的认识,对我的前端开发成长有很大的帮助,另外我也用eggjs作为后端框架来处理前端数据,对后端也有了一点基本认识,开发小程序的各种想法和思路都可以用于今后其他的情况。

接下来说一下整体的结构,上两张图片,感觉更容易说清逻辑。看不清可以右键->查看图像。

先说一下整体数据的结构。JSON保存一个数组,该数组由bill这样的无数个对象组成。除了图片中的说明外,我再解释一些可能存在的疑点。

initTime和initId。因为initTime是不重复的,而又不可能存在同一个用户同时创建两个账单的情况。所以这两个信息用于定位一个账单。

name。账单名可以重复,因为定位账单不是通过账单名。但如果加入了两个账单名相同的账单可能会比较伤脑筋。

util.formatTime(new Date())。new Date()确实可读,但是有点臃肿,用方法将new Date()的内容进行了更改得出的可读性更好。

billHash。sha1加密的内容只要发生一点细微变化,加密出的字符串都会完全不同,因此很适合用来作为判断内容是否变化的标志。所以刷新时先发送本地的billHash到后端,后端判断billHash是否有区别就可以知道客户端这边的信息是不是最新的,不是最新的才发账单信息,而不是每次一刷新后端就直接发一个账单信息过去,这样可以减小后端的压力。

hash。为了防止被怀疑后端篡改账单信息,每一笔被确认的明细都会对自身进行一次sha1加密并将结果保存到明细中。原计划每次被确认后,都通过模版消息发送该hash给该账单的所有用户,存疑的用户可以根据提供的账单结构还原明细信息,从在线工具网站加密进行对比,因为模版消息是即时发送的,代表当时的账单信息,我无法修改这些既定信息,即使后边篡改了也无效,只要数据被改了,hash绝对是不一样的。后来考虑到用户量可能为0,以及需要高于平常人的电脑水平,搁浅了,但是这个属性依旧保留了下来。

 

其次是页面结构。

app加载周期进行登录操作,从成功回调中获得用户code发送到后端获取openid,定义一个数组,通过openid从后端遍历所有账单查找group属性中是否有该openid键名,如果有则存入数组,此处可以用filter方法快速完成。然后下发openid和数组回到前端。前端将openid直接存入全局变量id,将数组与本地缓存的黑名单数组配合进行过滤后得到的数组,存入全局变量bills并备份到本地缓存bills。至此app加载周期结束,其他逻辑处理交给入口页index。

index入口页有两个方向,一是直通bill Tab,一是重定向到share-solve转发处理页。onLoad周期先确定是否有页面参数,如果有判断为通过转发页面进入,将参数存入全局变量share,然后重定向到share-solve,index页销毁,周期结束。如果没有,则为正常进入,获取用户设置查看是否已经获得userInfo scope授权,如果获得,直接调用对应API获取userInfo并存入全局变量userInfo,跳转至bill Tab。如果没有获得,显示一个button用于获取用户授权,余下逻辑与之前相同。

转发处理页面share-solve,需要账单信息即全局变量bills,但会有share-solve页面已经加载完毕而app加载周期还没有取得bills的可能性,因此需要添加一个回调函数。之前保存的share参数为对象,保存了分享的账单的initId和initTime用于定位账单,定位到账单之后,判断该账单的group中是否有当前用户,如果有则提示用户已经加入本账单并跳转至bill Tab,如果没有则发送initId和initTime以及用户信息userInfo和当前时间到后端,后端完成修改后将经修改的当前账单传回,前端将该账单入队到bills数组,然后跳转至bill Tab。

来到bill Tab页。和share-solve页同理,因为bill Tab同样需要全局bills,也会发生share-solve页的情况。所以需要添加一个回调函数。之后将bills存入data,用wx:for把各个账单数据传入bill-intro组件处理,完成bill Tab页面渲染。该页面添加了编辑能力,稍为复杂一些,暂述最关键的逻辑。

bill Tab页其实有一个功能键,可以在非编辑模式下导航到new页面。new页面会读入一串字符,为新账单的名字name,点击确认会向后端发送details为空的账单并将该账单入队到全局bills,给全局billIndex赋值1,之后重定向到bill-detail页。

通过在非编辑模式下点击bill-intro组件也可以进入到bill-detail页。在传递数据给bill-intro组件时,同时也把index传给了bill-intro,而该index实际就是该账单在全局bills中的位置,触发点击事件会将index赋值给全局billIndex,然后导航到bill-detail页。

bill-detail页的核心在于全局billIndex,billIndex会指挥bill-detail渲染某一指定的账单,即把bills[billIndex]传入data进行渲染。用wx:for把该账单明细数据传入bill-single-detail组件处理,完成bill-detail页面关键信息的渲染。另外bill-detail的onShow周期会用API触发刷新,刷新事件函数会向后端获取当前账单信息并更新当前账单信息,触发页面重渲染。

bill-single-detail组件会检查当前用户对本笔明细的意见,然后调整渲染的信息。如果当前用户还没有对本笔明细表态,会提供两个选项,确认或拒绝,两个选项会向后端发送opinion信息和账单定位信息(initId,initTime)以及明细定位信息(master, startTime),然后用API触发刷新,使刷新事件函数运行。点击组件和bill-intro同理,将index赋值给全局detailIndex,进入到detail-opinion页,页面自然会渲染bills[billIndex].details[detailIndex]的用户意见。

bill-detail页有一个功能键可以导航到new-bill-detail页。new-bill-detail页面会读入至少一串字符即明细消费额,还可以选填备注。点击确认会向后端发送一个用户意见均为待确认的detail和当前账单定位信息initId和initTime,然后页面回到bill-detail页。

bill Tab及衍生页面的逻辑大致说清楚了,转到userInfo Tab页。

该页面主要有三个导航。

第一个导航到log页面,log页面负责渲染本地缓存logs,而logs保存的是登录时间的数组。即使用记录页。

第二个导航到忽略组页面,指被用户人为放入黑名单的账单信息会再次显示,并且提供恢复功能,恢复后需要到bill Tab页刷新才能看到账单,因为重新获取整个并过滤bills只存在于app加载周期和bill Tab页的刷新事件函数中,提供bill Tab onShow周期刷新对后端的压力比较大,所以只提供bill Tab手动刷新能力。

第三个导航到关于页面,是该小程序的简单介绍以及免责声明和我的联系方式。

到此我讲完了小程序大概的整个逻辑处理。

虽然是第一次开发小程序,总体而言我觉得我尽力去做了,可惜本身应用面太狭窄,确实是很遗憾的事,这次整个开发过程权当练习和致敬区块链这个如蜂巢一般美妙的设计。再接再厉,回头复习JS和框架知识了。希望暑期的实习能让我快速成长。

用微信扫一扫扫描下方小程序码或者微信搜索“蜂巢账单”,就可以看到我的实物作品啦。

JS复习好助手–wangdoc

小程序功能基本开发完毕,因此对我而言,小程序即将告一段落了。今年暑期实习的目标是头条,因此要准备复习JS以及刷题,感觉去实习的话,还是有希望的,毕竟头条的内推码很多。。。简历不会被刷,我要好好把握机会。

在这里特别推荐阮一峰老师的wangdoc,是ES5标准的JS教程,很详细,有例子,该有的都有,做的题都可以在这里找到出处。毕竟也用了JS这么久了,对JS也有了更多的理解,回头再来看教程,真的是感觉收获很大,又让我对JS的很多概念明白得更清晰了。网址:wangdoc.com

 

被对象包裹的对象定义属性须知

即对被对象包裹的对象调用Object.defineProperty(obj, 'prop', {})时,最后一个属性描述对象参数需要写明value、enumerable、writable、configurable各属性的值,如果只写了value的值,那么此时余下属性全部默认为false。

什么是被对象包裹的对象?打个比方,比如{a:{}},此时a属性的键值{}就是被对象包裹的对象,此时调用Object.defineProperty(obj.a, 'b', {value:1}),obj.a.b就会无法遍历,不可写,不可配置。数组作为特殊版本的对象,如果你的对数组使用上述方法的时候,也需要注意这个问题。

常规情况都是不会发生这个问题的,比如赋值操作,出入栈队都不会涉及到属性描述对象,但是只要用了上述的方法,就一定要记得把属性描述对象全部写完,不要只写一个value,不然排查问题会带来麻烦。

当属性无法遍历的时候,JSON.stringfy会直接忽略这个属性,因此使用微信小程序缓存也会无法保存这个属性,所以在遇到这种特殊情况的时候,一定要注意。

 

微信小程序的组件间传递

参考官方文档,解决了我一个今天没想通的问题。自定义组件 · 小程序(页面最底部:细节注意事项)

当值进行组件间传递时,比如当前页面传递一个对象到子组件obj属性,这时进行的是深复制操作,也就是说这时,组件中this.properties.obj引用的是一个内容与之前对象完全相同但内存地址不同的全新的对象。所以说之后通过全局变量获取this.properties.obj并不能得到之前页面内的对象引用,对全局变量的修改也并不能影响到之前页面上的对象数据,其实这也是个好事,只不过我之前不知道,所以一直没想通为什么,加控制台打印的结果也是false,之前有印象官方文档说什么深复制什么,回头来看,算是给我解惑了。

JS复习计划

最近做微信小程序的过程中,根据我对小程序的功能规划,我发现我不得不去监听一个很重要的全局变量,因为我如果不用监听方式去解决这个问题,之后完善小程序的功能的时候,可能会有很多麻烦,也会导致我的代码更臃肿,有一种很将就的感觉,不太舒服。

然而,微信开发者工具的框架并没有像Vue一样提供现成的watch功能,需要靠自己去实现,在网上看了一会儿其他人怎么去实现之后,我也照这样子整了一个出来,但是实际上并没有像我想象的那样正常工作,而是没有反应没有发生任何变化。

大概思路可能就是属性描述对象的存取器(get和set),总之到最后,我感觉框架用多了也不是件好事,至少在使用框架的时候,对JS有更多的了解,了解框架是怎么通过原生JS去实现的这个功能,到最后会更重要。否则框架用久了,就只会框架了,框架会不停地迭代淘汰,但是自己却不知道框架下边的JS是怎么工作的,那就完蛋了。

逻辑层关系整理的重要性

最近学习了下微信小程序,在做一个小项目。

我发现经常去理清逻辑层关系,真的是非常重要,我的这个项目逻辑都比较简单,但是我已经觉得光用脑袋并没有那么容易理清关系了。

同样的,经常去理清逻辑层关系,可以更早的发现自己开始设计上的一些问题,使整个关系更清晰更松散,同时可以培养设计良好逻辑层关系的思维,感觉就像在造思想上的轮子,在思维的不断更新中,做项目的效率就会提高不少,同时做出来的东西的路线也更清晰。

每天敲点代码,每天学点新东西,每天都能感觉到自己的进步,这种感觉是很棒的。

短期目标:个人小站React版,打造一个让自己满意的组件

近期学习React,感觉学了Vue之后来看React教程很多概念都能理解了。也感觉到了一些Vue和React的不同之处。

之后想用React重做我的小站,使它更模块化,构架更加清晰。然后从我的小站构成组件中选择复用性较高的进行common化,然后发布到Github上边,作为我的一个小项目,哈哈。