繁体   English   中英

Javascript包含以后不在页面中加载

[英]Javascript includes not loading for later in the page

我们有一个Rails应用程序,我们都包括内部的HTML头我们的应用程序相关application.js

//= require jquery
//= require analytics
// other stuff...

然后在单个页面上,我们在页面底部有一个脚本标记用于analytics

<script>
  analytics.track('on that awesome page');
</script>

这通常很好,但偶尔我们会看到错误analytics is not defined ,最近才在Chrome 43上analytics is not defined 。因为所有内容都应该同步加载,这似乎应该是开箱即用的,但我将脚本更改为:

<script>
  $(document).ready(function () {
    analytics.track('on that awesome page');
  });
</script>

而现在我们偶尔会看到$ is not defined 我们没有看到来自同一IP的任何其他错误,否则我会怀疑application.js 任何其他想法为什么它可能会破裂? 您可以在此处查看示例页面。

完整的application.js

// Polyfills
//= require es5-shim/es5-shim
//= require es5-shim/es5-sham
//= require polyfills
//
// Third party plugins
//= require isMobile/isMobile
//= require jquery
//
//= require jquery.ui.autocomplete
//= require jquery.ui.dialog
//= require jquery.ui.draggable
//= require jquery.ui.droppable
//= require jquery.ui.effect-fade
//= require jquery.ui.effect-slide
//= require jquery.ui.resizable
//= require jquery.ui.tooltip
//
//= require jquery_ujs
//= require underscore
//= require backbone
//= require backbone-sortable-collection
//= require bootstrap
//= require load-image
//= require react
//= require react_ujs
//= require classnames
//= require routie
//= require mathjs
//= require moment
//= require stink-bomb
//= require analytics
//
// Our code
//= require_self
//= require extensions
//= require extend
//= require models
//= require collections
//= require constants
//= require templates
//= require mixins
//= require helpers
//= require singletons
//= require actions
//
//= require object
//= require components
//= require form_filler
//= require campaigns
//= require form_requests
//= require group_wizard
//= require step_adder

Chalk = {};
underscore = _;

_.templateSettings = {
  evaluate:    /\{\{(.+?)\}\}/g,
  interpolate: /\{\{=(.+?)\}\}/g,
  escape:      /\{\{-(.+?)\}\}/g
};

moment.locale('en', {
  calendar: {
    lastDay: '[Yesterday at] LT',
    sameDay: '[Today at] LT',
    nextDay: '[Tomorrow at] LT',
    lastWeek: 'dddd [at] LT',
    nextWeek: '[Next] dddd [at] LT',
    sameElse: 'L LT'
  }
});

更新:

我们偶尔会看到这种情况。 我们也看到过在我们在application.js之前加载脚本然后在其中引用它的情况:

javascript_include_tag 'mathjs'
javascript_include_tag 'application'

我们经常看到math is not defined错误。 我想知道在加载mathjs或其他脚本期间是否发生错误以防止它被加载,但事实上它发生在很多不同的库上,并且很少发生,这使得它看起来不太可能。 我们确实进行了一些调试检查,以查看我们的application.js是否已完全加载,而且通常似乎不是,即使稍后在页面中访问类似Jquery的东西。

这样做的一个动机是避免关于脚本运行时间过长的旧浏览器通知,但我们可能会放弃并将其全部放入application.js以避免错误。

如果您不等待加载定义analytics的脚本,或者您没有定义加载javascript文件的顺序,则会发生这种情况。 在尝试调用其方法track之前,请确保始终加载定义analytics的脚本。 根据您的设置,脚本可能以随机顺序加载,从而导致这种不可预测的行为。

你试图确保所有内容$(document).ready(function () {});加载,但是监听器$(document).ready(function () {}); 只是确保DOM准备就绪,而不是分析可用。 在这里你有同样的问题。 $只是jQuery所以$ is not defined意味着jQuery尚未加载。 所以可能你的脚本在jQuery加载之前出现并试图调用尚未定义的内容。

你的问题的基础可能在于你的假设:

一切都应该同步加载

一切都绝对不是同步加载的。 HTTP 1.1协议支持piplining,并且由于分块传输编码 ,在您的主网页加载完成之前,您引用的对象可能会也可能不会完成加载。

所有这些都是异步发生的,您不能保证它们的加载顺序。 这就是为什么浏览器在加载单个网页时与Web服务器建立多个并行连接的原因。 因为javascript和jQuery是事件驱动的,所以它们本质上是异步的,如果你不能很好地理解这种行为,它会变得混乱

使问题更加复杂的是,文件onload JavaScript事件(请记住,jQuery只是扩展JavaScript) “在DOM准备就绪时调用,可以在图像和其他外部内容加载之前调用。” 是的,这个外部内容可以包含您到jquery.js脚本的链接。 如果在DOM之后加载,那么您将看到错误“$ is not defined”。 由于链接脚本尚未加载,因此未定义jquery选择器。 同样,与您的其他链接库。

尝试使用$(window).load()代替。 这应该在所有引用的对象 DOM都已加载时起作用。

由于脚本倾向于以随机顺序加载,因此您可以强制分析脚本在一切准备就绪后加载。

这项任务有各种方法。

