7

我正在尝试在基于 Backbone.js 和 require.js 的单页站点中使用 Mixpanel 事件跟踪。

查看 Mixpanel 提供的用于剪切和粘贴到常规网页的片段,我可以告诉他们已经推出了自己的异步加载机制,从独立资源中提取实际的Mixpanel API,做一些额外的工作来设置“人” ' 等属性,最后通过全局命名空间暴露出 'mixpanel' 对象。

我尝试为片段或独立 API 添加 shim 配置条目,但都不能正常工作。

通过我的研究,我在 github 上发现了一个项目,它完全符合我的要求,但是它已经有几年历史了,并且基于“旧”的 mixpanel API。在新版本中,Mixpanel 对代码片段和 API 进行了一些我无法理解的重大更改。

我希望有人了解 Mixpanel 片段和/或 AMD 和 require.js,并且可以引导我完成此操作。

4

4 回答 4

13

有两件有趣的事情使这个问题变得很奇怪:

  1. mixpanel 库要求您在加载它之前定义 window.mixpanel。
  2. mixpanel 库将 window.mixpanel 重新定义为它的 init 进程的一部分。

开箱即用,mixpanel 片段在加载 lib 之前不支持 get_distinct_id (以及根据定义,同步的任何调用),但在加载 mixpanel lib 之前会存根其他方法(例如跟踪)排队。因此我们有两种选择:

选项 1. 放弃异步支持并等待库加载 -要点

此方法通过创建一个预初始化模块来设置 mixpanel lib 所需的 window.mixpanel 依赖项,然后将其指定为对 lib 本身的依赖项。然后要求“mixpanel”将阻塞,直到库完全加载。

<html>
    <head>
        <title>Mixpanel AMD Example - Sync</title>
        <script type="text/javascript" src="http://requirejs.org/docs/release/2.1.8/minified/require.js"></script>
        <script type="text/javascript">
            requirejs.config({
                paths : { 'mixpanel': "//cdn.mxpnl.com/libs/mixpanel-2.2.min" },
                shim: {
                    'mixpanel': {
                        deps: ['mixpanel-preinit'],
                        exports: 'mixpanel'
                    }
                }
            });
            define("mixpanel-preinit", function(require) {
                // this is a stripped down version of the mixpanel snippet that removes the loading of the lib via external script tag and the stubs for queuing calls
                var b=window.mixpanel=window.mixpanel||[];var i,g;b._i=[];b.init=function(a,e,d){function f(b,h){var a=h.split(".");2==a.length&&(b=b[a[0]],h=a[1]);b[h]=function(){b.push([h].concat(Array.prototype.slice.call(arguments,0)))}}"undefined"!==typeof d?c=b[d]=[]:d="mixpanel";b._i.push([a,e,d])};b.__SV=1.2;
                b.init("YOUR TOKEN");
            });
        </script>
    </head>
    <body>
        <script type="text/javascript">
            require(['mixpanel'], function(mixpanel) {
                mixpanel.track("my event", {prop1: "val1"}); 
                console.log(mixpanel.get_distinct_id()); 
            });
        </script>
    </body>
</html>

选项 2. 提供“加载”回调来更新模块的属性。-要点

如果您真的需要异步支持,则需要在加载 mixpanel 库后更新存根的方法。我不推荐这样做,因为(除其他原因外)它会在复制后导致 window.mixpanel !== mixpanel。这也意味着您必须防止像 get_distinct_id() 这样的同步调用出现竞争条件。如果该库尚未加载,它将是未定义的。注意:我建议如果你必须有异步支持,你应该只通过 window.mixpanel 调用而不是所有这些疯狂。

<html>
    <head>
        <title>Mixpanel AMD Example - Async</title>
        <script type="text/javascript" src="http://requirejs.org/docs/release/2.1.8/minified/require.js"></script>
        <script type="text/javascript">
            requirejs.config({
                paths : { 'mixpanel-lib': "//cdn.mxpnl.com/libs/mixpanel-2.2.min" }
            });

            define("mixpanel", function(require) {
                var b = window.mixpanel || [];
                if (!b.__SV) { var i, g; window.mixpanel = b; b._i = []; b.init = function (a, e, d) { function f(b, h) { var a = h.split("."); 2 == a.length && (b = b[a[0]], h = a[1]); b[h] = function () { b.push([h].concat(Array.prototype.slice.call(arguments, 0))) } } var c = b; "undefined" !== typeof d ? c = b[d] = [] : d = "mixpanel"; c.people = c.people || []; c.toString = function (b) { var a = "mixpanel"; "mixpanel" !== d && (a += "." + d); b || (a += " (stub)"); return a }; c.people.toString = function () { return c.toString(1) + ".people (stub)" }; i = "disable track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config people.set people.set_once people.increment people.append people.track_charge people.clear_charges people.delete_user".split(" "); for (g = 0; g < i.length; g++) f(c, i[g]); b._i.push([a, e, d]) }; b.__SV = 1.2 }

                // go ahead and start loading the mixpanel-lib
                require(['mixpanel-lib']);

                b.init("YOUR TOKEN", {loaded: function() { 
                    // now that we know mixpanel is loaded, copy the prop references to our module def
                    for(var prop in window.mixpanel) {
                        b[prop] = window.mixpanel[prop];
                    }
                }}); 
                return b;
            });
        </script>
    </head>
    <body>
        <script type="text/javascript">
            require(['mixpanel'], function(mixpanel) {
                mixpanel.track("my event", {prop1: "val1"}); 
                console.log(mixpanel.get_distinct_id()); // probably undefined
            });
        </script>
    </body>
</html>
于 2013-09-23T19:05:49.187 回答
5

以下解决方案适用于 mixpanel api 2.2

添加带有以下垫片的混合面板 -

path : {
    'mixpanel' : '//cdn.mxpnl.com/libs/mixpanel-2.2.min'
}

shim : {
    'mixpanel' : {
        exports : 'mixpanel'
    },
}

并使用以下 requirejs 模块而不是 mixpanel 给出的代码段 -

define('mixpanel-snippet', [], function(){
    var b = window.mixpanel || [];
    if (!b.__SV) {
        var i, g;
        window.mixpanel = b;
        b._i = [];
        b.init = function (a, e, d) {
            function f(b, h) {
                var a = h.split(".");
                2 == a.length && (b = b[a[0]], h = a[1]);
                b[h] = function () {
                    b.push([h].concat(Array.prototype.slice.call(arguments, 0)))
                }
            }
            var c = b;
            "undefined" !==
            typeof d ? c = b[d] = [] : d = "mixpanel";
            c.people = c.people || [];
            c.toString = function (b) {
                var a = "mixpanel";
                "mixpanel" !== d && (a += "." + d);
                b || (a += " (stub)");
                return a
            };
            c.people.toString = function () {
                return c.toString(1) + ".people (stub)"
            };
            i = "disable track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config people.set people.set_once people.increment people.append people.track_charge people.clear_charges people.delete_user".split(" ");
            for (g = 0; g < i.length; g++)
                f(c, i[g]);
            b._i.push([a, e, d])
        };
        b.__SV = 1.2
    }
    b.init("YOUR TOKEN");
    require(['mixpanel'], function(mixpanel){});

    return b;
});

我只是从 mixpanel 中获取了片段,删除了异步 mixpanel 加载并将其包装在 requirejs 模块定义中。

更改模块底部的“YOUR TOKEN”。

与 require 调用一起使用的示例——

require([
    'mixpanel-snippet',
], function (mixpanel) {
        mixpanel.track("Landing Page with AMD SHIM");
});

编辑:第二个是稍作修改后的正确答案。mixpanel 脚本的工作方式是它需要在代码片段中的 init 调用在实际 mixpanel 加载之前发生。诀窍是在 init 调用之后需要 mixpanel。我已经编辑了第二个答案并删除了第一个答案,这是要点

编辑:@johanandren Requirejs 对评论的回答遵循 AMD 原则,并且脚本加载的顺序不固定。如果您需要在使用 mixpanel-snippet 之前加载 mixpanel,可以使用以下 hack。

//at the end of mixpanel-snippet code mentioned above force the script to block until mixpanel is loaded

b.init("YOUR TOKEN");
var wait = true;
require(['mixpanel'], function(mixpanel){wait = false;});
while(wait){}
return b;

** 它违反了 AMD 的异步加载功能,强制脚本阻塞,而且即使在 vanila mixpanel 片段中,加载也是异步的,并且无法保证初始 api 调用的可用性

于 2013-07-29T07:18:25.787 回答
0

这对我有用。将您的 mixpanel 片段放在名为 mixpanel-snippet.js 的 js/lib 目录中。

在您的 app.js 中,将以下 shim 添加到 require.config:

'mixpanel-snippet': {
  exports: 'mixpanel'
}

在您的 require 函数中,将“mixpanel-snippet”添加到所需的数组并初始化 mixpanel:

require(['jquery', 'backbone', 'app/router', 'mixpanel'], function ($, Backbone, Router) {
    var router = new Router();
    Backbone.history.start();
    mixpanel.init(key);
    mixpanel.track("Landed on Start up Page");
});

如果有帮助,我可以提供一个完整的 app.js 示例,但这应该可以帮助您入门。让我知道这个是否奏效。

于 2013-08-01T15:49:26.617 回答
0

从 mixpanel 的 2.7.x 版本开始,他们现在支持与 requirejs 兼容的多个 AMD/UMD 版本,您可以在他们的 github 页面上获取它:https ://github.com/mixpanel/mixpanel-js

requirejs(['./mixpanel.amd'], function(mixpanel) {

    mixpanel.init("FAKE_TOKEN", {
        debug: true,
        loaded: function() {
            mixpanel.track('loaded() callback works but is unnecessary');
            alert("Mixpanel loaded successfully via RequireJS/AMD");
        }
    });

    mixpanel.track('Tracking after mixpanel.init');

});
于 2016-05-06T19:41:06.387 回答