La mejor manera de usar jQuery alojado en Google, pero recurrir a mi biblioteca alojada en Google falla

StackOverflow https://stackoverflow.com/questions/1014203

Pregunta

¿Cuál sería una buena forma de intentar cargar el jQuery alojado en Google (o otras bibliotecas alojadas en Google), pero ¿cargar mi copia de jQuery si falla el intento de Google?

No digo que Google sea escamoso. Hay casos en los que la copia de Google está bloqueada (aparentemente en Irán, por ejemplo).

¿Configuraría un temporizador y buscaría el objeto jQuery?

¿Cuál sería el peligro de que salgan ambas copias?

Realmente no busco respuestas como " solo use el de Google " o " solo usa el tuyo. " Entiendo esos argumentos. También entiendo que es probable que el usuario tenga en caché la versión de Google. Estoy pensando en fallos para la nube en general.


Editar: esta parte se agregó ...

Dado que Google sugiere usar google.load para cargar las bibliotecas ajax, y realiza una devolución de llamada cuando haya terminado, me pregunto si esa es la clave para serializar este problema.

Sé que suena un poco loco. Solo estoy tratando de averiguar si se puede hacer de manera confiable o no.


Actualización: jQuery ahora está alojado en el CDN de Microsoft.

http://www.asp.net/ajax/cdn/

¿Fue útil?

Solución

Puedes lograrlo así:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"></script>
<script>
   window.jQuery || document.write('<script src="/path/to/your/jquery"><\/script>');
</script>

Esto debería estar en el <head> de su página y cualquier controlador de eventos listo para jQuery debería estar en el <body> para evitar errores (¡aunque no es infalible!).

Una razón más para no usar jQuery alojado en Google es que en algunos países, el nombre de dominio de Google está prohibido.

Otros consejos

La forma más fácil y limpia de hacer esto con diferencia:

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="path/to/your/jquery"><\/script>')</script>

Esto parece funcionar para mí:

<html>
<head>
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">
// has the google object loaded?
if (window.google && window.google.load) {
    google.load("jquery", "1.3.2");
} else {
    document.write('<script type="text/javascript" src="http://joecrawford.com/jquery-1.3.2.min.js"><\/script>');
}
window.onload = function() {
    $('#test').css({'border':'2px solid #f00'});
};
</script>
</head>
<body>
    <p id="test">hello jQuery</p>
</body>
</html>

La forma en que funciona es usar el objeto google que llama a http://www.google.com/ jsapi se carga en el objeto window. Si ese objeto no está presente, estamos asumiendo que el acceso a Google está fallando. Si ese es el caso, cargamos una copia local usando document.write. (Estoy usando mi propio servidor en este caso, use el suyo para probar esto).

También pruebo la presencia de window.google.load - También podría hacer una comprobación typeof para ver que las cosas son objetos o funciones, según corresponda. Pero creo que esto hace el truco.

Aquí está solo la lógica de carga, ya que el resaltado de código parece fallar desde que publiqué toda la página HTML que estaba probando:

if (window.google && window.google.load) {
    google.load("jquery", "1.3.2");
} else {
    document.write('<script type="text/javascript" src="http://joecrawford.com/jquery-1.3.2.min.js"><\/script>');
}

Aunque debo decir que no estoy seguro de que si esto es una preocupación para los visitantes de su sitio, debería jugar con el API de bibliotecas AJAX de Google en absoluto.

Dato curioso : Al principio intenté usar un bloque try..catch para esto en varias versiones, pero no pude encontrar una combinación tan limpia como esta. Me interesaría ver otras implementaciones de esta idea, simplemente como un ejercicio.

Si tiene modernizr.js incrustado en su sitio, puede usar el yepnope.js incorporado para cargar sus scripts de forma asincrónica, entre otros jQuery (con respaldo).

Modernizr.load([{
    load : '//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js'
},{
    test : window.jQuery,
    nope : 'path/to/local/jquery-1.7.2.min.js',
    both : ['myscript.js', 'another-script.js'],
    complete : function () {
        MyApp.init();
    }
}]);

Esto carga jQuery del Google-cdn. Luego se verifica si jQuery se cargó correctamente. Si no (& Quot; nope & Quot;), se carga la versión local. También se cargan sus scripts personales: & Quot; ambos & Quot; indica que el proceso de carga se inicia independientemente del resultado de la prueba.

Cuando todos los procesos de carga están completos, se ejecuta una función, en el caso 'MyApp.init'.

Personalmente prefiero esta forma de carga de scripts asíncronos. Y como confío en las pruebas de características proporcionadas por modernizr al construir un sitio, de todas formas lo tengo incrustado en el sitio. Así que en realidad no hay gastos generales.

Hay algunas soluciones excelentes aquí, pero me gustaría ir un paso más allá con respecto al archivo local.

En un escenario en el que Google falla, debería cargar una fuente local, pero quizás un archivo físico en el servidor no sea necesariamente la mejor opción. Menciono esto porque actualmente estoy implementando la misma solución, solo que quiero recurrir a un archivo local que se genera por una fuente de datos.

Mis razones para esto es que quiero tener algo de mente cuando se trata de hacer un seguimiento de lo que cargo de Google frente a lo que tengo en el servidor local. Si quiero cambiar las versiones, querré mantener mi copia local sincronizada con lo que estoy tratando de cargar desde Google. En un entorno donde hay muchos desarrolladores, creo que el mejor enfoque sería automatizar este proceso para que todo lo que uno tuviera que hacer fuera cambiar un número de versión en un archivo de configuración.

Aquí está mi solución propuesta que debería funcionar en teoría:

  • En un archivo de configuración de la aplicación, almacenaré 3 cosas: URL absoluta para la biblioteca, URL para la API de JavaScript y el número de versión
  • Escribir una clase que obtenga el contenido del archivo de la propia biblioteca (obtiene la URL de la configuración de la aplicación), la almacena en mi fuente de datos con el nombre y el número de versión
  • Escribir un controlador que extraiga mi archivo local de la base de datos y lo almacene en caché hasta que cambie el número de versión.
  • Si cambia (en la configuración de mi aplicación), mi clase extraerá el contenido del archivo en función del número de versión, lo guardará como un nuevo registro en mi fuente de datos, luego el controlador se activará y servirá la nueva versión.

En teoría, si mi código está escrito correctamente, todo lo que tendría que hacer es cambiar el número de versión en la configuración de mi aplicación y luego ¡viola! Tiene una solución alternativa que es automática y no tiene que mantener archivos físicos en su servidor.

¿Qué piensan todos? Tal vez esto sea excesivo, pero podría ser un método elegante para mantener sus bibliotecas AJAX.

Bellota

if (typeof jQuery == 'undefined') {
// or if ( ! window.jQuery)
// or if ( ! 'jQuery' in window)
// or if ( ! window.hasOwnProperty('jQuery'))    

  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.src = '/libs/jquery.js';

  var scriptHook = document.getElementsByTagName('script')[0];
  scriptHook.parentNode.insertBefore(script, scriptHook);

}

Después de intentar incluir la copia de Google del CDN.

En HTML5, no necesita configurar el atributo type.

También puedes usar ...

window.jQuery || document.write('<script src="/libs/jquery.js"><\/script>');

Es posible que desee utilizar su archivo local como último recurso.

Parece que a partir de ahora, la propia CDN de jQuery no es compatible con https. Si lo hizo, es posible que desee cargar desde allí primero.

Entonces aquí está la secuencia: Google CDN = & Gt; Microsoft CDN = & Gt; Su copia local.

<!-- load jQuery from Google's CDN -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script> 
<!-- fallback to Microsoft's Ajax CDN -->
<script> window.jQuery || document.write('<script src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.3.min.js">\x3C/script>')</script> 
<!-- fallback to local file -->
<script> window.jQuery || document.write('<script src="Assets/jquery-1.8.3.min.js">\x3C/script>')</script> 

Cargue condicionalmente la última versión / legacy de jQuery y respaldo:

<!--[if lt IE 9]>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
    <script>window.jQuery || document.write('<script src="/public/vendor/jquery-legacy/dist/jquery.min.js">\x3C/script>')</script>
<![endif]-->
<!--[if gte IE 9]><!-->
    <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
    <script>window.jQuery || document.write('<script src="/public/vendor/jquery/dist/jquery.min.js">\x3C/script>')</script>
<!--<![endif]-->
  • Paso 1: ¿No se pudo cargar jQuery? (marque jQuery variable)

Cómo verificar una variable no definida en JavaScript

  • Paso 2: Importar dinámicamente (el respaldo) archivo javascript

¿Cómo incluyo un archivo JavaScript en otro archivo JavaScript?

Debido al problema de prohibición de Google, prefiero usar el cdn de Microsoft http://www.asp.net/ajaxlibrary/cdn.ashx

¡Aquí hay una gran explicación sobre esto!

¡También implementa retrasos de carga y tiempos de espera!

http://happyworm.com/blog/2010/01/28/a-simple-and-robust-cdn-failover-for-jquery-14-in-one-line/

Para aquellas personas que usan ASP.NET MVC 5, agregue este código en su BundleConfig.cs para habilitar el CDN para jquery:

bundles.UseCdn = true;
Bundle jqueryBundle = new ScriptBundle("~/bundles/jquery", "//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js").Include("~/Scripts/jquery-{version}.js");
jqueryBundle.CdnFallbackExpression = "window.jQuery";
bundles.Add(jqueryBundle);

ACTUALIZACIÓN:
Esta respuesta resultó ser incorrecta. Por favor, vea los comentarios para la explicación real.


La mayoría de sus preguntas han sido respondidas, pero en cuanto a la parte final:

  

¿Cuál sería el peligro de que salgan ambas copias?

Ninguno realmente. Perdería el ancho de banda, podría agregar algunos milisegundos descargando una segunda copia inútil, pero no hay daño real si ambos salen adelante. Por supuesto, debe evitar esto utilizando las técnicas mencionadas anteriormente.

Hice un Gist que debería cargar dinámicamente jQuery si aún no está cargado, y si la fuente falla, procede a fallos (unidos entre muchas respuestas): https://gist.github.com/tigerhawkvok/9673154

¡Tenga en cuenta que planeo mantener el Gist actualizado pero no esta respuesta, por lo que vale!

/* See https://gist.github.com/tigerhawkvok/9673154 for the latest version */
function cascadeJQLoad(i) { // Use alternate CDNs where appropriate to load jQuery
    if (typeof(i) != "number") i = 0;
    // the actual paths to your jQuery CDNs
    var jq_paths = [
        "ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js",
        "ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.0.min.js"
    ];
    // Paths to your libraries that require jQuery
    var dependent_libraries = [
        "js/c.js"
    ];
    if (window.jQuery === undefined && i < jq_paths.length) {
        i++;
        loadJQ(jq_paths[i], i, dependent_libraries);
    }
    if (window.jQuery === undefined && i == jq_paths.length) {
        // jQuery failed to load
        // Insert your handler here
    }
}

/***
 * You shouldn't have to modify anything below here
 ***/

function loadJQ(jq_path, i, libs) { //load jQuery if it isn't already
    if (typeof(jq_path) == "undefined") return false;
    if (typeof(i) != "number") i = 1;
    var loadNextJQ = function() {
        var src = 'https:' == location.protocol ? 'https' : 'http';
        var script_url = src + '://' + jq_path;
        loadJS(script_url, function() {
            if (window.jQuery === undefined) cascadeJQLoad(i);
        });
    }
    window.onload = function() {
        if (window.jQuery === undefined) loadNextJQ();
        else {
            // Load libraries that rely on jQuery
            if (typeof(libs) == "object") {
                $.each(libs, function() {
                    loadJS(this.toString());
                });
            }
        }
    }
    if (i > 0) loadNextJQ();
}

function loadJS(src, callback) {
    var s = document.createElement('script');
    s.src = src;
    s.async = true;
    s.onreadystatechange = s.onload = function() {
        var state = s.readyState;
        try {
            if (!callback.done && (!state || /loaded|complete/.test(state))) {
                callback.done = true;
                callback();
            }
        } catch (e) {
            // do nothing, no callback function passed
        }
    };
    s.onerror = function() {
        try {
            if (!callback.done) {
                callback.done = true;
                callback();
            }
        } catch (e) {
            // do nothing, no callback function passed
        }
    }
    document.getElementsByTagName('head')[0].appendChild(s);
}

/*
 * The part that actually calls above
 */

if (window.readyState) { //older microsoft browsers
    window.onreadystatechange = function() {
        if (this.readyState == 'complete' || this.readyState == 'loaded') {
            cascadeJQLoad();
        }
    }
} else { //modern browsers
    cascadeJQLoad();
}

Google Hosted jQuery

  • Si le interesan los navegadores antiguos, principalmente versiones de IE anteriores a IE9, esta es la versión de jQuery más compatible
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
  • Si no & # 8217; no te importa oldIE, este es más pequeño y más rápido:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>

Plan de copia de seguridad / respaldo

  • De cualquier manera, debe utilizar un recurso alternativo local en caso de que el CDN de Google falle (poco probable) o esté bloqueado en una ubicación desde la cual sus usuarios acceden a su sitio (un poco más probable), como Irán o, a veces, China.
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script>if (!window.jQuery) { document.write('<script src="/path/to/your/jquery"><\/script>'); }
</script>

Referencia: http://websitespeedoptimizations.com/ContentDeliveryNetworkPost.aspx

Considero que debería escapar del último < a \ x3C en cadena. Cuando el navegador ve, considera que este es el final del bloque de secuencia de comandos (dado que el analizador HTML no tiene idea sobre JavaScript, no puede distinguir entre algo que solo aparece en una cadena y algo que realmente está destinado a finalizar la secuencia de comandos elemento). Por lo tanto, aparecer literalmente en JavaScript que está dentro de una página HTML (en el mejor de los casos) provocará errores y (en el peor de los casos) será un gran agujero de seguridad.

<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.0.0.min.js"></script>
<script>window.jQuery || document.write('<script src="js/jquery-2.0.0.min.js">\x3C/script>')</script>
if (typeof jQuery == 'undefined')) { ...

O

if(!window.jQuery){

No funcionará si la versión de cdn no está cargada, porque el navegador se ejecutará en esta condición y durante la descarga del resto de los javascripts que necesitan jQuery y devuelve un error. La solución fue cargar scripts a través de esa condición.

    <script src="http://WRONGPATH.code.jquery.com/jquery-1.4.2.min.js" type="text/javascript"></script><!--  WRONGPATH for test-->
  <script type="text/javascript">
  function loadCDN_or_local(){
    if(!window.jQuery){//jQuery not loaded, take a local copy of jQuery and then my scripts
      var scripts=['local_copy_jquery.js','my_javascripts.js'];
      for(var i=0;i<scripts.length;i++){
      scri=document.getElementsByTagName('head')[0].appendChild(document.createElement('script'));
      scri.type='text/javascript';
      scri.src=scripts[i];
    }
  }
  else{// jQuery loaded can load my scripts
    var s=document.getElementsByTagName('head')[0].appendChild(document.createElement('script'));
    s.type='text/javascript';
    s.src='my_javascripts.js';
  }
  }
  window.onload=function(){loadCDN_or_local();};
  </script>

Usando la sintaxis Razor en ASP.NET, este código proporciona soporte alternativo y funciona con una raíz virtual:

@{var jQueryPath = Url.Content("~/Scripts/jquery-1.7.1.min.js");}
<script type="text/javascript">
    if (typeof jQuery == 'undefined')
        document.write(unescape("%3Cscript src='@jQueryPath' type='text/javascript'%3E%3C/script%3E"));
</script>

O haga un ayudante ( descripción general del ayudante ):

@helper CdnScript(string script, string cdnPath, string test) {
    @Html.Raw("<script src=\"http://ajax.aspnetcdn.com/" + cdnPath + "/" + script + "\" type=\"text/javascript\"></script>" +
        "<script type=\"text/javascript\">" + test + " || document.write(unescape(\"%3Cscript src='" + Url.Content("~/Scripts/" + script) + "' type='text/javascript'%3E%3C/script%3E\"));</script>")
}

y úsalo así:

@CdnScript("jquery-1.7.1.min.js", "ajax/jQuery", "window.jQuery")
@CdnScript("jquery.validate.min.js", "ajax/jquery.validate/1.9", "jQuery.fn.validate")

Aunque escribir document.write("<script></script>") parece más fácil para el back-end de jQuery, Chrome da un error de validación en ese caso. Así que prefiero romper & Quot; script & Quot; palabra. Por lo tanto, se vuelve más seguro como arriba.

<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.11.1.min.js"></script>
<script>if (typeof jQuery === "undefined") {
   window.jqFallback = true;
   document.write("<scr"+"ipt src='http://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.1/jquery.min.js'></scr"+"ipt>");
} </script>

Para problemas a largo plazo, sería mejor registrar las fallas de respaldo de JQuery. En el código anterior, si el primer CDN no está disponible, JQuery se carga desde otro CDN. Pero es posible que desee saber ese CDN erróneo y eliminarlo permanentemente. (este es un caso muy excepcional) También es mejor registrar problemas de reserva. Entonces puede enviar casos erróneos con AJAX. Debido a que JQuery no está definido, debe usar JavaScript de vainilla para la solicitud de AJAX.

<script type="text/javascript">
    if (typeof jQuery === 'undefined' || window.jqFallback == true) {
        // XMLHttpRequest for IE7+, Firefox, Chrome, Opera, Safari
        // ActiveXObject for IE6, IE5
        var xmlhttp = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
        var url = window.jqFallback == true ? "/yourUrl/" : "/yourUrl2/";
        xmlhttp.open("POST", url, true);
        xmlhttp.send();
    }
</script>

La imposibilidad de cargar el recurso desde un almacén de datos externo más allá de su control es difícil. Buscar funciones faltantes es totalmente falaz como un medio para evitar sufrir un tiempo de espera, como se describe aquí: http://www.tech-101.com/ support / topic / 4499-issues-using-a-cdn /

Otro recurso alternativo que reemplaza ajax.googleapis.com con cdnjs.cloudflare.com :

(function (doc, $)
{
    'use strict';

    if (typeof $ === 'undefined')
    {
        var script = doc.querySelector('script[src*="jquery.min.js"]'),
            src = script.src.replace('ajax.googleapis.com', 'cdnjs.cloudflare.com');

        script.parentNode.removeChild(script);
        doc.write('<script src="' + src + '"></script>');
    }
})(document, window.jQuery || window.Zepto);
  • Puede apegarse a una versión jQuery especificándola en la cadena
  • Perfecto para la gestión de activos que no funciona con fragmentos HTML
  • Probado en la naturaleza: funciona perfectamente para usuarios de China

Puede usar código como:

<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>window.jQuery || document.write('<script type="text/javascript" src="./scripts/jquery.min.js">\x3C/script>')</script>

Pero también hay bibliotecas que puede usar para configurar varios posibles retrocesos para sus scripts y optimizar el proceso de carga:

  • basket.js
  • RequireJS
  • yepnope

Ejemplos:

basket.js Creo que la mejor variante por ahora. Guardará su script en el almacenamiento local, lo que acelerará las próximas cargas. La llamada más simple:

basket.require({ url: '/path/to/jquery.js' });

Esto devolverá una promesa y puede hacer la próxima llamada por error o cargar dependencias en caso de éxito:

basket
    .require({ url: '/path/to/jquery.js' })
    .then(function () {
        // Success
    }, function (error) {
        // There was an error fetching the script
        // Try to load jquery from the next cdn
    });

RequireJS

requirejs.config({
    enforceDefine: true,
    paths: {
        jquery: [
            '//ajax.aspnetcdn.com/ajax/jquery/jquery-2.0.0.min',
            //If the CDN location fails, load from this location
            'js/jquery-2.0.0.min'
        ]
    }
});

//Later
require(['jquery'], function ($) {
});

yepnope

yepnope([{
  load: 'http://ajax.aspnetcdn.com/ajax/jquery/jquery-2.0.0.min.js',
  complete: function () {
    if (!window.jQuery) {
      yepnope('js/jquery-2.0.0.min.js');
    }
  }
}]);

Casi todas las CDN públicas son bastante confiables. Sin embargo, si le preocupa el dominio de Google bloqueado, simplemente puede recurrir a algún otro CDN público como PageCDN , CDNJS o jsDelivr :

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
   window.jQuery || document.write('<script src="https://pagecdn.io/lib/jquery/3.2.1/jquery.min.js"><\/script>');
</script>

Sin embargo, en tal caso, es posible que prefiera usar otra CDN como su opción preferida y recurrir a Google CDN para evitar solicitudes fallidas y tiempos de espera:

<script src="https://pagecdn.io/lib/jquery/3.2.1/jquery.min.js"></script>
<script>
   window.jQuery || document.write('<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"><\/script>');
</script>
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top