Eisen's Blog

© 2023. All rights reserved.

最近写 chrome extension 的一些心得

June 10, 2012

chromeextension

有幸找到一个可以专门写 chrome extension 的工作,非常开心,可以专心的写 chrome extension 了。看了一些别人的 chrome extension 自己也有了一些新的体会,写在这里,留个未来的自己。

最近在做的这个扩展是 mei.fm 的一键收藏扩展。整体来说,这个扩展的功能并不是非常复杂。但是,再简单也是有一定工作量的。而且,程序是为变化而生的,最初的架构体系做的比较顺手对以后的工作也是有好处的。

调研

首先,我做了一些调研的工作。重点查看了 evernote clipper 以及 pocket 两个类似的产品。看了他们之后才知道做一个这样的插件没有表象那么简单。尤其是读了 pocket extension 的源码确实是给我了不少的启发。

pocket 里面有一个很不错的结构就是让 background page 作为处理事务的中枢。让 content page 以及 popup 的所有请求利用 chrome extension 所提供的 sendRequest 的 api 发送给 background 去处理,然后 bg 将处理的结果以 callback 的形式发送回去。

chrome.extension.sendRequest(string extensionId, any request, function responseCallback)

chrome.extension.onRequest.addListener(function(any request, MessageSender sender, function sendResponse) {...});

详见https://code.google.com/chrome/extensions/tabs.html 以及 https://code.google.com/chrome/extensions/extension.html

这样做的好处就是可以更好的组织自己的代码,让 content page 以及 popup 去专注于页面的展示,而把逻辑以及 ajax 的东西集中在 bg 去做。比如,在 pocket 中会用到一个 content script 用来显示保存的状态。它每次动作对 bg 发送请求,让 bg 把链接保存到 pocket,然后它根据 bg 返回的信息,进行相应的信息提示的工作。

图丢了

实践

代码的组织

我很仔细的看了 pocket 的代码组织并非常的认同它的组织方式,那么我就直接采取了它的结构。把与 mei.fm 请求相关的方法单独做一个命名空间,api。然后所有其他页面的逻辑请求都通过 sendRequest 的方式发送给 bg.js 来处理。但是,chrome extension 的 onRequest 监听器不监听自己页面的 request 事件,那么我只好对 bg 页面的处理单独做了处理(这个很让人伤心)。

面对频繁改变的 api 接口的办法

其实我觉得既然已经做到了 chrome extension 的地步了,那么其提供的 api 应当是比较稳定的了。但是,很遗憾,不是这样。甚至是在我把插件做完的时候,有些 api 还是处于无法使用的状态。那么,我自己需要一个模拟 api 的机制了。

    if(debug) {
      var data = JSON.parse('{"requestId":"url68a1-9","providerId":"solrs-0.1","object":"url","errCode":0,"dice":0,"items":[{"id":"5c9ccc75421a2acbffa982f5fd123134","score":0.92352885,"provider":"solrs-0.1","detail":{"best_title":"皇马拜仁裁判确定:欧冠奥运决赛主裁 吹拜仁漏 3 点球","url":"http://sports.sina.com.cn/g/2012-04-24/09206035821.shtml"}},{"id":"051e0cd0943d490f51cb7be1a502dbb9","score":0.24958704,"provider":"solrs-0.1","detail":{"best_title":"视频-2011 百大进球 TOP20 梅西 C 罗鲁尼内马尔竞风流","url":"http://sports.sina.com.cn/g/video/2011Top100goal/index.shtml"}}]}');
      setTimeout(function() {
        if(data.items && data.items.length) {
          data = _pre_process(data);
          console.log(['cmd', data]);
          callback && callback.success && callback.success(data.items);
        } else {
          callback && callback.failed && callback.failed();
        }
      }, 1000);
    } else {
      $.ajax({
        url: request_url,
        data: {title: title, uid: localStorage.uid, cnt: 2, ts: 0, app: 'mei.fm', url: url},
        timeout: 5000,
        dataType: 'json',
        success: function(data) {
          if(data.items && data.items.length) {
            data = _pre_process(data);
            callback && callback.success && callback.success(data.items);
          } else {
            callback && callback.failed && callback.failed();
          }
        },
        error: function() {
          callback && callback.failed && callback.failed();
        }
      });
    }
  }

用 setTimeout 去模拟一个 ajax 请求真是伤不起唉。然后面对返回的数据结构的不确定,我甚至有做个数据结构验证的东西...

不要忘了断网的情况

虽说这是个特殊的情况,但是我还是通过设置 ajax 的 timeout 去做了这个断网的处理,所以调用 jquery 的 api 差不多全部都是 $.ajax 而不是 $.post 或者 $.get。

感想

不管东西是多大多小,想做的完美其实都是要下功夫的。面对频繁的变化,你的代码是否可以让你很容易的扩展,或者是修改功能呢?我自己是不敢保证的。努力去做到这个才能算是不错的设计。我还是需要在这条路上走的更远。

然后就是单元测试的问题了。代码的修改是不可避免的,尤其是做所谓的重构。但是,目前来看,我很不想去做这个工作。因为我没有想到好的办法去做这个单元测试的工作。如果修改了代码反而让程序跑不起来了确实就让人更揪心了。如何拆分代码,并用更好的办法去测试代码也是个很重要的事情。

更好的使用 git 有可能很好的减少自己的心理负担。什么时候应该打 tag 怎么撤销自己的一个 commit 怎么去拉一个分支都是我需要做学习的事情。