作者: meathill

  • Backbone.Collection.fetch小优化一则

    使用Backbone开发无限滚动应用的时候,必然会用到Collection.fetch()加载后面的内容。在Backbone的设计中,fetch之后,Collection会把取回来的内容“智能合并”到当前结果集中,该添加的添加、该修改的修改、该删除的删除,并且触发不同的事件。

    这样就会带来一个问题,因为我们知道,向DOM树当中添加元素的次数越少越好,因为每次添加都会导致重新计算布局和重绘;但是Backbone又是依次触发add事件的,怎么办呢?

    其实也容易,因为set完成后,Collection还会广播sync事件,所以我们可以在响应add事件时拼装jquery对象,响应sync事件时再把它们添加到DOM树中。代码如下:

    var InfinityScroll = Backbone.View.extend({
      initialize: function () {
        this.collection.on('add', this.collection_addHandler, this);
        this.collection.on('sync', this.collection_syncHandler, this);
      },
      collection_addHandler: function (model) {
        var html = this.template(model.toJSON());
        this.fragment = this.fragment ? this.fragment.add(html) : $(html);
      },
      collection_syncHandler: function () {
        if (this.fragment) {
          this.$el.append(this.fragment);
          this.fragment = null;
        }
      }
    });
    
  • 的pathname在不同浏览器中表现不同

    <a href="download://gamename/游戏名称">Download</a>

    在桌面版Chrome 32里,这样的<a>,其pathname会被解析成“//gamename/游戏名称”。

    在Android 4.2的WebView里,会被解析成“/游戏名称”。

  • Nervenet + Backbone

    介绍Nervenet起源时我提到,Nervenet一名很大程度上来自于Backbone。使用Backbone进行开发期间,我体会到很多方便之处,也发现仍有不少障碍横垣在前,于是总结之前的经验,取Robotlegs之长,做出了这个框架。当然,Nervenet并非专门针对Backbone开发,自然也不会依赖它,不过由于设计初衷和后期实现,结合Nervenet使用Backbone时着实有一些优势。

    context.createInstance创建实例

    我们知道,依赖注入的对象一般都是类的实例;类实例化时,可能会用到一些尚未注入的属性,所以通常需要在注入后调用实例的postConstruct方法,才能彻底完成构造。Backbone为了实现其独特的架构,将真正的构造函数construct隐藏了起来,开发者操作的initialize函数实际上是初始化函数,从执行顺序来讲与上文中的postConstruct基本一致。同时,Backbone的几大基础类都接受传入初始化对象,即new Backbone.View(options);中的optionsoptions中若包含elmodel等属性,将直接用来填充类自身的属性,甚至从页面当中找到dom结构操作。

    若采用普通的依赖注入,则需要在每个类中再实现一个postConstruct方法,增加维护成本和迁移成本。使用Nervenet提供的context.createInstance方法,可以在实例化对象时,扫描参数对象,完成注入,只需要保留initialize函数,非常简单,而且可以直接沿用之前的代码。

    var view = context.createIntance(Backbone.View, {
      model: '{{$model}}'
    });

    从代码可以看出,要完成上述功能,还必须实现一个特性:“注入指定类型的对象”。Javascript是弱类型语言,代码中不包含对象类型,所以之前的类库只能根据“属性名”注入。对于Backbone来说,modelcollection这些都是特有的关键字,要注入的也多是它们,而要注入model,就必须先map一个值到model这个key中——这意味着同一组map只能给单一Backbone.View对象注入依赖,在实际开发难以想象。Nervenet的优势在于可以由“属性值”指定注入的内容,这样不同的View,可以指定不同的model,问题迎刃而解。

    // 给不同的view注入不同的model
    context.createInstance(Backbone.View, {
      model: '{{$user-model}}'
    });
    
    context.createInstance(Backbone.View, {
      model: '{{$job-model}}'
    });

    infuse.js的解决方案是“子注入器”,通过创建子注入器,构造不同的map,就可以对不同对象注入不同的值了)

    mediatorMap管理mediator

    了解Backbone的人都知道,虽然名为View,但其实Backbone.View更接近一般意义上的Mediator,而通常意义上的View则由HTML+CSS负责实现。我通常使用Backbone.View开发组件,组件拼接起来就是完整功能的页面嘛。所以我就设计了一个功能来帮助我管理mediator,就是context.mediatorMap,它主要提供三个方法:

    .map(selector, MediatorClass[, options]),通常和.check(dom)连用。前者将选择器和类关联,后者则检查某个DOM节点下是否有符合选择器的节点,并为它们自动创建mediator

    // 先这么注册一下
    context.mediatorMap.map('.checkbox', MyCheckbox, options);
    
    // 然后就可以用了
    context.mediatorMap.check(document.getElementById('my-form'));

    .createMediator(dom, MediatorClass[, args]),直接为某DOM节点创建mediator

    // 直接为DOM创建mediator
    context.mediatorMap.createMediator(document.body, MyGUI);

    创建mediator的过程基本相同,自动注入依赖,并把目标DOM节点会作为第一个参数,或者第一个参数对象的el属性传给mediator的构造函数——后者当然是为了兼容Backbone。

    Backbone基于jQuery,所以操作一个DOM和操作一组DOM对它来说没有什么区别,留意到这点后,我在Nervenet中也作相应照顾,可以通过options设置isSingle=true;表示使用者希望使用一个实例来管理所有符合要求的节点。默认false,会针对每个DOM节点创建独立的mediator

    可惜的是,HTML没有提供方便的手段获取DOM节点的变化,所以暂时只好使用手工检查了;另外,querySelectorAll这个方法也存在一定的兼容性问题,所以,目前mediatorMap仍然只是0.1.2版的测试功能,以后可能会有各种改动。

  • 破pure

    pure是我用过的最差的前端框架:1. 布局不兼容老版flex-box,大量Android手机用不了;2. 外层框架字间距缩小,还得单独恢复;3. .pure-button里一堆莫名其妙的属性,用Android手机自带的WebKit无法正常tap。小是小,没带来啥价值,毛病又多,所以我一直不喜欢雅虎系的东西。

  • 2014年计划

    (2014-12-25更新)发现过去一年和计划中差别甚大……


    去年很努力,然而因为种种原因,收获寥寥。年底一体检,体重增加了,脂肪肝也加重了……虽然其它几项指标有所回落,不过锻炼减肥已经迫在眉睫。另一方面,公司发展得不错,工资虽然没达到预期不过也涨了些,年底应该还会发点奖金。所以今年我准备给自己安排一个Gap Year,调整一下再出发。

    所以今年我不会再接顾问以外的活计——当然钱老板走后也没人找我了……业余项目也不再拓展,肉大师有空弄就弄,没空就算;Nervenet会用在公司项目生产中,借机可以继续开发;没开始的想法就算了。其实Live说的也挺对:觉得事情做不完是因为没找到真正想做的,找到想做的就会一直做下去了。现在公司的项目带给我的成就感和收入基本足够,今年我不会再投入业余时间到业余项目中了。

    另外为了公司发展,今年尽可能向产品转型,年初项目完成后,如果有机会,多做产品少做开发。当然,开发我肯定不会丢下,只不过将现在19:1的时间分配换成7:3甚至6:4。

    其它详细计划如下:

    1. 完成Nervenet,应用到管理员后台重构中,之后宣传,维护开源站点
    2. 管理员后台增加部署工具、EDM、ERP,完工后移交给新人。至此,史上最强后台诞生,预计一年内只会有各种零敲碎打的需求;整个框架可以方便的扩展到其它后台当中去。
    3. 开发新产品
    4. 减肥,目标105KG
    5. 篮球恢复到ZOL时期水准
    6. 旅游
      1. 岘港会安
      2. 台北花莲
      3. 土耳其
    7. 过年开车回老家
    8. 看5本以上的技术书籍(其实找值得看的书挺难的)
    9. 每月至少写3篇技术文章(不太够,凑合吧)
    10. 捡回来Flash开发,主攻移动平台,买个Intellij试试
    11. 南下广州
  • 告别2013

    工作

    给朋友打工是一种很特殊的体验。因为之前彼此了解,多半感觉各种不靠谱,未必能产生“可以合作”的想法,不过我也不像悲观主义者那样,觉得朋友万不能产生经济关系——我的底线就是工资准点发,其它别无所求。

    于是过去的一年里,我就处于这种“这货到底行不行啊?”和“好吧,就这样吧“的状态。不过站在年尾往前看,公司确实成长的出乎意料。去年年终,我们还坐在借来的办公室里,十几个人七八杆枪;如今人数将近半百,岗位职责明确,流程清晰,即将拿到第二笔融资,不免让人感觉不可思议。如此看来,未来的日子里我多半还是会在这里继续努力吧。

    家庭

    成为父亲是更加特殊的体验。虽然之前也猜想过,当爹到底是啥感觉,不过看到孩子来到世上那一刹那的画面,仍然觉得百感交集,难以言表。对于姆二,我只希望他健康成长,除此再没什么想法,取名安逸一生,盼能如愿。

    万幸截至到目前,孩子健康活泼,作息时间也较有规律。爷爷奶奶外公外婆都很喜欢,也都愿意帮忙,给我省下很多时间和精力。家庭也在朝好的方向变化,真棒!

    个人

    去年本想趁着老婆休产假的时间好好努力工作一把,结果发现自己已经不适应长时间的独自生活,那段日子反而过得很辛苦。

    整体来说去年一年都比较拼,业余时间几乎都贡献给代码了,到后来身体状况不佳,只好再拿游戏补亏空。然而如前面所说,其实效率一般,所以站在年根往前看,那些业余项目真没哪个拿得出手……哎,真郁闷啊……倒是搭网站越来越顺手了,wordpress真是个好工具。

    不过公司项目做的还算不错,无论是产品本身还是布局,都比较令人满意。今年应该可以把管理员后台收尾,移交给新同事,我就可以投入到新项目中。值得一提的是新版广告墙的失败,得到很多经验教训,对我日后的移动开发助益颇大。

    告别2013

    2013年,很多东西都在变,有些事情计划的很好结果不好,有些东西没怎么计划顺其自然反而收获颇丰。希望在2014年里,能把2013年亏欠的补回来,能把做好的做得更好。

  • 创业公司两年半记

    (最近代码写得有点猛,换换脑子哔哔点闲言碎语)

    参加工作后,第一份工作就在发展迅猛的大公司,老板极善洗脑,我本人也年轻自带鸡血,于是奋发图强,励精图治。五年时光弹指一挥,公司已然发展成原先的五倍,我自己也从小白晋升到高工,结果跑得太快忘记了为何出发,蓦然回首,发现华发不存,残眉依稀,终于反省之后离开了那里。之前有文若干篇记录,此间不再赘述。

    我觉得,作为一个技术新人,去大公司有很多好处:学会尊重使用流程和规范,了解公司的运作方式,有个专业的导师领着入门,试着被高段位的客户和领导逼压,作品能直面百万级访问量的冲击,等等等等。然而每个人都必须明白,公司有其自己的运行轨道;公司大了还会有大公司病,所以要么调整自己适应公司,要么等发现自己和公司产生分歧时,理智的选择新工作。我不愿再被大公司折磨,于是很高兴地选择了一家小公司,M公司,去高高兴兴地写代码。

    M公司是家页游创业公司,我去的时候,正值他们第一款游戏达到顶峰,老板横刀立马准备大展鸿图。可惜人算不如天算,页游本身的生命周期有限而续命手段滞后,同时上马新产品过多致精力分散,新产品未能收获老产品那样的成功。当然这不是我离开的原因,打工嘛,只有老板给得起工资,我其实不在乎公司将来如何。

    不过这时候另外一个朋友的公司发展红火——其实他一早也找我去,可惜我当时不认同他的产品——邀我过去共谋大业,这次没啥好犹豫的,很快就答应了,然后办离职再就职,接着就干到现在。

    创业公司的老板很重要,老板的个人风格就决定了公司初期的风格,所以如果跟着一个爱好画大饼的老板或者跟着一个爱拼才会赢的老板,那就可以等着妻离子散了。反之,就可能是我现在的状态:有点忙,并不累,工作效率很高,产品产出既能满足成就感也能补上公司的需要。也许我们还能跑得更快,也有可能会直接掉沟里,谁知道呢。

    总之我很满意我现在的工作,我也很期待公司来年的发展,创业意味着各种可能,以前我爱慕安逸,现在发现,创业公司也能如此有人情味,真好。

  • 写在出发之前

    写在出发之前

    即将过去的一年(简称去年)对我们家来说,是动荡的一年。老家房子拆迁,于是我名下有了房产,借用拆迁款也买了汽车;儿子出生,身份和家庭结构发生了巨大的变化;同住4年半的堂兄移民成行,我们也离开了居住4年的知春路;公司起起伏伏,眼看年底居然真的隐隐露出王霸之气。

    我跟老婆去年则过得相对平淡——好吧,其实她还跑了趟青海湖,我就只有通宵回老家的份——相教往年多少都要出去旅游一下的,于是乎决定新年就在外面过吧。

    决定之后就开始找地方,选来选去选定岘港会安。出于得瑟的目的,我在群里吆喝了一嗓子:“岘港会安过新年,有同去的木?来几个凑别墅了~~”本来没指望能喊来人,结果居然立刻就有人响应,而且是一呼四应,变成了八人团。也好,这种群体出游的场面,从高中之后就在没经历过了,希望这次能成为美好的经验。

    眼瞅着要出发了,万事俱备,唯一不太确定的,就是前3天要住的岘港豪华别墅酒店(Luxury Villas Danang),发邮件也不回,订房间也没说跨年晚会的事儿,实在让人放心不下。不过,吉人自有天相,人品男发威的时刻到了!

    出发,新的一年,新的生活!

    岘港豪华别墅酒店
    图片取自Agoda
  • boostrap3的不良部分

    Bootstrap 3的脑残部分略多啊……比如.hidden-xs,明显应该是在超小设备上隐藏,结果里面赫然写着:display: block !important,important个毛啊……

  • 无心之举竟然成了产品布局的关键一环,我真TM是天才!!

    为啥没早点想出来呢……这会儿调薪方案都定了,字都签了……