- Điểm nhanh những thay đổi đáng chú ý
- 1. Query Loop – từ “tiện lợi” đến “chuẩn sản xuất”
- Thực chiến: tạo biến thể (variation) cho Query Loop
- Khi nào vẫn nên dùng PHP custom (và tối ưu ngay)
- 2. Block Data Layer – đọc/ghi dữ liệu như ứng dụng
- Ví dụ: lấy danh sách bài viết & đếm tổng qua store
- 3. Interactivity API – UI phản hồi nhanh, vẫn SSR
- Ví dụ: nút “Load more” cho danh sách
- 4. Block Bindings – ràng buộc dữ liệu không cần “bóc” thủ công
- Ví dụ: bind Paragraph vào post meta summary
- 5. Theme.json & Style Variations – dồn sức cho design-tokens
- Mẫu theme.json tinh gọn
- 6. Server-rendered block (render_callback) + cache “đúng cách”
- 7. Checklist “nâng cấp tư duy 2025”
- 8. Anti-patterns nên bỏ ngay
- Kết
Điểm nhanh những thay đổi đáng chú ý
- Query Loop trưởng thành: ít “hack” PHP hơn, cấu hình được nhiều biến thể (variations), dễ tái dùng với Patterns.
- Block Data Layer (dựa trên
@wordpress/data): đọc/ghi nội dung, taxonomy, media qua store đồng nhất, cache tốt, dễ test. - Interactivity API: thêm hành vi client (state, actions) mà không cần SPA, vẫn SSR, vẫn Progressive Enhancement.
- Block Bindings: ràng buộc block với meta/option/taxonomy, giảm code “dán keo” trong render.
- Theme.json + Style Variations: đẩy thiết kế vào cấu hình, đồng bộ design-tokens và giảm CSS thủ công.
1. Query Loop – từ “tiện lợi” đến “chuẩn sản xuất”
Query Loop không chỉ là block lặp bài viết. Ở 2025, bạn nên coi nó là lớp trình bày, để phần dữ liệu chuyển dần sang block attributes + variations + Patterns thay vì viết PHP custom query rải rác.
Thực chiến: tạo biến thể (variation) cho Query Loop
Tạo một biến thể hiển thị truyện/manga nổi bật mà không phải forkhard.
{
"name": "init/query-loop-featured",
"title": "Query: Truyện nổi bật",
"parent": [ "core/query" ],
"attributes": {
"query": {
"perPage": 6,
"postType": [ "manga" ],
"order": "desc",
"orderBy": "date",
"sticky": "only"
},
"displayLayout": { "type": "grid", "columns": 3 }
},
"scope": [ "inserter" ]
}
Lợi ích: biên tập viên chỉ việc chèn “Query: Truyện nổi bật”, không đụng tới tham số nâng cao. Bạn quản lý logic truy vấn tập trung.
Khi nào vẫn nên dùng PHP custom (và tối ưu ngay)
<?php
$q = new WP_Query([
'post_type' => 'manga',
'fields' => 'ids', // chỉ lấy ID cho nhẹ
'posts_per_page' => 12,
'no_found_rows' => true, // không cần phân trang
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
'meta_query' => [
[ 'key' => 'score', 'value' => 7, 'compare' => '>=', 'type' => 'NUMERIC' ]
],
'orderby' => 'meta_value_num',
'meta_key' => 'score'
]);
foreach ( $q->posts as $post_id ) {
// Render nhẹ: tự lấy đúng meta cần, tránh kéo cả post object to đùng
}
Nguyên tắc vàng: “chỉ lấy thứ mình cần” + “tắt cache phụ khi không dùng” để tránh phình RAM.
2. Block Data Layer – đọc/ghi dữ liệu như ứng dụng
Thay vì tự gọi REST/SQL tùy tiện, dùng @wordpress/data và các store lõi (core, core/editor, v.v.) để đồng bộ state, caching, và tối ưu re-render.
Ví dụ: lấy danh sách bài viết & đếm tổng qua store
import { select, resolveSelect } from '@wordpress/data';
// Đọc đồng bộ từ cache (nếu có)
const posts = select('core').getEntityRecords('postType', 'post', { per_page: 5 });
// Đọc có chờ (tự fetch nếu chưa có)
const fetchPosts = async () => {
const list = await resolveSelect('core').getEntityRecords('postType', 'post', { per_page: 5 });
const count = select('core').getEntityRecordsTotalItems?
select('core').getEntityRecordsTotalItems('postType', 'post') : list?.length;
console.log({ list, count });
};
Tip: gói logic vào custom hook (React) để tái sử dụng trong block edit.
3. Interactivity API – UI phản hồi nhanh, vẫn SSR
Thêm trạng thái và hành vi ngay trong HTML via data-wp-* mà không cần SPA. Phù hợp cho filter nhỏ, like button, load-more…
Ví dụ: nút “Load more” cho danh sách
<div
data-wp-interactive
data-wp-context='{"page":1,"loading":false}'
data-wp-watch='callbacks.onMount'
>
<ul data-wp-bind--innerHTML="state.itemsHTML"></ul>
<button
data-wp-on--click="actions.loadMore"
data-wp-bind--disabled="state.loading"
>Load more</button>
</div>
export default {
state: {
page: 1,
loading: false,
itemsHTML: ''
},
callbacks: {
onMount( { state, actions } ) {
actions.loadMore();
}
},
actions: {
async loadMore( { state, element } ) {
if (state.loading) return;
state.loading = true;
const res = await fetch(`/wp-json/wp/v2/posts?per_page=5&page=${state.page}`);
const data = await res.json();
const html = data.map(p => `<li><a href="${p.link}">${p.title.rendered}</a></li>`).join('');
state.itemsHTML += html;
state.page += 1;
state.loading = false;
}
}
};
Điểm ăn tiền: markup đầu tiên vẫn SSR nên SEO tốt, JS chỉ “nâng cấp” hành vi.
4. Block Bindings – ràng buộc dữ liệu không cần “bóc” thủ công
Rút ngắn khoảng cách giữa dữ liệu và giao diện: bind trực tiếp post meta/option cho block Text/Paragraph, hạn chế render callback phức tạp.
Ví dụ: bind Paragraph vào post meta summary
{
"version": 3,
"name": "init/bound-summary",
"title": "Bound Summary",
"parent": [ "core/paragraph" ],
"usesContext": [ "postId", "postType" ],
"attributes": {
"content": {
"type": "string",
"__experimentalRole": "content",
"__experimentalBinding": {
"source": "core/post-meta",
"args": { "key": "summary" }
}
}
}
}
Kết quả: Block Paragraph tự hiển thị/ghi nội dung meta summary của bài viết hiện tại.
5. Theme.json & Style Variations – dồn sức cho design-tokens
Đưa màu, font, spacing, radius… vào theme.json để editor và frontend đồng bộ. Dùng Style Variations tạo “skin” khác nhau mà không forktheme.
Mẫu theme.json tinh gọn
{
"version": 3,
"settings": {
"color": {
"palette": [
{ "slug": "primary", "color": "#5435ff", "name": "Primary" },
{ "slug": "ink", "color": "#1f2328", "name": "Ink" }
]
},
"spacing": { "units": [ "px", "rem" ], "blockGap": "1rem" },
"typography": { "fontFamilies": [ { "fontFamily": "Inter, system-ui", "slug": "inter" } ] }
},
"styles": {
"elements": {
"button": { "border": { "radius": "999px" } }
}
}
}
6. Server-rendered block (render_callback) + cache “đúng cách”
Khi cần logic nặng, cứ SSR block nhưng nhớ cache theo attributes để tránh render lại vô ích.
<?php
register_block_type( 'init/featured-manga', [
'render_callback' => function( $atts, $content, $block ) {
$key = 'init_fm_' . md5( wp_json_encode( $atts ) );
$html = get_transient( $key );
if ( false === $html ) {
$ids = get_posts([
'post_type' => 'manga',
'fields' => 'ids',
'numberposts' => 6,
'meta_key' => 'score',
'orderby' => 'meta_value_num',
'order' => 'DESC',
'suppress_filters' => true
]);
ob_start();
echo '<ul class="init-featured">';
foreach ( $ids as $id ) {
echo '<li><a href="' . esc_url( get_permalink( $id ) ) . '">' . esc_html( get_the_title( $id ) ) . '</a></li>';
}
echo '</ul>';
$html = ob_get_clean();
set_transient( $key, $html, 15 * MINUTE_IN_SECONDS );
}
return $html;
}
] );
7. Checklist “nâng cấp tư duy 2025”
- Ưu tiên Query Loop + Variations + Patterns, hạn chế PHP template rời rạc.
- Đọc/ghi dữ liệu qua Data Layer, tránh viết fetch tự do gây lệch state/caching.
- Thêm tương tác bằng Interactivity API trước khi nghĩ tới SPA.
- Ràng buộc dữ liệu bằng Block Bindings để giảm boilerplate.
- Theme.json làm trung tâm cho tokens và style variations.
- SSR block & cache theo thuộc tính với transient/object cache.
8. Anti-patterns nên bỏ ngay
- Nhồi
WP_Querytrong mọi block nhỏ → hãy gom và cache. - Dùng
posts_per_page=-1+ load cả post object → chuyển sangfields="ids". - Gắn JS rời rạc, không theo Data/Interactivity → khó bảo trì, khó test.
- CSS rải rác file → đưa vào
theme.jsonvà block styles.
Kết
Nếu 2019 là năm của “custom functions + plugin”, thì 2025 là năm của “block-first”. Hãy để Query Loop, Data Layer, Interactivity và Bindings làm nền; còn bạn tập trung vào trải nghiệm, tối ưu và tính đơn giản. Làm đúng, site nhanh hơn, code sạch hơn, và team biên tập cũng hạnh phúc hơn.
Bình luận