Fsiaonma's Blog


  • 首页

  • 归档

  • 标签

告别时还是得用力点

发表于 2016-04-30   |  

近一年来,回家时候,楼下便利店的哥们总会向我打听我游戏的情况。而我也总会寒暄式地告诉他,快了,快了。后来换了新工作,我也没去告诉他,之后大概也有半年了,每次的对话都是一如既往,甚至已经变成了机械性的。直到前段时间,他的店已经停止营业了,并没有继续经营下去,估计是赚够了吧(玩笑)。但这意味着,我也许这辈子再也见不到这个在一年里(准确来说是一年半),每天向我打打招呼,嘘嘘寒,问问暖的哥们。甚至连上一次的机械性对话发生在什么时候,什么场景,也记不太清了,能够确定的是,那一次对话也许就是最后一次了。

小区突然出现一只小猫,总在我家附近到处游荡,每晚回家,总会逗一逗它,它也会逗一逗我,很好玩,和其它猫不一样,它爱跟人,我走它也走,我停它也停。有一次还跟着我跑到我家门。慢慢的,晚上回家也习惯性的找到它,逗逗它。可以说是习惯了它的存在。突然某一天,它再也没有出现,估计是被人领养了吧。但习以为常的存在突然改变,总是不太容易让人习惯。至少,它应该在头一天晚上告诉我,它已经找到好人家,至少让我替它高兴一下,或者还会去喝上一杯,再来个道别 party,怎么也不该这样说走就走。

《北京遇上西雅图2》提到的《查令十字街84号》,海伦,法兰克通讯20年,二十年的通讯,唯一的相遇机会,却是由法兰克几个月前的去世告终。最有可能,也是唯一一次的见面,没有实现,上一次的通信反而成了永恒。戏剧里的老爷爷,教了老奶奶写4个字(她自己的名字),老奶奶用到的时候确实在老爷爷已经远远离去的时候。

会让人恐惧,有一天,某某的微博,朋友圈永远不再更新了,QQ永远不再上线了,甚至不再出现在世界的任何角落。突然就不在了,消失了。让人感到遗憾和伤感,无可奈何。更过不去的,到最后我们并没有来得及好好告别,大喝一杯,畅谈一夜,就潦草的离散了。引用《少年派的奇幻漂流》里的一句话:All of life is an act of letting go, but what hurts the most is not taking a moment to say goodbye。好好告别,翻译为,好好珍惜更为贴切吧,既然离去突如其来,还是珍惜每一次的相遇吧!所以我们告别的时候还是得用力点,多说一句,也许就是最后一句,多看一眼,弄不好就是最后一眼。

最后引一段用 约翰·邓恩 的诗歌:
All mankind is one volume,
When one man dies,
one chapter is not torn out of the book,
but translated into a better language.
And every chapter must be so translated,
God employs several translators,
Some pieces are translated by age,
Some by sickness, some by war, some by justice,
But God’s hand shall bind up all our scattered leaves again for that library,
Where every book shall lie open to one another.

nodeHttpsServer

发表于 2016-03-05   |  

用Node.js创建自签名的HTTPS服务器

  1. 创建自己的CA机构
  2. 创建服务器端证书
  3. 创建客户端证书
  4. 将证书打包

创建自己的CA机构

  • 为CA生成私钥

openssl genrsa -out ca-key.pem -des 1024

  • 通过CA私钥生成CSR

openssl req -new -key ca-key.pem -out ca-csr.pem

  • 通过CSR文件和私钥生成CA证书

openssl x509 -req -in ca-csr.pem -signkey ca-key.pem -out ca-cert.pem

可能会遇到的问题

你需要root或者admin的权限
Unable to load config info from /user/local/ssl/openssl.cnf
对于这个问题,你可以从网上下载一份正确的openssl.cnf文件,
然后set OPENSSL_CONF=openssl.cnf文件的本地路径

创建服务器端证书

  • 为服务器生成私钥

openssl genrsa -out server-key.pem 1024

  • 利用服务器私钥文件服务器生成CSR

openssl req -new -key server-key.pem -config openssl.cnf -out server-csr.pem

  • 这一步非常关键,你需要指定一份openssl.cnf文件。可以用这个
[req]  
    distinguished_name = req_distinguished_name  
    req_extensions = v3_req  

[req_distinguished_name]  
    countryName = Country Name (2 letter code)  
    countryName_default = CN  
    stateOrProvinceName = State or Province Name (full name)  
    stateOrProvinceName_default = BeiJing  
    localityName = Locality Name (eg, city)  
    localityName_default = YaYunCun  
    organizationalUnitName  = Organizational Unit Name (eg, section)  
    organizationalUnitName_default  = Domain Control Validated  
    commonName = Internet Widgits Ltd  
    commonName_max  = 64  

[ v3_req ]  
    # Extensions to add to a certificate request  
    basicConstraints = CA:FALSE  
    keyUsage = nonRepudiation, digitalSignature, keyEncipherment  
    subjectAltName = @alt_names  

[alt_names]  
    #注意这个IP.1的设置,IP地址需要和你的服务器的监听地址一样
    IP.1 = 127.0.0.1
  • 通过服务器私钥文件和CSR文件生成服务器证书

openssl x509 -req -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -in server-csr.pem -out server-cert.pem -extensions v3_req -extfile openssl.cnf

创建客户端证书

  • 生成客户端私钥

openssl genrsa -out client-key.pem

  • 利用私钥生成CSR

openssl req -new -key client-key.pem -out client-csr.pem

  • 生成客户端证书

openssl x509 -req -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -in client-csr.pem -out client-cert.pem

HTTPS 服务器代码

var https = require('https');
var fs = require('fs');

var options = {
    key: fs.readFileSync('./keys/server-key.pem'),
    ca: [fs.readFileSync('./keys/ca-cert.pem')],
    cert: fs.readFileSync('./keys/server-cert.pem')
};

https.createServer(options,function(req,res){
    res.writeHead(200);
    res.end('hello world\n');
}).listen(3000,'127.0.0.1');

HTTPS 客户端代码

var https = require('https');
var fs = require('fs');

var options = {
    hostname:'127.0.0.1',
    port:3000,
    path:'/',
    method:'GET',
    key:fs.readFileSync('./keys/client-key.pem'),
    cert:fs.readFileSync('./keys/client-cert.pem'),
    ca: [fs.readFileSync('./keys/ca-cert.pem')],
    agent:false
};

options.agent = new https.Agent(options);
var req = https.request(options,function(res){
console.log("statusCode: ", res.statusCode);
  console.log("headers: ", res.headers);
    res.setEncoding('utf-8');
    res.on('data',function(d){
        console.log(d);
    })
});
req.end();

req.on('error',function(e){
    console.log(e);
}) 

将证书打包

  • 打包服务器端证书

openssl pkcs12 -export -in server-cert.pem -inkey server-key.pem -certfile ca-cert.pem -out server.pfx

  • 打包客户端证书

openssl pkcs12 -export -in client-cert.pem -inkey client-key.pem -certfile ca-cert.pem -out client.pfx

服务器端代码

var https = require('https');
var fs = require('fs');

var options = {
    pfx:fs.readFileSync('./keys/server.pfx'),
    passphrase:'your password'
};

https.createServer(options,function(req,res){
    res.writeHead(200);
    res.end('hello world\n');
}).listen(3000,'127.0.0.1');

客户端代码

var https = require('https');
var fs = require('fs');

var options = {
    hostname:'127.0.0.1',
    port:3000,
    path:'/',
    method:'GET',
    pfx:fs.readFileSync('./keys/server.pfx'),
    passphrase:'your password',
    agent:false
};

options.agent = new https.Agent(options);
var req = https.request(options,function(res){
console.log("statusCode: ", res.statusCode);
  console.log("headers: ", res.headers);
    res.setEncoding('utf-8');
    res.on('data',function(d){
        console.log(d);
    })
});

req.end();

req.on('error',function(e){
    console.log(e);
})
  • (来源 http://cnodejs.org/topic/54745ac22804a0997d38b32d)

flppy bird 制作流程

发表于 2016-02-29   |  

如题, 我们需要制作一款 flppy bird 游戏,就是曾经在微信疯传了一周的小鸟跳水管略心游戏。
无疑问,可以选用 cocos2d 制作,但由于是页面游戏,避免影响加载过大,我选用了 lite 版本。
ok,我们开始制作,首先我们需要把整个过程拆分为几步。

  1. 找到资源图片
  2. 编写重力系统
  3. 编写障碍物出场顺序以及移动逻辑
  4. 小鸟与障碍物的碰撞检测
  5. 算分
  6. 结算条件,以及重新开始

找资源

这个经过在网上简单搜索(http://pan.baidu.com/s/1pJ3CEYB) 在这里可以拿到进本上所有图片资源。

重力系统逻辑

小鸟只在 y 轴移动,并不产生 x 轴位移,根据重力公式 v = v0 + at 依据重力加速度,不断计算出小鸟
在 y 方向上的实时速度即可。(假定上方为正方向,速度为可正可负,加速度为负。)点击屏幕一下,提供
小鸟一个往上的较大速度,即可实现跳跃效果。

障碍物的产生

障碍物高度为320,场景高度为600,只需要计算好上面柱子的取值范围,用上面的柱子位置减去一半高度,
再减去空余位置再减一半高度,就可以得出下面柱子的y坐标。(底部柱子y坐标 = 上面柱子y坐标 - 一半高度 - 空余位置 - 一半高度)。当障碍物中产收,平移到最左边,然后移除到场景外即可。

碰撞逻辑

判断小鸟的正切矩形,与所有所有柱子的正切矩形是否有交集即可。

算分

当柱子的 x 坐标小于小鸟的 x 坐标,分数 +1。

计算条件

当小鸟碰撞,或小鸟的 y 坐标小于0,结算,重新开始。

到此为止,恭喜你,制作出广告日收入过亿的虐心游戏了。(PS: 不要怀疑名字打错了,是故意打少一个a的,否则就没有山寨的味道了。)

前后端分离实践

发表于 2016-02-22   |  

后端 MVC 时代

描述:

MVC 在后台,服务器负责路由跳转,所有页面的渲染

存在问题:

前端写Demo,后端套页面

  • 后端需要写HTML
  • 前端仍然确认后端写的HTML

前端写View层,后端只管数据

  • 前端需要熟悉后端语言
  • 前端需要了解后端架构

前端 MVC 时代

描述:

  • 提供数据
  • 处理业务逻辑
  • Server-side MVC架构
  • 代码跑在服务器上

    前端:

  • 接收数据,返回数据
  • 处理渲染逻辑
  • Client-side MV* 架构
  • 代码跑在浏览器上

存在问题:

  • 无法统一协作模式,代码充满了约定
  • JS跟CSS,依賴於後端產出的HTML
  • 有的数据來自AJAX,有的数据印在DOM上
  • 有的业务邏輯在前端,有的在Model层,更多的是在View层
  • 前端依赖服务端开发环境
  • 在服务端View层高度耦合
  • 沟通成本高
  • 职责不清晰

前端 MV* 时代

描述:

引入 nodejs 作为前端范畴,优化前端相关行为。

JAVA

  • 服务层
  • 提供数据接口
  • 维持数据稳定
  • 封装业务逻辑

NodeJS

  • 跑在服務器上的JS
  • 转发数据,串接服务
  • 路由设计,控制逻辑
  • 渲染页面,体验优化
  • 更多的可能

HTML + CSS

  • 跑在浏览器上的JS
  • CSS、JS加載與運行
  • DOM操作
  • 任何的前端框架與工具
  • 共用模版、路由

表单设计浅析

发表于 2016-02-22   |  

近日读了《Web表单设计_点石成金的艺术》这本书,更多的从用户心理进行切入,分析表单的设计各个注意事项,体会很多,写下这篇笔记记录一些重点内容,同时希望能在真实使用中注意这些细节。

背景

我们必须知道,现实生活中大多数人讨厌表单,在流程中最容易丢失用户的环节。因此,基于这个背景,表单设计的目标貌似变得简单,就是让用户“填完”。

注意事项

至于如何能够让用户实现填完的目标,可能需要注意以下几点:

  • 尽量减少痛苦,填写过程应尽量简洁,容易。
  • 说明填写完成路径,表单应清晰告诉用户,如何才是正确完成路径。
  • 考虑具体使用场景,需要根据特定场景,设计特定表单。
  • 保持沟通一致,表单内容应和所有信息场景一致。

组织表单

选取问题

由于表单项都相当于是一个问题,所谓问题就是要通过思考才能回答的。因此,问题(表单项)的多少以及复杂度,都需要进行评筛选和优化,考虑没有该问题是否可以,如果必须要问,考虑是否有更合理的提问方式。

构建对话

表达是直接与用户对话的,现实生活中很少人愿意回答一个陌生人提出的一些列问题,因此对于表单问题的描述,需要平易近人。如:“生源地” 可以换成 “你可能来自”、 “发卡行” 可以换成 “哪家银行发行” 等。是的表单与用户的对话更贴近于交流。

内容组织

将表单的内容划分成多个有意义的组,这样给人更直观更整体的感觉。例如“必填” 与 “选填” 分开,“用户信息” 与 “组织信息” 分开等。

完成之路

表单命名

表单命名的重要性不仅仅在于能说明当前表单的用途,更重要的是能告诉用户完成后能干什么,这是很多设计者都忽略的。

清晰的浏览线

如果用户不知道当前填到哪个步骤,也不清楚接下来有多少需要继续填,放弃的几率很高。因此,流程复杂的表单,需要有清晰的浏览线,让用户知道当前的填写进度。

分散注意力最少

剔除所有可以剔除的分散注意力元素,让用户能集中与表单项,这样能使表单更加清晰,更加简洁。(当然,如果能让表单隐藏在用户不知不觉的操作中,这样就不需要突出表单,反而越多分散的点越好)

表单元素布局

每个表单基本都是有3个核心元素组成,标签,输入框,动作。标签提出问题,输入框让用户填写答案,动作让用户提交答案。
而这3者之间的不同布局却有不同的效果,

标签

标签的对齐方式分为:“顶对齐”,“左对齐”,“右对齐”。

顶对齐

用户名:
<输入框>
密码:
<输入框>
确认密码:
<输入框>

优点:由于问题与填答案区域异常接近,用户只需要向下操作,也提供了清晰的完成路线,这种对齐方式能使用户以最快速度完成表单填写。同是横向空间较大,较为灵活。
缺点:占用较大的垂直空间,当表单向很多的时候会显得臃肿,输入框间距需要控制在输入框高度的 50% 到 70%,标签间会被输入框阻断,不利于快速快速了解表单的整体状况。

左对齐

用户名:<输入框>
密码:<输入框>
确认密码:<输入框>

优点:标签都顶在左边,用户能迅速了解表单的整体状况,需要填写哪些内容。
缺点:由于填写过程需要从左到右,从上到下的操作,填写速度会受到很大影响。

右对齐

…用户名:<输入框>
…….密码:<输入框>
确认密码:<输入框>

优点:介乎上对齐与左对齐之间。
缺点:介乎上对齐与左对齐之间。

因此,如果需要用户库快速完成表单填写,可以选用顶对齐方式,如果需要用户缓慢填写,并清晰知道表单内容,可选择左对齐方式。

输入框

输入框有暗示答案的功能,正如:门上有把手,暗示着大家门能开,门上有“推”字样,告诉大家如何开。输入框的长度就能暗示答案长度,而 placeholder 属性将告诉大家如何填写答案。

动作

动作分为2种
主动作,如提交,保存
次动作,如重置,取消
应尽量减少次动作。

《杭JS》会议 & 前端 UI 分离方案

发表于 2016-02-22   |  

继《京JS》后,再遇《杭JS》

会议纪要

参加了6.21、6.22为期2天的杭JS会议,再次听到多为前端大牛的精彩演讲。可惜的是出发前遇到大雨,飞机延误,导致错过了21号下午部分演讲,幸好有 slider 和 ppt ,尽可能地弥补错过的环节。回到广州,赶上原来的工作进度后,总算能找出这么一段时间,记录一下杭JS会议的精彩内容。

《Then.js 异步库的实现原理及优缺点》—严清

从异步编程原理开始,引入异步编程话题,对比了各种异步编程方案和原理,如: Callback、Event、Promise、Generator,也对比了主流的多个异步编程框架,如:when、co、async,再通过代码实例与数据,阐述 thunk 与 Then.js 的优缺点。

《淘宝前后端分离实践》 — 赫门

赫门同学的演讲提到了多个令人眼前一亮的观点,演讲也非常的生动。首先,赫门同学打破了一贯的传统,重新定义了前后端,将原来的:前端面向浏览器,后端面向服务器。重定义为:前端浏览器 + nodejs,后端服务器。把 nodejs 划分到前端范畴,nodejs 主要能承担3项工作,数据转发,路由逻辑控制,渲染页面以达到效果优化。一句话印象尤深,“首屏 nodejs 渲染,其他客户端渲染”。

《优化你的Angular Web App》— sofish

之前有了解过 sofish,来杭州时也听朋友提起过他,在他的分享中,并不是默认大家都是 ng 大神,恰恰相反,他从基础出发,从最基本的层面阐述 ng 的优化方案以及使用技巧,同时带出一系列外围工具,方便大家开发与使用。

《开源项目的管理与维护》— 郭宇

相信大家看到郭宇同学的第一印象和我应该相同,认识到郭宇同学对开源项目的热衷程度接近疯狂,从他的项目就能看到她对开源的理解与热爱,他的分享也无不贯穿这个主题。

《如何持续技术学习》— 玉伯

玉伯同学不用说,相信大家都一定了解,sea.js 作者,github 排名第一,已经没有不认识他的理由。这次会议他并咩有分享技术性话题,而是谈到了我们的生活,我们的学习,以及成长。有一点个人是非常赞同的,“我们必须找到一件能让自己沉下心来的事情,哪怕是背单词也好,能找到这件事情,便能过渡每一个让我们烦躁的时刻。”虽然并不是他分享的重点,但这句轻描淡写的话,确是有点睛之用。玉伯同学同时提出,ouput > input,只有当我们产出的时候,自然而然会自己寻找资料,主动学习,大家应更关注与做,而不是关注与学,只有在做的过程中才能有真正意义上的学。

《Web Components标准:前端开发的新一次技术革命》 — 陈本峰

Web Components 能算上是一种革命,能让开发者足够的疯狂,陈本峰作为 W3C 标准制定者之一,能带给我们的便是下一代 web 标准的变化,同时告诉我们 web 发展的趋势,同时介绍了云适配公司的开源 UI 库,另人惊喜。

思考

前端工程师 与 UI设计师 的分离实践

赫门同学的分享,使我想起了曾经的一个项目,与赫门同学不同,不在于前后端职责分离,而是更细,前端工程师与UI设计师的职责分离,在前端工作的过程中,其实我们需要明确,前端工程师的涉及范畴,设计师的涉及范畴。

以往的工作方式

UI设计师画好场景图,UI设计师切成小图,前端工程师拿到小图重新还原成前端场景,再继续写前端业务逻辑,实现与服务器接口对接等逻辑。

缺点

  • UI设计师应该只管效果图,不需理会业务逻辑,前端工程师应该只管业务逻辑,不需理会场景拼接。而在我们的传统合作方式中,这部分工作总有多多少少的重合。
  • UI设计师已经画出了场景,却要把场景拆分,前端工程师却重新把拆分后的组合,这部分工作一次拆,一次组,双方都做了一定程度的无用功。

优化后的工作方式

UI设计师直接设计好原图,通过工具货脚本,直接生成到html文件,中间过程通过工具过渡。

优点

  • 使得工作职能更加明确,UI设计师专注于设计,前端工程师专注于业务逻辑。

现实情况

由于以前开发游戏项目,已写了一套叫 Psdium-Views 工具,这里是 工具 说明,当时实现了 psd 场景图到游戏场景的过渡,由于只是项目需要,只是实现了简单的效果,现开源该项目,希望能让大家参与进来,完善该项目,最终能投入到前端应用中使用,实现 UI 与 程序 的清晰分离。

cocos2d-js 制作 2048

发表于 2016-02-22   |  

由于已经长时间没有使用 cocos2d-js 开发,再加上 cocos2d-js 已从当初的 alpha2 迭代到现在的 3.0,许多接口都有颠覆性的改变,要从新熟悉 cocos2d-js 必须自我制作一款简单的游戏作为实践。

步骤一:

前期准备工作。(包括:下载 cocos2d-js 源码,准备好 api 文档,准备好教程用例),源码可以从 github 上下载,项目里头包括了相关的 api 文档,同时项目里也拥有了教程用例,前期算是非常的顺利。

步骤二:

搭建 2048 项目。接住 cocos2d-js 线上的星球大战作为基础,可以搭建出一个基本的游戏框架。删除多余文件,以及资源,可以开始编码开发。

步骤三:

需求分析。《2048》是一款碎石类游戏,通过合并相同数字的方块,最终组合成 2048。每一次移动同时会随即生成一个 2 或 4 的方块。

步骤四:

技术预研究。需要生成多个layer层,在每个layer层上配置数字信息。通过键盘控制方块移动。过程中要了解 layer 的生成,文字的生成,坐标世界观,动画播放,键盘事件绑定等。

步骤五:

逻辑编写。整个工程需要有2个主要类,

  • 一个为游戏场景,
  • 一个为方块类。
    在过程中,首先要渲染出底层最主要的游戏场景。其次,分步骤按逻辑生成带有数字的方块,并根据玩家操作,将方块进行合并。

  • 一个对象数组,每项里面记录每个方块的位置,当前是否已经被有方块,以及方块对象。

  • 一个适配对象,记录每个数字类型的字体颜色,以及方块背景颜色,方便生成方块。 * 一套生成方块的算法,算法的原则是,在空白处生成2或4的方块。
  • 每次移动过程后,记录下全局的所有空白格子,并且随机在这些格子上生成方块。
    * 一套判断移动算法:该算法是一套递归过程,每个方格移动的时候需要判断2种情况:
         * 如果该方向上有其他方块,我要知道该方块最终能移动到的位置。
         * 如果该方向上无其他方块,则将当前方块移动到棋盘边界。
    * 一套检测合并的算法。
          * 每次移动操作过后,遍历全局,开是否有重复的方块出现在同一个格子出,如果有,则移除其中一个方块,并将另外一块方块的数值乘以2。
    
  • 编写键盘回调,这个过程需要找出,键盘上每个键对应的键码,测试后得出,上:38,左:37,下:40,右:39。

项目地址:https://github.com/fsiaonma/2048

PSD解析工具 “Psdium-Views” 制作规划

发表于 2016-02-22   |  

简介:

我们做前端游戏研发,很多时候可能与美术之前有较多的配合。正常情况下,美术负责画图,研发负责逻辑。但往往在这个过程中,有部分工作总会重复。
在某些情况下,美术需要兼顾程序的整个场景,需要从研发的角度考虑切片,继而花费一部分时间去完成切图工作,继而提供具体资源图片的位置坐标,有需要时还需要提供文字属性对照表。
同样,在很多情况下,对于研发来说,拿到的并不是一个视图层,而是一张张资源图片以及一张张效果图,研发对照效果图,利用资源图片拼凑出一个又一个的场景。

问题:

这样问题就浮现出来了,既然美术已经设计出一个场景,为何研发不能直接使用,而是美术要先把该场景拆分,研发再拼凑呢?这样的步骤明显是非常的多余的,这样有2大缺点:

1.分工变得不明确,美术做了一部分研发工作,研发做了一部分美术工作。
2.基于第一点,会严重降低项目开发效率,因为这部分的工作是完全重复,没必要的。

当期流程:

美术组建游戏场景(Psd文件)==> 美术拆分场景(切图)==> 研发根据切图组建视图层场景 ==》 研发开发游戏逻辑。

优化后流程:

美术组建游戏场景(Psd文件)==》 通过工具生成视图层场景 ==》 研发开发游戏逻辑。

Psdium-Views 研发前准备:

由于需要解析 Psd 文件,可利用 ExtendScript 语言,遍历 Psd 文件各个图层,获取相关信息,进而生成相对应的游戏视图层文件。
由于脚本是完全的脚本语言,所以需要一项目管理工具,这里使用了我个人开发的《N-Builder》 进行项目管理。

研发计划:

开发主要解决2大问题:
1.psd 图片如何生成到 png 或 jpg 图,并且能被程序所识别。
2.命名,程序的本质是通过判断 psd 的图层或元件命名来判断该组件的类型的。如何才能让命名变得方便,易懂,是最关键的一步。

项目地址:https://github.com/fsiaonma/Psdium-Views/

v8 上的 JS 性能优化

发表于 2016-02-22   |  

这次到北京的主要目的,主要在于参加由 JSConf 组织主办的《京JS》会议,去年同期该组织也曾经举办过《沪JS》,会议主要内容为 《前端js的相关优化》 以及 《nodejs服务器端的相关技术分享》。

这次会议的收获主要归结为以下几点:

  1. 一场专注于 JavaScript 和 Node.js 的技术会议。
  2. 开发者对 nodejs 的使用频率成大幅上升。
  3. 各大公司对 nodejs 的投入力度加大。
  4. 了解到国外业界牛人对 web 及 nodejs 开发的理解,及踩过的坑。

下面主要分享一下个人感兴趣的几个主题。

百度 云/端一体化框架 Clouda

客户端/服务器端通过 Clouda 框架实现数据模型打通。即一个 Model 前后通用。再也不用担心我做把数据序列化,然后在后端反序列化这样重复的苦力活了。同时 subpub 机制也实现了云、端数据的准实时同步。后端的变化可以准实时地反应到前端呈现。MVVM 实现了视图和数据模型的双向绑定。而 Clouda 实现了客户端和服务端数据的双向绑定。

阿里 多浏览器前端测试服务 totorojs

一套基于网络的多浏览器管理服务,实现一键运行,多浏览器同时测试,汇总生成报表的功能。由于基于网络,IE6/7/8/9/10/11 等,甚至包括手机端,都可以在自己的机器、自己的系统下运行,最后由该服务统一管理和调度。

网易 实时游戏框架 pomelo

基于 Node.js 的高性能、分布式游戏服务器框架。包括基础的开发框架和相关的扩展组件(库和工具包),可以帮助你省去游戏开发枯燥中的重复劳动和底层逻辑的开发。Pomelo 不但适用于游戏服务器开发, 也可用于开发高实时 Web 应用,它的分布式架构可以使 Pomelo 比普通的实时 Web 框架扩展性更好。

Firefox OS

WEB APP API 提供强大的本地操作能力。以Gecko浏览器引擎为核心,采用HTML5来开发。所有应用都基于网络,希望用户一开机就能够连接上互联网,但也可通过HTML5 cache Manifest或相关API在脱机时使用

Google AngularJS

这是这次北京之行的意外收获,AngularJS 号称是下一代 web 应用的开发框架,在会议中也有不少讲师提到这个框架,现场也有出售关于这个框架的书籍。不过可惜,现场没有买到,在第一天上午就卖完了。后来经过多方了解,这个框架确实有其内在的美,值得大家参考学习。

V8 性能解决方案

这是我个人最最感兴趣的一个会议主题,演讲者为 Opera 的 Sphinx 团队的核心成员,虽然演讲能力不是有待提高,但其谈到的内容,确实改变了我对 JS 优化的思考角度。下面将结合我自己的理解,剖析一下关于JS在V8上的优化策略。

1.隐藏类

1
2
3
4
5
6
7
8
function test(x) {
this.x = x;
}
console.time("time1");
for (var i = 0; i < 100000000; ++i) {
var obj = new test(i);
}
console.timeEnd("time1");

1
2
3
4
5
6
7
8
9
function test1(x) {
this[x] = x;
}

console.time("time2");
for (var i = 0; i < 100000000; ++i) {
var obj = new test1(i);
}
console.timeEnd("time2");

以上2段代码之所以在速度上有天壤之别,其原因就在于V8的属性快速查找方式–隐藏类机制所导致的。每当有对象在 V8 上创建时,V8 会为该对象动态生成隐藏类,每当有新属性添加到该对象时隐藏类会对应改变,隐藏类可重用。

当 new Point(x, y) 执行的时候,一个新的 Point 对象会被创建出来。如果这是 Point对象第一次被创建,V8 会为它初始化一个隐藏类,不妨称作 C0。因为这个对象还没有定义任何属性,所以这个初始类是一个空类。到这个时候为止,对象 Point 的隐藏类是 C0。

执行函数 Point 中的第一条语句(this.x = x;)会为对象 Point 创建一个新的属性x。此时,V8 会:
在 C0 的基础上创建另一个隐藏类 C1,并将属性 x 的信息添加到 C1 中:这个属性的值会被存储在距 Point 对象的偏移量为 0 的地方。
在 C0 中添加适当的类转移信息,使得当有另外的以其为隐藏类的对象在添加了属性 x之后能够找到 C1 作为新的隐藏类。此时对象 Point 的隐藏类被更新为 C1。

优化策略:
1.在构造函数中初始化所有属性
2.始终按照同样顺序初始化属性
3.不要加载未初始化或已删除的元素

2.内联缓存,JIT 编译

1
2
3
4
5
6
7
8
9
function add(x, y) { 
return x + y;
}
console.time("time1");
for (var i = 0; i < 90000000; ++i) {
add('a','b');
add(1, 2);
}
console.timeEnd("time1");
1
2
3
4
5
6
7
8
9
10
11
12
function add1(x, y) { 
return x + y;
}

console.time("time2");
for (var i = 0; i < 90000000; ++i) {
add1('a', 'b');
}
for (var i = 0; i < 90000000; ++i) {
add1(1, 2);
}
console.timeEnd("time2");

JIT 编译(Just-in-time Compile)

   传统编译流程:
       代码段 -> 语法树 -> 中间码 -> 机器语言

   v8编译流程:
       代码段 -> 语法树 -> 机器语言

   没有解释器,属性访问通过内联缓存代码完成。

内联缓存(Inline Cache)

在第一次执行到访问某个对象的属性的代码时,V8 会找出对象当前的隐藏类。同时,V8 会假设在相同代码段里的其他所有对象的属性访问都由这个隐藏类进行描述,并修改相应的内联代码让他们直接使用这个隐藏类。当 V8 预测正确的时候,属性值的存取仅需一条指令即可完成。如果预测失败了,V8 会再次修改内联代码并移除刚才加入的内联优化。

 实例代码中,例子一命中 0 次,例子二命中失败 2 次。

优化策略:
1.不要使函数体过大
2.使方法职能单一,即确保属性变量只使用相同隐藏类包含的对象

3.数组模式(字典模式/快速模式)

快速模式:对应C语言的数组,速度快,紧凑
字典模式:对应C语言的哈希表,速度慢,松散

1.如果数组索引从 0 到 length-1,且无空洞,进入快速模式。
2.当预分配数组大于等于 100000,且数组有空洞,进入字典模式。
3.当预分配数组小于 100000,无论有无空洞,数组进入快速模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var t = new Array(100000); 

console.time('time1');
for (var i = 0; i < 100000; ++i) {
t.push(1);
}
console.timeEnd('time1');

var t = [];

console.time('time2');
for (var i = 0; i < 100000; ++i) {
t.push(1);
}
console.timeEnd('time2');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var t = new Array(99999);

console.time("time1");
for (var i = 0; i < 99999; ++i) {
t.push(1);
}
console.timeEnd("time1");

var t = new Array(99999 + 1);

console.time("time2");
for (var i = 0; i < 99999; ++i) {
t.push(1);
}
console.timeEnd("time2");

优化策略:
1.从0开始连续的初始化数组,以避免数组进入字典模式
2.不要预分配一个超大数组(比如长度大于等于 100000)
3.不要删除数组中的元素,尤其是数字数组。同样会转换为字典模式
4.不要访问未初始化或已删除的数组元素

偶遇疯狂猜图CTO:

第一天的午饭是一个偶然的机会,和《疯狂猜图》的 CTO 一起吃了顿午饭,交流了一下一些游戏相关的开发心得,感觉在中国游戏的成功并不在于技术有多牛,更多的是想法有多特别,其实在IT的世界,技术从是天外有天,我们要抓紧的往往是当下这一刻的想法,哪怕仅仅是一个闪念。

BYVoid:

出发到北京的前几天,正好关注到清华牛人郭家宝(BYVoid),这次《京JS》会议偶然发现了他也在现场有点惊讶,这里就带过一下了,哈!

Fsiaonma

Fsiaonma

sam

9 日志
© 2016 Fsiaonma
由 Hexo 强力驱动
主题 - NexT.Pisces