我的技术和生活

  • 给WebStorm添加FileWatcher支持Browserify

    给WebStorm添加FileWatcher支持Browserify

    按惯例先介绍本文角色:

    Browserify 是一款让我们在浏览器端使用require('modules')来管理模块的工具。它会分析代码中的依赖关系,然后进行编译,打包成一个文件,我们只需要在HTML中引用生成的文件即可。

    WebStorm 则是JetBrains出品的开发利器,集成了各种开发工具和环境,价格不贵,速度很快,样式好看,我非常喜欢。

    Browserify 也提供了个工具,叫 watchify ,可以监控文件变化,即时编译。不过我更喜欢让WebStorm帮我处理这些事情,所以小小研究了一下,配置成功。

    具体步骤

    1. 通过 NPM 安装 Browserify
      npm install browserify -g
      
    2. [Preferences] -> [Tools] -> [File Watchers]

    3. 点击“+”按钮,没有内建方案,所以选择“<custom>”

      WebStorm Preferences

    4. 名字(Name)随便写,描述(Description)可以留空,重点是下面几项

    5. FileType 选择 JavaScript

    6. Scope 监控范围,内建的都不合适,所以点“…”按钮,弹出自定义的监控范围

    7. 新建一个域,名字和项目一样,好认。展开项目目录,选择存放JS文件的目录,点右侧的“Include”和“Include Recursively”将该目录下所有文件和文件夹都包含进来。OK保存。

      WebStorm Scope

    8. Program 是要执行的程序,这里自然是 Browserify,Mac下通常在/usr/local/bin/browserify

    9. Arguments 参数,参考 Browserify 的文档介绍,常规命令是

      browserify index.js -o bundle.js --debug
      

      其中 index.js 是我的入口JS,那么直接去掉 browserify 就好了。

    10. Working Directory 工作目录,点击右侧的“Insert Macros”按钮,选择 “$ProjectFileDir$”。后面接上目录“/js/”。

    11. 完成!

      WebStorm Browserify File Watcher

    小优化

    1. 前面我们设置监控范围的时候比较粗放,这样生成 bundle.js 之后会再跑一遍,有点不爽,所以图中我把 bundle.js 过滤掉了

    2. 确保这个File Watcher正常工作后,可以去把 Show console 设置成Never,这样在编辑到一半的时候就不会有错误提示了。

  • bower小技巧一则

    bower小技巧一则

    早先看到过一个用socket服务在线调试PHP的工具,后来怎么也找不到,于是决定自己造轮子。socket.io提供WebSocket服务,nodejs(今天竟然发布了5.0stable,简直丧心病狂) + express.js做服务器端。以前没有正经用过express.js,不过感觉上把Web所需的内容和nodejs所需的内容混在一起应该不是什么最佳实践,还是分开比较好。

    使用express.static('public')可以把public目录映射为web根目录。但是用bower管理依赖的话,默认安装目录在项目的根目录,虽然也可以映射出去,不过实际路径和项目路径有异,Webstorm里的黄色波浪线看起来也很不舒服。

    Google一下果然bower可以解决这个问题。在项目根目录下创建.bowerrc文件,按照JSON的写法,写入:

        {
          "directory": "public/components"
        }
    

    就可以指定别的目录作为安装依赖的位置。然后可以移动原来的依赖文件夹,也可以重新执行bower install

  • Chrome表单验证和keyup导致的灵异问题

    Chrome表单验证和keyup导致的灵异问题

    先说下环境,Mac OS El Capitan + Chrome 46,框架是Backbone + Boostrap。

    我做了一个自动搜索组件,独立测试时一切正常,放到产品中,别的都没问题,只有回车会出问题。代码在这里

    不用说,这个问题很诡异,调试了半天没有头绪,只能通过观察现象去推测:

    1. 没有报错
    2. 除了回车,其它功能正常
    3. 除了回车,keyUpHandler都能被正确触发
    4. 回车后,光标跳到其它元素

    看来看去,第4条最可疑——我按的是回车,它会什么会跳到别的单元格呢?

    这次运气比较好,表单中的接下来的几个元素刚好是日期,用到Bootstrap Datetimepicker这个插件,focus之后会自动填充日期。于是我发现,每次跳到的“其它元素”,都是原先空白的,而且是required的;不会跳到固定的,或者紧挨着的那个文本框。

    于是我便想,会不会是:

    1. 表单submitkeydown之后触发
    2. 触发之后进行表单验证,发现有未填的required元素,于是跳到该元素并提示
    3. Bootstrap Datetimepicker响应focus时间,填入日期,提示消失
    4. 我的Typeahead响应blur事件,隐藏列表
    5. keyup事件触发,但是输入框已没有焦点,就没有触发

    这个推测看起来有点道理,于是我把侦听的事件改成keydown,问题果然解决。

  • 未命名文章 2006

    我觉得吧,财务纪律走的是劝退流。

    “我想买几本书。”

    “好啊,买吧。”

    “想申请点经费。”

    “先买,再报销。”

    “好的。”

    问题是没有人会一口气买几百块的书,也没有那么多值得买的书,所以大约7、8个月,终于凑够了要报销。

    “书买的差不多了,麻烦报销。”

    “请给明细。”

    “我这书买了大半年,从好几家买的,单价也不定几折,做明细太麻烦了。”

    “必须明细哦亲。”

    “……”

    “还要写申请,要有明细,包含书名单价数量哦。”

    “可是我买的时候不知道要买什么啊!”

    “你现在知道了嘛。酱紫,你补发个采购需求,其中写上明细,请领导审批。”

    “……”

    “领导审批同意后,你再上报购书明细至公司行政,请行政部门补登记备案,办理下发、领用手续。”

    “……”

    “以上流程完毕后,走报销程序,办理报销。”

    “去他妈的不报了,几百块老子当喂狗。”

    真实的故事。

    PS:“喂狗”一词其实不太准确,给同事买书我很乐意,重点在于表达心情。

    姆伊PS:你别动不动就当喂我好么?我啥都没吃到啊!!汪汪!!

  • 再访富国岛

    再访富国岛

    2011年,我参加工作的第五个年头,201二进宫的第二个年头,我和老婆去越南富国岛度蜜月。这趟旅程给我带来的远不止美好的回忆:第一次出国,各种新奇的经历,让我禁不住重新思考过去五年在201的种种经历,最终决定立刻离开。

    所以我现在经常建议年轻人出国转转,修正修正三观。

    言归正传。这次国庆本来计划去甲米或者别的什么没去过的敌方,后来订票订晚了,到处都很贵,于是想起来这个性价比极高的地方:上次曾经订下一个5年之约,虽然这还差半年,也算是还愿了。

    越南,富国岛,似乎没有太大的变化,Mango Bay有好吃的食物和开放的房间,Bai Sao依然美丽动人,Le Verenda的服务和环境也仍然让人心旷神怡。不过对于我们而言,最大的变化是这次带了姆二。亲子游最大的变化就是一切日程以孩子为准:早上起来吃早饭,然后找个地方玩一下;然后吃午饭,睡午觉;醒了再找个地方玩一会儿,接着就吃晚饭;然后回去洗澡准备睡觉。第二天重演这个过程。

    原本想躺在沙滩上发呆,或者坐在露台上看书,或者两个人一起做马杀鸡,结果全成了陪小朋友挖沙土或者游泳。而且感觉时间过得飞快,一睁一闭,就回来上班了!有了孩子就是这样,带了他要后悔,不带的话,可能更后悔。

    Anyway,仍然希望孩子慢慢长大,慢慢将我们的世界分享给他。

  • 初尝Gulp

    初尝Gulp

    Gulp自出世起,热度就很高。它利用了Node的Stream机制,速度很快;函数式的写法也很便于阅读。这两大优势让它一跃成为最受欢迎的批处理工具,几乎占据了半壁江山(2015前端工具普查)。

    我之前一直用Grunt,为了不落后于时代,前几天在一个业余项目中试用了一下Gulp,以下是我的感受。

    确实好读

    虽然无法得到证实,不过我相信Grunt的设计中有很多都是借鉴Ant得来的——在没有Grunt之前,我曾经用Ant脚本 + Google Closer Compiler + Google Closer Stylesheets 压缩代码——比如,它的配置是JSON格式的,跟XML非常相像。Grunt的问题也在这里:声明任务需要写JSON配置文件,不同的任务需要写在一起,然后通过registerTask('task', ['task1', 'task2:js', 'task3', ....])来组织。这样前后脱节,确实既不好写,也不好读。个把月回来完全看不懂也不算意外……

    而Gulp的设计则更加自由,它充分利用JavaScript的函数式写法,一个任务就处理一批文件,从上往下看,结构非常清晰:

        gulp.task('js', function () {
          gulp.src(['js/'])
            .pipe(concat())
            .pipe(replace('@version@', version))
            .pipe(uglify())
            .pipe(gulp.dest('dist/js/app.js'));
        });
    

    胜者:Gulp

    速度快,然无卵

    Gulp受推崇的另一大原因就是速度快,一般的任务都是毫秒级。

    不过,这对于我来说意义不大。作为一名人民币玩家,哥的惯用IDE是WebStorm和PHPStorm,均支持文件实时编译和自动刷新等常用功能。所以我只需要批处理工具帮我压缩文件和打包即可。

    这个回合:打平。

    Stream VS Native,异步 VS 同步

    前面提到,Gulp利用了Node的Stream机制,它的内部是处理文档流。这方面Grunt比较保守,仍然是把文件读进内存,然后进行字符串操作。二者的机制不同不好对比,不过作为从页面仔成长起来的前端工程师,不得不承认我对的理解并不深刻,对文件读取和二进制操作也不甚了了,所以在理解二者和开发业务插件的时候,我宁可选择Grunt。

    另外,Gulp的任务是异步的,所以在我的使用场景中——先clean目录,然后各种压缩,最后打包成zip——就很难搞(所以我觉得很多人用Gulp只是为了实时编译预处理文件)。后来用了sequence插件才搞定。

    这方面我觉得Grunt虽然保守,但是更接近前端开发者的知识结构,学习成本更低。

    胜者:Grunt

    杂项

    Gulp的团队应该是有代码洁癖的,比如,他们有一个官方插件仓库,这很正常;但是他们竟然还有一个Gulp插件黑名单……简直匪夷所思……

    所以Gulp团队显然不愿意重复发明轮子,比如简单的clean目录,他们就不愿意做,而且也不让别人做,因为已经有很多工具可以实现这个功能了……但是对于初学者比如我来说,就会在插件引擎中找半天,最后还是Google到相关指引才得到正确的做法。

    这方面Grunt团队做的很好。Grunt插件中有一组是官方提供的,以grunt-contrib为命名空间,基本上,使用这套组件就可以完成我所有的日常工作了。

    胜者:Grunt


    总结

    大趋势上,Gulp的确势头迅猛。但是从实际体验中,我觉得Grunt目前更成熟,也能满足工作所需。

    我并不想说Gulp不好,只是现在的我用起来不太顺手。如果哪天我有时间好好搞搞后端,搞搞Node开发,把Stream概念搞清楚,也许我也会转投Gulp。不过目前,还是继续用Grunt吧。

  • Handlebars 4

    Handlebars 4

    图文……好像有关。

    Handlebars自从升到2.0之后,就放开了,版本号蹭蹭涨,如今已经升到4.0版。

    这个版本最显著的变化在于层级结构,以往的版本中,所有的Block Helper都可以形成一个层级。对于{{#each}}{{#with}}这种还好说,层级关系比较明确,很好理解;但是{{#if}},也有可能形成一个层级,这就很奇怪了。尤其像我前文所述,它只是可能形成一个层级,就更诡异了。

    所以这个修改其实是件好事儿。

    接下来,代码时间。

    Before

        {{#each list}}
          {{#if is_true}}
            <p>这里需要向上两层才能找到值。{{../../value}}注意,这里是两层。</p>
          {{/if}}
        {{/each}}
    
        var data = {
            list: [
              // items..
            ],
            value: 'value'
          }
          , template = Handlebars.compile(hbs);
        template(data);
    
    

    After

        {{#each list}}
          {{#if is_true}}
            <p>这里需要向上两层才能找到值。{{../value}}那,这里只有一层了</p>
          {{/if}}
        {{/each}}
    

    JavaScript是一样的。

    总结

    为了写这篇文章又去看了一遍Handlebars的文档,发现新增了不少特性,尤其开始支持子表达式,看来有必要再好好看一遍。

  • 近期要学习实践的新技术

    近期要学习实践的新技术

    众所周知,程序员是一个必须随时更新知识技能的职业。这其中,前端程序员尤甚。虽然我自称全栈,多半也要依赖前端技术作为小无相功驱使内力,方能打出少林寺七十二绝技写出其它平台的产品。所以,主动学习,更新知识技能是我必须坚持的。

    同时,在前端这个充满变革的领域,我不能把各种新奇的东西都放到实际生产中去。所以,不定期的集中学习就不可避免。

    那么,近期要学习实践的新技术有哪些呢?

    1. Bootstrap 4
    2. gulp
    3. Angular 2 直接从2开始吧,计划做个Todo
    4. React + React Native 要不做鸡血君?
    5. Polymer
    6. Material Design Lite
    7. Phonegap 5 自然是迁移游戏泡泡
    8. Eletron 结合别的技术做个写博客的工具。
    9. socket.io
    10. browserify

    希望能在年底前搞定。

  • 未命名文章 1978

    React Native开始支持Android了,看来近期有必要去学学看了。做个什么好呢?

  • 记一个踩过三次的坑……

    记一个踩过三次的坑……

    图文无关。太长时间没写文被降权了么,怎么最近流量跌这么厉害……

    老后台的架构仍然是PHP渲染页面,这里我选择mustache.php作为模板引擎。在某些场景下(比如搜索),我希望在前端使用复用同样的模板。正如前面一篇文章所说,我选择的是Handlebars.js,它号称支持mustache语法。

    然则在使用当中,我发现二者的实现仍然是有区别的。先上模板。

        {{#value}}{{value}}{{/value}}
    

    这段模板很简单,检查value是否存在,如果是的话则将其输出。这时:

    • mustache.php先以value作为逻辑判断依据;接下来,在value里找不到value这个key,便回到上一层,输出value的值
    • 而在handlebars.js眼中,{{value}}的类型也是很重要的,如果是String或者Number,那就直接输出;如果是Object,它就会采用{{#with ..}}{{/with}}操作,从value里面取值,而value里没有value,所以输出空。

    当模板为

        {{#has_value}}{{value}}{{/has_value}}
    

    此时,因为has_value是布尔值,所以Handlebars.js放弃把这个对象当做执行环境,而是继续维持当前的层级,所以就能正确输出value的值。


    这个问题虽然不很复杂,但是调试起来并不方便,而且文档中也没提过,所以三次踩坑每次都耽误不少时间。记录如此,避免再次遭遇。