¿Debe CSS siempre preceder a Javascript?


897

En innumerables lugares en línea, he visto la recomendación de incluir CSS antes de JavaScript. El razonamiento es generalmente de esta forma :

Cuando se trata de ordenar su CSS y JavaScript, desea que su CSS sea lo primero. La razón es que el hilo de renderizado tiene toda la información de estilo que necesita para renderizar la página. Si el JavaScript incluye primero, el motor de JavaScript debe analizarlo todo antes de continuar con el siguiente conjunto de recursos. Esto significa que el hilo de renderizado no puede mostrar completamente la página, ya que no tiene todos los estilos que necesita.

Mi prueba real revela algo bastante diferente:

Mi arnés de prueba

Utilizo el siguiente script de Ruby para generar demoras específicas para varios recursos:

require 'rubygems'
require 'eventmachine'
require 'evma_httpserver'
require 'date'

class Handler  < EventMachine::Connection
  include EventMachine::HttpServer

  def process_http_request
    resp = EventMachine::DelegatedHttpResponse.new( self )

    return unless @http_query_string

    path = @http_path_info
    array = @http_query_string.split("&").map{|s| s.split("=")}.flatten
    parsed = Hash[*array]

    delay = parsed["delay"].to_i / 1000.0
    jsdelay = parsed["jsdelay"].to_i

    delay = 5 if (delay > 5)
    jsdelay = 5000 if (jsdelay > 5000)

    delay = 0 if (delay < 0) 
    jsdelay = 0 if (jsdelay < 0)

    # Block which fulfills the request
    operation = proc do
      sleep delay 

      if path.match(/.js$/)
        resp.status = 200
        resp.headers["Content-Type"] = "text/javascript"
        resp.content = "(function(){
            var start = new Date();
            while(new Date() - start < #{jsdelay}){}
          })();"
      end
      if path.match(/.css$/)
        resp.status = 200
        resp.headers["Content-Type"] = "text/css"
        resp.content = "body {font-size: 50px;}"
      end
    end

    # Callback block to execute once the request is fulfilled
    callback = proc do |res|
        resp.send_response
    end

    # Let the thread pool (20 Ruby threads) handle request
    EM.defer(operation, callback)
  end
end

EventMachine::run {
  EventMachine::start_server("0.0.0.0", 8081, Handler)
  puts "Listening..."
}

El mini servidor anterior me permite establecer retrasos arbitrarios para archivos JavaScript (tanto del servidor como del cliente) y retrasos arbitrarios de CSS. Por ejemplo,http://10.0.0.50:8081/test.css?delay=500 me da un retraso de 500 ms para transferir el CSS.

Yo uso la siguiente página para probar.

<!DOCTYPE html>
<html>
  <head>
      <title>test</title>
      <script type='text/javascript'>
          var startTime = new Date();
      </script>
      <link href="http://10.0.0.50:8081/test.css?delay=500" type="text/css" rel="stylesheet">
      <script type="text/javascript" src="http://10.0.0.50:8081/test2.js?delay=400&amp;jsdelay=1000"></script> 
  </head>
  <body>
    <p>
      Elapsed time is: 
      <script type='text/javascript'>
        document.write(new Date() - startTime);
      </script>
    </p>    
  </body>
</html>

Cuando incluyo el CSS primero, la página tarda 1,5 segundos en renderizarse:

CSS primero

Cuando incluyo el JavaScript primero, la página tarda 1,4 segundos en renderizarse:

JavaScript primero

Obtengo resultados similares en Chrome, Firefox e Internet Explorer. En Opera, sin embargo, el orden simplemente no importa.

Lo que parece estar sucediendo es que el intérprete de JavaScript se niega a comenzar hasta que se descargue todo el CSS. Entonces, parece que tener JavaScript incluido primero es más eficiente ya que el hilo de JavaScript obtiene más tiempo de ejecución.

Me falta algo, ¿la recomendación de colocar CSS incluye antes de JavaScript no es correcta?

Está claro que podríamos agregar asíncrono o usar setTimeout para liberar el hilo de renderizado o poner el código JavaScript en el pie de página, o usar un cargador de JavaScript. El punto aquí es sobre el pedido de bits esenciales de JavaScript y bits CSS en la cabeza.


120
¿1511 vs 1422 es una diferencia estadísticamente significativa? Eso es 6 por ciento. El umbral general para la diferencia de rendimiento humano notable a promedio es de aproximadamente el 20 por ciento.
Jeff Atwood

15
El punto es que el reordenamiento elimina este retraso arbitrario, puede establecer el retraso a lo que desee, es solo una demostración del problema.
Sam Saffron

