Blog

Blog

PHODAL

亚马逊1Button App源码分析之RequireJS

看到亚马逊的Chrome插件亚马逊购物助手上线了,打开京东出现了,亚马逊相关商品的推荐广告。想想他们两家的竞争也是够猛的,在请求的资源中发现了requirejsunderscore等等,于是便想看看里面的代码。先从一些简单的地方慢慢开始:

RequireJS

简单地先来说一下基本的顺序:

下面是插件中的main.html

<html>
  <head>
  </head>
  <body>
    <script type="text/javascript" src="lib/bit/update-event-interceptor.js"></script>
    <script type="text/javascript" src="lib/requirejs/require.js"></script>
    <script type="text/javascript" src="lib/requirejs/requirejs-config.js"></script>
  </body>
</html>

update-event-interceptor.js看上去是给Chrome用的,这里就先忽略之

chrome.runtime.onInstalled.addListener(window.UpdateEventInterceptor.onInstalled);

接着便请求requirejs-config

require.config({
    baseUrl: "native-src",
    shim: {
        underscore: {
            exports: "_"
        },
        md5: {
            exports: "md5"
        }
    }
});

require(["bit/ext/chromelike/main"], function() {});

里面引用了underscoremd5,然后请求了

 native-src/bit/ext/chromelike/main

这是一个很长很长的文件。。

Amazon main.js

看了看的main.js确实是很长很长,也发现了几点:

  • 他们似乎在main.js里面把所有的事情都做了。
  • 他们似乎不用IDE或者说不用JSLint?

说说第二点,发现了

var SIX_HOURS = 21600000;

var cc_suffix_map = {
    'us' : '20',
    'ca' : '20',
    'uk' : '21',
    'gb' : '21',
    'fr' : '21',
    'de' : '21',
    'es' : '21',
    'it' : '21',
    'jp' : '22',
    'cn' : '23'
};

是没有被用到的。

Amazon HelperKlass

首先迎面而来的是很长的一个HelperKlass,定义了那样的一个函数

var HelperKlass = function() {
    this.initialize.apply(this, arguments);
}

接着用underscore扩展了原型。。。

_.extend(HelperKlass.prototype, {})

里面定义了这样一堆方法

  • initialize
  • attributionManager
  • getRemoteAttributionParameters
  • getAlertTypeFromAlertID
  • toQueryString
  • _getParams
  • getAttributionContext
  • getRoot
  • getRootAsync
  • getTaggedRoot
  • getPingEndpointAsync
  • getGatewayUrl
  • getLocale
  • getLocaleAsync
  • getNotificationUrl
  • wishlistHelper
  • executeScript
  • injectJS
  • navigateToGateway
  • handleResponseCallback
  • handleShimError
  • markAllAlertsAsRead
  • loadPanelIframe

这个多达500行的类居然不是在一个单独的文件里。。。其中各种用到了Promise模式。

其中还有一个叫MessageHandler的类来处理不同的Message,于是还有相对应的一个start函数。而实际上我们需要关心的是chrome.extension.onConnect事件监听器

onConnect 事件监听器

Chrome的文档上有对于这个的解释

chrome.extension.onConnect.addListener(function(Port port) {...});

接收到本扩展进程的页面或content script发来的连接时触发。

对应的在Amazon中有下面的代码:

chrome.extension.onConnect.addListener(function(port) {
    if (!port){
        return;
    }
    Logger.log(port.name);
    if (port.name === 'UBPAppsChromePanel') {
        panelPort = port;
        panelPort.onDisconnect.addListener(function() {
            panelPort = false;
        });
    }
    port.onMessage.addListener(function(message) {
        Logger.log('Extension got message');
        Logger.log(message);
        MessageHandler.handleMessage(message, port);
    });
});

MessageHandler中有这样的一个逻辑

case "Options_acceptTermsOfUse":
    applicationState.setCurrentState("termsOfUsageAccepted");
    storage.set({'options.acceptedTermsOfUse': true});
    start();
    break;

start()做的便是诸如termsOfUsageAccepted这一类的事情。

最后函数的执行被绑定在

(function() {

    ...

    var oneButtonApp = new OneButtonApp({
        platformServiceBootstrapper: platformServiceBootstrapper,
        sandboxDelegate: sandboxDelegate,
        localeDelegate: Helper,
        transportStyle: "postMessage",
        transportStrategyOptions: {},
        productCompassManager: productCompassManager,
        platformCode: "chrome",
        featureConfigs: featureConfigs,
        tokenVendor: tokenVendor,
        simpleStorageDelegate: $simpleStorage,
        pinger: pinger,
        guidManager: guidManager
    });

    oneButtonApp.start(function() {
        Logger.log("All started");
    });

    window.oneButtonApp = oneButtonApp;

}());

这样的匿名函数里?

OneButtonApp

在OneButtonApp里用了Promise来说明了这个过程:

    start: function(cb) {
        return Promise.bind(this)
            .then(function() {
                return this._bootstrapPlatformService();
            })
            .then(function() {
                return this._startPlatformService();
            })
            .then(function() {
                return this._bootstrapProcessManager();
            })
            .then(function() {
                return this._startProcessManager();
            })
            .then(function() {
                return this._startInitialProcesses();
            })
            .then(function() {
                return this._registerProductCompassSystemHandler();
            })
            .then(function() {
                // In the interest of keeping things "simple",
                // ProductCompassManager is managed independently
                // for now. The intent is to convert it to
                // a "process" at some point.
                return this._wireProductCompassManager();
            })
            .then(function() {
                return this._productCompassManager.cycle();
            })
            .catch(function (err) {
                console.log(err);
            })
            .nodeify(cb);
    },

可以让我们知道这个是怎么运作起来的。

关于我

Github: @phodal     微博:@phodal     知乎:@phodal    

微信公众号(Phodal)

围观我的Github Idea墙, 也许,你会遇到心仪的项目

QQ技术交流群: 321689806
comment

Feeds

RSS / Atom

最近文章

关于作者

Phodal Huang

Engineer, Consultant, Writer, Designer

ThoughtWorks 技术专家

工程师 / 咨询师 / 作家 / 设计学徒

开源深度爱好者

出版有《前端架构:从入门到微前端》、《自己动手设计物联网》、《全栈应用开发:精益实践》

联系我: h@phodal.com

微信公众号: 最新技术分享

标签