Récemment, j'obtiens cette erreur postMessage ne peut pas être clonée . Cela se produit sur la plupart des navigateurs les plus récents tels que Chrome 68, Firefox 61.0, IE11, Edge).
Échec de l'exécution de 'postMessage' sur 'Window':
function (a){if(qe.$a.hasOwnProperty(a))return qe.$a[a]}
n'a pas pu être cloné.
La trace de la pile est:
Erreur: Impossible d'exécuter 'postMessage' sur 'Window':
function (a){if(qe.$a.hasOwnProperty(a))return qe.$a[a]}
n'a pas pu être cloné.
at _reportEvent (à l’évaluation: (: 1: 35637),: 94: 35)
at eval (évaluation à (: 1: 35637),: 55: 5)
at eval (évaluation à (: 1: 35637),: 433: 11)
La recherche dans la source de ma page dans DevTools montre gtm.js
comme source du fragment de code:
J'ai un code de suivi Google Tag Manager sur ma page. Pourquoi cela arrive-t-il?
Cela se produit tout le temps, si quelque chose ne peut pas être dupliqué par le algorithme de clone structuré. Cet algorithme est utilisé par window.postMessage
. Si nous lisons la documentation à partir de window.postMessage
pour le premier paramètre:
message
Données à envoyer à l’autre fenêtre. Les données sont sérialisées à l'aide de algorithme de clone structuré .
puis ouvrez la description de algorithme de clone structuré (voir dernier lien ci-dessus), puis on peut lire:
L'algorithme de clone structuré est un algorithme défini par la spécification HTML5 pour la copie d'objets JavaScript complexes. Il est utilisé en interne lors du transfert de données depuis et vers les travailleurs via
postMessage()
ou lors du stockage d'objets avecIndexedDB
. Il construit un clone en récursant à travers l'objet d'entrée tout en conservant une carte des références précédemment visitées afin d'éviter des cycles de parcours infinis.Ce qui ne fonctionne pas avec un clone structuré
- Les objets
Error
etFunction
ne peuvent pas être dupliqués par l'algorithme de clone structuré; tenter de le faire lève une exceptionDATA_CLONE_ERR
.- Tenter de cloner
DOM
nœuds lève également une exceptionDATA_CLONE_ERR
.Certains paramètres d'objets ne sont pas conservés:
- Le champ
lastIndex
des objetsRegExp
n'est pas préservé.- Les descripteurs de propriété, les setters et les getters (ainsi que les fonctionnalités similaires à des métadonnées) ne sont pas dupliqués. Par exemple, si un objet est marqué en lecture seule à l'aide d'un descripteur de propriété, il sera en lecture-écriture dans la copie, puisqu'il s'agit de la condition par défaut.
- La chaîne de prototypes ne fait pas l’objet d’une duplication.
Types pris en charge
- Tous les types primitifs (Remarque: mais pas les symboles)
Boolean
objetString
objetDate
RegExp
(Remarque: le champ lastIndex n'est pas conservé.)Blob
File
FileList
ArrayBuffer
ArrayBufferView
(Remarque: cela signifie essentiellement tous les tableaux typés comme Int32Array, etc.)ImageData
Array
Object
(Remarque: cela inclut uniquement les objets simples (par exemple, à partir de littéraux d'objet))Map
Set
Je l'ai testé avec quelques objets et je peux vous montrer les exemples suivants lorsque cela se produit ...
Exemple avec une fonction personnalisée
var obj = {something: function(){}};
window.postMessage(obj, '*'); // DataCloneError
Exemple avec fonction native
var obj = {something: window.alert};
window.postMessage(obj, '*'); // DataCloneError
La même chose que nous verrons avec des fonctions natives comme Boolean
, Date
, String
, RegExp
, Number
, Array
.
Exemple avec un objet natif
var obj = {something: document};
window.postMessage(obj, '*'); // DataCloneError
Exemple avec un objet d'élément HTML
var obj = {something: document.createElement('b')};
window.postMessage(obj, '*'); // DataCloneError
Nous pourrions écrire plus d'exemples si nous lisons la description de L'algorithme de clone structuré ci-dessus, mais je pense que cela suffit.
Dans notre code, nous ne pouvions utiliser que les types supportés (voir la liste ci-dessus) dans nos objets. Mais dans notre code, nous devons contacter les développeurs de ce code et leur écrire comment ils doivent corriger leur code. Dans le cas de Google Tag Manager, vous pouvez l'écrire en le forum officiel de Google Tag Manager avec une description de la manière dont ils doivent corriger leur code.
Solution de contournement pour certains navigateurs
Dans certains navigateurs, vous ne pouvez pas remplacer les méthodes natives pour des raisons de sécurité. Par exemple IE ne permet pas de remplacer window.postMessage
. Mais d’autres navigateurs tels que Chrome permettent de remplacer cette méthode comme suit:
var postMessageTemp = window.postMessage;
window.postMessage = function(message, targetOrigin, transfer)
{
postMessageTemp(JSON.parse(JSON.stringify(message)), targetOrigin, transfer)
};
Mais notez que window
est un objet global du contexte JavaScript et qu'il n'est pas créé à partir de prototype
. En d'autres termes: vous ne pouvez pas le remplacer par window.prototype.postMessage = ...
.
Exemple avec solution de contournement
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.');
Comment implémenter cette solution de contournement
S'il vous plaît mettez cette fonction window.postMessage
Surchargée dans la partie script de votre page HTML avant le script Google Tag Manager. Mais, mieux, vous pourriez aider les développeurs de Google Tag Manager à comprendre et à corriger cette erreur et vous pouvez attendre le script corrigé de Google Tag Manager.
Ces erreurs sont causées par les robots Facebook qui exécutent du code JavaScript.
J'ai eu des occurrences de cette erreur de ces adresses IP (toutes dans les plages d'IP de Facebook) et des agents d'utilisateur:
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)
Pour obtenir une liste à jour des adresses IP des robots Facebook, consultez cette commande dans https://developers.facebook.com/docs/sharing/webmasters/crawler/ :
whois -h whois.radb.net -- '-i Origin AS32934' | grep ^route
Vous devrez mettre à jour votre mécanisme de rapport d'erreur pour filtrer les erreurs de ces plages IP.
Vous pouvez le faire côté client en JavaScript en déterminant l'adresse IP de l'utilisateur en cas d'erreur (voir Comment obtenir l'adresse IP du client à l'aide de JavaScript? ).
Ou vous pouvez le faire côté serveur. Voici un exemple pour 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));
}
}