HTML5 rock为这类东西提供了一个非常好的片段 此外,根据您的需要,您可以使用像require.js这样的模块加载器。 使用加载promises和Require.js也很可爱 通常,您使用大量的JavaScript模块加载器将帮助您解决这个问题。

我保持了最丑陋,效率最低,最终仍然坚实的方法。 等待外部库加载可能会产生巨大的瓶颈,应该考虑并且也要非常小心地处理。 分析抓取脚本是async并且很难处理。 在这些情况下,我更喜欢

加载外部延迟脚本在某种程度上很简单:

function loadExtScript(src, test, callback) {
  var s = document.createElement('script');
  s.src = src;
  document.body.appendChild(s);

  var callbackTimer = setInterval(function() {
    var call = false;
    try {
      call = test.call();
    } catch (e) {}

    if (call) {
      clearInterval(callbackTimer);
      callback.call();
    }
  }, 100);
}

看一下这个例子,我在加载脚本时用promise函数动态加载jQuery: http//jsfiddle.net/4mtyu/487/

这是一个按顺序加载Angular和jQuery的链接演示: http//jsfiddle.net/4mtyu/488/

考虑上面的示例,您可以将分析加载为:

loadExtScript('https://code.jquery.com/jquery-2.1.4.js', function () {
    return (typeof jQuery == 'function');
}, jQueryLoaded);

function jQueryLoaded() {
    loadExtScript('https://analytics-lib.com/script.js', function () {
        return (typeof jQuery == 'function');
    }, analyticsLoadedToo);
}

function analyticsLoadedToo() {
    //cool we are up and running
    console.log("ready");
}

此代码将按照确切的顺序加载放在libs中的每个脚本URL,等待一个完全加载,然后再添加下一个。 它不像让浏览器那样优化,但它允许您监视错误并强制加载顺序。

(function(){
    var libs = [
        "http://example.com/jquery.js",
        "http://example.com/tracker.js",
        "http://example.com/myscript.js"
    ];
    var handle_error = function() {
        console.log("unable to load:", this.src);
    };
    var load_next_lib = function() {
        if(libs.length) {
            var script = document.createElement("script");
            script.type = "text/javascript";
            script.src = libs.shift();
            script.addEventListener("load", load_next_lib);
            script.addEventListener("error", handle_error);
            document.body.appendChild(script);
        }
    };
    load_next_lib();
})();

但我会建议您检查您网站的每个<script>标记,看看它们是否具有defer=""async=""属性。 大多数问题都来自这些,因为它们告诉浏览器稍后执行脚本。 他们也可能只是错误的顺序。

正如其他人所描述的那样,您正在加载脚本,然后在脚本加载完成之前尝试执行代码。 由于这是围绕HTML中相对少量代码的偶尔间歇性错误,您可以通过一些简单的轮询来修复它:

(function runAnalytics () {
  if (typeof analytics === 'undefined') {
    return setTimeout(runAnalytics, 100);
  }
  analytics.track('on that awesome page');
}());

...同样可以用于其他库,希望你看到模式:

(function runJQuery () {
  if (typeof $ === 'undefined') {
    return setTimeout(runJQuery, 100);
  }
  $(document).ready(...);
}());

编辑:正如评论中所指出的, load事件将更适合于此。 问题是这些事件可能在脚本执行时已经触发,您必须为这些场景编写回退代码。 为了不写10行以上的代码来执行1行,我提出了一个简单的,非侵入性的,快速和脏的,肯定会在每个浏览器轮询解决方案上工作。 但是为了不再获得任何投票,这里有一个更合适的繁琐和过于复杂的解决方案,以防你有一些牛肉投票:

<script>
// you can reuse this function
function waitForScript (src, callback) {
  var scripts = document.getElementsByTagName('script');
  for(var i = 0, l = scripts.length; i < l; i++) {
    if (scripts[i].src.indexOf(src) > -1) {
      break;
    }
  }
  if (i < l) {
    scripts[i].onload = callback;
  }
}

function runAnalytics () {
  analytics.track('on that awesome page');
}

if (typeof analytics === 'undefined') {
  waitForScript('analytics.js', runAnalytics);
} else {
  runAnalytics();
}

function runJQuery () {
  $(document).ready(...);
}

if (typeof $ === 'undefined') {
  waitForScript('jquery.min.js', runJQuery);
} else {
  runJQuery();
}
</script>

我们仍然每天都会看到这个错误几次,并且没有得到任何结论性的答案。 我最好的猜测是,较早的脚本超时下载,或者用户在链接之间快速导航,阻止页面完全加载。 他们也可能只是使用后退按钮在页面上的脚本中导致奇怪的行为。

它有一个很酷的解决方案。 为您的应用程序正确加载js。 在这里,我首先加载jQuery然后加载analytics.js。 我希望这将解决您支持html5的浏览器的问题。 码:

    var fileList =[
                    'your_file_path/jQuery.js',
                    'your_file_path/analytics.js'
                  ];

    fileList.forEach(function(src) {
    var script = document.createElement('script');
    script.src = src;
    script.async = false;
    document.head.appendChild(script);
 });

此代码段将首先加载jQuery,然后加载analytics.js文件。 希望这能解决您的问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM