本周停更通知

这周末老家来了三个亲戚,陪了近两天,而且前几天一直忙于公司的一个紧急项目,暂时没有时间也没有干货可分享(抑或没来得及整理),见谅。

react-webpack雏形诞生

请了一个礼拜的陪产假,6天多没碰电脑,最后一个晚上再次拿起react,又弄了一个demo,然后这两天继续改进,终于可以非常勉强的拿出来分享一下,当然还有很多不足,后续会持续改进。最大的一个不足(或者叫缺失更合适囧)是,还没有加入Ajax的支持,下一步先解决这个问题,工程化还有很多优化空间。

地址: https://github.com/stoneChen/myReact

react

DIY使用Node代理HTTP请求

近日,为了更加方便前后端联调,响应组里小伙伴的需求,为grunt工作流中的connect任务增加代理中间件,将非静态资源请求转发到测试环境或后端本地服务。

google了一番,找到了 proxy-middleware 这个模块,正好符合我的需求,使用很简单,只需在connect的中间件数组里添加一项即可:

1
2
3
4
5
6
7
8
9
10
var doProxy = require('proxy-middleware');
//...other code
connect: {
livereload: {
options: {
middleware: function (connect) {
return [
//其他中间件
connect().use('/', doProxy('http://example.com'))
]

后来,为了在服务启动过程中可根据配置文件代理host的不同值,实时代理不同host,改为在中间件里读取yml文件配置的host:

1
2
3
4
5
6
7
8
9
//在靠后的中间件里
//每次请求都读取代理host配置,若配置了该值,则代理到这个host,若未配置,则使用本地mock
var apiRoot = envCfg.proxy;
if (apiRoot) {
grunt.log.writeln('apiRoot found, dispatch to `' + apiRoot + '`');
return doProxy(apiRoot)(req, res, next);
} else {
grunt.log.writeln('apiRoot NOT found, use local mocking');
}

往github上推了一个 demo

完成了这个需求后,读了一下 [proxy-middleware](https://github.com/andrewrk/node-proxy-middleware) 的源码,里面包含了对http头的各种处理,包括cookie,主要通过一个新的http/https模块的request对象,结合源request对象中的http头(包括cookie),向指定host发起一个新的http请求,在响应回调中,将代理host 的响应pipe给源响应(添加了少数头字段),完成了整个代理的过程。

回头想想,之前书上看过的HTTP知识这次真的派上用场了,正好《深入浅出NodeJS》快看完了,作者说Node可以帮助我们更加底层的了解HTTP原理,真的是这样!反观早前从事的JAVA开发,那框架封装的简直了,根本难以理解HTTP的运作原理。赞Node!

《深入浅出NodeJS》这本书对Node底层的介绍偏多,感觉实战还远远不够,还应该再来一本。

昨晚『开发者头条』推送了一条『百度母婴技术团队基于 ReactJS 实现 webapp』的消息,瞬间又激动了,里面提到的整个架构,与我之前所想大都相符,抓紧时间整出一套可行的方案!

发发牢骚,谈谈我心目中的新一代架构

国庆后的这几天,是我至少近一年半来,心里最不踏实,最没有成就感的一段时间。项目进度始终不理想,虽然主要责任不在我们前端,后端的接口不断在变,总是在返工,心力憔悴。我虽然没有直接负责开发某个模块,但是经常在前后端之间游走,以及引导同事熟悉项目的开发。这段时间自己少有时间可以了解新技术,即使有时间,也因为脑子一团浆糊无法静下心来。难道是因为自己的不进步而感到焦虑么。国庆期间花了大把时间看的Webpack/React,某个文档只剩下一个尾巴,至今还未结束掉,状态甚是不好。白天上班时,几乎每天都是『在自己电脑前待不到几分钟就被叫走,各种奔波,开会』这样的状态。这样的工作状态会让人觉得非常空虚,没有安全感。觉得是时候应该调整下策略,使自己的工作变得更有价值了。

再说说我心目中的下一代前端架构吧。

在我看来,前端开发大致经历了这么几个阶段:

  1. 『原始社会』,一切都是用原生javascript开发
  2. PrototypeJS/Mootools/jQuery等类库封装DOM操作,用这些类库解决几乎所有前端问题,主页面后端渲染
  3. 基于简化DOM操作的类库,封装各种UI组件,主页面后端渲染
  4. 框架(如Angular,React)封装绝大部分DOM操作,并封装各类UI组件,主页面前端渲染,并诞生了前端路由,形成了SPA,甚至前后端同构应用
    虽然对Angular有了一定经验,对其高效率的开发赞叹不已,但对于大型应用Angular似乎还是难以驾驭。

当我知晓有React这个框架的时候,我正在学习Angular,当时并没有对它关注太多,随着业界多方对之的宣扬,逐渐增加了对它的了解,去阅读了官方文档,并试着敲下若干demo,别有一番体验。再之后,了解了FLUX思想,在此之前我甚至都没有『数据流向』这个概念,只知道双向数据绑定真的很棒,随着应用变的越来越复杂,双向数据绑定变得难以掌控。

随着近段时间对React + Flux的了解,心中逐渐有了一个模糊的轮廓:

  1. React:View渲染
  2. react-router:react的路由组件
  3. react-redux:react的Flux实现
  4. redux-devtools:redux配套的开发者工具,使调试更加容易
  5. react-hot-module-replacement:react配套的热模块替换,更新组件代码并保留state,提升开发体验与开发效率
  6. ES6:从未发现ES6如此美妙,解决了很多ES5的痛点,尤其是模块系统和对象/函数的语法糖,更是向未来标准看齐;通过babel转换为ES5
  7. CSS模块化:这个优先级靠后
  8. Eslint:用于代码规范检测,保证代码质量,统一代码风格
  9. 前端单元测试,UI测试,尝试TDD
  10. Webpack:
    a) 打包javascript,css,图片,各种loader编译(包括babel,css预处理/后处理,图片压缩/base64转换/多倍图)
    b) 与npm结合,实现一行命令切换dev,prod环境
    c) 合理输出sourceMapping
    d) 可持续构建生成最优html,javascript,css,并解决浏览器缓存问题(自动添加文件hash后缀)
    以上是我近段时间总结的一系列需要实现的需求,网上有一些案例,实现了小部分需求,很多细节还需要继续推敲。

使用XtraFinder增强OS X的finder

XtraFinder

国庆后的周末只有一天,很多事情都赶到了一起,这次的文章就从简了,介绍一款OS X的finder增强工具XtraFinder

介绍页比较简单,安装后的偏好设置也基本上看几眼就明白(而且大部分是中文的)。更推荐使用homebrew cask安装,执行sudo brew cask install xtrafinder即可(需root权限)。运行后会出现在顶部工具栏上(logo跟finder一致),所有功能在其偏好设置内开启/关闭。

介绍几个个人觉得比较实(zhuang)用(bi)的功能:

  1. 首当其冲的便是高大上的彩色侧边栏图标:
    真的是为finder增『色』不少~开关在『Appearance』 -> 『显示彩色侧栏图标』
  2. tab功能,自OS X 10.9后已经支持
  3. 不少人对于OS X不支持像windows那样的CTRL + X / CTRL + V剪切功能而烦恼(其实剪切可以通过 ⌘+C / ⌘ + ⎇ + V 实现),现在你可以通过XtraFinder实现,位于『特性』 -> 『剪切和粘贴』
  4. 获取文件(夹)的路径一直是OS X的痛点,通过XtraFinder有两种方式可以获取:
    a) 勾选 『将项目添加到Finder菜单中』-> 『拷贝路径』,然后在你想要获取路径的文件(夹)上右键,就可以看到多了一项『拷贝路径』:
    直接点击『拷贝路径』默认就是拷贝了路径到剪贴板。
    b) 在a的基础上,勾选 『特性』 -> 『在工具栏中添加XtraFinder的功能』,然后回到finder,在工具栏右键选择『自定义工具栏…』,将『拷贝路径功能拖上去』:

    然后,选择需要获取路径的文件(夹),再点击工具栏上那个按钮即可将该路径复制到粘贴板了
  5. 在OS X中新建文件也是一个痛点,在上面的截图中,我另外把『New File』框出来了,和『拷贝路径』同理,前提要勾选 『将项目添加到Finder菜单中』-> 『新建文件』,可自定义添加别的类型文件:

     

