Como hacer code splitting de bloques para acelerar el editor en WordPress

Contents

Por qué hacer code splitting de bloques en el editor de WordPress

El editor de bloques (Gutenberg) carga cada vez más JavaScript. Si todos los bloques comparten un único bundle grande, el editor tarda más en arrancar, el tiempo de parseo y ejecución aumenta y la experiencia de edición se resiente, sobre todo en dispositivos móviles o conexiones lentas. El code splitting permite dividir la lógica de los bloques en fragmentos más pequeños que se cargan solo cuando son necesarios (por ejemplo, cuando el usuario selecciona un bloque concreto). El resultado: editor más rápido, menor uso de CPU y menos memoria.

Estrategia general

Las ideas clave para acelerar el editor con code splitting son:

  • Separar la “carga inicial” del editor (scripts mínimos para renderizar la interfaz y la lista de bloques) del heavy lifting de cada bloque.
  • Usar import dinámico (import()) para generar “chunks” que se cargan bajo demanda.
  • Asegurar que el publicPath de Webpack y la ruta de los chunks es la correcta para que el runtime encuentre y descargue los archivos generados.
  • Registrar adecuadamente los scripts en PHP y/o usar block.json asset file para que WordPress sirva los bundles.

Requisitos previos y estructura recomendada

Asumo un plugin o tema con un directorio build/ donde se generan los assets. Una estructura típica:

  • my-block/
    • src/index.js (registro del bloque)
    • src/edit.js (componente pesado edit)
    • src/save.js
    • block.json
    • build/ (salida de webpack)
    • my-block.php (registro en PHP)

Ejemplo: block.json (mínimo)

{
  apiVersion: 2,
  name: mi-plugin/mi-bloque,
  title: Mi bloque optimizado,
  category: widgets,
  editorScript: file:./build/index.js,
  script: file:./build/frontend.js,
  style: file:./build/style.css
}

Ejemplo: index.js (registro y lazy load del edit)

En lugar de importar el componente edit.js directamente, se exporta un envoltorio que carga el componente de manera dinámica sólo cuando se necesita. Esto genera un chunk separado para el edit.

import { registerBlockType } from @wordpress/blocks
import save from ./save

// Wrapper que carga el editor de forma asíncrona
const EditWrapper = (props) => {
  const { useState, useEffect } = wp.element
  const [EditComponent, setEditComponent] = useState(null)

  useEffect(() => {
    // Cargar el chunk sólo cuando el bloque se monta (o cuando sea seleccionado)
    let mounted = true
    import(/ webpackChunkName: mi-bloque-edit / ./edit)
      .then((module) => {
        if (mounted) {
          setEditComponent(() => module.default)
        }
      })
      .catch((err) => {
        // Manejo básico de errores
        console.error(Error cargando el editor del bloque:, err)
      })
    return () => {
      mounted = false
    }
  }, [])

  if (!EditComponent) {
    // Render ligero mientras se carga el chunk
    return wp.element.createElement(p, null, Cargando editor…)
  }

  return wp.element.createElement(EditComponent, props)
}

// Registrar el bloque con el wrapper
registerBlockType(mi-plugin/mi-bloque, {
  edit: EditWrapper,
  save,
})

Mejora adicional: cargar el edit sólo cuando el bloque está seleccionado

Para reducir aún más la carga, sólo importamos el editor cuando el usuario selecciona el bloque (prop isSelected disponible en el edit props).

const EditWrapper = (props) => {
  const { useState, useEffect } = wp.element
  const { isSelected } = props
  const [EditComponent, setEditComponent] = useState(null)

  useEffect(() => {
    if (!isSelected  EditComponent) return
    let mounted = true
    import(/ webpackChunkName: mi-bloque-edit / ./edit)
      .then((module) => {
        if (mounted) setEditComponent(() => module.default)
      })
    return () => { mounted = false }
  }, [isSelected, EditComponent])

  if (EditComponent) {
    return wp.element.createElement(EditComponent, props)
  }

  // Render simple cuando no está seleccionado
  return wp.element.createElement(div, null, wp.element.createElement(strong, null, Mi bloque))
}

