如果我们不曾相遇

风起

你推荐了这首歌给我,细细的听了一遍,然后开始了一下午的单曲循环。

歌曲

在线试听

歌词

如果我们不曾相遇 我会是在哪里
如果我们从不曾相识 不存在这首歌曲
每秒都活着 每秒都死去 每秒都问着自己
谁不曾找寻 谁不曾怀疑 茫茫人生奔向何地

那一天 那一刻 那个场景 你出现在我生命
从此后 从人生 重新定义 从我故事里苏醒

如果我们不曾相遇 你又会在哪里
如果我们从不曾相识 人间又如何运行
晒伤的脱皮 意外的雪景 与你相依的四季
苍狗又白云 身旁有了你 匆匆轮回又有何惧

那一天 那一刻 那个场景 你出现在我生命
每一分 每一秒 每个表情 故事都充满惊奇

偶然与巧合 舞动了蝶翼 谁的心头风起
前仆而后继 万千人追寻 荒漠唯一菩提
是擦身相遇 或擦肩而去 命运犹如险棋
无数时间线 无尽可能性 终于交织向你

缘起

以为自己不懂爱,直到遇见了你。

开源了两个小插件

开源两个自己之前写的小工具,koa-router-decoratorreact-native-dynamic-stylesheet.

koa-router-decorator

链接在此:koa-router-decorator
第一个是用装饰器写路由的工具。

想法源于flask,之前看flask写的路由,觉得用装饰器写的话,会比较优美,然后就写了这个插件。
效果如下:

react-native-dynamic-sty2lesheet

链接在此:react-native-dynamic-stylesheet
这个则是自己在写RN App时候,有一些样式需要按照状态变化,写起来很繁琐,然后就自己写了个插件,专门用于生成动态的样式表。

用上之后自己在这方面的效率确实高了好几倍,还是很开心的。

这些天做过的一些分享

这段时间,一直专注于给家园工作室团队内部去做一些分享。
有规范的制订,也有前端的学习,还有关于学习方面的分享。

REM + Flex 布局

第一份:REM + Flex 布局

讲的是我在写移动端页面,和学习Flex布局中的一些经验和好的资源。

家园项目Git规范

第二份:家园项目Git规范

讲的是给家园工作室制订的Git规范,参照Git Flow,但是根据我们的实际情况,增删了部分内容,比如取消了 dev, release 等辅助分支。

学习如何学习 人生元编程

第三份:学习如何学习 人生元编程

讲的是自己在学习过程中,结合各种方法与教程,所总结与探索出的,一份适合自己的学习方法

JavaScript实现列表无限加载

起因

之前自己在使用这种网站时,经常看到无限加载的效果。
今天正好看到了getBoundingClientRect这个Api,就想着试试看如何实现Infinite scroll的效果。

原理

在需要无限加载的列表底部,埋下一个隐藏元素。
当不断滑动时,隐藏元素将出现在视窗(viewport)里,也就意味着当前浏览的列表已经到底部了。
这时候就需要进行列表加载。
大概的HTML结构如下:

1
2
3
4
5
6
7
8
9
10
<div>
<ul class="article-list">
<li>我是文章</li>
<li>我是文章</li>
<li>我是文章</li>
<li>我是文章</li>
<li>我是文章</li>
</ul>
<div class="infinite-scroll-signal"></div>
</div>

也就是:滑动列表 => 隐藏的无限加载指示器出现在视图 => 开始加载

那么重点就是检测隐藏的无限加载指示器是否出现在视图窗口。
还好,我们有getBoundingClientRect这个Api。

getBoundingClientRect

通过查阅MDN,得知:

Element.getBoundingClientRect()方法返回元素的大小及其相对于视口的位置。而除了 width 和 height 外的属性都是相对于视口的左上角位置而言的。

至于兼容性,一片绿,可以放心使用。


Can I Use getboundingclientrect? Data on support for the getboundingclientrect feature across the major browsers from caniuse.com.

DOMRect 对象

getBoundingClientRect()方法的返回值是一个 DOMRect 对象,这个对象是由该元素的 getClientRects() 方法返回的一组矩形的集合, 即:是与该元素相关的CSS 边框集合 。

对象的属性如下图所示:

其中的 top, left, bottom, right 均是元素自身相对于视图左上角而言的。
top, left属性而言,很好理解。而bottom, right则一开始搞的有点懵,后面通过devtools观察,发现bottom是元素的最底部相对于视图窗口左上角而言的,而right则是元素的最右侧相对于视图窗口左上角而言的。
其中right-left为元素的宽度,bottom - top则是元素的高度。

检测元素是否出现于视图窗口中

在这里,有两种情况,一个是元素是否出现于视图窗口中,另一种则是元素是否完全出现于视图窗口中。
两种情况的区别在于一个是部分出现,一个是完全出现。

下面我把两种情况都写出来:

  1. 部分出现在视图窗口中
1
2
3
4
5
6
7
8
9
10
11
function checkIsPartialVisible (element) {
const rect = element.getBoundingClientRect()
const {
top,
left,
bottom,
right
} = rect
const isPartialVisible = top >= 0 && left >= 0
return isPartialVisible
}
  1. 全部出现于视图窗口中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function checkIsTotalVisible (element) {
const rect = element.getBoundingClientRect()
const {
top,
left,
bottom,
right
} = rect
const isTotalVisible = (
top >= 0
&&
left >= 0
&&
bottom < document.documentElement.clientHeight
&&
right < document.documentElement.clientWidth
)
return isTotalVisible
}

那么问题来了:我们到底选用那种呢?
从无限加载这个业务场景出发,埋在列表最下边的加载触发器都非常小且不可见,因此推荐选用第二种,也就是完全出现于视图窗口的方式。
至于第一种,更适合检测该元素是否已经出现在视图窗口,但并不要求全部出现的情况。

实战

具体可以看我在jsfiddle上写的demo:
无限加载实例

后续

后续更多的则是一些性能优化的事情,比如debounce或者throttle来减少scroll事件调用次数,加入ajax加载,loading indicator等。
那些都是属于具体的业务范围了,这儿不做讨论。

iNCU App开发实践 - 架构设计

App架构?

架构很多时候,是个很玄的词。
软件开发中,架构真实的存在并影响着软件研发、运行与维护时的质量。

好的架构,明眼人一看就知道。比如Linux内核,又比如一些优质的开源软件。
而其他架构的质量,则往往难以判断。

嗯,关于这次iNCU App的架构,自己虽然觉得有些地方仍有不足,但是已经是自己尽全力的作品了。

这次分享出来,也是想分享自己在App架构上的实践与思考,如果有不足的地方,还希望各位多多指正。

思考🤔

App在正式开发之前,自己思考了两周,内容是 App的分层,文件的结构与技术选型等。

App的分层,在我看来是个非常重要的部分,假使View, Model, Router等层级杂糅在一起,在代码达到一定规模时,开发将会变的非常痛苦。容易出现耦合性高,BUG多,修改难等诸多问题,牵一发而动全身,从而陷入无边际的重构或者修改BUG的过程。

为了不陷入这种“地狱”状态,所以宁可多花时间在前期的思考与尝试中。

React Native

React Native是一个很特殊的框架。跨平台、复用性高、热更新等特性听起来很美好,但实际开发中会发现限制和BUG也很多。
很多功能,想要实现,只能靠原生拓展,但是这边能写原生的人只有一个……所以大部分原生拓展只能依赖开源的RN模块去实现。

团队

种种限制之下,对App的任何功能的开发与拓展,与我而言都不亚于一场战争。
毕竟,我也大概只能在家园工作室呆几个月不到的时间了,而这个App在一两年的时间内,还是需要做一个维护与拓展的,如何平衡这个问题,也需要自己耗费诸多脑力去思考。
如果只是为了自己开心,上一堆花式炫技的技术或者选用一堆不稳定的模块,其实只是顾着自己一时爽快,对于整个团队而言是弊大于利了。

另外自己写代码也快有两年时间了,直到今年年初,通过看书才悟出了“程序的第一要义是明确”的这个道理。而这个项目,也希望给学弟学妹们留一个好的例子,写代码很多时候真的不需要花式炫技,代码可读性高,可维护性好,健壮性强才是真正值得去追求的。

启动工作

与之前相比,在写这个App的功能或者模块时,自己会先画个思维导图或者流程图,确保逻辑是通畅的、可实现的。而不是边写边想边重构。

因为大概自己也很清楚,难点和问题从来就不在实现的具体方式和技术上,而是逻辑和流程上。有了具体的逻辑,写东西那是唰唰的快。

比如说关于网络层的思考:

自己画了详细的思维导图,考虑了可能会出现的所有情况,所以在开始写代码的时候,便显得有把握的多,后续的开发过程中也证明了这种思路的可行性,没有大修大改的情况,只是在这个基础上做一个修修补补。

设计模式

嗯,其实也算不上设计模式,但是之所以还是取了这个标题,是因为自己觉得整个App的研发过程中,充满着设计模式的思想。就是:

把变与不变的地方分开

基于这点,在开发过程中,更多的考虑了变与不变,从而在此基础上实现了文件结构,模块划分等。
至于后续的开发体验,只能说爽~~~
即使改了需求,也只需要改动一小块地方,耦合性低,再配合自己写的单元测试,可以说是效率极高。

测试

测试,是iNCU在开发中的核心环节,也是iNCU顺利开发的保护伞。也就是从这个项目开始,真正理解了测试的作用。
在这里的测试,包括 单元测试、Api接口测试、snapshot测试三种。
其中单元测试针对一些函数和功能,确保正确运行。
Api接口测试则是因为这儿接入了五六个后端,需要确保后端的接口运行正确与返回的数据正确。
Snapshot则是为了保证基础组件的正常运行,防止出现版本升级突然挂掉,或者修改属性也不知道的问题。

三种测试一起使用,保证了项目在修改,升级的稳定性。

文件结构

首先放上项目的文件结构:

.
├── __tests__ # 全局测试文件
├── common # 通用工具
│   ├── __tests__
│   └── analytics # 数据分析 
├── components # 基础组件
│   └── __tests__
├── constants # 常量
├── containers # container
├── networks # 网络层
│   ├── __tests__
│   ├── api # api
│   ├── interceptors # 拦截器,用于对网络请求做一些拦截处理
│   │   ├── request
│   │   └── response_error
│   └── interfaces # 接口定义文件
├── pages # 具体页面 View层
│   ├── __tests__
├── redux # Redux
│   ├── modules # 具体的Redux 模块
│   └── sagas # sagas 
└── typings # 自定义的接口文件

文件结构在我看来,一直是个很重要的内容,因为在项目发展的过程中,一个设计不佳的文件结构会给人带来很大的困扰,比如开发新功能或修改老功能。

上面放的就是iNCU项目的文件的结构,做了一个分层的处理,从而保证开发有序。
而在Redux的处理上,按官方推荐的那种结构来,在项目扩大化时,将会遇到繁琐的问题。
因此对于Redux中模块的处理,参照了 dva 和 vuex 的实现,聚合在一起,从而避免一些不必要的操作。

结语:

这篇博客是2017-04-17开的坑,因为自己在忙一些别的事情,所以今天(2017年05月06日)才补完后半部分。
无论如何,也算是写出了自己一直想说的一些事情吧~

前端路漫漫,且行且歌~

TCP/IP 学习

起因

之前自己学习的时候,碰到一些与计算机网络相关的问题。有侧重于HTTP的,也有侧重于TCP/IP等偏底层知识的。
今天对着阿里的《技术之瞳》这本书,准备系统学习一下计算机科学相关的知识。而书中的重点,就是TCP/IP。

TCP/IP 三次握手和四次挥手

首先要说的,就是最经典的问题之一,TCP/IP 三次握手和四次挥手。
具体可以看图,内容应该也很直白。

自己在学习过程中,产生的疑点在于三次挥手中的第三次握手。
也就是发送方发送ACK信号这一条。
在自己的理解中,既然已经确认了对方确认了自己的请求,那么自己再发送ACK信号是否是多余的?

后续再继续查阅相关文档时候发现,并不是自己想的那样,《图解TCP/IP》这本书简化了细节但有些时候是也不利于自己的理解。

第一次握手(SYN=1, seq=x):
客户端发送一个 TCP的 SYN 标志位置1的包,指明客户端打算连接的服务器的端口,以及初始序号 X,保存在包头的序列号(Sequence Number)字段里。
发送完毕后,客户端进入 SYN_SEND 状态。

第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1):
服务器发回确认包(ACK)应答。即 SYN 标志位和 ACK 标志位均为1。服务器端选择自己 ISN 序列号,放到Seq 域里,同时将确认序号(Acknowledgement Number)设置为客户的 ISN 加1,即X+1。
发送完毕后,服务器端进入 SYN_RCVD 状态。

第三次握手(ACK=1,ACKnum=y+1)
客户端再次发送确认包(ACK),SYN标志位为0,ACK标志位为1,并且把服务器发来 ACK的序号字段+1,放在确定字段中发送给对方,并且在数据段放写ISN
发送完毕后,客户端进入 ESTABLISHED 状态,当服务器端接收到这个包时,也进入 ESTABLISHED 状态,TCP握手结束。

引用自:TCP三次握手四次挥手

是的,《图解TCP/IP》这本书简化了客户端进入 XXX 状态这一点,所以看得时候是有些云里雾里的。

具体则是:

为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
“已失效的连接请求报文段”的产生在这样一种情况下:client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。”
引用自:简析TCP的三次握手与四次分手

确定唯一目标

在发送数据时,如何确定唯一的发送目标。
之前自己想的是ip地址+MAC地址,后续看书时候发现并不是。
MAC地址很多时候只是充当了下一跳的作用,而非唯一地址。

而确定唯一目标的关键,则在于IP+端口号。

有了 IP 地址,为什么还要用 MAC 地址?

这个是在看ARP协议时候,产生的问题。
后面发现是因为历史遗留与需要唯一识别的原因。
具体的内容可以看:有了 IP 地址,为什么还要用 MAC 地址?

iNCU App开发实践 - 技术选型篇

不知不觉App已经上线快一个月了。而自己也总算结束了匆忙且无序的生活。生活节奏开始走向了正轨。
上个月说好的在博客更新iNCU项目中的点滴,也从这篇博客开始,慢慢的书写。
开发过程中,想的有点久,东西有点多。
今天就先从技术选型开始讲起。

主要技术

作为一个程序员,自然要把App中使用的开源技术,放到感谢的栏目中。
以下是App中核心的技术与框架。

技术选型の观点

关于技术选型,其实是一个老生常谈的问题。每个人都有各自的想法与观点。因为技术选型的观点不同而在社区引起争论的现象比比皆是。

于我而言,技术选型最重要的一点是:效用
效用在这儿指的是技术选型对整个项目/个人/团队所起的作用。
假如从这一点出发,便能避免很多不必要的问题与怀疑。

对项目的效用

首先则是对项目的效用,也就是要选用的技术对项目所起的作用与满足的需求。
一味追新,则可能经常性的碰见Breaking Changes,大部分时间都耗在弥补升级的坑上了。
一味保守,则可能出现效率不高,无法应付未来需求的问题。用jQuery当然很稳定,但是如果是大型SPA,则会出现力不从心的情况。

从iNCU项目本身而言,从技术层面出发,选用的都是偏稳定,生态繁荣,依旧在发展中的技术。
比如React Native,比如TypeScript,两者目前都是偏稳定的技术,一定时期内不会有太大的Breaking Changes,能稳定且持续的开发下去。

从需求层面出发,iNCU最好是能够发布Android与iOS版本,而目前团队移动端人手不足,短期内培养出移动端开发人员是个不现实的愿望。所以只能自己亲自上阵。
而我作为一个前端,技术选型上自然偏向了HTML5和RN。最后在评估几次需求与使用RN和HTML5(Cordova, ionic)写了好几个demo时,做出了最后的选择,RN。

对我而言,RN的优点如下:

  • 满足需求,能实现iOS/Android端的开发
  • 性能较强
  • 可拓展性强,后期可以通过原生拓展实现一些高级功能
  • 使用纯 JS + CSS Flexbox 语法编写,上手难度低,无语言切换成本

RN的缺点则是:

  • 团队目前前端主技术栈为 Vue,学习React的人比较少,后期维护可能会有问题。
  • RN仍不够稳定,一定时间后可能会出现较多的Breaking Changes,升级可能有障碍

再三权衡之下,选择了RN作为主开发框架。

对个人的效用

因为自己目前也只是一名学生,所以很看重技术选型给自己带来的效用。
换句话就是,选这个技术对自己的成长有什么帮助?或者能给自己带来什么?
带着这种想法去做技术选型,去学新技术,则会明确许多,而不是盲目的追新。

假使我已经能熟练运用Vue,理解Vue的思想与要解决的问题,那么下一个项目一定是React/Angular等未知领域的框架。
即使Vue用起来很方便,由于经验的积累,写起来也很快。但是对于个人成长而言,所起的作用已经不大,是时候去接触新的思想,新的框架,而不是继续留在舒适区。

这也是我在日常学习中很注重的一个方面:刻意练习。

在这种情况下,我选择了TypeScript作为自己的主开发语言,于我个人而言,TS的类型系统写起来很爽,也能让自己避免一些低级错误,还起到了基础的ESLint的作用,可谓是一箭三雕。
而事后也证明,熟练使用TS,让自己的开发效率大幅度提升,同时BUG数量比之前的少了很多。同时也能学到一些静态语言的知识。

另外一种情况则是自己选用的RxJS,整个项目中,就一个函数使用了RxJS,功能也很简单。

但是自己却觉得很划得来,因为RxJS的函数式写法很优雅,赏心悦目,看着就很开心。(对,为了开心😊)

至于自己对RxJS的作用和优劣,我在V2EX一个RxJS的问题中有回复:

前些天一直在学 Rx ,想引入项目后面还是放弃了。
目前看来 Rx 适合复杂的异步时间和数据流等,简单的小场景用 Rx 只是自己给自己找麻烦。而且 Rx 的侵入性特别强,基本上用了之后都是 Rx 那一套东西了。
最后贴一句前两天自己看到的一句话:不要为了 Rx 而 Rx 。

以上两个就是基于自身考虑的技术选型情况。
我从今年以来,一直有个想法,那就是掌握框架,或者说掌握哪个框架并不重要,掌握解决问题的方法,理解框架的本质,才是真正核心的。

对团队的效用

iNCU是个团队的项目,自然需要考虑后续维护的问题。
那么技术选型也应该是这样。
之前有在Redux + Redux-sagaRedux + RxJS + Redux-Observable中纠结很久,最后选择了目前最成熟的Redux + Redux-saga,无他,RxJS的侵入性和门槛太高了,不利于他们的后期学习与维护。
同时也是基于对应用场景的分析,不想为了 Rx 而 Rx 。,自己玩玩还行,但如果放在团队中,可能就容易坑到人了。

自己关于团队技术选型,观点则是:

引入技术是要解决实际问题的,引入的收益最好大于大家接受他的开销。否则是得不偿失。

结语

以上则是自己对于iNCU App的技术选型,和自己对于技术选型的一些思考和见解。
如果你有什么更好的看法,欢迎随时找我交流~

我的第二个App, iNCU上线一周啦~

从去年十二月写完2017年的年度总结后,便没有再去写博客了。
不是不想写,而是在忙一个项目,一个对我而言很重要很重要的项目。
我的App,iNCU

当时App上线时,有三天的预热活动,自己也写了三天的说说,来表达自己对这个项目的情感。
以下的三段为空间说说全文。

项目起因

@佛爷 两年前向你申请加入家园,答应要做一个安卓App。三个月后转了前端,你却很支持我的决定。如今时光荏苒已过两年,当年的约定我还一直都牢牢记着。
从去年八月份开始到今天这半年多时间,或许只有自己清楚为了这个App的出现,到底做了多少事情。
明天App就上线了,是两年的心血,是两年的成长,也是给绵延两年的承诺所交出的答卷,希望你能收到并满意。
最后的最后,感谢学长你在少年一无所知的蛮荒年代,给予他前行的期望与勇气,支撑着他走下去直至见到光明。
【不忘初心,方得始终。2015.03.27-2017.03.09】

项目进展与个人

刚看了看项目提交记录,898个commit,10000+行代码,从安卓iOS到前端后台,产品的每一个功能,都是自己再三思考和权衡的最优解。开发过程中,两次重构,无数次改动,只为追求更优的用户体验,写出更好的代码。整个项目做下来,恍如隔世却也脱胎换骨。

项目结尾时想说的话

这个App,算是这两年来自己的心血之作,也作为礼物送给自己,送给家园,送给每一个期望南昌大学变的更美好一些些的你我他。(鞠躬)

技术

做整个项目,只想说一声脱胎换骨。(不过也导致上线后几天不想写代码,玩脱了)
具体的技术内容,剩下几天我将一一道来。

既然来了

最近的事情有点多,再加上自己想的也多,所以有很多烦恼。
包括待人接物,处理事情,项目管理等一堆烦恼堆积在身边,不知道用什么态度去处理比较好。

晚上一边啃泡面,一边看着Bilibili的中国诗词大会比赛视频。
直接看了决赛获奖者武亦姝的那一场,觉得很喜欢她这种在比赛中的表现与对诗歌的热爱,于是又去看了她之前的比赛。

【中国诗词大会】武亦姝 cut合集的第一个视频中,看到了她的自我介绍与准备。最后在总结时她说了一句:

我是武亦姝,既然来了就做到最好吧。

说话的时候悠然且闲适,仿佛习惯了如此这般。

在电脑面前啃泡面的我,一瞬间触动万千,也想通了很多事情。

编程源于热爱,偶尔也会有不知道如何选择,该去哪儿,该做什么,做到什么样的时候。
现在的话,倒是突然有了自己的态度和想法。

我是刘子健,既然来了就做到最好吧。

生活

如果天空是黑暗的,那就摸黑生存;如果发出声音是危险的,那就保持沉默;如果自觉无力发光的,那就蜷伏于墙角。但不要习惯了黑暗就为黑暗辩护;不要为自己的苟且而得意;不要嘲讽那些比自己更勇敢热情的人们。我们可以卑微如尘土,不可扭曲如蛆虫。