3
fue su retraso de 100 ms? la diferencia en tus capturas de pantalla es de 89 ms. En su URL es delay=400&amp;jsdelay=1000y delay=500que no está cerca de 100 ms o 89 ms. Supongo que no tengo claro a qué números te refieres.
Jeff Atwood

44
"Si el Javascript incluye lo primero, el motor de Javascript tiene que analizarlo todo antes de continuar con el siguiente conjunto de recursos. Esto significa que el hilo de representación no puede mostrar completamente la página, ya que no tiene todos los estilos que necesita ". - si la inclusión de JS está en la cabeza, la JS se ejecutará antes de que se muestre la página, independientemente de si la inclusión de CSS fue anterior o posterior.
nnnnnn

162
No estoy seguro si ha considerado esto, pero la percepción del tiempo de carga también es importante. Entonces, por ejemplo, si cargar el CSS primero le da incluso solo el color / textura de fondo de la página, parecería ser más rápido. Los tiempos de carga absolutos pueden no ser indicativos de esto.
Rakesh Pai

Respuestas:


712

Esta es una pregunta muy interesante. Siempre he puesto mis CSS <link href="...">s antes de mi JS<script src="..."> porque "una vez leí que es mejor". Entonces tienes razón; ¡Ya es hora de que hagamos una investigación real!

Configuré mi propio arnés de prueba en Node (código a continuación). Básicamente, yo:

  • Se aseguró de que no hubiera almacenamiento en caché HTTP, por lo que el navegador tendría que realizar una descarga completa cada vez que se carga una página.
  • Para simular la realidad, incluí jQuery y el H5BP CSS (por lo que hay una cantidad decente de script / CSS para analizar)
  • Configure dos páginas: una con CSS antes del script, una con CSS después del script.
  • Grabado cuánto tiempo tardó el script externo en <head> ejecutarse el
  • Grabado cuánto tiempo tardó en <body>ejecutarse el script en línea en , que es análogo aDOMReady .
  • Retrasó el envío de CSS y / o script al navegador en 500 ms.
  • Ejecuté la prueba 20 veces en los 3 principales navegadores.

Resultados

Primero, con el archivo CSS retrasado 500ms:

     Browser: Chrome 18    | IE 9         | Firefox 9
         CSS: first  last  | first  last  | first last
=======================================================
Header Exec |              |              |
Average     | 583ms  36ms  | 559ms  42ms  | 565ms 49ms
St Dev      | 15ms   12ms  | 9ms    7ms   | 13ms  6ms
------------|--------------|--------------|------------
Body Exec   |              |              |
Average     | 584ms  521ms | 559ms  513ms | 565ms 519ms
St Dev      | 15ms   9ms   | 9ms    5ms   | 13ms  7ms

A continuación, configuro jQuery para retrasar 500 ms en lugar del CSS:

     Browser: Chrome 18    | IE 9         | Firefox 9
         CSS: first  last  | first  last  | first last
=======================================================
Header Exec |              |              |
Average     | 597ms  556ms | 562ms  559ms | 564ms 564ms
St Dev      | 14ms   12ms  | 11ms   7ms   | 8ms   8ms
------------|--------------|--------------|------------
Body Exec   |              |              |
Average     | 598ms  557ms | 563ms  560ms | 564ms 565ms
St Dev      | 14ms   12ms  | 10ms   7ms   | 8ms   8ms

Por último, me puse tanto jQuery y CSS para retrasar por 500 ms:

     Browser: Chrome 18    | IE 9         | Firefox 9
         CSS: first  last  | first  last  | first last
=======================================================
Header Exec |              |              |
Average     | 620ms  560ms | 577ms  577ms | 571ms 567ms
St Dev      | 16ms   11ms  | 19ms   9ms   | 9ms   10ms
------------|--------------|--------------|------------
Body Exec   |              |              |
Average     | 623ms  561ms | 578ms  580ms | 571ms 568ms
St Dev      | 18ms   11ms  | 19ms   9ms   | 9ms   10ms

Conclusiones

Primero, es importante tener en cuenta que estoy operando bajo el supuesto de que tiene scripts ubicados en el <head>documento (en lugar de al final del <body>). Existen varios argumentos con respecto a por qué puede vincular sus scripts en el <head>frente al final del documento, pero eso está fuera del alcance de esta respuesta. Esto es estrictamente acerca de si <script>s debe ir antes de <link>s en el <head>.

En los navegadores modernos de DESKTOP, parece que vincularse a CSS nunca proporciona una ganancia de rendimiento. Poner CSS después de la secuencia de comandos le brinda una ganancia trivial cuando se retrasan tanto la CSS como la secuencia de comandos, pero le brinda grandes ganancias cuando se retrasa la CSS. (Mostrado por las lastcolumnas en el primer conjunto de resultados).

Dado que vincular a CSS por última vez no parece afectar el rendimiento, pero puede proporcionar ganancias en ciertas circunstancias, debe vincular a hojas de estilo externas después de vincular a secuencias de comandos externas solo en navegadores de escritorio si el rendimiento de los navegadores antiguos no es una preocupación. Siga leyendo para conocer la situación móvil.

¿Por qué?

Históricamente, cuando un navegador encuentra una <script>etiqueta que apunta a un recurso externo, el navegador deja de analizar el HTML, recupera el script, lo ejecuta y luego continúa analizando el HTML. Por el contrario, si el navegador encuentra una <link>hoja de estilo externa, continuará analizando el HTML mientras recupera el archivo CSS (en paralelo).

Por lo tanto, el consejo ampliamente repetido de poner primero las hojas de estilo: se descargarían primero, y el primer script para descargar podría cargarse en paralelo.

Sin embargo, los navegadores modernos (incluidos todos los navegadores que probé anteriormente) han implementado el análisis especulativo , donde el navegador "mira hacia adelante" en el HTML y comienza a descargar recursos antes de que se descarguen y ejecuten los scripts.

En navegadores antiguos sin análisis especulativo, poner los scripts primero afectará el rendimiento ya que no se descargarán en paralelo.

Soporte de navegador

El análisis especulativo se implementó por primera vez en: (junto con el porcentaje de usuarios de navegadores de escritorio de todo el mundo que utilizan esta versión o superior a partir de enero de 2012)

  • Chrome 1 (WebKit 525) (100%)
  • IE 8 (75%)
  • Firefox 3.5 (96%)
  • Safari 4 (99%)
  • Opera 11.60 (85%)

En total, aproximadamente el 85% de los navegadores de escritorio en uso actualmente admiten la carga especulativa. Poner scripts antes de CSS tendrá una penalización de rendimiento en el 15% de los usuarios a nivel mundial ; YMMV basado en la audiencia específica de su sitio. (Y recuerda que ese número se está reduciendo).

En los navegadores móviles, es un poco más difícil obtener números definitivos simplemente debido a lo heterogéneo que es el navegador móvil y el panorama del sistema operativo. Dado que el renderizado especulativo se implementó en WebKit 525 (lanzado en marzo de 2008), y casi todos los navegadores móviles valiosos se basan en WebKit, podemos concluir que "la mayoría" de los navegadores móviles deberían admitirlo. De acuerdo con el modo peculiar , iOS 2.2 / Android 1.0 usa WebKit 525. No tengo idea de cómo se ve Windows Phone.

Sin embargo, ejecuté la prueba en mi dispositivo Android 4 y, aunque vi números similares a los resultados de escritorio, lo conecté al nuevo depurador remoto fantástico en Chrome para Android, y la pestaña Red mostró que el navegador realmente estaba esperando descargar CSS hasta que los JavaScripts se hayan cargado por completo; en otras palabras, incluso la versión más reciente de WebKit para Android no parece admitir el análisis especulativo. Sospecho que podría estar apagado debido a las limitaciones de CPU, memoria y / o red inherentes a los dispositivos móviles.

Código

Perdona el descuido - esto fue Q&D.

app.js

var express = require('express')
, app = express.createServer()
, fs = require('fs');

app.listen(90);

var file={};
fs.readdirSync('.').forEach(function(f) {
    console.log(f)
    file[f] = fs.readFileSync(f);
    if (f != 'jquery.js' && f != 'style.css') app.get('/' + f, function(req,res) {
        res.contentType(f);
        res.send(file[f]);
    });
});


app.get('/jquery.js', function(req,res) {
    setTimeout(function() {
        res.contentType('text/javascript');
        res.send(file['jquery.js']);
    }, 500);
});

app.get('/style.css', function(req,res) {
    setTimeout(function() {
        res.contentType('text/css');
        res.send(file['style.css']);
    }, 500);
});


var headresults={
    css: [],
    js: []
}, bodyresults={
    css: [],
    js: []
}
app.post('/result/:type/:time/:exec', function(req,res) {
    headresults[req.params.type].push(parseInt(req.params.time, 10));
    bodyresults[req.params.type].push(parseInt(req.params.exec, 10));
    res.end();
});

app.get('/result/:type', function(req,res) {
    var o = '';
    headresults[req.params.type].forEach(function(i) {
        o+='\n' + i;
    });
    o+='\n';
    bodyresults[req.params.type].forEach(function(i) {
        o+='\n' + i;
    });
    res.send(o);
});

css.html

<!DOCTYPE html>
<html>
    <head>
        <title>CSS first</title>
        <script>var start = Date.now();</script>
        <link rel="stylesheet" href="style.css">
        <script src="jquery.js"></script>
        <script src="test.js"></script>
    </head>
    <body>
        <script>document.write(jsload - start);bodyexec=Date.now()</script>
    </body>
</html>

js.html

<!DOCTYPE html>
<html>
    <head>
        <title>CSS first</title>
        <script>var start = Date.now();</script>
        <script src="jquery.js"></script>
        <script src="test.js"></script>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <script>document.write(jsload - start);bodyexec=Date.now()</script>
    </body>
</html>

test.js

var jsload = Date.now();


$(function() {
    $.post('/result' + location.pathname.replace('.html','') + '/' + (jsload - start) + '/' + (bodyexec - start));
});

jquery.js era jquery-1.7.1.min.js


137
Esta es una respuesta fantástica, ¡gracias por usar la ciencia! Según su resultado "en los navegadores modernos, parece que vincular a CSS primero nunca proporciona una ganancia de rendimiento" , creo que la respuesta al título de la pregunta es , el antiguo consejo de CSS primero es claramente inválido.
Jeff Atwood

Con respecto a la actualización de @ josh3736 sobre el inverso en el móvil ... este es un caso para no saltar el arma en este cambio significativo. Me gustaría saber cómo se comportan otros navegadores móviles (webkit, gecko, presto, trident, etc.) ya que el rendimiento en los dispositivos móviles suele ser más importante.
scunliffe

1
También debe intentar agregar algo de lentitud para imprimir el css / js, para simular las velocidades de un servidor lento.
kirb

1
"Primero, es importante tener en cuenta que estoy operando bajo el supuesto de que tiene scripts ubicados en el <head>documento (en lugar de al final del <body>)". Lo destacaría mucho antes en la respuesta, como en la parte superior. Incluyendo una scripten la headque hace referencia a un archivo externo casi nunca es correcto, desde casi cualquier punto de vista (ciertamente no es el rendimiento). No recuerdo haber tenido que hacerlo en la vida real. Tal vez la línea o dos impares del script en línea , pero eso es todo. El valor predeterminado, sin muy buenas razones contrarias, debería ser el final del cuerpo.
TJ Crowder

1
Con una votación negativa, jQuery no es JavaScript y solo hincha más la página, por lo tanto, los resultados al usarla no son objetivos.
John

303

Hay dos razones principales para poner CSS antes que JavaScript.

  1. Los navegadores antiguos (Internet Explorer 6-7, Firefox 2, etc.) bloquearían todas las descargas posteriores cuando comenzaran a descargar un script. Entonces, si ha a.jsseguido b.css, se descargan secuencialmente: primero a y luego b. Si ha b.cssseguido a.js, se descargan en paralelo para que la página se cargue más rápidamente.

  2. No se procesa nada hasta que se descargan todas las hojas de estilo, esto es cierto en todos los navegadores. Las secuencias de comandos son diferentes: bloquean la representación de todos los elementos DOM que están debajo de la etiqueta de secuencia de comandos en la página. Si coloca sus scripts en HEAD, significa que se bloquea la representación de toda la página hasta que se descarguen todas las hojas de estilo y todos los scripts. Si bien tiene sentido bloquear todo el procesamiento de las hojas de estilo (para que obtenga el estilo correcto la primera vez y evite el destello de contenido sin estilo FOUC), no tiene sentido bloquear el procesamiento de toda la página para los scripts. A menudo, los scripts no afectan a ningún elemento DOM o solo a una parte de los elementos DOM. Es mejor cargar los scripts lo más bajo posible en la página, o incluso mejor cargarlos de forma asincrónica.

Es divertido crear ejemplos con Cuzillion . Por ejemplo, esta página tiene un script en el HEAD, por lo que toda la página está en blanco hasta que finalice la descarga. Sin embargo, si movemos el script al final del bloque BODY, el encabezado de la página se procesa, ya que esos elementos DOM aparecen sobre la etiqueta SCRIPT, como puede ver en esta página .


10
El honrado Steve me ganó la respuesta, pero agregaré
Juan Pablo Buritica

44
vea qué navegadores admiten el asyncatributo, que Steve recomienda aquí cuando dice "aún mejor cárguelos de forma asincrónica" - stackoverflow.com/questions/1834077/…
Jeff Atwood

Oye, ¿puedes decirme por qué alguien vincularía archivos CSS con @importdirectivas?
Josh Stodola

66
¿Cuál es la fuente de 2), y si es cierto, ¿puede explicar por qué en ocasiones una página terminará de cargar el contenido, luego el CSS se aplica un segundo o dos más tarde? (Esto ha sucedido, rara vez, en mis propias páginas donde el CSS estaba en las etiquetas <head>)
Izkata

2
¿Entonces deberíamos poner jQuery+ jQuery UI+ $(document).ready(function () { });al final de la página? ¿Funcionará siempre como se esperaba?
Olivier Pons

42

No enfatizaría demasiado los resultados que obtuviste, creo que es subjetivo, pero tengo una razón para explicarte que es mejor poner CSS antes de js.

Durante la carga de su sitio web, hay dos escenarios que vería:

CASO 1: pantalla en blanco> sitio web sin estilo> sitio web con estilo> interacción> sitio web con estilo e interactivo

CASO 2: pantalla en blanco> sitio web sin estilo> interacción> sitio web con estilo> sitio web con estilo e interactivo


Sinceramente, no puedo imaginar que alguien elija el Caso 2. Esto significaría que los visitantes que usan conexiones lentas a Internet se enfrentarán a un sitio web sin estilo, que les permite interactuar con él usando Javascript (ya que ya está cargado). Además, la cantidad de tiempo que pasa mirando un sitio web sin estilo se maximizaría de esta manera. ¿Por qué alguien querría eso?

También funciona mejor como estados jQuery

"Cuando se utilizan secuencias de comandos que dependen del valor de las propiedades de estilo CSS, es importante hacer referencia a hojas de estilo externas o elementos de estilo incrustados antes de hacer referencia a las secuencias de comandos".

Cuando los archivos se cargan en el orden incorrecto (primero JS, luego CSS), cualquier código Javascript que dependa de las propiedades establecidas en los archivos CSS (por ejemplo, el ancho o la altura de un div) no se cargará correctamente. Parece que con el orden de carga incorrecto, las propiedades correctas son "a veces" conocidas por Javascript (¿tal vez esto es causado por una condición de carrera?). Este efecto parece mayor o menor dependiendo del navegador utilizado.


2
¿Cómo haría para garantizar que se cargó todo el CSS antes de que se ejecute JavaScript? ¿Puedes? o si su javascript es lo suficientemente robusto para lidiar con la situación en la que los estilos no necesariamente se cargan
Jonnio

@Jonnio Si su JS tiene una dependencia, entonces debe hacer explícita esa dependencia. De lo contrario, siempre tendrá problemas de sincronización poco frecuentes. Los módulos ES6 son una buena manera de hacer que eso suceda, pero también hay muchas bibliotecas que podrían usarse.
kmkemp

26

¿Se realizaron sus pruebas en su computadora personal o en un servidor web? ¿Es una página en blanco, o es un sistema complejo en línea con imágenes, bases de datos, etc.? ¿Sus scripts realizan una acción simple de evento de desplazamiento o son un componente central de cómo su sitio web representa e interactúa con el usuario? Aquí hay varias cosas a considerar, y la relevancia de estas recomendaciones casi siempre se convierten en reglas cuando te aventuras en el desarrollo web de alto calibre.

El propósito de la regla "poner hojas de estilo en la parte superior y scripts en la parte inferior" es que, en general, es la mejor manera de lograr una representación progresiva óptima , que es fundamental para la experiencia del usuario.

Todo lo demás a un lado: suponiendo que su prueba sea válida y que realmente esté produciendo resultados contrarios a las reglas populares, no sería una sorpresa, de verdad. Cada sitio web (y todo lo que se necesita para hacer que todo aparezca en la pantalla de un usuario) es diferente e Internet está en constante evolución.


1
Aprecio el punto que está resaltando en negrita, pero el OP está hablando de lo que sucede cuando varía el orden con ambos en la parte superior y ninguno en la parte inferior.
nnnnnn

1
Por lo tanto, "suponiendo que [su] prueba sea válida".
skippr

21

Incluyo archivos CSS antes de Javascript por una razón diferente.

Si mi Javascript necesita hacer un tamaño dinámico de algún elemento de la página (para aquellos casos de esquina en los que CSS es realmente un elemento principal en la parte posterior), cargar el CSS después de que el JS se rige puede conducir a condiciones de carrera, donde el elemento se redimensiona antes que los estilos CSS se aplican y, por lo tanto, se ven extraños cuando los estilos finalmente entran en acción. Si cargo el CSS de antemano, puedo garantizar que las cosas se ejecuten en el orden previsto y que el diseño final sea lo que quiero que sea.


2
esto se romperá un día en algún navegador. No estoy adivinando
jcolebrand

1
jcolebrand: Sí, creo que no había bebido suficiente café cuando escribí esto. (En retrospectiva, supongo que lo importante es evitar la carga dinámica de CSS y poner el JS dentro de un evento domReady si necesita hacer un tamaño dinámico)
hugomg

Los scripts no deberían cambiar ninguna pantalla. Ese es el trabajo de CSS. HTML = contenido, CSS = Cómo mostrar contenido, javascript cambia contenido dinámicamente. Además, js solo debe actuar después (o mientras) DOMContentLoaded se dispara con algunas situaciones pequeñas pero muy específicas.
brunoais

