Mostrando entradas con la etiqueta Optimización. Mostrar todas las entradas
Mostrando entradas con la etiqueta Optimización. Mostrar todas las entradas

Combinar Scripts y CSS

Con el fin de optimizar el rendimiento de nuestra web, nos valdremos de la técnica de minimizar las peticiones al servidor, en este caso agrupando nuestros scripts en uno solo (CSS por un lado, Javascript por el otro).

Este Tip mezcla herramientas y prácticas al momento de llevar a cabo nuestros desarrollos, veamos como.

Todos sabemos la importancia de organizar bien nuestros proyectos, carpetas para imágenes, hojas de estilos, Javascript, etc. Y a su vez dentro de cada una de ellas podemos tener varios archivos o incluso subcarpetas.

Esta práctica es muy buena y debemos mantenerla; pero podriamos hacer una pequeña modificación a la hora de subir nuestro sitio al servidor de producción: juntar todas las hojas de estilos en una sola, todos nuestros Javascripts en una solo y asi con todos los archivos que lo permitan (para las imágenes podriamos usar la técnica de CSS Sprites).

De esta manera reduciremos la cantidad de peticiones al servidor, por ejemplo en nuestro sitio tenemos la libreria Scriptaculous y un Javascript propio llamado "misfunciones.js". En este caso tendriamos 2 peticiones al servidor, pero ¿qué pasa si copiamos ambos códigos y los pegamos en un único archivo llamado "scripts.js"? A nivel funcionalidad no perdemos nada, ya que se mantiene todo intacto. A nivel organizativo si perjudica, ya que "desarmariamos" nuestra estructura de directorios, por eso se recomienda hacerlo en el momento de pasar los archivos a producción y NO en etapa de desarrollo.

La mejora importante es que reducimos a la mitad las peticiones la servidor con una técnica muy sencilla. Si a esto lo multiplicamos por la cantidad de scripts que componen nuestra Web veremos que podemos mejorar mucho con muy poco.

Contamos con algunas herramientas para automatizar estas tareas, como por ejemplo CSS Merge.

Aqui les dejo un script en PHP (clic en "expandir" para verlo) tomado del blog de Eliot para hacerlo y como detalle adicional podemos utilizar JSmin para comprimir el Javascript resultante.

// Written by Ed Eliot (www.ejeliot.com) - provided as-is, use at your own risk
   
   /****************** start of config ******************/
   define('FILE_TYPE', 'text/javascript'); // type of code we're outputting
   define('CACHE_LENGTH', 31356000); // length of time to cache output file, default approx 1 year
   define('CREATE_ARCHIVE', true); // set to false to suppress writing of code archive, files will be merged on each request
   define('ARCHIVE_FOLDER', 'js/archive'); // location to store archive, don't add starting or trailing slashes
   
   // files to merge
   $aFiles = array(
      'js/yahoo.js',
      'js/event.js',
      'js/connection.js',
      'js/blog-search.js'
   );
   /****************** end of config ********************/
   
   // this is prepended to all file / folder paths so files and archive folder should be specified relative to this
   $sDocRoot = $_SERVER['DOCUMENT_ROOT'];
   
   /*
      if etag parameter is present then the script is being called directly, otherwise we're including it in 
      another script with require or include. If calling directly we return code othewise we return the etag 
      representing the latest files
   */
   if (isset($_GET['version'])) {
      $iETag = (int)$_GET['version'];     
      $sLastModified = gmdate('D, d M Y H:i:s', $iETag).' GMT';
      
      // see if the user has an updated copy in browser cache
      if (
         (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $_SERVER['HTTP_IF_MODIFIED_SINCE'] == $sLastModified) ||
         (isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] == $iETag)
      ) {
         header("{$_SERVER['SERVER_PROTOCOL']} 304 Not Modified");
         exit;
      }
      
      // create a directory for storing current and archive versions
      if (CREATE_ARCHIVE && !is_dir("$sDocRoot/".ARCHIVE_FOLDER)) {
         mkdir("$sDocRoot/".ARCHIVE_FOLDER);
      }
      
      // get code from archive folder if it exists, otherwise grab latest files, merge and save in archive folder
      if (CREATE_ARCHIVE && file_exists("$sDocRoot/".ARCHIVE_FOLDER."/$iETag.cache")) {
         $sCode = file_get_contents("$sDocRoot/".ARCHIVE_FOLDER."/$iETag.cache");
      } else {
         // get and merge code
         $sCode = '';
         $aLastModifieds = array();
         foreach ($aFiles as $sFile) {
            $aLastModifieds[] = filemtime("$sDocRoot/$sFile");
            $sCode .= file_get_contents("$sDocRoot/$sFile");
         }
         // sort dates, newest first
         rsort($aLastModifieds);
         
         if (CREATE_ARCHIVE) {
            if ($iETag == $aLastModifieds[0]) { // check for valid etag, we don't want invalid requests to fill up archive folder
               $oFile = fopen("$sDocRoot/".ARCHIVE_FOLDER."/$iETag.cache", 'w');
               if (flock($oFile, LOCK_EX)) {
                  fwrite($oFile, $sCode);
                  flock($oFile, LOCK_UN);
               }
               fclose($oFile);
            } else {
               // archive file no longer exists or invalid etag specified
               header("{$_SERVER['SERVER_PROTOCOL']} 404 Not Found");
               exit;
            }
         }
      }
   
      // send HTTP headers to ensure aggressive caching
      header('Expires: '.gmdate('D, d M Y H:i:s', time() + CACHE_LENGTH).' GMT'); // 1 year from now
      header('Content-Type: '.FILE_TYPE);
      header('Content-Length: '.strlen($sCode));
      header("Last-Modified: $sLastModified");
      header("ETag: $iETag");
      header('Cache-Control: max-age='.CACHE_LENGTH);
   
      // output merged code
      echo $sCode;
   } else {
      // get file last modified dates
      $aLastModifieds = array();
      foreach ($aFiles as $sFile) {
         $aLastModifieds[] = filemtime("$sDocRoot/$sFile");
      }
      // sort dates, newest first
      rsort($aLastModifieds);
      
      // output latest timestamp
      echo $aLastModifieds[0];
   }


Saludos y que lo disfruten!

Bookmark and Share

Usar CSS Sprites

Haciendo mucho hincapié en reducir al máximo las peticiones al servidor nos podremos valer de esta técnica muy antigua y adaptada al Desarrollo Web.

La idea es agrupar la mayor cantidad de imágenes posibles en una sola y utilizar un "enmascaramiento" para mostrar la que necesitemos en cada caso.

Por ejemplo la página principal de Google utiliza la siguiente imágen:

Sprite Google

Alli podemos ver todos los iconos que componen a esta web y la forma de utilizarla resulta muy sencilla. Consiste en valernos de las propiedades "background-image" y "background-position" para producir el "enmascaramiento". Podemos verlo de una forma gráfica:

Sprite Google

Para utilizar la flecha debemos ocultar el resto. Podemos automatizar esta tarea siguiendo estos pasos:

  1. Creamos un .zip con las imágenes que vamos a utilizar.
  2. Ingresamos a alguna web que nos genere el código en base a este conjunto de imágenes, por ejemplo: CSS Sprites Generator.
  3. Luego de procesar el archivo que enviamos nos generará el código CSS que tendremos que utilizar en cada caso y la única imágen que deberemos subir al servidor.

Esta es simplmente una explicación muy básica de esta técnica, ya que los usos pueden ser muy diversos y la mejora en el rendimiento es muy notoria.


Saludos y que lo disfruten!

Bookmark and Share

Usar Imágenes Embebidas

En estos sucesivos Tips para Optimización de Sitios Web hacemos hincapié en reducir las peticiones al servidor para alivianar la carga del mismo y las transferencias.

Otra buena práctica es Usar Imágenes Embebidas. La idea es sencilla, se "codifica" la imágen que vayamos a utilizar y en lugar de hacer referencia a ella mediante una ruta (relativa o absoluta) en el atributo "src" del tag "img", directamente enviemos la imágen "embebida" en el código, en formato de texto plano gracias a la Base 64.

Lo que deberiamos hacer es convertir una imágen a este formato, para eso podemos valernos de diversas herramientas, como por ejemplo Base 64 Online. Cargamos una imágen que tengamos en nuestra PC, le damos a "Convert the source data" y listo!.

Una vez que tengamos la imágen codificada sólo tenemos que reemplazar en el atributo "src" del tag "img" la ruta a la imagen por el texto generado; teniendo en cuenta que antes de colocar el código generado debemos poner el tipo MIME y la base, de esta manera: data:image/jpg;base64,.

Yo convertí una imágen y me devolvió el siguiente código (aunque no lo crean el texto siguiente es una imágen):

iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz
AAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAT7SURB
VFiFtZddbBRVFMd/Z2b2o93d7i6FbtuUbin9kFbZLjVtpV0tdAWkBEgpUSKxgmLRaDCASsAommiM
BhMS3nzywSefNDXwpC+EBPBBoiQm0oAVI5ZqIbbdbffj+jCz7bb0gy70JP/cmXPPnPM/5557Zwal
FLkCKAfOA605+3iA4B5d1/tbWlqUx+OJAY/m4kcjR7HZbKej0WhlRUUFkUjE6fV6vxIR22L95ERA
RLaHw+F9JSUlGTLU1dWt1TTtgyUnICKeoqKiLxoaGhCB3r1QVQEFBQWUlZW9LSKhJSWgadrxaDRa
pOs6m9vh8RAcegl8BRAMBnW32316yQiIyKqmpqYjgUCANVXQudHU+71wpBdshhAMBp8SkR1LQsDv
938eiURsTgc8twNEAGXOPVIFuzrB5XJRWFh46n4b8r4JiEiktbV1p2EYtK+H/HwYi8GJT+G7702b
7k7w+2DFihWrNU1746EREBGtuLj4TGNjI74CaFln6s9fgsEh+OYcTEyA0wEv7ga73Y7P53tPRAof
CgGgp6OjY62u62xsA0M3lTdumuNYHP68ZV5v3gCV5eD3+726ri+4LRckICLuioqKT0KhECUBqK+e
mvtneOr67yFz1AVe7QFN0/B4PL0isuaBCADHtm3bVgSwoQWz6Sz8e2fKaPD2lL45BM1hcLvdhmEY
p3ImICIr6+vrj9bW1lIVhNIiIG0iPgax+AwCKSBpjod6QNeFvLy8Z0QkmhMBTdPe6erqcojAE+Gs
7NOgUtNt0+kpcqSguhy2toPD4UDX9ZOLJiAixc3NzQfKy8upXAk+N4gVQNKQ7zC7PiOBZSApc04s
Eq90m2eF3W5vFZH2RRFwOBxvdXd32wEaaq3MrAqodBqV+A+SgwwMDNDf30++XEeN3yI9PoxKjEBy
glXFik3rQdd1ROTEbHGMObIv7OzsfC0QCFBYEMPnvE1y+DKpsV9QyWFUahRQ3PgVLlwwn/HZIDVy
jyde3m7w9VlBRKIi0qyUurhgBbxe79G2tjZnX18f7jsfEb/+IYmhs6TH/kBNjEBKQQpC1pbMd0JN
GWYTToMiVJmgLTRBOp0GePceitbXTXb2XsMwfk8mk95IA3z72WwUTRm6C5Fe6NkKx16Y2+7Cz9B5
ePI2rJT6KXMz2xK8nkwmvQCHn7UymUOWu+HKl2A35rdbXwfN9XDxKgAngN2ZuWlLICIu4E2AcDW0
r2WWsk7Hzb8gHlvY7vBkSLqyT8eZPXAQWA6wK7Kw030fQ+MBqNoDl6/Ob9vRAH7PZMzj9xAQEQOY
XKlN66x9PQfG43D2kmk7GodzF+e3NxREw5OJ7hGRlTMr0AmUAgSLoKZ4/oycGrz/PJQsgycfg/1P
L1yxLY2TsXRgP2TtAhHps0iwdwOc6eWhy+BdqD0IVsgBYJUopRCRUkuhZ4xL/dBUA03VsKYMXA5w
OSHPbh7DLgfkOcCmmw7jCXMpYhMwOg5jFm7fhR/74dJvcOUGjCemcdqS+cvZybQX7f3D0FEiuT0L
nMz0gAJiuZQ1mZos6WIlBYxn90AA2AzUAKsxfzw9gBPIs0Yn4ADm++JNAhNA3ELMwihwE+gHrgE/
KKWuZXrAZjnPnxHIOQscFiE34ALsQMIKMAKMAeNZBLKR0Y9Z9rFp7wIRAXNrGpgNabOubVagDGxZ
yGSdtIgkrApkj8kspFRW0P8BcVptcl859dUAAAAASUVORK5CYII=