还有一些finder实用功能它自身就有提供,比如『显示』-> 『显示路径栏』,『显示状态栏』等。

之所以会写这篇文章,是因为国庆期间更新了 OS X 10.11,导致了XtraFinder不可用了,Google了一下,跟系统的debug模式有关,参见 这篇文章。根据文章上说,将系统debug关闭,再重启,XtraFinder果然可以启动了,不过似乎有些功能失效了,比如『自动调节宽度』,等待开发者团队修复这些问题吧。

DIY利用NodeJS生成团队工作汇总表

这个想法源自最初基于Excel | Numbers二进制文件繁琐的记录过程。不得否认,Excel | Numbers功能很强大,可以绘制出非常完美的表格。

我们之前的做法是,表头制作好以后,由多人合作将此文件中的表格填充完整。关键是,我们通过QQ等工具以文件发送的形式合作完成这份表格,只能以『一人填写完再转给下一个人』的方式保证文件内容不冲突,或者说是保证内容完整性,这样的统计效率很低。

然后,我思考,可不可以用版本控制来完成这件事呢?似乎可以,但有个大问题,它是二进制文件,版本控制工具无法对其做出精确的修改记录,也就无法自动合并内容。就好比图片,一旦冲突,只能选择一个保留,无法合并修改。

我再思考,markdown不也可以做表格吗,而且它是基于文本的,版本控制可以合并修改。但是,直接用markdown写表格,也蛮痛苦的,很不直观,各种参差不齐,处女座又要被逼死的节奏:

1
2
3
4
5
6
7
|Date|Week|Incident|李四|王五|赵六|张三|
|---|---|---|---|---|---|---|
|1|三||开始XXX项目|Animate.css||css|
|2|四||Grunt|Webpack||js|
|3|五||完成了XXX开发,完成了XXX评审|Gulp||html|
|4|六|公休日|||||
|5|日|公休日|||||

换做是html似乎会更多累赘,一坨td标签,不忍直视。这个时候,我突然想到有一个模板叫 jade,它就是提倡简洁语法的Node模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
table
tr
th Date
th Week
th Incident
th 李四
th 张三
tr
td 1
td 三
td
td CSS
td JAVASCRIPT

Oh,No! 我刚写到第2行就受不了了,还是要写标签就不说了,人和任务很难对应起来,这是什么鬼体验。。。

另外,以上方案共同的最大的问题是,日期、星期全部都要手动填写,这就坑爹了吧(使用Excel | Numbers也好不到哪去- -)。

于是,我想到了利用NodeJS帮我们做数据转换(包括日期星期的自动计算),再结合jade模板,生成html表格。那我们需要人工维护的数据在哪里呢?第一个想到的肯定是json文件,但忽然又想起半个月前玩codepen的时候,里面提到了HAML(和 jade是同路子的),我想起了另一种新兴的数据文件,叫做 YAML。早前并没有太关注它,再次google之,有篇文章提到了json作为配置文件的几个痛点,我此刻才『幡然醒悟』:

  1. json无法写注释
  2. 到处都是双引号
  3. 深层次的json结构,大括号与中括号有堆金字塔的感觉,剪不断,理还乱- -
    比起YAML的简洁性,json逊色很多。

于是乎,YAML搞起!YAML文件的扩展名为yml。

第一版的形式是,团队所有人维护同一个数据yml,数据结构大致是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
year: 2015 #年
month: 9 #月
incidents: null #事件节点
members: #团队成员
- name: '张三' #成员姓名
tasks: #当月任务列表
1: 'Angular' #{日期}:{做了什么事情}
2: 'React'
3: 'Ember'
- name: '李四'
tasks:
1: 'jQuery'
2: 'Moment'
3: 'Backbone'

再通过Node解析yml,生成js对象数据,结合jade模板生成html。

经过一个晚上的奋战,终于搞定了!

第二天,考虑到多人维护同一个yml还是有一些不方便,于是改进为,每人维护自己的yml,最后统一生成html。

第二版的形式是,在仓库中为每个月创建类似 『201509』这样的目录,团队每个成员往里面push自己的yml(比如ls.yml),格式为:

1
2
3
4
5
6
7
name: '李四'
tasks:
1: 'Angular'
2: 'React'
3: 'Ember'
6: 'Bootstrap'
8: '请假'