@brunoais: Sin embargo, algunos diseños solo se pueden crear con Javascript. Por ejemplo, cualquier cosa que necesite ser redimensionada dinámicamente debe hacerse a través de Javascript y algunas cosas (como tener un tamaño de 100% - 20px) necesitan que Javascript se haga de forma portátil en los navegadores antiguos.
hugomg

@missingno En esos casos, solo use el evento DOMContentLoaded, de todos modos. Pero entiendo lo que quieres decir. (Stupid IE!)
brunoais

10

¿La recomendación de incluir CSS antes de JavaScript no es válida?

No si lo tratas como una simple recomendación. Pero si lo trata como una regla dura y rápida ?, sí, no es válido.

Desde https://developer.mozilla.org/en-US/docs/Web/Reference/Events/DOMContentLoaded

La hoja de estilo carga la ejecución de la secuencia de comandos de bloque, por lo que si tiene un <script> después, <link rel="stylesheet" ...>la página no finalizará el análisis, y DOMContentLoaded no se activará hasta que se cargue la hoja de estilo.

Parece que necesita saber en qué se basa cada script y asegurarse de que la ejecución del script se retrase hasta después del evento de finalización correcto. Si el script se basa solo en el DOM, puede reanudarse en ondomready / domcontentloaded, si se basa en imágenes para cargar o en hojas de estilo para aplicar, entonces si leo la referencia anterior correctamente, ese código debe diferirse hasta el evento de carga.

No creo que un tamaño de calcetín se ajuste a todos, aunque esa es la forma en que se venden y sé que un tamaño de zapato no sirve para todos. No creo que haya una respuesta definitiva para cargar primero, estilos o guiones. Es más una decisión caso por caso de qué se debe cargar en qué orden y qué se puede diferir hasta más tarde como no estar en la "ruta crítica".

Para hablar con el observador que comentó que es mejor retrasar la capacidad de los usuarios para interactuar hasta que la hoja sea bonita. Hay muchos de ustedes por ahí y molestan a sus contrapartes que sienten lo contrario. Llegaron a un sitio para cumplir un propósito y los retrasos en su capacidad de interactuar con un sitio mientras esperan que las cosas que no importan terminen de cargarse son muy frustrantes. No digo que estés equivocado, solo que debes saber que existe otra facción que no comparte tu prioridad.

Esta pregunta se aplica particularmente a todos los anuncios que se colocan en sitios web. Me encantaría que los autores del sitio mostraran solo divisiones de marcador de posición para el contenido del anuncio y se aseguraran de que su sitio estuviera cargado e interactivo antes de inyectar los anuncios en un evento de carga. Incluso entonces, me gustaría ver los anuncios cargados en serie en lugar de todos a la vez porque afectan mi capacidad de desplazarme incluso por el contenido del sitio mientras se cargan los anuncios hinchados. Pero ese es solo el punto de vista de una persona.

  • Conozca a sus usuarios y lo que valoran.
  • Conozca a sus usuarios y qué entorno de navegación utilizan.
  • Sepa qué hace cada archivo y cuáles son sus requisitos previos. Hacer que todo funcione tendrá prioridad sobre la velocidad y la belleza.
  • Utilice herramientas que le muestren la línea de tiempo de la red cuando desarrolle.
  • Pruebe en cada uno de los entornos que utilizan sus usuarios. Puede ser necesario modificar dinámicamente (lado del servidor, al crear la página) alterar el orden de carga en función del entorno de los usuarios.
  • En caso de duda, modifique el orden y vuelva a medir.
  • Es posible que la mezcla de estilos y scripts en el orden de carga sea óptima; no todos de uno, luego todos los demás.
  • Experimente no solo en qué orden cargar los archivos sino también dónde. ¿Cabeza? ¿En cuerpo? Después del cuerpo? DOM listo / cargado? ¿Cargado?
  • Considere las opciones asíncronas y diferidas cuando sea apropiado para reducir el retraso neto que experimentará el usuario antes de poder interactuar con la página. Prueba para determinar si ayudan o duelen.
  • Siempre habrá compensaciones a considerar al evaluar el orden óptimo de carga. Pretty vs. Responsive es solo uno.

1
El artículo vinculado ya no dice "La hoja de estilo carga la ejecución del script de bloque". ¿Eso ya no es verdad?
Greg

@ Greg - Sigue siendo cierto. Las secuencias de comandos deben poder consultar los atributos de estilo DOM, por lo que las hojas de estilo aún bloquean la ejecución de la secuencia de comandos. Es posible que no bloqueen la carga del script , si son inteligentes, pero bloquearán los eventos script.onLoad.
Jimmy Breck-McKye

10

Actualizado el 16/12/2017

No estaba seguro acerca de las pruebas en OP. Decidí experimentar un poco y terminé rompiendo algunos de los mitos.

Synchronous <script src...>bloqueará la descarga de los recursos debajo de él hasta que se descargue y ejecute

Esto ya no es verdad . Echa un vistazo a la cascada generada por Chrome 63:

<head>
<script src="//alias-0.redacted.com/payload.php?type=js&amp;delay=333&amp;rand=1"></script>
<script src="//alias-1.redacted.com/payload.php?type=js&amp;delay=333&amp;rand=2"></script>
<script src="//alias-2.redacted.com/payload.php?type=js&amp;delay=333&amp;rand=3"></script>
</head>

Inspector de red Chrome -> cascada

<link rel=stylesheet> no bloqueará la descarga y ejecución de scripts debajo de él

Esto es incorrecto . La hoja de estilo no bloqueará la descarga, pero que va a bloquear la ejecución del script ( pequeña explicación aquí ). Echa un vistazo al gráfico de rendimiento generado por Chrome 63:

<link href="//alias-0.redacted.com/payload.php?type=css&amp;delay=666" rel="stylesheet">
<script src="//alias-1.redacted.com/payload.php?type=js&amp;delay=333&amp;block=1000"></script>

Herramientas de desarrollo de Chrome -> rendimiento


Teniendo en cuenta lo anterior, los resultados en OP se pueden explicar de la siguiente manera:

CSS primero:

CSS Download  500ms:<------------------------------------------------>
JS Download   400ms:<-------------------------------------->
JS Execution 1000ms:                                                  <-------------------------------------------------------------------------------------------------->
DOM Ready   @1500ms:                                                                                                                                                      

JS Primero:

JS Download   400ms:<-------------------------------------->
CSS Download  500ms:<------------------------------------------------>
JS Execution 1000ms:                                        <-------------------------------------------------------------------------------------------------->
DOM Ready   @1400ms:                                                                                                                                            

Esa es también la razón por la cual document.write () es una de las peores ideas jamás creadas para HTMLDOM.
brunoais

1
The reason is that the script may want to get coordinates and other style-dependent properties of elements, like in the example above. Naturally, it has to wait for styles to load. javascript.info/… ¿Por qué no se aplica la misma suposición en el caso de JS primero? Para mí no tiene mucho sentido, el orden de ejecución de JS no dice nada sobre su propósito.
Thorsten Schöning

4

No estoy exactamente seguro de cómo está probando el tiempo de 'render' mientras usa el script java. Sin embargo considere esto

Una página en su sitio es 50k, lo cual no es irrazonable. El usuario está en la costa este mientras su servidor está en el oeste. MTU definitivamente no es 10k, por lo que habrá algunos viajes de ida y vuelta. Puede tomar 1/2 segundo para recibir su página y hojas de estilo. Por lo general, (para mí) javascript (a través del complemento jquery y demás) son mucho más que CSS. También hay lo que sucede cuando su conexión a Internet se atora en la mitad de la página, pero ignoremos eso (me sucede ocasionalmente y creo que el CSS se procesa, pero no estoy 100% seguro).

Como css está en la cabeza, puede haber conexiones adicionales para obtenerlo, lo que significa que potencialmente puede terminar antes de que lo haga la página. De todos modos, durante el tipo que toma el resto de la página y los archivos javascript (que son muchos más bytes), la página no está diseñada, lo que hace que el sitio / conexión parezca lento.

INCLUSO SI el intérprete JS se niega a comenzar hasta que el CSS haya terminado, el tiempo necesario para descargar el código javascript, especialmente cuando el servidor está lejos del tiempo CSS, lo que hará que el sitio no se vea bonito.

Es una pequeña optimización, pero esa es la razón.


1
El servidor está en la costa este, fwiw. También, aparentemente, no eres consciente del hecho de que ahora usan un CDN.
jcolebrand

3

Aquí hay un RESUMEN de todas las respuestas principales anteriores (o tal vez a continuación más adelante :)

Para los navegadores modernos, coloque css donde quiera. Analizarían su archivo html (al que llaman análisis especulativo ) y comenzarían a descargar css en paralelo con el análisis html.

Para los navegadores antiguos, siga colocando CSS en la parte superior (si no desea mostrar primero una página desnuda pero interactiva).

Para todos los navegadores, coloque javascript lo más abajo posible en la página, ya que detendrá el análisis de su html. Preferiblemente, descárguelo de forma asincrónica (es decir, llamada ajax)

También hay algunos resultados experimentales para un caso particular que afirma que poner javascript primero (en oposición a la sabiduría tradicional de poner CSS primero) brinda un mejor rendimiento, pero no hay un razonamiento lógico dado y carece de validación con respecto a la aplicabilidad generalizada, por lo que puede ignóralo por ahora.