Nos quedaría algo asi:

Mi imágen base 64

Como podemos observar es sencillo y en lugar de hacer una petición al servidor la imágen viaja en modo texto.

Saludos y que lo disfruten!

Bookmark and Share

Definir Fecha de Expiración

Si vamos a mostrar el mismo contenido a todos los usuarios durante un tiempo "X", ¿por qué hacer trabajar al sistema para que haga una consulta a la base de datos, o ejecutar una script cada vez si el resultado va a ser el mismo?

Cuando tus páginas son cacheadas el servidor no perderá ni tiempo ni recursos en regenerar una y otra vez estas, unicamente las cargará de la cache.

Existen tres tipos de caches que pueden actuar durante el proceso de solicitud de un documento web:
  1. Cache de Agente de Usuario (User-Agent): Está presenten en los navegadores web y lógicamente sólo funcionan para un único usuario.
  2. Cache Compartida o proxy-cachés directos: Este tipo de cache son utilizados por los proveedores de servicios de Internet y empresas para ahorrar ancho de banda. La comparten todos los usuarios que accedan.
  3. Cache pasarela o proxy-cachés inversos: Funcionan como respaldo de un servidor web de forma transparente para los usuarios. Es posible trabajar con varias cache pasarela de manera conjunta para implementar una Red de Distribución (CDN)

El funcionamiento del cache en términos muy básicos seria:
  1. Se realiza una petición de un recurso al servidor y se valida la fecha de modificación.
  2. En caso de que la fecha de modificación sea la misma que esta guardada en el cache NO es necesario que el servidor envie el archivo, directamente se envia al usuario el "chacheado".
  3. Si el archivo que se solicita ha sido modificdo, el servidor lo envia, se chachea y se repite el proceso en futuras consultas.

Puede seguir mi tutorial para Chachear Páginas con PHP.

Saludos y que lo disfruten!

Bookmark and Share

Evitar Redirecciones

Como hemos visto en los sucesivos Tips tras cada petición se envian datos (cabeceras) que por mas minimas que sean en la suma total de elementos que las solicitan la cuenta final puede resultar abultada.

Hay un error muy común en los desarrollos que es olvidar poner en una ruta que apunta a una carpeta la barra final ("/"), que indica justamente que es una carpeta. Parece un detalle menor pero si lo vemos un poco en detalle nos daremos cuenta que no lo es tanto:

Usaremos una direccion de ejemplo: "http://misitio.com". En en Index tenemos un link que apunta a la carpeta Portfolio de la siguiente manera: "http://misitio.com/portfolio" (notese la falta de la barra final). Cuando intentemos acceder a esa URL la petición será para buscar el archivo llamado "portfolio" (sin extensión, recuerden que existen archivos sin extensión) en lugar de buscar la carpeta llamada asi.

Al no encontrar dicho archivo automáticamente se hará una redirección a "http://misitio.com/portfolio/" (notese la barra al final) para dar con la carpeta especificada.

Si de entrada hubieramos colocado esa barra nos habriamos ahorrado esa redirección con todas las peticiones en vano que se hicieron en un principio.

Saludos y que lo disfruten!

Bookmark and Share

Usar una Red de Distribución (CDN)

Esta técnica es utilizada en archivos estáticos, entendiendo por esto archivos que NO son generados dinámicamente: imágenes, Javascript, CSS, etc.

Almacenando estos "archivos estáticos" en un servidor externo lograremos alivianar la carga del servidor y ganaremos estabilidad.

Algunos ejemplos de esta técnica llamada "off-load" son:

[*] Imágenes: Amazon S3, Flickr, ImageShack u otro servicio de alojamiento de imágenes.
[*] JavaScript: Google Ajax Library o Google App Engine
[*] Formularios web: WuFoo, FormSpring
[*] Feeds RSS: Feedburner
[*] Encuestas y votaciones: SurveyMonkey, PollDaddy

Amazon S3 merece una mención especial por su enorme crecimiento (es utilizado por muchisimas empesas reconocidas) y planes hiperflexibles con una relación precio/calidad excelente. En su WEB nos encontramos con un calculador de nuestro costo ingresando unos pocos datos estimados.

Saludos y que lo disfruten!

Bookmark and Share

Comprimir Recursos

La optimización de un sitio es un trabajo un tanto arduo. ¿Porqué?, sencillo: porque no existe una fórmula mágica sino que hay que analizar que es lo que sucede de trasfondo y actuar en base a ello. Es por esto que cada "pequeña acción" que podamos realizar va a aportar al resultado final.

La compresión de recursos estáticos es una de estas pequeñas acciones a llevar a cabo, y entender el porqué es claro: es necesario disminuir al máximo el número de peticiones y/o el tamaño de los archivos que se envian. La compresión se encarga de esto último, el contenido es comprimido antes de enviarlo al navegador.

La herramienta por excelencia para esto se llama gzip: es una abreviatura de GNU ZIP, por lo que es catalogado como "Software Libre" y su popularidad se debe a que es soportado por la gran mayoría de los navegadores y su efectividad es muy alta.

En la actualidad la mayoría de los navegadores soportan dos formatos de compresión: "gzip" y "deflate", y esto se puede ver en la propiedad "Accept-Encoding" de la petición realizada.

Para ver esto pueden usar alguna de las herramientas que aquí menciono: Link

La compresión se aconseja en archivos que sean de texto: HTML, CSS, Javascript. No asi en imágenes, ya que estan viajan comprimidas y seria contraproducente para el rendimiento del servidor, ya que en ocasiones puede consumir muchos recursos, lo cual es particularmente malo si el sitio se encuentra hospedad en una cuenta de alojamiento compartido.

[*] Usar gzip en Apache 1.3*: En este caso la compresión gzip es manejada por el módulo "mod_gzip". Para ello se pueden utilizar las sentencias "mod_gzip_item_include" para incluir archivos a comprimir y "mod_gzip_item_exclude" para excluir archivos. Luego editamos el archivo ".htaccess" e incluimos el siguiente código:


mod_gzip_on Yes
mod_gzip_item_include file \.html$
mod_gzip_item_include file \.php$
mod_gzip_item_include file \.css$
mod_gzip_item_include file \.js$

mod_gzip_item_include mime ^application/javascript$
mod_gzip_item_include mime ^application/x-javascript$
mod_gzip_item_include mime ^text/.*
mod_gzip_item_include handler ^application/x-httpd-php

mod_gzip_item_exclude mime ^image/.*


[*] Usar gzip en Apache 2.*: En este caso la compresión es gestionada por el módulo "mod_deflate". Para comprimir los archivos podemos utilizar la sentencia "AddOutputFilterByType" de la siguiente forma:

AddOutputFilterByType DEFLATE text/html text/css application/x-javascript

Saludos y que lo disfruten!

Bookmark and Share

Tips para Optimización de Sitios Web

Este será el POST que organicé los sucesivos Tips que vaya posteando referidos a la Optimización de Sitios Web.

Los que llevamos un tiempo como desarrolladores de Sitios Web tenemos preconceptos que en cierta medida están pasando de moda o en ciertos casos nunca estuvieron en lo cierto. Si a esto le sumamos los avances tanto tecnológicos (mejores equipos, conexiones, etc.), como de investigaciones sobre el tema (lease Steve Souders) nos encontramos con muchas cosas nuevas por probar, o al menos tener en cuenta.

De esto se van a tratar los Tips que voy a listar, espero que los pongan en práctica.

[*] Tip 1: Comprimir Recursos (link)
[*] Tip 2: Usar una Red de Distribución (CDN) (link)
[*] Tip 3: Evitar redirecciones (link)
[*] Tip 4: Definir fecha de Expiración (link)
[*] Tip 5: Usar Imágenes Embebidas (link)
[*] Tip 6: Usar CSS Sprites (link)
[*] Tip 7: Combinar Scripts y CSS (link)

Saludos y que los disfruten!

Bookmark and Share