WordPress 经典主题自定义器完全指南
WordPress 自定义器是主题开发中不可或缺的工具,它让用户能够实时预览对网站的修改。对于主题开发者而言,掌握自定义器的进阶功能——上下文控制、选择性刷新、PostMessage 传输和设置验证——意味着能够打造出更流畅、更的用户体验。本文从实战角度出发,系统讲解这些功能的应用场景和实现方法,帮助开发者理解如何让自定义器更智能、更。无论你是刚开始接触 WordPress 主题开发,还是希望提升现有主题的自定义器体验,这份指南都能提供实用的参考。
一、上下文控制:让界面随预览页面动态变化
WordPress 4.0 和 4.1 引入了一个重要特性:自定义器面板中的控件可以根据用户在预览窗口中浏览的页面自动显示或隐藏。
核心概念:所谓上下文控制,就是指某个自定义器控件只在特定页面类型下才显示。比如,你的主题只在首页显示页眉图像和站点描述,那么当用户在预览窗口中切换到其他页面时,这两个控件就应该自动隐藏。
实现方式:使用 active_callback 参数。
基础示例:
php
// 获取核心控件并设置显示条件 $wp_customize->get_section( 'header_image' )->active_callback = 'is_front_page'; $wp_customize->get_control( 'blogdescription' )->active_callback = 'is_front_page';
上述代码中,is_front_page() 是 WordPress 内置的条件判断函数。当用户预览的不是首页时,页眉图像区块和站点描述控件会自动隐藏。
在注册时设置回调:
php
$wp_customize->add_section( 'featured_content', array(
'title' => __( '推荐内容', 'your-theme' ),
'priority' => 130,
'active_callback' => 'is_front_page',
) );
更复杂的逻辑判断:
当需要判断当前是否为页面(或特定页面 ID)时,可以自定义回调函数:
php
// PHP 5.3+ 支持闭包写法
'active_callback' => function() {
return is_page( 42 );
}
// 兼容 PHP 5.2 的写法
'active_callback' => 'prefix_is_specific_page',
function prefix_is_specific_page() {
return is_page( 42 );
}
在自定义控件类中重写方法:
php
class WP_Customize_Greeting_Control extends WP_Customize_Control {
function active_callback() {
return is_front_page();
}
}
利用过滤器全局控制:
php
// 在预览单篇文章时,隐藏所有没有描述信息的控件
function filter_controls_by_description( $active, $control ) {
if ( '' === $control->description && is_singular() ) {
$active = false;
}
return $active;
}
add_filter( 'customize_control_active', 'filter_controls_by_description', 10, 2 );
自动化机制:当一个区块(Section)内的所有控件都被上下文隐藏时,该区块会自动隐藏。同理,如果面板(Panel)内的所有区块都被隐藏,面板也会自动隐藏。
二、选择性刷新:告别全页刷新的等待
WordPress 4.5 引入的选择性刷新(Selective Refresh)是自定义器体验的一次重要提升。传统方式下,用户在自定义器中修改任何设置,预览窗口都需要整个刷新。选择性刷新则只更新变化的部分。
核心优势:
- 速度更快:只刷新变化区域,而非整个页面
- 逻辑复用:无需在 JavaScript 中重复 PHP 的渲染逻辑
- 准确可靠:通过 Ajax 请求获取真实渲染结果,与前端展示完全一致
- 编辑快捷键:WordPress 4.7 起,可直接在预览区域点击编辑图标定位到对应控件
选择性刷新的工作原理:用户在自定义器中调整设置 → 触发 Ajax 请求 → 服务器重新渲染相关部分 → 返回新 HTML → 替换预览中对应区域。
注册局部刷新(Partial):
php
function mytheme_customize_register( $wp_customize ) {
$wp_customize->selective_refresh->add_partial( 'blogdescription', array(
'selector' => '.site-description',
'container_inclusive' => false,
'render_callback' => function() {
bloginfo( 'description' );
},
) );
}
add_action( 'customize_register', 'mytheme_customize_register' );
Partial 参数详解:
| 参数 | 类型 | 说明 |
|---|---|---|
| settings | array | 关联的设置 ID,默认与 partial ID 相同 |
| selector | string | 页面中需要刷新的元素选择器 |
| container_inclusive | boolean | true 表示替换整个容器,false 只替换容器内部 |
| render_callback | function | 生成刷新内容的回调函数 |
| fallback_refresh | bool | 若找不到 partial 元素时是否降级为全页刷新 |
JavaScript 事件监听:
javascript
// 局部渲染完成时触发
wp.customize.selectiveRefresh.bind( 'partial-content-rendered', function( placement ) {
// 此处可重新初始化 JavaScript 组件
} );
// 数据返回时触发
wp.customize.selectiveRefresh.bind( 'render-partials-response', function( response ) {
// 处理服务器返回的数据
} );
// 小工具移动时触发
wp.customize.selectiveRefresh.bind( 'partial-content-moved', function( placement ) {
// 处理小工具位置变化
} );
三、小工具与选择性刷新
主题端启用:
php
add_theme_support( 'customize-selective-refresh-widgets' );
重要前提:侧边栏注册时必须包含包含小工具 ID 的包裹元素。标准的 register_sidebar() 默认已经满足:
php
register_sidebar( array(
'name' => __( '主侧边栏', 'your-theme' ),
'id' => 'sidebar-main',
'before_widget' => '<div id="%1$s" class="widget %2$s">', // 必须包含 id="%1$s"
'after_widget' => '</div>',
'before_title' => '<h3 class="widget-title">',
'after_title' => '</h3>',
) );
小工具端启用:
php
class My_Custom_Widget extends WP_Widget {
public function __construct() {
parent::__construct(
'my_custom_widget',
__( '自定义小工具', 'your-textdomain' ),
array(
'description' => __( '示例小工具', 'your-textdomain' ),
'customize_selective_refresh' => true, // 关键设置
)
);
// 在自定义器预览时总是加载样式
if ( is_customize_preview() ) {
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_styles' ) );
}
}
}
JavaScript 驱动的小工具支持:
javascript
// 监听渲染事件,重新初始化 JS 组件
wp.customize.selectiveRefresh.bind( 'partial-content-rendered', function( placement ) {
if ( 'widget-7' === placement.partial.id ) {
// 重新绑定事件或初始化三方组件
}
} );
// 监听移动事件
wp.customize.selectiveRefresh.bind( 'partial-content-moved', function( placement ) {
if ( placement.partial.id.indexOf( 'widget' ) === 0 ) {
// 处理小工具位置变化
}
} );
四、PostMessage:实现真正无延迟的预览
默认情况下,自定义器通过刷新预览窗口来反映更改。选择性刷新虽然只更新局部,但仍有 Ajax 请求的延迟。PostMessage 传输方式则通过 JavaScript 直接操作 DOM,实现即时预览。
设置传输方式:
php
// 修改已存在的设置
$wp_customize->get_setting( 'blogname' )->transport = 'postMessage';
$wp_customize->get_setting( 'blogdescription' )->transport = 'postMessage';
// 注册新设置时指定
$wp_customize->add_setting( 'custom_css_code', array(
'transport' => 'postMessage',
) );
添加预览 JavaScript:
php
function mytheme_preview_js() {
wp_enqueue_script(
'mytheme-preview',
get_template_directory_uri() . '/js/preview.js',
array( 'customize-preview', 'jquery' )
);
}
add_action( 'customize_preview_init', 'mytheme_preview_js' );
预览 JavaScript 示例:
javascript
( function( $ ) {
// 监听站点标题变化
wp.customize( 'blogname', function( value ) {
value.bind( function( to ) {
$( '.site-title' ).text( to );
} );
} );
// 监听站点描述变化
wp.customize( 'blogdescription', function( value ) {
value.bind( function( to ) {
$( '.site-description' ).text( to );
} );
} );
// 监听自定义 CSS 变化
wp.customize( 'custom_css_code', function( value ) {
value.bind( function( to ) {
$( '#custom-theme-style' ).html( to );
} );
} );
} )( jQuery );
适用场景:
- 文本内容更新(标题、描述)
- CSS 类切换(主题样式切换)
- 内联样式修改
- 简单 DOM 属性变更
五、设置验证:确保数据正确性
WordPress 4.6 引入了设置验证 API,与已存在的清理(Sanitization)机制相辅相成。
清理 vs 验证:
- 清理(Sanitization):将数据转换为安全格式,是”有损”操作,如
absint()将任何值转为整数 - 验证(Validation):检查数据是否满足特定条件,是”无损”操作,只判断是否符合规则
验证的核心特性:
- 保存前验证所有修改的设置
- 任一设置无效则整个保存请求被拒绝
- 错误信息直接展示给用户
- 所有设置保持”未保存”状态,等待修正
PHP 端验证示例:
php
$wp_customize->add_setting( 'established_year', array(
'sanitize_callback' => 'absint',
'validate_callback' => 'validate_established_year'
) );
function validate_established_year( $validity, $value ) {
$year = intval( $value );
if ( empty( $year ) || ! is_numeric( $value ) ) {
$validity->add( 'required', __( '请填写有效的年份', 'your-theme' ) );
} elseif ( $year < 1900 ) {
$validity->add( 'year_too_old', __( '年份不能早于 1900 年', 'your-theme' ) );
} elseif ( $year > date( 'Y' ) ) {
$validity->add( 'year_too_new', __( '年份不能晚于当前年份', 'your-theme' ) );
}
return $validity;
}
通过过滤器添加验证(适用于已注册的设置):
php
add_filter( 'customize_validate_established_year', 'validate_established_year', 10, 3 );
验证回调接收三个参数:
WP_Error对象(初始为空)- 待验证的值
WP_Customize_Setting实例
客户端验证:
对于使用 postMessage 传输且无选择性刷新的设置,需添加客户端验证,否则错误信息只能在保存时才能发现。客户端验证不能替代服务端验证。
javascript
wp.customize( 'established_year', function( setting ) {
setting.validate = function( value ) {
var year = parseInt( value, 10 );
var code, notification;
// 验证是否为空
code = 'required';
if ( isNaN( year ) ) {
notification = new wp.customize.Notification( code, {
message: '请输入有效的年份'
} );
setting.notifications.add( code, notification );
} else {
setting.notifications.remove( code );
}
// 验证是否太老
code = 'year_too_old';
if ( year < 1900 ) {
notification = new wp.customize.Notification( code, {
message: '年份不能早于 1900 年'
} );
setting.notifications.add( code, notification );
} else {
setting.notifications.remove( code );
}
// 验证是否太新
code = 'year_too_new';
if ( year > new Date().getFullYear() ) {
notification = new wp.customize.Notification( code, {
message: '年份不能晚于当前年份'
} );
setting.notifications.add( code, notification );
} else {
setting.notifications.remove( code );
}
return value;
};
} );
通知类型:
error:错误通知(服务器返回的 WP_Error 自动映射为此类型)warning:警告通知info:信息通知- 自定义类型:可通过 CSS 样式
.notice-foo自定义
非错误通知示例:
javascript
wp.customize( 'blogname', function( setting ) {
setting.bind( function( value ) {
var code = 'long_title';
if ( value.length > 20 ) {
setting.notifications.add( code, new wp.customize.Notification( code, {
type: 'warning',
message: '主题建议标题长度不超过 20 个字符'
} ) );
} else {
setting.notifications.remove( code );
}
} );
} );
总结
WordPress 自定义器的进阶功能为主题开发者提供了构建级后台体验的工具集:
- 上下文控制让界面更智能,只显示当前场景相关的选项
- 选择性刷新大幅提升预览速度,同时保持渲染准确性
- PostMessage实现真正无延迟的即时预览
- 设置验证确保数据完整性,配合客户端验证提供即时反馈
在实际开发中,建议将选择性刷新作为所有主题的标配,并在适当场景结合 PostMessage 进一步提升体验。验证机制则应在所有自定义设置中实施,保障数据质量和用户操作的正确性。这些功能相互配合,能够让用户在使用自定义器时获得流畅、清晰的操作体验。