Entonces, para responder la pregunta: Sí. La recomendación de incluir el CSS antes de JS no es válida para los navegadores modernos. Coloque CSS donde desee y coloque JS hacia el final, como sea posible.


1

Steve Souders ya ha dado una respuesta definitiva pero ...

Me pregunto si hay un problema tanto con la prueba original de Sam como con la repetición de Josh.

Parece que ambas pruebas se realizaron en conexiones de baja latencia donde la configuración de la conexión TCP tendrá un costo trivial.

No estoy seguro de cómo esto afecta el resultado de la prueba, y me gustaría mirar las cascadas para las pruebas a través de una conexión de latencia 'normal' pero ...

El primer archivo descargado debería obtener la conexión utilizada para la página html, y el segundo archivo descargado obtendrá la nueva conexión. (Vaciar los primeros altera esa dinámica, pero no se está haciendo aquí)

En los navegadores más nuevos, la segunda conexión TCP se abre especulativamente, por lo que la sobrecarga de la conexión se reduce / desaparece, en los navegadores más antiguos esto no es cierto y la segunda conexión tendrá la sobrecarga de abrirse.

No sé con certeza cómo / si esto afecta el resultado de las pruebas.


no sigue, a menos que tenga una tubería que es increíblemente rara, es muy poco probable que se reduzca la configuración de la conexión ... de acuerdo, la prueba debe repetirse en baja latencia
Sam Saffron

Si miras esta cascada, puedes ver dónde Chrome especulativamente abre una segunda conexión antes de que sea necesaria webpagetest.org/result/… (IE9 hace lo mismo) ... Estaba pensando en una latencia normal para fines de TCP en lugar de baja - qué tipo de entorno en que se realizó la prueba?
Andy Davies

2
Re: "Steve Souders ya ha dado una respuesta definitiva pero ..." Lo que pasa con la evolución web es que no hay respuestas definitivas. :) Hay 3-4 formas de cargar scripts y las cosas cambian. El actual correcta semántica en realidad debería haber sido por Steve decir "CSS poner ante síncrono JavaScript" De lo contrario la gente se equivoca al generalizar como que sea una regla para todos los scripts ...
hexalys

Sí, pero la mayoría de las personas solo incluyen scripts sincrónicamente, por lo que el consejo de Steve es bueno para los no iniciados.
Andy Davies

1

Creo que esto no será cierto para todos los casos. Porque css descargará paralelo pero js no puede. Considere para el mismo caso,

En lugar de tener un solo CSS, toma 2 o 3 archivos CSS y pruébalo de esta manera,

1) css..css..js 2) css..js..css 3) js..css..css

Estoy seguro de que css..css..js dará mejores resultados que todos los demás.


0

Tenemos que tener en cuenta que los nuevos navegadores han funcionado en sus motores Javascript, sus analizadores, etc., optimizando el código común y los problemas de marcado de una manera que los problemas experimentados en los navegadores antiguos como <= IE8 ya no son relevantes, no solo con en lo que respecta al marcado, pero también al uso de variables de JavaScript, selectores de elementos, etc. Puedo ver en un futuro no muy lejano una situación en la que la tecnología ha llegado a un punto en el que el rendimiento ya no es un problema.


El rendimiento siempre es un problema. Casi ignoro que existen navegadores que no siguen la especificación. Acabo de preparar mi código de manera que los que siguen la especificación funcionen a toda velocidad y los demás simplemente lo hago de tal manera que funcione. Entonces, por ejemplo, si solo funciona en IE8, todo está bien.
brunoais

-5

Personalmente, no pondría demasiado énfasis en tal "sabiduría popular". Lo que pudo haber sido cierto en el pasado bien podría no serlo ahora. Supongo que todas las operaciones relacionadas con la interpretación y la representación de una página web son completamente asíncronas ("buscar" algo y "actuar sobre él" son dos cosas completamente diferentes que podrían ser manejadas por diferentes hilos, etc. ), y en cualquier caso, totalmente fuera de su control o preocupación.

Pondría referencias CSS en la parte "principal" del documento, junto con cualquier referencia a secuencias de comandos externas. (Algunas secuencias de comandos pueden exigir que se coloquen en el cuerpo y, de ser así, obligarlas)

Más allá de eso ... si observa que "esto parece ser más rápido / lento que eso, en este / ese navegador", trate esta observación como una curiosidad interesante pero irrelevante y no deje que influya en sus decisiones de diseño. Demasiadas cosas cambian demasiado rápido. (¿Alguien quiere apostar cuántos minutos pasarán antes de que el equipo de Firefox salga con otro lanzamiento provisional de su producto? Sí, yo tampoco).

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.