分类: 技术

各种开发心得,包括语言、软件工程、开发工具等

  • 使用 Phantomjs 导出 PDF

    使用 Phantomjs 导出 PDF

    接到一个新需求,给用户导出电子协议,也就是 PDF 文档。因为我们后台使用 PHP,所以自然就去寻找 PHP 的解决方案。看了几个库,包括 Packagist 几万十几万下载的库,唉,不得不说,虽然 PHP 是世界上最好的语言,但是 PHP 开发者,审美水平真的,说好听点就是,没法看……

    第二个问题是,PHP 很多都要“拼” PDF,一行一行,一个元素一个元素,组装起一个完整的文档。相当费力,而且样式不好控制。从 HTML 转换也有类似的问题。研究了一会儿觉得蛋越来越疼,唉,算了,还是换 Phantomjs 吧。

    Phantomjs 是一个命令行 Webkit 工具,我们可以把它理解成不输出页面的浏览器,但它支持浏览器的各种功能,因为有 Webkit 嘛。所以渲染网页然后抓图就是小菜一碟了。

    用 Phantomjs 输出 PDF 非常简单:

    • 首先,约定好宽高(为方便打印,我们的电子协议要分页,而且有页头页脚),完成页面模板。
    • 完成 Phantomjs 脚本。因为只用它生成文档,所以不需要 Web 服务。
    • 用 PHP 调用脚本,生成 PDF 文档,然后 readfile 给用户下载。

    这样做的好处是我们随时可以预览效果,HTML 好读好改,PHP 替换其中的内容也很方便。而且代码非常简单,结合官方示例,很快就写出来了:

    'use strict';
    
    var page = require('webpage').create()
      , system = require('system')
      , args = system.args
      , url = args.length > 1 ? args[1] : 'http://www.dianjoy.com/'
      , filename = args.length > 2 ? args[2] : 'tmp';
    
    page.viewportSize = {
      width: 800,
      height: 1100
    };
    url = decodeURIComponent(url);
    page.open(url, function (status) {
      console.log(status);
      if (status === 'success') {
        page.render('/tmp/pdf/' + filename + '.pdf');
      }
      phantom.exit();
    });
    

    部署这段代码最大的问题反而是 GFW 导致 npm install phantomjs -g 失败,直接下载 zip 也不行(因为放在 Amazon S3 上)。于是继续给病魔加油,早日弄死方校长,及其它筑墙士。

    第二个问题则是 PHP 执行脚本老不成功。只看文档很简单:

    exec('/usr/local/phantomjs/bin/phantomjs pdf.js http://meathill.com/ meathill');
    

    但实际上既没有生成文档,也没有任何返回,调试半天,我突然想起来前阵子用 Apktool 解析安装包的时候也遭遇过类似的问题,于是在末尾加上 2>&1 问题竟然就解决了。

    Google 之,也不是很懂。回头再说吧。


    其它参考:

    shell_exec


    图文无关。其实是一位大学友人今日喜得贵子,放张她的奇怪照片祝贺她。

  • 解决 PHP 导出 CSV 的乱码问题

    解决 PHP 导出 CSV 的乱码问题

    项目当中遭遇一个奇怪的问题:

    导出 CSV,文本编码使用 UTF-8,使用 Mac + Numbers,Windows + WPS 打开都正常,使用 Windows + Office 就乱码(Mac + Office 没有测试)。用记事本打开另存为,编码的确是 UTF-8。

    后来发现,用 EditPlus++ 打开,然后另存为 “UTF-8 BOM”,就可以正常打开了。看来应该是这个 BOM 的问题。

    于是乎参考 StackOverflow 这个答案,给输出的开头加上 "\xEF\xBB\xBF",果然解决了问题。

  • Chrome 扩展存储

    Chrome 扩展存储

    以前写的有问题,编辑下。

    研究了半天,原来 Chrome 扩展不支持文件存储,只能使用 chrome.storage 保存持久化数据。

    提供两种形式,一种可以在浏览器间自动同步,适用登录用户保存设置,比如微博的“眼不见心不烦”,chrome.storage.sync,提供一个 key 8K,最大512个 key,总数据量100K(即不可能512个 key 都装满)的存储。这个方式对读写频率也有限制,想想也好理解,比精要往 Google 的云同步嘛。

    另外一种则更接近平时用的 localStorage,叫 chrome.storage.local。它的限制很少,只要总量不大于5M即可(可以通过设置 unlimitedStorage: true 来取消上限)。

    使用的时候,需要在 manifest.json 里声明权限

        {
          "permissions": [
            "storage"
          ]
        }
    

    这样的话就比较符合我的预期了,用户可以任意保存字幕到本地,太多了自己删掉就是,如果希望云同步就付费或者看广告。

  • 保持 Chrome 插件 popup.html 长期打开

    保持 Chrome 插件 popup.html 长期打开

    开发 Chrome 插件时,如果使用 popup.html,调试时反复审查元素很麻烦。此时可以用

    chrome-extension://插件id/popup.html
    

    在新标签页打开,就简单多了。

  • 春节计划

    春节计划

    好吧,春节期间继续为公司把官网的响应式改好了。原本计划的东西都没做……


    今年特别忙,一直上班到现在。终于把需要弄的都搞完了,可以放松一下。明天开始过年,今年变故比较多,原本计划的去这儿去那儿就算了,在家歇歇,放松放松。

    不过还是想干点正事儿,这几周都在赶进度做测试,业余项目弄得比较少,博客也没怎么写。未来七天都在家,没有出远门的计划,就弄一弄吧。

    1. 把上传组件用ES6重构了,然后修改一下抓取的UI
    2. 把 Satis 的抓取脚本弄了
    3. [填坑完成!2016-12-07] 写一篇 Satis 使用的博客
    4. 把插件模式的语言改成 JS

    差不多了,希望能完成。

  • 直播写代码

    晚上直播写代码啦!如果有人看的话,大概10点半到11点半。

    以 Web 相关技术为主,写的多半是各种 side project。

    地址:http://www.douyutv.com/meathill

    欢迎光临,可以点播。

  • PHP匿名函数使用父作用域的变量

    PHP匿名函数使用父作用域的变量

    (自古图文不相关)

    同事问为什么要用 array_maparray_filter之类的函数,用 foreach 不就好了?

    答:这样写出来的代码语义更清晰,阅读更容易。

    那么如何使用其它变量呢?global 么?

    答:global 肯定不合适,不过怎么写我也不知道。待我查查。

    在PHP中,不能像JS那样直接使用闭包里的其它变量,必须通过声明继承的语法,写出来是这样的:

        <?php
    
        $arr = [1, 2, 3];
        $split = 2; // 分界
        $arr = array_map(function ($value) use ($split) {
          return $value < $split ? 0 : $value;
        }, $arr);
        var_dump($arr);
    
        // 输出
        // 0, 2, 3
    

    重点是那个 use ($split)


    参考:

  • Compass + cleancss 导致的灵异小问题

    Compass + cleancss 导致的灵异小问题

    问题不大,不过很诡异。代码如下:

    SASS:

    .some-class
      display: none
    
    .other-class:blank
      display: none
    

    HTML:

    <div class="some-class">...</div>
    

    理论上说,这样这个div应该不显示。在本项目中,它的确没显示;但是在另外一个将本项目作为依赖的项目中,它却显示出来了。

    经检查,本地开发和部署时,直接使用 compass compile 生成CSS,compass 配置中,设置输出模式(output_style)为 compressed,结果是这样的:

    .some-class{display:none}.other-class:blank{display:none}
    

    而在作为依赖时,用到的则是 grunt-contrib-cssmin 处理过的CSS,刚才那句就被压缩成

    .some-class,.other-class:blank{display:none}
    

    :blank伪类尚未被Chrome中支持,于是整条规则都被忽略,导致div显示出来。

  • 啊,前端的轮子啊

    啊,前端的轮子啊

    时势造英雄。

    IT科技几十年来诞生过不少概念,但直接拿来就能作为宣传材料的不多,HTML5算一个。借助智能手机、微信微博快速普及带来的平台优势,HTML5快速成长,接连攻陷投资界、创业界、广告界,现在说起H5,在我的圈子里几乎无人不知无人不晓。

    资金充裕,整个行业自然地位上升,财务上升;继而自然从业者增加;继而就会出产大量的产品。而作为前端从业人员,最大的感觉就是:轮子真TM多啊……这不,我前几天计划学习一下近来出现的各种新技术,今天正好看到Bable最新入门The Complete Guide to ES6 with Babel 6,正好我写xgame-api的时候饱受node不能完全支持ES6之苦,马上坐正准备好好看。

    然后就看到:

    Keeping up to date

    Here’s a little story: while writing these guides, I learned that the require hook in babel-core used by Gulp and Mocha is already set to be deprecated.

    跟上变化

    讲个段子:就在我写这套入门的时候,babel-core里的require钩子,就是Gulp和Mocha使用的那个,被标记为“弃用”

    Babel6是24天之前发布的,这套文章是上周写的,核心API已经发生变化了。这让我不禁去想,紧跟业界潮流的成本会不会太高了?ES6的确提供了不少语法糖,以及终于像样的模块管理。如果现在就开始使用ES6,不仅可以提前享受到这一切,到标准真正到来的时候,可以省去升级的时间。

    不过这里面之考虑了标准变化,没有考虑需求变化和产品变化。如果是抽象性很强的代码,比如某种算法库,或者某个功能组件,那么从开发之日起,遇到的变化可能不会太多太大。但如果是业务代码,就不得不遭遇各种各样的修改和变化,于是功能稳定便于维护就更加重要。紧跟潮流,意味着必须付出多余的精力去应对各种不稳定导致的节外生枝,一不小心,业务逻辑就要延期交付鸟。

    又有个切身例子。Tiger Prawn(后面简称TP)立项的时候,模块管理无论是AMD还是CMD还是sea.js都难以让人满意,所以我就用了最简单的直接引用和命名空间。前几日用Browserify感觉不错,赶上TP遇到个问题,就寻思着重构下。结果努力一天之后,架构变化还是太复杂,遂放弃。次日修正实际问题只花了半天。生产中,还是优秀的架构设计更重要,毕竟开发和维护效率至上;追新追潮流几乎都可以算是作死的行为。

    那么什么时候学习新技术呢?程序员都应该做业余项目,不是私活,而是根据一些平日里瞎想出来的需求,用一些新技术去实现,即使实现不了,也能够实地演练各种新技术。这样,有朝一日新技术成熟了,就可以很快应用到新产品中。

  • 给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,这样在编辑到一半的时候就不会有错误提示了。