wp-javascript
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWordPress JavaScript & AJAX
WordPress中的JavaScript与AJAX
Reference for JavaScript integration in WordPress: script registration and enqueuing,
passing data from PHP to JS, AJAX request/response patterns, the Heartbeat API,
jQuery usage, and loading strategies.
本指南是WordPress中JavaScript集成的参考手册:包含脚本注册与入队、PHP向JavaScript传递数据、AJAX请求/响应模式、Heartbeat API、jQuery使用方法以及加载策略。
1. Enqueuing Scripts
1. 脚本入队
WordPress manages JavaScript dependencies through a registration and enqueuing system.
Never output tags directly — always use the enqueue API.
<script>WordPress通过注册和入队系统管理JavaScript依赖。切勿直接输出标签——务必使用入队API。
<script>Basic Pattern
基础模式
php
add_action( 'wp_enqueue_scripts', 'map_enqueue_frontend_scripts' );
function map_enqueue_frontend_scripts(): void {
wp_enqueue_script(
'map-frontend', // Handle (unique identifier).
plugins_url( 'assets/js/frontend.js', __FILE__ ), // Full URL to file.
array( 'jquery' ), // Dependencies.
'1.0.0', // Version (cache busting).
array( 'in_footer' => true ) // Load in footer.
);
}php
add_action( 'wp_enqueue_scripts', 'map_enqueue_frontend_scripts' );
function map_enqueue_frontend_scripts(): void {
wp_enqueue_script(
'map-frontend', // 句柄(唯一标识符)。
plugins_url( 'assets/js/frontend.js', __FILE__ ), // 文件完整URL。
array( 'jquery' ), // 依赖项。
'1.0.0', // 版本号(用于缓存刷新)。
array( 'in_footer' => true ) // 在页脚加载。
);
}Enqueue Hooks
入队钩子
| Hook | Where Scripts Load | Callback Parameter |
|---|---|---|
| Frontend pages | None |
| Admin pages | |
| Login page | None |
| 钩子名称 | 脚本加载位置 | 回调函数参数 |
|---|---|---|
| 前端页面 | 无 |
| 后台管理页面 | |
| 登录页面 | 无 |
Conditional Loading (Admin)
条件加载(后台)
Load scripts only on your plugin's admin page — not on every admin screen:
php
add_action( 'admin_enqueue_scripts', 'map_enqueue_admin_scripts' );
function map_enqueue_admin_scripts( string $hook_suffix ): void {
// $hook_suffix examples: 'toplevel_page_my-plugin', 'settings_page_my-plugin-settings'.
if ( 'toplevel_page_my-plugin' !== $hook_suffix ) {
return;
}
wp_enqueue_script(
'map-admin',
plugins_url( 'assets/js/admin.js', __FILE__ ),
array( 'jquery', 'wp-util' ),
MAP_VERSION,
array( 'in_footer' => true )
);
}仅在你的插件后台页面加载脚本——不要在所有后台界面都加载:
php
add_action( 'admin_enqueue_scripts', 'map_enqueue_admin_scripts' );
function map_enqueue_admin_scripts( string $hook_suffix ): void {
// $hook_suffix示例:'toplevel_page_my-plugin', 'settings_page_my-plugin-settings'.
if ( 'toplevel_page_my-plugin' !== $hook_suffix ) {
return;
}
wp_enqueue_script(
'map-admin',
plugins_url( 'assets/js/admin.js', __FILE__ ),
array( 'jquery', 'wp-util' ),
MAP_VERSION,
array( 'in_footer' => true )
);
}Conditional Loading (Frontend)
条件加载(前端)
php
add_action( 'wp_enqueue_scripts', 'map_enqueue_conditionally' );
function map_enqueue_conditionally(): void {
// Only load on single posts.
if ( ! is_single() ) {
return;
}
wp_enqueue_script( 'map-single', /* ... */ );
}php
add_action( 'wp_enqueue_scripts', 'map_enqueue_conditionally' );
function map_enqueue_conditionally(): void {
// 仅在单篇文章页面加载。
if ( ! is_single() ) {
return;
}
wp_enqueue_script( 'map-single', /* ... */ );
}Register vs Enqueue
注册 vs 入队
php
// Register (makes handle available, doesn't load yet).
wp_register_script( 'map-charts', plugins_url( 'assets/js/charts.js', __FILE__ ), array(), '2.0.0', true );
// Enqueue later when needed (e.g., inside a shortcode callback).
function map_chart_shortcode( $atts ): string {
wp_enqueue_script( 'map-charts' ); // Now it loads.
return '<div id="map-chart"></div>';
}Registration is useful when a script should only load conditionally (inside a
shortcode, meta box, or specific template).
php
// 注册(使句柄可用,但暂不加载)。
wp_register_script( 'map-charts', plugins_url( 'assets/js/charts.js', __FILE__ ), array(), '2.0.0', true );
// 在需要时再入队(例如,在短代码回调函数中)。
function map_chart_shortcode( $atts ): string {
wp_enqueue_script( 'map-charts' ); // 此时脚本才会加载。
return '<div id="map-chart"></div>';
}注册适用于仅在特定条件下才需要加载的脚本(例如,短代码、元框或特定模板内)。
2. Passing Data from PHP to JavaScript
2. 从PHP向JavaScript传递数据
wp_localize_script()
wp_localize_script()
Passes PHP values to JavaScript as a global object. Must be called after the
script is enqueued/registered and before / fires.
wp_head()wp_footer()php
wp_enqueue_script( 'map-ajax', plugins_url( 'assets/js/ajax.js', __FILE__ ), array( 'jquery' ), '1.0.0', true );
wp_localize_script( 'map-ajax', 'mapAjax', array(
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'map_ajax_nonce' ),
'homeUrl' => home_url( '/' ),
'i18n' => array(
'loading' => __( 'Loading...', 'my-plugin' ),
'error' => __( 'An error occurred.', 'my-plugin' ),
),
) );In JavaScript, the data is available as a global:
js
console.log( mapAjax.ajaxUrl ); // "https://example.com/wp-admin/admin-ajax.php"
console.log( mapAjax.nonce ); // "a1b2c3d4e5"
console.log( mapAjax.i18n.loading ); // "Loading..."Limitations:
- All values are cast to strings (numbers become , booleans become
"5"or"")."1" - For complex data, use with
wp_add_inline_script()instead.wp_json_encode()
将PHP值作为全局对象传递给JavaScript。必须在脚本入队/注册之后、/触发之前调用。
wp_head()wp_footer()php
wp_enqueue_script( 'map-ajax', plugins_url( 'assets/js/ajax.js', __FILE__ ), array( 'jquery' ), '1.0.0', true );
wp_localize_script( 'map-ajax', 'mapAjax', array(
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'map_ajax_nonce' ),
'homeUrl' => home_url( '/' ),
'i18n' => array(
'loading' => __( 'Loading...', 'my-plugin' ),
'error' => __( 'An error occurred.', 'my-plugin' ),
),
) );在JavaScript中,数据可通过全局对象访问:
js
console.log( mapAjax.ajaxUrl ); // "https://example.com/wp-admin/admin-ajax.php"
console.log( mapAjax.nonce ); // "a1b2c3d4e5"
console.log( mapAjax.i18n.loading ); // "Loading..."局限性:
- 所有值都会被转换为字符串(数字变为,布尔值变为
"5"或"")。"1" - 对于复杂数据,请使用配合
wp_add_inline_script()替代。wp_json_encode()
wp_add_inline_script()
wp_add_inline_script()
Injects a block tied to a registered handle. Supports or
positioning (default: ).
<script>'before''after''after'php
wp_enqueue_script( 'map-app', plugins_url( 'assets/js/app.js', __FILE__ ), array(), '1.0.0', true );
wp_add_inline_script( 'map-app', sprintf(
'var mapConfig = %s;',
wp_json_encode( array(
'apiBase' => rest_url( 'map/v1/' ),
'nonce' => wp_create_nonce( 'wp_rest' ),
'maxItems' => 50, // Stays as integer.
'debug' => WP_DEBUG, // Stays as boolean.
) )
), 'before' );Use when your main script needs the config variable at load time.
'before'注入与已注册句柄关联的块。支持或位置(默认:)。
<script>'before''after''after'php
wp_enqueue_script( 'map-app', plugins_url( 'assets/js/app.js', __FILE__ ), array(), '1.0.0', true );
wp_add_inline_script( 'map-app', sprintf(
'var mapConfig = %s;',
wp_json_encode( array(
'apiBase' => rest_url( 'map/v1/' ),
'nonce' => wp_create_nonce( 'wp_rest' ),
'maxItems' => 50, // 保持为整数类型。
'debug' => WP_DEBUG, // 保持为布尔类型。
) )
), 'before' );当主脚本在加载时需要配置变量时,使用位置。
'before'Script Translation (i18n)
脚本国际化(i18n)
For Block Editor scripts using :
wp.i18n.__()php
wp_set_script_translations( 'map-editor', 'my-plugin', plugin_dir_path( __FILE__ ) . 'languages' );对于使用的区块编辑器脚本:
wp.i18n.__()php
wp_set_script_translations( 'map-editor', 'my-plugin', plugin_dir_path( __FILE__ ) . 'languages' );3. Script Dependencies & Built-in Libraries
3. 脚本依赖与内置库
Common WordPress Script Handles
常见WordPress脚本句柄
| Handle | Library | Notes |
|---|---|---|
| jQuery (latest bundled) | Runs in noConflict mode |
| jQuery core without migrate | Lighter, no deprecated API shims |
| | REST API requests with nonce |
| | Block Editor components |
| | Block Editor state management |
| | JS action/filter system |
| | |
| | AJAX helpers, Underscore templates |
| Underscore.js | Utility library |
| Backbone.js | MV* framework |
| MediaElement.js | Audio/video player |
| ThickBox | Modal dialogs |
| 句柄名称 | 库名称 | 说明 |
|---|---|---|
| jQuery(最新捆绑版本) | 以noConflict模式运行 |
| 不含migrate的jQuery核心 | 更轻量,无已弃用API的兼容层 |
| | 带nonce的REST API请求 |
| | 区块编辑器组件 |
| | 区块编辑器状态管理 |
| | JavaScript动作/过滤器系统 |
| | JS中的 |
| | AJAX辅助工具、Underscore模板 |
| Underscore.js | 实用工具库 |
| Backbone.js | MV*框架 |
| MediaElement.js | 音视频播放器 |
| ThickBox | 模态对话框 |
jQuery in WordPress
WordPress中的jQuery
WordPress loads jQuery in noConflict mode — the shortcut is not available globally.
$js
// WRONG — $ is undefined in noConflict mode.
$( '.my-element' ).hide();
// Option 1: Use the full name.
jQuery( '.my-element' ).hide();
// Option 2: Wrap in an IIFE (recommended).
( function( $ ) {
$( '.my-element' ).hide();
$( document ).ready( function() {
// DOM ready code.
} );
} )( jQuery );
// Option 3: jQuery ready shorthand.
jQuery( function( $ ) {
$( '.my-element' ).hide();
} );WordPress以noConflict模式加载jQuery——全局环境中无法使用快捷方式。
$js
// 错误写法——noConflict模式下$未定义。
$( '.my-element' ).hide();
// 选项1:使用完整名称。
jQuery( '.my-element' ).hide();
// 选项2:包裹在立即执行函数表达式中(推荐)。
( function( $ ) {
$( '.my-element' ).hide();
$( document ).ready( function() {
// DOM加载完成后的代码。
} );
} )( jQuery );
// 选项3:jQuery ready快捷写法。
jQuery( function( $ ) {
$( '.my-element' ).hide();
} );Loading Strategies (WordPress 6.3+)
加载策略(WordPress 6.3+)
| Strategy | Behavior |
|---|---|
| Script executes after DOM is constructed, in order |
| Script executes immediately when downloaded, no order |
| (default) | Blocking — halts parsing until script runs |
php
wp_enqueue_script( 'map-analytics', plugins_url( 'assets/js/analytics.js', __FILE__ ), array(), '1.0.0', array(
'in_footer' => true,
'strategy' => 'defer', // Non-blocking, ordered execution.
) );WordPress automatically adjusts strategies to prevent dependency conflicts. If a
dependency uses blocking, the dependent script cannot use .
async| 策略 | 行为 |
|---|---|
| 脚本在DOM构建完成后按顺序执行 |
| 脚本下载完成后立即执行,不保证顺序 |
| 默认 | 阻塞式——在脚本运行前暂停页面解析 |
php
wp_enqueue_script( 'map-analytics', plugins_url( 'assets/js/analytics.js', __FILE__ ), array(), '1.0.0', array(
'in_footer' => true,
'strategy' => 'defer', // 非阻塞,按顺序执行。
) );WordPress会自动调整策略以避免依赖冲突。如果某个依赖项使用阻塞式加载,那么依赖它的脚本无法使用策略。
async4. AJAX
4. AJAX
WordPress routes all AJAX requests through . The standard
pattern: register (and optionally ) hooks,
verify the nonce with , sanitize input, process data, and return
a JSON response via or . On the client
side, use with the localized AJAX URL and action name.
<!-- Full PHP handler and jQuery client examples: resources/ajax-patterns.md -->wp-admin/admin-ajax.phpwp_ajax_{action}wp_ajax_nopriv_{action}check_ajax_referer()wp_send_json_success()wp_send_json_error()jQuery.post()WordPress将所有AJAX请求路由到。标准流程:注册(可选)钩子,使用验证nonce,清理输入数据,处理业务逻辑,然后通过或返回JSON响应。在客户端,使用配合本地化的AJAX URL和动作名称。
<!-- 完整的PHP处理程序和jQuery客户端示例:resources/ajax-patterns.md -->wp-admin/admin-ajax.phpwp_ajax_{action}wp_ajax_nopriv_{action}check_ajax_referer()wp_send_json_success()wp_send_json_error()jQuery.post()Response Functions
响应函数
| Function | Use Case |
|---|---|
| |
| |
| Raw JSON (no success wrapper) |
All three call internally — do not or after them.
wp_die()echoexit| 函数名称 | 使用场景 |
|---|---|
| 返回 |
| 返回 |
| 返回原始JSON(无success包装) |
这三个函数都会在内部调用——调用后不要使用或。
wp_die()echoexitNonce Verification
Nonce验证
| Function | Use Case |
|---|---|
| Dies on failure (use for AJAX handlers) |
| Returns false on failure (manual check) |
| 函数名称 | 使用场景 |
|---|---|
| 验证失败时终止程序(用于AJAX处理程序) |
| 验证失败时返回false(手动检查) |
Admin-Only vs Public AJAX
仅后台 vs 公开AJAX
| Hook Pattern | Who Can Call |
|---|---|
| Logged-in users only |
| Non-logged-in users only |
| Both hooks registered | All users |
Register only if the endpoint must work for anonymous visitors.
Always verify a nonce even on logged-in-only endpoints.
nopriv| 钩子模式 | 可调用用户群体 |
|---|---|
| 仅已登录用户 |
| 仅未登录用户 |
| 同时注册两个钩子 | 所有用户 |
仅当端点需要对匿名访客开放时,才注册钩子。即使是仅对已登录用户开放的端点,也务必验证nonce。
nopriv5. Fetch API & wp.apiFetch (Modern Alternative)
5. Fetch API & wp.apiFetch(现代替代方案)
For modern JavaScript (no jQuery dependency), use or the native
Fetch API with the WordPress REST API instead of admin-ajax.php.
wp.apiFetch对于现代JavaScript(无jQuery依赖),请使用或原生Fetch API配合WordPress REST API,替代admin-ajax.php。
wp.apiFetchwp.apiFetch
wp.apiFetch
js
// Automatically includes X-WP-Nonce header.
wp.apiFetch( { path: '/wp/v2/posts?per_page=5' } )
.then( function( posts ) {
console.log( posts );
} );
// POST request.
wp.apiFetch( {
path: '/map/v1/settings',
method: 'POST',
data: { key: 'value' },
} ).then( function( response ) {
console.log( response );
} );Requires as a dependency:
wp-api-fetchphp
wp_enqueue_script( 'map-modern', plugins_url( 'assets/js/modern.js', __FILE__ ), array( 'wp-api-fetch' ), '1.0.0', true );js
// 自动包含X-WP-Nonce请求头。
wp.apiFetch( { path: '/wp/v2/posts?per_page=5' } )
.then( function( posts ) {
console.log( posts );
} );
// POST请求。
wp.apiFetch( {
path: '/map/v1/settings',
method: 'POST',
data: { key: 'value' },
} ).then( function( response ) {
console.log( response );
} );需要将作为依赖项:
wp-api-fetchphp
wp_enqueue_script( 'map-modern', plugins_url( 'assets/js/modern.js', __FILE__ ), array( 'wp-api-fetch' ), '1.0.0', true );Native Fetch with REST API
原生Fetch配合REST API
js
fetch( mapConfig.apiBase + 'settings', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': mapConfig.nonce,
},
body: JSON.stringify( { key: 'value' } ),
} )
.then( r => r.json() )
.then( data => console.log( data ) );js
fetch( mapConfig.apiBase + 'settings', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': mapConfig.nonce,
},
body: JSON.stringify( { key: 'value' } ),
} )
.then( r => r.json() )
.then( data => console.log( data ) );When to Use What
方案选择
| Approach | Best For |
|---|---|
| admin-ajax.php | Legacy code, simple form submissions, no REST API |
| Block Editor scripts, admin React UI |
| Native Fetch + REST | Headless/decoupled, modern JS, custom REST routes |
| 方案 | 最佳适用场景 |
|---|---|
| admin-ajax.php | 遗留代码、简单表单提交、无REST API的场景 |
| 区块编辑器脚本、后台React界面 |
| 原生Fetch + REST API | 无头/解耦架构、现代JS、自定义REST路由 |
6. Heartbeat API
6. Heartbeat API
The Heartbeat API provides near-real-time server polling. WordPress uses it for
post locking, autosave, and login session management.
Heartbeat API提供近实时的服务器轮询功能。WordPress将其用于文章锁定、自动保存和登录会话管理。
How It Works
工作原理
- Browser fires a "tick" every 15-120 seconds (default: 60s on most pages, 15s on post editor).
- Client-side code attaches data via event.
heartbeat-send - Server processes data through filter.
heartbeat_received - Client receives response via event.
heartbeat-tick
- 浏览器每隔15-120秒触发一次“tick”(默认:大多数页面为60秒,文章编辑器为15秒)。
- 客户端代码通过事件附加数据。
heartbeat-send - 服务器通过过滤器处理数据。
heartbeat_received - 客户端通过事件接收响应。
heartbeat-tick
Hooks
钩子
| Hook | Type | Side | Purpose |
|---|---|---|---|
| Event | JS | Attach data before sending |
| Event | JS | Process server response |
| Event | JS | Handle connection errors |
| Filter | PHP | Process data for logged-in users |
| Filter | PHP | Process data for logged-out users |
| Filter | PHP | Modify heartbeat interval |
| 钩子名称 | 类型 | 端侧 | 用途 |
|---|---|---|---|
| 事件 | JS | 发送前附加数据 |
| 事件 | JS | 处理服务器响应 |
| 事件 | JS | 处理连接错误 |
| 过滤器 | PHP | 处理已登录用户的数据 |
| 过滤器 | PHP | 处理未登录用户的数据 |
| 过滤器 | PHP | 修改心跳间隔 |
Use Cases
使用场景
- Post locking — warn when another user is editing the same post.
- Autosave — periodically save draft content.
- Session management — extend logged-in session while user is active.
- Real-time notifications — poll for new data without WebSockets.
- Dashboard widgets — auto-refresh stats or activity feeds.
- 文章锁定——当其他用户正在编辑同一篇文章时发出警告。
- 自动保存——定期保存草稿内容。
- 会话管理——用户活跃时延长登录会话时长。
- 实时通知——无需WebSocket即可轮询新数据。
- 仪表盘小工具——自动刷新统计数据或活动动态。
7. wp.template (Underscore Templates)
7. wp.template(Underscore模板)
WordPress bundles which provides for client-side
HTML rendering using Underscore.js template syntax. Define templates in PHP
using blocks (typically in
), then render in JavaScript with .
Requires as a script dependency.
wp-utilwp.template()<script type="text/html" id="tmpl-{name}">admin_footerwp.template( '{name}' )wp-utilWordPress捆绑了,它提供用于通过Underscore.js模板语法在客户端渲染HTML。在PHP中使用块定义模板(通常在中),然后在JavaScript中使用进行渲染。需要将作为脚本依赖项。
wp-utilwp.template()<script type="text/html" id="tmpl-{name}">admin_footerwp.template( '{name}' )wp-utilTemplate Syntax
模板语法
| Syntax | Purpose | Example |
|---|---|---|
| Escaped output (XSS-safe) | |
| Raw HTML output (trust the source) | |
| JavaScript logic (if/for) | |
| 语法 | 用途 | 示例 |
|---|---|---|
| 转义输出(防XSS) | |
| 原始HTML输出(需信任数据源) | |
| JavaScript逻辑(if/for) | |
8. Common Mistakes
8. 常见错误
| Mistake | Fix |
|---|---|
Outputting | Always use |
| Loading scripts on every admin page | Check |
Using | Wrap in |
Hardcoding | Pass via |
| Forgetting nonce in AJAX requests | Always create with |
Echoing after | These call |
Not registering | Frontend AJAX for visitors needs |
| Loading jQuery from CDN instead of bundled | Use the WordPress handle |
Using | Use |
| Heartbeat running at 15s on non-editor pages | Use |
| Not declaring script dependencies | List all deps so WordPress loads them in correct order |
| Mixing admin-ajax and REST API unnecessarily | Pick one approach per feature — REST API for new code |
| 错误 | 修复方案 |
|---|---|
直接输出 | 始终使用 |
| 在所有后台页面加载脚本 | 在 |
未包裹jQuery就使用 | 包裹在 |
在JS中硬编码 | 通过 |
| AJAX请求中忘记使用nonce | 始终用 |
在 | 这些函数内部会调用 |
公开AJAX未注册 | 面向访客的前端AJAX需要 |
| 使用CDN加载jQuery而非WordPress捆绑版本 | 使用WordPress的 |
对非字符串数据使用 | 使用 |
| 非编辑器页面的Heartbeat以15秒间隔运行 | 使用 |
| 未声明脚本依赖 | 列出所有依赖项,让WordPress按正确顺序加载 |
| 不必要地混合admin-ajax和REST API | 每个功能选择一种方案——新代码优先使用REST API |