Critical Css
Seo - Recursos bloqueantes
Cualquier enlace externo, que trae código de forma síncrona a la página web, puede ralentizar la carga de la página. Por ejemplo, los enlaces a recursos de css o javascript.
El motor de búsqueda de Google hace puntuaciones a las páginas y las que tienen mejor puntuación tendrán una mayor probabilidad de salir en su primera página de búsqueda. Los recursos bloqueantes influyen en las puntuaciones. Por ello, las empresas se esfuerzan a optimizar sus páginas webs según las políticas de puntuación de Google porque el posicionamiento del seo natural puede influir muchísimo en sus ventas Online.
Para solucionar este problema, una de las soluciones es cargar los recursos externos tales como css de forma asíncrona. Pero si lo hacemos así, las páginas webs se verían mal en un instante porque los estilos no han sido cargados antes del renderizado del código HTML. De allí, salió el concepto del Critical CSS como solución.
# Cargar css de forma síncrona
<link rel="styleSheet" href="ejemplo.css" type="text/css" />
# Cargar css de forma asícrona
<link rel="preload" href="estilo_original.css" type = "text/css" as="style" onload="this.onload = null; this.rel ='stylesheet';"/>
# Cargar css de forma inline.
<head>
<style>
body{background-color: darkorange}
</style>
</head>
Qué es Critical Css
Es el trozo de código css imprescindible inyectado entre líneas en el código de HTML para que la página web se muestre presentable. Todos otros recursos de css prescindibles serán cargados de manera asíncrona.
Esta manera nos permite cargar los recursos de css sin bloquear el redenrizado del HTML.
Solución aportada por Magento
Magento empezó a buscar solución desde el mayo de 2019, desde la versión 2.3.* (falta confirmar este dato).
La solución fue tomar código de un fichero que contiene los estilos de css imprescindibles y ponerlo en todas las páginas de HTML. Esta funcionalidad puede ser activada o desactivada. Cuando el Critical Css es activado, los demás enlaces a recursos de css se cargarán de forma asíncrona. Mientras se cargan todos los recursos de css, aparecerá una imagen animada de carga tapando toda la página.
Estructura de los ficheros de la aplicación
Se han creado los siguientes ficheros en la extensión Magento_Theme y luma para cumplir el objetivo.
- Theme/view/frontend/layout/default_head_blocks.xml
- Theme/view/frontend/templates/html/header/criticalCss.phtml
- Theme/Block/Html/Header/CriticalCss.php
- Theme/etc/frontend/di.xml
- app/design/frontend/Magento/luma/web/css/critical.css
- Theme/view/frontend/layout/default.xml
- Theme/view/frontend/templates/html/main_css_preloader.phtml
- Theme/Controller/Result/AsyncCssPlugin.php
- Theme/etc/config.xml
- Theme/etc/adminhtml/system.xml
Explicación del código
default_head_blocks.xml
Si el Critical Css está activado, cargamos el html que contiene el css imprescindible con el block critical_css_block y coloqueremos el código css entre los tags head del HTML. En el block, le pasamos un objeto de la clase CriticalCss.
<referenceBlock name="head.additional">
<block name="critical_css_block" as="critical_css" template="Magento_Theme::html/header/criticalCss.phtml" ifconfig="dev/css/use_css_critical_path">
<arguments>
<argument name="criticalCssViewModel" xsi:type="object">Magento\Theme\Block\Html\Header\CriticalCss</argument>
</arguments>
</block>
<!-- Todo: Block css_rel_preload_script will be removed in next release as polyfill isn't used anymore -->
<block name="css_rel_preload_script" ifconfig="dev/css/use_css_critical_path" template="Magento_Theme::js/css_rel_preload.phtml"/>
</referenceBlock>
criticalCss.phtml
Invocamos el método getCriticalCssData del objeto criticalCssViewModel. El nombre de este objeto es un alias asignado en el fichero default_head_blocks.xml. Su nombre real es CriticalCss.
<?php
/**
* @var \Magento\Theme\Block\Html\Header\CriticalCss $criticalCssViewModel
*/
?>
<?php $criticalCssViewModel = $block->getData('criticalCssViewModel'); ?>
<style type="text/css" data-type="criticalCss">
<?= /* @noEscape */ $criticalCssViewModel->getCriticalCssData() ?>
</style>
CriticalCss.php
Esta clase tiene un método getCriticalCssData, que devuelve el código css imprescindible para la presentación de la página. Es invocado en el fichero criticalCss.phtml para pintar el código css inline entre los tags head. Si el fichero css/critical.css no se encuentra en el Theme asignado a la tienda, se buscará este fichero en los themes padres. Si no el fichero no es encontrado en ninguno theme, devolverá una cadena de texto vacío. La ruta del fichero del css crítico se especifica en el di.xml.
/**
* Returns critical css data as string.
*
* @return bool|string
*/
public function getCriticalCssData()
{
try {
$asset = $this->assetRepo->createAsset($this->filePath, ['_secure' => 'false']);
$content = $asset->getContent();
} catch (LocalizedException | NotFoundException $e) {
$content = '';
}
return $content;
}
di.xml
En el di.xml, especificamos la ruta del fichero critical.css. Incluso podemos cambiarle el nombre al fichero. La ruta es asignada al argumento filePath del constructor del objeto de la clase CriticalCss.
<type name="Magento\Theme\Block\Html\Header\CriticalCss">
<arguments>
<argument name="filePath" xsi:type="string">css/critical.css</argument>
</arguments>
</type>
critical.css
Este fichero contiene aquellos estilos imprescindibles de css. Podemos generar este código o manualmente o con la ayuda de alguna herramienta por ejemplo: Penthouse o Critical.
default.xml
Lo que se pretende conseguir aquí es mostrar la imagen animada de recarga en todas las páginas mientras se están cargando los recursos css. Para eso, inyectamos el bloque main_css_preloader, que contiene un fichero html main_css_preloader.phtml. La funcionalidad Critical Css tiene que estar activada.
<referenceContainer name="main">
<container name="content.top" label="Main Content Top">
<block name="main_css_preloader" as="main_css_preloader" template="Magento_Theme::html/main_css_preloader.phtml" ifconfig="dev/css/use_css_critical_path"/>
</container>
</referenceContainer>
main_css_preloader.phtml
<div data-role="main-css-loader" class="loading-mask">
<div class="loader">
<img src="<?= $escaper->escapeUrl($block->getViewFileUrl('images/loader-1.gif')); ?>"
alt="<?= $escaper->escapeHtml(__('Loading...')); ?>">
</div>
<?= /* @noEscape */ $secureRenderer->renderStyleAsTag(
"position: absolute;",
"div.loader img"
) ?>
</div>
AsyncCssPlugin.php
Se forman los enlaces de css asíncronos y se ponen por debajo del css crítico si esta funcionalidad está activada.
System.xml
La configuración del activado del Critical CSS está en:
- Tab (menú del lado izquierdo en el admin dentro de system): advanced
- Sección: dev
- grupo: css
- campo: use_css_critical_path
El identificador dev es el nombre de una de las secciones del tab advanced del system (menú del admin). La sección está asignada a este tab en el fichero Backend/etc/adminhtml/system.xml. No olvidemos el principio básico de los ficheros xml de Magento. Se mezclan los elementos xml entre sí antes de ser procesados. Aunque los ficheros xml estén en módulos diferentes, al final, es un solo fichero xml de su tipo.
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
<system>
<section id="dev" translate="label" type="text" sortOrder="920" showInDefault="1" showInWebsite="1" showInStore="1">
<group id="css">
<field id="use_css_critical_path" translate="label comment" type="select" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1">
<label>Use CSS critical path</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
<comment>
<![CDATA[<strong class="colorRed">Warning!</strong> Be sure that you have critical.css file for your theme. Other CSS files will be loaded asynchronously.]]>
</comment>
</field>
</group>
</section>
</system>
</config>
Config.xml
La configuración por defecto del critical css está en el fichero config.xml. Cuando el valor de la ruta está guardado en la base de datos, en la tabla config_data, ese valor tendrá prioridad sobre el valor del config.xml. Si esta ruta está en config.php, la configuración no podrá ser cambiada en producción desde el admin. Si la configuración está en env.php tendrá prioridad absoluta. Con eso, se puede configurar por entorno. Imaginemos que en el entorno de testeo, no usamos el critical css, pero en el entorno pre-productivo y productivo sí lo usamos.
<config>
<default>
<dev>
<css>
<use_css_critical_path>0</use_css_critical_path>
</css>
</dev>
</default>
</config>