Consideraciones de Webpack / build

Si usas @wordpress/scripts (recomendado), los import() ya generarán chunks. Sin embargo es crítico que el publicPath esté bien configurado para que el runtime encuentre los archivos chunk. Dos opciones:

  1. Configurar webpack.output.publicPath a la URL pública de tu carpeta build.
  2. Inyectar dinámicamente __webpack_public_path__ con wp_add_inline_script en PHP para que apunte a la ruta correcta del plugin/tema antes de cargar el bundle principal.

Ejemplo de webpack.config.js (mínimo)

const path = require(path)

module.exports = {
  entry: ./src/index.js,
  output: {
    path: path.resolve(__dirname, build),
    filename: index.js,
    chunkFilename: [name].chunk.js,
    publicPath: , // vacío: se establecerá dinámicamente desde PHP
  },
  // resto de configuración (loaders, plugins...) según @wordpress/scripts o tu configuración
}

Registrar assets y establecer publicPath desde PHP

Antes de encolar el script principal, se inyecta un pequeño inline script que define __webpack_public_path__ para indicar dónde están los chunks. Debe ejecutarse antes del bundle principal.

 script_handle,
    ) )
}
add_action( init, mi_bloque_register )
?>

Checklist para que todo funcione

  • Los chunks se generan en build/ y son accesibles públicamente (ver en el navegador: /wp-content/plugins/mi-plugin/build/).
  • El inline script con __webpack_public_path__ se ejecuta antes del bundle principal.
  • Las rutas en block.json (file:./build/index.js) apuntan a ficheros que existen tras build.
  • Comprobar en la pestaña Network del DevTools que los chunks se descargan sólo cuando toca (al seleccionar el bloque, etc.).

Medir la mejora

Usar Lighthouse (Performance), Network -> Waterfall y el panel de Performance / Coverage en DevTools para ver:

  • Tiempo hasta primer render del editor
  • Tiempo de parseo y evaluación de scripts
  • Número y tamaño de peticiones JS iniciales vs. bajo demanda

Buenas prácticas y recomendaciones

  • Cargar recursos pequeños e imprescindibles en el bundle inicial y dejar componentes pesados (UI compleja, librerías externas) para chunks separados.
  • Evitar múltiples import dinámicos pequeños que provoquen muchas peticiones agrupar componentes pesados que se usan juntos.
  • Controlar la caché usando nombres de archivos con hash en producción (contenthash) para invalidar correctamente.
  • Probar en dispositivos reales (móvil) y conexiones lentas para validar mejoras.

Ejemplo mínimo de edit.js (componente pesado)

import { useState } from @wordpress/element
import { InspectorControls } from @wordpress/block-editor

export default function Edit( { attributes, setAttributes } ) {
  const [heavyState, setHeavyState] = useState(0)

  // Simular carga de librería pesada o UI complejo
  return (
    <>
      
        

Panel de controles pesados

Editor del bloque con UI compleja

Valor: {heavyState}

) }

Errores comunes y cómo solucionarlos

  • Chunks 404: publicPath mal configurado. Verifica wp_add_inline_script o webpack.output.publicPath.
  • Chunks no se generan: asegúrate de usar import() y que tu build realmente crea nombres chunkFilename.
  • Bloque sin función edit en producción: revisar consola por errores de importación y dependencias faltantes en index.asset.php.
  • Caché en CDN: invalidar caché si cambias nombres de chunks o publicPath.

Conclusión

El code splitting para bloques reduce el coste inicial del editor y mejora la experiencia del usuario. La técnica más práctica es usar import() para separar componentes pesados (especialmente el editor) en chunks que se cargan bajo demanda, junto con una configuración de publicPath adecuada inyectada desde PHP. Con una correcta configuración de build y registro de scripts, los beneficios de rendimiento son notables en tiempo de carga, parseo y responsividad del editor.



Acepto donaciones de BAT's mediante el navegador Brave 🙂



Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *