技术博客迁移至Coldfunction

我已经写了6年的博客了,我仍然活跃在各种技术堆栈上并且愿意分享的我学到的东西,为了更好地分享,并且也希望更多人参与到这个社区中,我的技术博客已经迁移到了一个完全由自己设计的自由的网站 - Coldfunction,地址是:https://coldfunction.com/mgen. Coldfunction里的个人主页实际上弱化了博客的功能,更加社交化些,用户的动态(发表文章,回答问题,或者提出问题)都会做上面显示。个人主页动态RSS功能正在开发中,很快会上线。 这个博客的文章会继续保留,并作为我分享一些产品以及非技术话题的地方。 阅读全文 [...]

Coldfunction诞生之前: Designing the IDs

不同的网站会有不同的ID系统,不同的ID系统会造成不同的用户体验。究其原因,可能就是因为对不同ID(或者说用户名)的不同策略造成了差异,对于用户名,细分的话就是如下几种可能: 用户的显示名称:用户信息在页面上显示的名称。 用户的登录名称:用户在登录时输入的名称。 用户的URL相关名称:用户相关资源在URL中显示的名称。 用户的唯一标示名称:用户之间的联系或者定位的唯一名称。 这四个名称,可以独立,也可以部分共用一个名称,这种不同的组合策略就造成了不同的用户体验。比如,如果用户的显示名称和用户的URL相关名称使用同一套数据的话,那么用户的改名成本会很大,因为一旦改了名字,之前的用户相关资源URL会失效,再比如,如果用户的显示名称和用户的登录名称使用一套数据,那用户的显示名称可能无法包含太多特殊字符,且每个用户的显示名称都是唯一的,然后改名的可能性会非常小(除非网站还提供非用户名称的登录方式)。 来举些更大众化的例子,比如QQ,它的用户的登录名称,用户的URL相关名称和用户的唯一标示名称都是用的QQ号,而用户的用户的显示名称是独立的,所以QQ账号可以随便改昵称,但是不能改自己的QQ号,同理,由于用户的URL相关名称也是QQ号,所以你的QQ空间的URL会包含你的QQ号,缺点是像电话号码一样,冗长所以不好记忆且不美观。另一个例子,微博的用户的显示名称和用户的唯一标示名称是同一套数据,所以一旦用户改了名称,以前@他的标签都会出现找不到人的情况。 还有就是大家熟知的Github和Stackoverflow。对于Github,所有上面的四个用户ID形态全部使用一套数据,这样会造成用户改显示名称的成本很高。对于Stackoverflow,则是另外一个样子,它的用户的显示名称和用户的URL相关名称和用户的登录名称都是分开的。所以用户可以随便改昵称,并且用户的昵称可以包含特殊字符比如空格,这样更美观些。而Stackoverflow的用户URL则是内部的用户数字ID,而且Stackoverflow还会在用户URL后加入用户昵称来优化SEO,所以一个Stackoverflow的用户URL是:http://stackoverflow.com/users/12345/abcdef(后面的abcdef是用户的显示名称,修改的话不影响URL,URL标识是12345),不过这样做有个缺点是URL不美观,Github的URL风格是github.com/abcdef,显然要美观好多。 OK,举了这么多例子,其实没有谁对谁错,不同的策略都各有千秋,对于Coldfunction,我的选择是: 用户的显示名称必须独立,因此,显示名称允许特殊字符,不需要确保唯一性,且随意修改。 用户的URL标识和唯一表示使用一套字符串数据,比如coldfunction.com/mgen是我的URL,@mgen可以@到我。 用户的URL在一级分段下,这样最美观,也就是不会出现xxx.com/users/mgen,而是xxx.com/mgen。 暂时不打算维护自己的登录体系,依靠第三方OAuth登录,因此不存在用户的登录名称这种ID,减少一个ID,就是让用户少记一些东西。 用户的财产 提到了URL,专门说下关于“用户的财产”概念,发现在国内网站经常会有这种问题(可能也是因为国内不太注重知识产权的意识),比如用户发的文章,URL就是xxx.com/posts/123456(很明显,从URL上看就是从数据库某个表里取一个数据),而漏掉了一个重要的数据,那就是用户本身。所以URL应该是xxx.com/myname/posts/123456,不过使用这种URL,首先当然是需要维护上面讲的用户的URL相关名称这种用户ID,不过为了SEO和URL本身的美观性,觉得还是非常值得的。 阅读全文 [...]

