无法在“窗口” GoogleTagManager上执行“ postMessage”

时间:2018-08-31 20:05:20

标签: javascript error-handling google-tag-manager

最近,我收到此 postMessage无法克隆错误。大多数最新的浏览器(例如Chrome 68,Firefox 61.0,IE11,Edge)都在发生这种情况。

  

无法在“窗口”上执行“ postMessage”:function (a){if(qe.$a.hasOwnProperty(a))return qe.$a[a]}无法克隆。

堆栈跟踪为:

  

错误:无法在“窗口”上执行“ postMessage”:function (a){if(qe.$a.hasOwnProperty(a))return qe.$a[a]}无法克隆。
  在_reportEvent处(在(:1:35637),: 94:35处评估)
  在评估时(评估在(:1:35637),:55:5)
  在评估(评估在(:1:35637),:433:11)

在DevTools中搜索我页面的源将显示gtm.js作为代码片段的源:

gtm.js shown as source of the function

我的页面上有一个Google跟踪代码管理器跟踪代码。为什么会这样?

3 个答案:

答案 0 :(得分:13)

如果 structured clone algorithm 无法复制某些内容,则始终会发生这种情况。 window.postMessage 使用此算法。如果我们从 window.postMessage 阅读了第一个参数的文档:

  

消息
  要发送到另一个窗口的数据。数据使用structured clone algorithm进行序列化。

,然后打开结构化克隆算法的描述(请参见上面的最后一个链接),那么我们可以阅读:

  

结构化克隆算法是HTML5规范定义的用于复制复杂JavaScript对象的算法。通过postMessage()与Worker之间传输数据或使用IndexedDB存储对象时,在内部使用它。它通过递归遍历输入对象来建立一个克隆,同时保留先前访问的引用的映射,从而避免无限遍历循环。

     

不适用于结构化克隆的东西

     
      结构化克隆算法无法复制
  • ErrorFunction对象;尝试这样做会抛出一个   DATA_CLONE_ERR例外。
  •   
  • 尝试克隆DOM节点同样会引发DATA_CLONE_ERR异常。
  •   
  • 某些对象的参数未保留:

         
        
    • lastIndex对象的RegExp字段未保留。
    •   
    • 属性描述符,setter和getter(以及类似元数据的相似功能)不会重复。例如,如果一个对象   使用属性描述符标记为只读,它将被读写   在重复项中,因为这是默认条件。
    •   
    • 原型链不会被复制。
    •   
  •   
     

支持的类型

     

我用一些对象对其进行了测试,当发生这种情况时,我可以向您展示以下示例...

具有自定义功能的示例

var obj = {something: function(){}};
window.postMessage(obj, '*'); // DataCloneError

具有本机功能的示例

var obj = {something: window.alert};
window.postMessage(obj, '*'); // DataCloneError

我们将在诸如BooleanDateStringRegExpNumberArray之类的本机函数中看到相同的结果。

本机对象示例

var obj = {something: document};
window.postMessage(obj, '*'); // DataCloneError

带有HTML元素对象的示例

var obj = {something: document.createElement('b')};
window.postMessage(obj, '*'); // DataCloneError

如果阅读上述结构化克隆算法中的描述,我们可以写更多示例,但是我认为在这里就足够了。

为避免该错误我们该怎么做

在我们的代码中,我们只能在对象中使用受支持的类型(请参见上面的列表)。但是,在不是我们的代码中,我们必须通过此代码与开发人员联系,并向他们编写如何纠正其代码。对于Google跟踪代码管理器,您可以将其写到the Official Google Tag Manager Forum并说明他们如何更正代码。

某些浏览器的解决方法

在某些浏览器中,出于安全原因,您无法覆盖本机方法。例如, IE不允许覆盖window.postMessage 。但是其他浏览器(例如Chrome)允许覆盖此方法,如下所示:

var postMessageTemp = window.postMessage;
window.postMessage = function(message, targetOrigin, transfer)
{
    postMessageTemp(JSON.parse(JSON.stringify(message)), targetOrigin, transfer)
};

但是请注意,window是JavaScript上下文的全局对象,不是从prototype创建的。换句话说:您无法使用window.prototype.postMessage = ...覆盖它。

解决方法示例

var obj = {something: window};

var postMessageTemp = window.postMessage;
window.postMessage = function(message, targetOrigin, transfer)
{
    function cloneObject(obj)
    {
        var clone = {};
        for(var i in obj)
        {
            if(typeof(obj[i]) == 'object' && obj[i] != null)
            {
                if((''+obj[i]) == '[object Window]')
                {
                    delete obj[i];
                    continue;
                }

                clone[i] = cloneObject(obj[i]);
            }
            else
                clone[i] = obj[i];
        }
        return clone;
    }

    // to avoid weird error causing by window object by JSON.stringify() execution.
    var clone = cloneObject(message);

    postMessageTemp(JSON.parse(JSON.stringify(clone)), targetOrigin, transfer)
};

window.postMessage(obj, '*');

console.log('We do not have any errors.');

如何实施此变通办法

请将此覆盖的window.postMessage函数放在HTML脚本页面的脚本部分中,然后放在Google跟踪代码管理器脚本之前。但是,以更好的方式,您可以帮助Google跟踪代码管理器的开发人员了解并纠正此错误,您可以等待更正的Google跟踪代码管理器脚本。

答案 1 :(得分:7)

这些错误是由Facebook搜寻器执行JavaScript代码引起的。

我从这些IP(都在Facebook IP范围内)和用户代理发生了此错误:

66.220.149.14 - Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0
  31.13.115.2 - Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
 173.252.87.1 - Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
69.171.251.11 - facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)

要获取Facebook搜寻器IP的最新列表,请参阅https://developers.facebook.com/docs/sharing/webmasters/crawler/中的以下命令:

whois -h whois.radb.net -- '-i origin AS32934' | grep ^route

您将需要更新错误报告机制,以从这些IP范围中过滤出错误。

您可以在JavaScript中的客户端上执行此操作,方法是在发生错误时确定用户的IP地址(请参见How to get client's IP address using JavaScript?)。

或者您可以在服务器端执行此操作。这是ASP.NET MVC的示例:

using System.Linq;
// Requires the IPAddressRange NuGet library:
// https://www.nuget.org/packages/IPAddressRange/
using NetTools;

public class FacebookClientDetector
{
    /// <summary>
    /// The list of CIDR ranges of facebook IPs that its crawlers use.
    /// To generate, run
    ///     whois -h whois.radb.net -- '-i origin AS32934' | grep ^route
    /// https://developers.facebook.com/docs/sharing/webmasters/crawler/
    /// </summary>
    static readonly string[] facebookIpRanges = new string[] {
        "204.15.20.0/22",
        "69.63.176.0/20",
        "66.220.144.0/20",
        "66.220.144.0/21",
        "69.63.184.0/21",
        "69.63.176.0/21",
        "74.119.76.0/22",
        "69.171.255.0/24",
        "173.252.64.0/18",
        "69.171.224.0/19",
        "69.171.224.0/20",
        "103.4.96.0/22",
        "69.63.176.0/24",
        "173.252.64.0/19",
        "173.252.70.0/24",
        "31.13.64.0/18",
        "31.13.24.0/21",
        "66.220.152.0/21",
        "66.220.159.0/24",
        "69.171.239.0/24",
        "69.171.240.0/20",
        "31.13.64.0/19",
        "31.13.64.0/24",
        "31.13.65.0/24",
        "31.13.67.0/24",
        "31.13.68.0/24",
        "31.13.69.0/24",
        "31.13.70.0/24",
        "31.13.71.0/24",
        "31.13.72.0/24",
        "31.13.73.0/24",
        "31.13.74.0/24",
        "31.13.75.0/24",
        "31.13.76.0/24",
        "31.13.77.0/24",
        "31.13.96.0/19",
        "31.13.66.0/24",
        "173.252.96.0/19",
        "69.63.178.0/24",
        "31.13.78.0/24",
        "31.13.79.0/24",
        "31.13.80.0/24",
        "31.13.82.0/24",
        "31.13.83.0/24",
        "31.13.84.0/24",
        "31.13.85.0/24",
        "31.13.86.0/24",
        "31.13.87.0/24",
        "31.13.88.0/24",
        "31.13.89.0/24",
        "31.13.90.0/24",
        "31.13.91.0/24",
        "31.13.92.0/24",
        "31.13.93.0/24",
        "31.13.94.0/24",
        "31.13.95.0/24",
        "69.171.253.0/24",
        "69.63.186.0/24",
        "31.13.81.0/24",
        "179.60.192.0/22",
        "179.60.192.0/24",
        "179.60.193.0/24",
        "179.60.194.0/24",
        "179.60.195.0/24",
        "185.60.216.0/22",
        "45.64.40.0/22",
        "185.60.216.0/24",
        "185.60.217.0/24",
        "185.60.218.0/24",
        "185.60.219.0/24",
        "129.134.0.0/16",
        "157.240.0.0/16",
        "157.240.8.0/24",
        "157.240.0.0/24",
        "157.240.1.0/24",
        "157.240.2.0/24",
        "157.240.3.0/24",
        "157.240.4.0/24",
        "157.240.5.0/24",
        "157.240.6.0/24",
        "157.240.7.0/24",
        "157.240.9.0/24",
        "157.240.10.0/24",
        "157.240.16.0/24",
        "157.240.19.0/24",
        "157.240.11.0/24",
        "157.240.12.0/24",
        "157.240.13.0/24",
        "157.240.14.0/24",
        "157.240.15.0/24",
        "157.240.17.0/24",
        "157.240.18.0/24",
        "157.240.20.0/24",
        "157.240.21.0/24",
        "157.240.22.0/24",
        "157.240.23.0/24",
        "129.134.0.0/17",
        "157.240.0.0/17",
        "69.171.250.0/24",
        "204.15.20.0/22",
        "69.63.176.0/20",
        "69.63.176.0/21",
        "69.63.184.0/21",
        "66.220.144.0/20",
        "69.63.176.0/20",
    };

    public static bool IsFacebookClient(string ip)
    {
        IPAddressRange parsedIp;
        if (!IPAddressRange.TryParse(ip, out parsedIp)) {
            return false;
        }

        return facebookIpRanges.Any(cidr => IPAddressRange.Parse(cidr).Contains(parsedIp));
    }
}

答案 2 :(得分:0)

您可能是我在通过Workbox window使用服务工作者时遇到的困惑的受害者。该软件包利用了其模块的两个不同实例。有一组静态模块。同样,还有另一个集合驻留在主Workbox模块的保护下。这些在内部调用其静态副本

var payload = {key: "value"},

{ Workbox, messageSW } = await import('workbox-window'), // these represent static modules without a contextual `this`

wb = new Workbox('/service-worker.js'); // calls to this works on a single instance that interfaces with the underlying static equivalents

这意味着虽然您需要做

messageSW(wb.getSW(), payload);

调用wb.messageSW(wb.getSW(), payload)会导致OP的错误,因为此处的实际有效负载是循环服务工作程序,而不是我们的对象文字有效负载。在这种情况下,实例化的版本将与:

wb.messageSW( payload);