Wordpress Documentation

advertise, Anuncio

WordPress HeadLess

🧱 Crear Custom Post Type + Meta

phpCopyEdit// functions.php o plugin
add_action('init', function() {
  register_post_type('curso', [
    'label' => 'Cursos',
    'public' => true,
    'show_in_rest' => true,
    'supports' => ['title', 'editor', 'custom-fields'],
  ]);
});

register_post_meta('curso', 'duracion', [
  'type' => 'string',
  'show_in_rest' => true,
  'single' => true,
  'sanitize_callback' => 'sanitize_text_field',
]);

🧱 Crear Bloque Gutenberg Personalizado (JSX)

jsxCopyEdit// edit.js
import { useBlockProps } from '@wordpress/block-editor';

export default function Edit({ attributes, setAttributes }) {
  return (
    <div {...useBlockProps()}>
      <h3>Mi bloque personalizado</h3>
      <p>Contenido editable o dinámico aquí</p>
    </div>
  );
}

// save.js
export default function Save() {
  return <div><h3>Mi bloque guardado</h3></div>;
}
jsonCopyEdit// block.json
{
  "apiVersion": 2,
  "name": "mi-bloque/personalizado",
  "title": "Mi Bloque",
  "category": "widgets",
  "editorScript": "file:./index.js"
}

🧠 WP-CLI para agregar una columna a la BD

phpCopyEdit// archivo update-db.php
global $wpdb;
$table = $wpdb->prefix . 'posts';

if ( $wpdb->get_var("SHOW COLUMNS FROM $table LIKE 'ltv'") !== 'ltv' ) {
  $wpdb->query("ALTER TABLE $table ADD COLUMN ltv DECIMAL(10,2) DEFAULT 0;");
}
bashCopyEditwp eval-file update-db.php

🔸 React + Next.js

📥 Fetch desde WordPress REST API

jsCopyEditimport { useEffect, useState } from 'react';

export default function Cursos() {
  const [data, setData] = useState([]);

  useEffect(() => {
    fetch('https://tusitio.com/wp-json/wp/v2/curso')
      .then(res => res.json())
      .then(setData);
  }, []);

  return (
    <div>
      {data.map(curso => (
        <article key={curso.id}>
          <h3>{curso.title.rendered}</h3>
        </article>
      ))}
    </div>
  );
}

🧊 Lazy Load de componentes

jsCopyEditimport dynamic from 'next/dynamic';

const ComponentePesado = dynamic(() => import('./ComponentePesado'), {
  ssr: false,
  loading: () => <p>Cargando…</p>,
});

export default function Page() {
  return (
    <>
      <h1>Mi Página</h1>
      <ComponentePesado />
    </>
  );
}

👀 Lazy Load con IntersectionObserver

jsCopyEditimport { useEffect, useRef, useState } from 'react';

export default function LazySection() {
  const ref = useRef();
  const [visible, setVisible] = useState(false);

  useEffect(() => {
    const observer = new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting) setVisible(true);
    });
    if (ref.current) observer.observe(ref.current);
    return () => observer.disconnect();
  }, []);

  return <div ref={ref}>{visible && <p>Contenido cargado perezosamente</p>}</div>;
}

âš¡ Redux Slice Minimal

jsCopyEdit// store.js
import { configureStore, createSlice } from '@reduxjs/toolkit';

const userSlice = createSlice({
  name: 'user',
  initialState: { nombre: '' },
  reducers: {
    setNombre: (state, action) => {
      state.nombre = action.payload;
    },
  },
});

export const { setNombre } = userSlice.actions;

export default configureStore({
  reducer: {
    user: userSlice.reducer,
  },
});
jsCopyEdit// uso en componente
import { useSelector, useDispatch } from 'react-redux';
import { setNombre } from './store';

const Componente = () => {
  const nombre = useSelector((state) => state.user.nombre);
  const dispatch = useDispatch();

  return (
    <div>
      <input onChange={e => dispatch(setNombre(e.target.value))} />
      <p>{nombre}</p>
    </div>
  );
};

🧠 Debounce con React

jsCopyEditimport { useState, useEffect } from 'react';

function useDebouncedValue(value, delay = 500) {
  const [debounced, setDebounced] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(handler);
  }, [value]);

  return debounced;
}

🚀 Core Web Vitals – Mejores prácticas

  • ✅ LCP: preload imágenes importantes + AVIF, critical CSS inline.
  • ✅ CLS: reservar espacio con width/height fijo, font-display: swap.
  • ✅ TBT: dividir bundles, usar requestIdleCallback, evitar JS innecesario.
  • ✅ Caching: usar Redis para guardar respuestas de API y wp_cache_set.

🧪 Test Básico de Componente React

jsCopyEditimport { render, screen } from '@testing-library/react';
import Componente from './Componente';

test('renderiza título', () => {
  render(<Componente />);
  expect(screen.getByText(/mi componente/i)).toBeInTheDocument();
});