git cherry-pick

QQ20160707-1

有一个 hotfix分支,merge 到 master 后,忘了 merge 回 develop就被删掉了,咋办

这个时候可以祭出 git 的 cherry-pick 功能,将某个分支上面指定的几条 commit,提交到另外一条分支上

QQ20160707-2

恩,本来有用 git-flow 脚本的,但是遇到过几次命令执行失败后,还是老老实实自己去切分支 merge 了

ckeditor 从入门到放弃

真心复杂

有一个 CKEDITOR 的全局空间,

有一个 CKEDITOR.instances的全局实例引用

有 Classic 编辑和 Inline 编辑两种模式

有 Plugin 也有 Widget

有自成一体的编译打包工具,与 AMD\CMD\UMD社区不兼容

官方兼容性

  • Desktop environments:Internet Explorer:
    • 8.0 and 9.0 – close to full support,
    • 10 and 11 – full support,
    • 9.0 Quirks Mode and 9.0 Compatibility Mode – limited support.
    • Firefox, Chrome, Safari, Microsoft Edge, Opera (Latest stable release) – full support.
  • Mobile environments:
    • Safari (iOS 6+) – close to full support,
    • Chrome (Android) – close to full support,
    • Internet Explorer Mobile (Windows Phone 8.1+) – support under evaluation.

继续阅读“ckeditor 从入门到放弃”

愉快的和Gist玩耍

github.com树大招风,国内时不时就不能访问,连里面的Gist也有问题。

WordPress有一个Gist的embed插件,但是这个插件是直接引入Gist的script,这个script是通过document.write()阻塞方式,在当前DOM位置插入Gist定制的内容。

所以,国内访问我博客,如果谋篇文章引用了Gist上面的内容,访问就会被拦掉了。

研究了下Nginx反向代理,用了一个子域名来承载各种代理资源,用一级目录来区分反向代理站点,世界一下子安静了。

整个优化改造过程没WordPress什么事,用Nginx的ngx_http_proxy_module模块做目标站点反向代理,用Nginx的ngx_http_sub_module模块来替换页面中gist.github.com关键字。

折腾一番,Nginx能力又提升了,哈哈。

现在打开还是有点慢,因为代理每次都要重新请求,由于Gist的资源是会更新的,不能直接暴力缓存,还得想想怎么去缓存这种动态数据。

用PhantomJS来给AJAX站点做SEO优化

腾讯问卷所有动态内容,全部由Ajax接口提供。

众所周知,大部分的搜索引擎爬虫都不会执行JS,也就是说,如果页面内容由Ajax返回的话,搜索引擎是爬取不到部分内容的,也就无从做SEO了。

先来看看效果

QQ20160321-1

去年一整年,搜索引擎收录都少得可怜。

更致命的是,被收录的页面,其搜索引擎里面显示的标题是最原始的html标题,权重如此高的地方,却被收录了一个没什么用的标题。

在去年年底完成实施了预渲染服务后,收录量蹭蹭蹭的起来了,并且收录的标题也都全部正常了。

而这所有的一切,除了Nginx接入层的配置是需要改动业务代码外,其他全部都是旁路机制。也就是说,自己做一套,可以给所有同类型业务共用,同时不会影响现有业务的任何代码任何流程。

继续阅读“用PhantomJS来给AJAX站点做SEO优化”

mac命令代理

内网装个东西就是麻烦,临时给某个命令走代理可以通过如下配置实现

 

iOS抓包新姿势 – rvictl

最近遇到一个诡异的问题,出现在特殊网络情况下,一旦劫持抓包就无法重现,怎么破?

Google告诉我们,iOS有一个rvictl的神器。

rvictl总共就3个参数

  • -l 显示当前活跃设备
  • -s 开始监听
  • -x 结束监听

其原理是将iOS设备的流量,像打日志一样复制一份到Mac上,在Mac上再通过Wireshark就能进行分析。这种做法,不像代理,不会干扰iOS设备正常的网络访问。

步骤

1.将iPhone通过数据线,连上Mac,并打开iTunes,复制设备UDID。

2.打开Terminal,输入  rvictl -s 设备UDID

3.打开Wireshark,在捕获选项里面选择rvi0这个设备,这个时候,iPhone所有TCP和UDP流量,都会打印到Mac上。

4.在Wireshark里面输入合适的过滤器,便于追踪目标流量。

用madge来绘制项目的依赖图

https://www.npmjs.com/package/madge

今天早上刚发现,很犀利的一个模块依赖检查工具,并可以可视化的绘制出来,支持AMD\CommonJS\ES6等模块依赖。

安装

使用说明

绘制依赖图

用处

业务做优化、重构的时候,可以先跑一次依赖图,看看都有哪些边边角角藏着废弃代码。

业务做局部模块优化的时候,也可以跑一遍依赖图,看看修改的模块会导致哪些地方出现变动。

iOS9.1终于可以关闭讨厌的300ms延迟了

通过viewprot配置来关闭,FastClick快要完成历史使命了。

Fast-Tap on iOS

On Safari for iOS, the 350 ms wait time to detect a second tap has been removed to create a “fast-tap” response. This is enabled for pages that declare a viewport with either width=device-width or user-scalable=no. Authors can also opt in to fast-tap behavior on specific elements by using the CSS touch-action property, using the manipulation value.

See http://www.w3.org/TR/pointerevents/#the-touch-action-css-property.

 

完整特性更新列表

https://developer.apple.com/library/prerelease/mac/releasenotes/General/WhatsNewInSafari/Articles/Safari_9_1.html

NodeJS那些事

下半年做了挺多活动型需求,因为我们业务人力有限,我在业务的策略是不依赖NodeJS。

而这些活动型需求,是可以用NodeJS来练练手。

ExpressJS

一个Web服务框架,几经转手,现在应该是IBM旗下的产品了。

以前我们用PHP来开发Web服务,语言层面屏蔽了很多HTTP协议的东西,可以专心业务逻辑。

而NodeJS不同,本身就跑Web服务(不管前面是否加个Nginx反向代理),所以挺多HTTP协议的细节需要我们深入了解的。

ExpressJS通过大量中间件,来帮我们屏蔽掉这些HTTP协议的细节,例如body-parsercookie-parser,帮我们解析HTTP Body和Cookies部分的内容。

app.use([path],function(req, res, next){})

整个ExpressJS,最重要的部分就是app.use()。不论是中间件?还是我们常见的app.get()\app.post(),都是从app.use()衍生出来。

每一个请求到达ExpressJS后,其处理流程是按顺序进入各个app.use()传入的回调函数中。

如果该app.use()带有path参数,则匹配path参数才会执行该回调函数。

如果该app.use()的回调函数最后还调用了next方法,则这次请求的处理流程会继续流向下一个app.use()

正因为如此,每个回调函数,只有一次调用的机会,你要么用来处理req阶段,要么用来处理res阶段。(宣称是下一代Web框架的Koa,则是利用ES6里面的语法糖,实现了一个回调函数有多次执行的机会)。

难道ExpressJS就不能让回调函数既处理req又处理res吗?非也,app.get()\app.post()就能同时处理req和res,只是ExpressJS的把能同时处理req和res的称为路由(Routing),而只能处理其中一种的称为中间件(Middleware)。这样形成一个不成文的约定,用中间件来加工req,用路由来加工req和res。

其实中间件一次只能处理一个阶段是有好处的。HTTP有一个特性,是HTTP Header必须早于HTTP Body返回。如果中间件也用来处理res,就会有非常大几率出现res.send()早于res.header()而导致的故障。平常我们的精力关注在路由上,中间件触发的故障会比较难发现和定位。

winston

在ExpressJS官网的最佳实践里有提到日志这点,平时我们用的console.log()是一个同步的语法,开发阶段问题不大,但不适合生产环节,官方推荐winstonBunyan两个库,我这里用winston。