相当简洁,易于维护。

再由专人添加当月的事件yml(_incidents.yml),格式为:

1
2
3
4
1: 'outing'
5: '加班'
16: '发布'
30: '内测'

每个人每天都可以写这个文件,并可直接生成汇总文件预览。

在月末,执行node sum {年月}(e.g.201509)即可在相应目录下生成sum.html,再push到github或gitlab,方便大家查看。

后来,由于在github 或gitlab上无法方便的预览html,而markdown可以,同时又生成了sum.md,期间又是踩了许多坑。最终得到了现在的版本,见 https://github.com/stoneChen/task-summary

总结

这种方式让团队每个成员都参与进来,效率高了不少。

但还有许多不足的地方,比如表格无法合并单元格,无法在多个维度下统计等等,仅能非常轻量级地汇总。这个月打算在团队里试运行。

其实最好的统计方式,还是开发一个专门的web站点,不仅仅可以统计每个人的工作内容,还可以做更多的事情,不过这种方式有点重,要看公司的具体情况了。

CSS3实现圆环式百分比

早前尝试实现过实现一个缺口圆环loading,我们来回顾一下:

 

See the Pen XmNdVy by cloudstone (@cloudstone) on CodePen.


 

主要使用了CSS3的border-radius、transform:rotate、transform-origin,以及白色长矩形的遮挡。 前两天偶然又看到了一个百分比的圆环式表示法,好奇,研究之。用chrome开发者工具摆弄了一番,终于明白其原理,但还是觉得它的实现过于繁琐,自己重新实现了一番,这是jquery版:

 

See the Pen Mabyzq by cloudstone (@cloudstone) on CodePen.


 

再看Angular版:

 

See the Pen YypWvr by cloudstone (@cloudstone) on CodePen.


 

LESS是一样的,但js明显要简练的多。 而且,在实现jquery版本时,也花了更多时间去调试,由此可见,操作DOM需要维护更多的状态,稍有不慎就会造成疏漏。虽然angular的双向绑定很强大,在简单场景下会事半功倍,但是在复杂场景下,有时定位问题显得力不从心,需要对Angular框架本身非常精通才可准确定位问题。由此引申出React+Flux的思想精髓,单向数据流肯定是会简化问题的,虽然从代码量上可能会比双向绑定多一点,但从长久来看,它将更有利于团队的发展。

使用codepen分享前端代码

之前文章的demo演示,都是直接贴关键代码到文章里,然后再把完整的代码提交到 runjs.cn ,将演示地址拷贝到文章里,读者需要点击一个链接才能查看demo效果,似乎麻烦了一点点。想到平时google到的一些老外的博客,很多喜欢用codepen来分享,不需要跳转到新的页面就可查看代码和效果,遂今日研究之。
站点地址: http://codepen.io/,是一款高效在线IDE。当然在线IDE有很多,详见 https://zh.wikipedia.org/zh/%E5%9C%A8%E7%BA%BFIDE

Demo

令人惊喜的是,此站点是在墙内的,并且可免费使用其常用功能。目前我发现的一些特点有:

  1. 暗色基调界面,符合现代IDE的主色调,代码高亮和字体看着很舒服
  2. 可引入现成的很多CSS、Javascript开源类库/框架
  3. 支持Emmet,甚至代码检查
  4. 支持HTML、CSS、Javascript各种预处理器,我们只需写预处理的代码即可
  5. 自动保存,实时预览
  6. 提供跨平台测试的友情链接(我没测试成功囧)
  7. 可方便的分享代码给小伙伴,可以直接发送全屏演示的地址,或将代码嵌入到我们的博客中(这也是本文的重点啦)
    为了演示代码的嵌入,我随意写了个小demo,上个图:

写完demo后,点击左下角的 Embed 按钮,出现如下对话框:

可进行若干颜色的设置,默认已经挺不错了,然后点击右上角的文本框,复制它,粘贴到我们的文章源代码中就大功告成啦,效果如下:

See the Pen VvaJPX by cloudstone (@cloudstone) on CodePen.

 


嗯,不错!以后我的文章就用这种方式上demo啦。你也赶快试试吧!

 

题外话:

这个礼拜开始根据公司UI部门给出的新UI,对bootstrap进行二次开发,顺便开始尝试gulp的使用,确实速度比grunt快的多,网上看了一些文章,很多人都是从grunt转过来的,继续深入。ReactJS的调研学习可能要暂停一小段时间。

另一方面,向公司申请了一台Linux机器,开始尝试搭建gitLab(我们目前还在用SVN囧),但愿一切顺利,虽然从SVN迁移到Git估计还要很长一段时间,但不管怎么说,这件事情总要有人推动才能实现,即使慢一点也行,幸福的日子就要开始啦!

iOS浏览器中fixed元素失效问题

最近参与一个ios混合App的开发,是挺大的一块功能,而且设计上是与原生风格一致的,即看设计图感觉不到这是网页。为了方便流程控制,就把整个屏幕区域交给了H5,H5有自己的标题栏,而且是fixed的,到这里还没有大问题,当fixed元素遇到文本框,问题就来了。

上一个Angular的 demo ,请用iphone查看,先往上滚屏,标题栏固定在顶部,没有问题。现在点击某个文本框,虚拟键盘弹出,这里滚动页面,发现标题栏定位已经失效了!无论是ios Safari(情况可能会好一些)或UIWebView,都有这个问题,来个截图:

223F185918DAAD9518B5E10740E194CC

经过一番google,查到这是Apple的一个设定,考虑到虚拟键盘已经占了很大一大块屏幕了,剩下的内容如果再有固定元素,那么可滚动查看的内容区域将更加小,所以当虚拟键盘弹出时,取消fixed元素的固定定位!但是这个『取消』有些蹩脚,在我们这个例子里,标题栏在页面中间的某个位置『绝对定位』了,太丑了,表示难以接受囧。

继续google,有人说可以在文本框获取焦点时,将fixed元素设置为absolute定位。尝试一番,效果比原来好了一些,而且也不违背Apple的设计,请看 优化版 。关键代码:

1
2
3
4
5
6
7
element
.on('focus', function () {
J_fixed.addClass('abs');
})
.on('blur', function () {
J_fixed.removeClass('abs');
})

嗯,效果已有所改善,但在我的项目中,有时候还是会定位失效囧。

于是,再尝试索性将标题栏通过某个手段定在顶部,请看 scroll事件版定位版 ,效果勉强接受,在滚动过程中,还是会随和页面移动,滚动停止时,才会触发scroll事件。关键代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var fixTop = function () {
var winScrollTop = J_win.scrollTop();
J_fixed.css('top', winScrollTop);
};
element
.on('focus', function () {
J_fixed.addClass('abs');
J_win.on(FIX_FIXED_EVENT_NAME, fixTop);
fixTop();
})
.on('blur', function () {
J_fixed.removeClass('abs');
J_win.off(FIX_FIXED_EVENT_NAME);
J_fixed.css('top', 0);
})

再试试另一个 定时版 ,关键代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var timer;
var fixTop = function () {
var winScrollTop = J_win.scrollTop();
J_fixed.css('top', winScrollTop);
};
element
.on('focus', function () {
J_fixed.addClass('abs');
timer = $interval(fixTop, 20);
})
.on('blur', function () {
J_fixed.removeClass('abs');
J_fixed.css('top', 0);
$interval.cancel(timer);
})

表面上看似乎效果与scroll版差不多,但据我的经验,这种方式会更可靠一点点,scroll事件不是太靠谱,定时的方式缺点是比较耗cpu,因为是频繁计算,可以将时间间隔改短一点,节省CPU。

 

另外,再送一点干货:

ios下弹出纯数字键盘方式:

1
<input type=”text” pattern=”\d*”>


1
<input type=”number” pattern=”\d*” >

 

隐藏键盘代码:

1
$('input:focus, select:focus').blur();

 

再扯点别的。

前几个礼拜的文章里,提到会尽量抽时间研究ReactJS,很抱歉,我没有做到,因为时间都投身在一线项目里了囧,包括加班也是赶项目。差不多一个礼拜前,甚至又在AngularJS和ReactJS之间动摇,到底公司新架构应该推哪一个呢。就在这两天,重新确定,『回到』ReactJS的怀抱,似乎React更可能是未来的方向,尤其是与Node的结合,以及React Native,这是一个长远的理想,Fighting!