万神劫

万物天地为剑,神鬼妖邪为剑
劫波万渡,宇宙苍穹尽为剑
是为万神劫!

4条评论 2014-12-28

node-webkit 的一些细节

前段时间,因为项目内 C++ 开发人员严重不足,所以只好把前端开发借来,使用 node-webkit 开发客户端程序
总的来说过程还是很顺利的,坑不算多,开发速度也不错,至少比 C++ 快很多
不过也不要过分乐观,毕竟客户端程序与 Web 不同,在产品细节、交互方式上都有很大的差异,所以在这方面需要花费不少时间

这篇文章只是开个头,记录了一些 毫无技术含量 的细节,目的是…… 表明虽然近一年没写博客,但是我还活着……

阻止文件拖拽进窗口

由于主界面其实就是 Chrome 的浏览器窗口,所以沿袭了它的一些行为,例如将外部文件拖进窗口时,node-webkit 会尝试打开文件
比如如果文件是图片,那么主窗口就会直接显示出这张图片,如果不禁用这个行为的话实在太容易穿帮了,示例代码如下:

$(window).on('dragover', function (e) {
    e.preventDefault();
    e.originalEvent.dataTransfer.dropEffect = 'none';
});
$(window).on('drop', function (e) {
    e.preventDefault();
});

禁用掉 window 对象的 dragover(让拖拽图标显示为禁用) 和 drop 事件即可

处理某些默认可拖拽的元素

HTML5 中定义了一套实现元素拖拽的方式,简单说,只要给元素加上 draggable=true 属性,这个元素就可以被拖拽了
大部分元素默认都是不可拖拽的,但是实践中发现,浏览器为 a, img 这两个元素默认加上了 draggable=true
如果应用中需要用到拖拽这种交互方式时,这会带来一些麻烦甚至 BUG,所以最好禁用掉
可以为每个 a 或者 img 元素显示地加上 draggable=false 的属性,或者用 JS 禁用掉 dragstart 事件

$(document).on('dragstart', 'a', function (e) {
    e.preventDefault();
});

避免窗口闪烁

node-webkit 执行时会先打开浏览器窗口,再加载资源
所以如果界面比较复杂而客户机器又比较烂的时候,会看到一个白屏,然后才是界面,甚至会有闪烁
这种穿帮的感觉实在是让人无法忍受,官方推荐先设置主界面不显示,然后在 DOM ready 之后再 show 出窗口
参考 https://github.com/rogerwang/node-webkit/wiki/Show-window-after-page-is-ready

当然这样应用的打开速度会感觉慢一点,如果官方能提供启动屏的配置就好了

输入框的右键菜单

node-webkit 界面中的输入框(input & textarea)是没有系统右键菜单的,所以如果要在输入框里复制粘贴只能使用快捷键
但是千万不要天真地以为所有用户都知道 Ctrl+C 和 Ctrl+V,比如曾经天真的我就为此收到了来自大量用户线上线下的投诉T_T
所以,请自己动手加上吧,node-webkit 提供了创建菜单和访问剪贴板的 API ,很方便,我的代码仅供参考:

    $(document).on('contextmenu', function (e) {
        e.preventDefault();
        var $target = $(e.target);
        var selectionType = window.getSelection().type.toUpperCase();
        if ($target.is(':text')) {   // TODO url/email/... 未加入判断哦
            var clipData = gui.Clipboard.get().get();
            menu.canPaste(clipData.length > 0);
            menu.canCopy(selectionType === 'RANGE');
            menu.popup(e.originalEvent.x, e.originalEvent.y);
        }
    });

    var menu = new Menu();

    var gui = require('nw.gui');

    function Menu() {
        this.menu = new gui.Menu();
        this.cut = new gui.MenuItem({
            label: '剪切',
            click: function () {
                document.execCommand('cut');
            }
        });

        this.copy = new gui.MenuItem({
            label: '复制',
            click: function () {
                document.execCommand('copy');
            }
        });

        this.paste = new gui.MenuItem({
            label: '粘贴',
            click: function () {
                document.execCommand('paste');
            }
        });

        this.menu.append(this.cut);
        this.menu.append(this.copy);
        this.menu.append(this.paste);
    }

    Menu.prototype.canCopy = function (bool) {
        this.cut.enabled = bool;
        this.copy.enabled = bool;
    };

    Menu.prototype.canPaste = function (bool) {
        this.paste.enabled = bool;
    };

    Menu.prototype.popup = function (x, y) {
        this.menu.popup(x, y);
    };

恩,其实如果加上“撤销”会更好,我又偷懒了……

系统 HTTP 代理

在 node-webkit 中,如果 HTTP 请求是由 XHR 对象发起的,那么会自动选择系统的 HTTP 代理(即 IE 代理设置)
不过既然是 node-webkit ,不可避免的会使用 node 来发起 HTTP 请求,而这种请求不会使用系统代理设置
这时候,为了适应用户的环境和方便调试(使用 Fiddler),最好还是能与 XHR 行为保持一致

var gui = require('nw.gui');
var proxy = gui.App.getProxyForURL('http://your.host.name');

proxy 的值可能是 "DIRECT" 或者 "PROXY http://127.0.0.1:8888"
参考 https://github.com/rogerwang/node-webkit/wiki/App#getproxyforurlurl

comments powered by Disqus