winston支持分级日志,自带info\warn\error三级

还可以传递自定义等级

我们可以在ExpressJS的最后一个app.use()里面,做一个兜底的异常处理回调。

这样就把我们没有预计到的异常,也兜底接住了,并记录在日志中便于回溯。

winston还有一个 winston.profile('name')的方法,用来记录两个点的时间间隔,可以做性能统计埋点。

Request & Promise

NPM依赖榜排行第七的库(不知道是第七还是第三),跑NodeJS服务经常能用到,用来调用第三方接口。

Request的具体用法和$.ajax()雷同。

由于Request是异步的,为了便于业务使用,最好用Promise对每个具体的API调用进行封装。NodeJS和IO.js合并后,已经完美支持Promise语法了。

Promise的语法这里不展开,直接说怎么封装Request。

业务调用的时候,就可以安心处理正常逻辑,异常已经被屏蔽了

你看,现在的业务逻辑就是渲染。而其他异常,则会被一路抛出,直到最后一个app.use()来做兜底异常处理。

child_process & PhantomJS

这里我要先先说说,NodeJS的到来,让我居然有机会学习进程\线程的编程。

这里贴一个ChildProces的官方栗子。

说完语法,说应用。

我们业务有两个地方用到了PhantomJS,PhantomJS支持CLI调用和Web服务两种方式,而其自身的Web服务是通过Mongoose实现的。

最初我们在使用PhantomJS的Web服务的时候,经常遇到其假死的状况。由于是假死,各种守护进程的策略没法实施(侦测不到进程的任何异常),最后同事采用暴力的定时kill后重启策略。

也许上面的不稳定,是我们PhantomJS的脚本写的有问题导致的,但不管怎么样,脚本问题导致服务不稳定是不能接受的,后来我们改用CLI的方式调用PhantomJS。

这里我们用ExpressJS替换了PhantomJS自带的Mongoose来实现Web服务,并每次通过子进程的方式唤起PhantomJS,而我们本身的ExpressJS由PM2来保障执行,这样就能彻底解决由于脚本质量导致的服务假死问题。

PM2

一个给NodeJS服务用的守护进程,支持API和静态配置,非常强大,我建议大部分用NodeJS跑持久化服务的地方都用PM2。

PM2有多种启动方式,通常情况,建议将PM2的启动配置静态化到一个pm2.json文件中,然后通过 pm2 start pm2.json来启动。

PM2支持将log重定向,这对于多硬盘\多分区的服务器是非常友好的,我们服务器的根目录容量就非常小,如稍加注意,就会被这些NodeJS的log给撑爆,需要重定向到大容量目录下。

使用jQuery操作data-attr的注意事项

今天又一次掉进这个坑里面。

data-attr是HTML5里面的一个新属性(其实这东西都好多年了),方便CSS\JS去读取DOM上面的属性值。

jQuery在很久之前,就封装了一个 $.fn.data() 的方法,而该方法是将数据存放在DOM内部的一个数据对象中。

在data-attr来了之后, $.fn.data() 也支持用来读取DOM上面的data-attr,但是,它会缓存这个结果到DOM内部的数据对象,他会缓存,缓存,缓存。

后续对这个key的所有读写操作,其实都是操作的这个数据缓存,而DOM上面的data-attr并不会发生任何变化。

The data- attributes are pulled in the first time the data property is accessed and then are no longer accessed or mutated (all data values are then stored internally in jQuery).

如果你有一些样式,是希望同步这个data-attr的状态的,用 $.fn.data() 来操作就会发生一些奇怪的事情(之前一次没细看jQuery这块的实现,只是发现出来的效果怪怪的),建议改用 $.fn.attr() 方法或者JS原生方法。

附:

为了和 $.data() 做区分,我这里用 $.fn.attr() 和 $.fn.data() 这种jQuery原型链上的方法来表示 $(selector).data()