我将JS脚本迁移到ES6模块。客户端代码的常见模式是将跟踪信息推送到true
对象上的全局数组。其中一个具体示例是GTM window
数组。
我想将这些全局变量抽象为ES6模块,而不是指示linter使用window.dataLayer
忽略它们,因为它使代码更容易理解。我的第一次尝试是创建像这样的垫片:
/* global dataLayer */
然而,正如"实施视图"中所解释的here部分,上面的代码相当于:
export default window.dataLayer;
因此,每个模块中的const *default* = window.dataLayer; // *not* legal JavaScript
export { *default* as default };
数组将是原始window.dataLayer
数组的副本:当GTM引导异步时,从模块推送的事件不会被看到因为只有本地window.dataLater
会有这些事件。
使用ES6模块处理像这些全局变量的推荐方法是什么,这不涉及使用代理等重量级解决方案?在模块化系统中有很多全局变量确实让人感觉很糟糕。
答案 0 :(得分:3)
与您在问题中描述的内容相反,处理此问题的正确方法是:
/* data-layer.js */
window.dataLayer = [];
export default window.dataLayer;
...以及任何需要推送到“dataLayer”数组的模块,只需要:
import dataLayer from 'data-layer';
// ... some code I assume
dataLayer.push({
'pageCategory': 'signup',
'visitorType': 'high-value'
});
由于ES6导入会将只读引用传递给导出,因此它不是您要导入的数组的副本,而是在data-layer.js中创建和导出的数组。
但那时棘手的部分就是你必须修改Google跟踪代码管理器容器代码段才能在加载后立即运行,而是只在所有脚本之后运行可以修改dataLayer
已经运行。
如果您正在将 GTM容器代码段作为模块加载,您可能需要先导入所有可能先修改dataLater的脚本(并确保该部分实际运行)调用代码段。
类似的东西:
/* gtm-container.js */
import 'this';
import 'that';
import otherThing from 'the-other-thing'; // lets assume it returns a promise
import dataLayer from 'data-layer'l
otherThing.then(value => {
invokeSnippet()
})
.catch(err => {
handleError(err);
});
function invokeSnippet() {
(function(w,d,s,i){dataLayer.push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'//www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXX')
}
但是,你知道这并不是解释如何做到这一点,只是一个想法,我不知道有什么不同的东西可以添加到dataLayer,因为你正在转向异步模块模式,你将有了解如何以异步方式运行所有这些脚本将改变您使用GTM系统的方式,该系统明确地编写为使用全局变量同步运行。
我想知道Google是否会尽快使用现代JavaScript更新这些内容,或者是否有更多模块化选项可用。
许多(大多数)ES6 / ES2015功能包括模块在今天(2016年1月)的浏览器中不可用,因此您需要某种构建或加载器来制作这些东西在浏览器中工作(比如webpack,browserify,stealjs,jspm,或者有babel或tracuer之类的东西)
http://stealjs.com/可能是最容易上手的。