Coldfunction诞生之前: Tags vs Category

斟酌了好久,到底是支持Tags还是Category。两者的区别是,Tags可以有多个,而Category只能有一个。一些偏向博客类型的网站会支持两者,比如Wordpress,一篇文章可以有一个Category,同时也可以有多个Tags,不过,通常一些QnA站点和博客爱好者更倾向于使用Tags. 我个人仔细考虑了下Tags的优缺点,个人觉得Tags可能不是太合适,或者说不合理。首先Tags的愿景到底是是什么?如果我没理解错的话,就是要把话题中包含的类型信息最大化的抽取出来,比如一篇文章讲在CentOS的nginx服务器下通过proxy_pass命令代理请求到pm2部署的基于Express的Node.js程序,那么Tags是不是可能有:pm2, 阅读全文 [...]

chi/Golang 1.6: 在服务器中加入防止panic的Middleware及Handler

本文测试环境 Go 1.6, chi 参考了下httprouter的PanicHandler实现,感觉可以用在任意支持所谓Middleware的Go Web框架中,原理就是在ServeHTTP前defer另一个函数调用,这个函数调用recover来停止panic,如果有panic的话继续调用用户的Handler达到永不Panic的意图。 我们把这套方法在另一个Web框架chi中实现, 三个函数neverPanic是Middleware,他会在ServeHTTP前defer调用capturePanic,后者会调用recover,最后再调用用户Handler: recoverFromPanic. // Middleware func 阅读全文 [...]

gulp 3.9.1: src中路径中的/**/位置会影响目标文件夹结构

本文测试环境 gulp 3.9.1, Node.js v6.1.0 今天被gulp坑了不少时间,一个简单的gulp.src和gulp.dest操作竟然结果不一样,原来gulp.src中glob的不同竟然会影响gulp.dest中目标文件夹的结构。 假设在源代码文件夹中有这样的目录: css/ b/ b.css a.css 如果在src glob中**前包含css如下: gulp.task('css', function() { let dest = '../dist'; return gulp.src(['css/**/*.css']) .pipe(changed(dest)) .pipe(debug()) //gulp-debug 阅读全文 [...]

bimg.v1/Golang: 安装libvips出现”Package vips was not found in the pkg-config search path”

本文试用环境 Golang 1.6, bimg v1(gopkg.in/h2non/bimg.v1) bimg会使用libvips来操作图像,libvips的部署参考一个Node.js工程:https://github.com/lovell/sharp 在MAC下安装很顺利,到Linux环境下(CentOS 7)出现了如下问题: # pkg-config --cflags vips vips vips vips Package vips was not found in the pkg-config search path. Perhaps you should add the directory containing `vips.pc' to the PKG_CONFIG_PATH environment variable No package 'vips' found 首先运行下vips -v看看libvips有没有安装成功,如果安装没问题的话,可能就是PKG_CONFIG_PATH环境变量问题,运行printenv 阅读全文 [...]

Chi/Golang 1.6: Mount Router后同时处理末尾有”/”的URL

本文测试环境 Golang 1.6, 2016-5-21版本的https://github.com/pressly/chi 比如想要同时处理/blog, /blog/和/blogList,在一个Router中可以这样: // r是Router变量 r.Get("/blog", blogHandler) r.Get("/blog/", blogWithTrailingSlashHandler) r.Get("/blog/list", blogListHandler) 如果把/blog作为一个嵌套的Router,则需要把/blog/作为嵌套Router的URL路径,然后单独再处理/blog,才可以达到同时处理/blog, /blog/的效果,如下代码: // r是Router变量 blogRouter 阅读全文 [...]

Golang 1.6: 在text/template中输出RFC3339样式的Time

本文测试环境 Golang 1.6 Go中的time.Time在JSON Marshal时默认输出样式是RFC3339(Go中用的是RFC3339Nano常量),而在使用text/template输出时则会使用系统本地样式,比如这段代码,首先创建一个包含time.Time的类型: type MyClass struct { Time time.Time } 然后分别输出JSON和Template的结果: now := time.Now() // 输出JSON obj := &MyClass{Time: now} b, err := json.Marshal(&obj) if err != nil { panic(err) } fmt.Println(string(b)) // 输出Template t := 阅读全文 [...]