WP DEVELOP

EP131. “自定义 SVG 菜单图标与仅限单页的 CSS”

首页 WordPress 开发课程 插件开发 CH1:PHP 基础 · EP131
约 13 分钟· #EP131#插件开发 CH1:PHP 基础
🔒 登录后可标记已读

两件事:① 把上一讲占位用的内建 dashicons-smiley 换成自己的 SVG 图标——演示了两种做法,作者更推荐「把 SVG 内容转成 Base64 编码直接内嵌进代码」,这样 WordPress 能像处理内建图标一样自动套用标准灰色/悬停高亮蓝色,而不是「指向一个独立的 SVG 文件」(这种做法图标会保留 SVG 原始颜色,不会被 WordPress 重新上色);② 手写 Words List 页面的表单 HTML,并学习怎么只在这一个后台页面加载专属的 CSS 文件,不影响其他后台页面。


涉及文件

  • wp-content/plugins/our-word-filter-plugin/index.php (修改)
  • wp-content/plugins/our-word-filter-plugin/styles.css (新建)
  • wp-content/plugins/our-word-filter-plugin/custom.svg (新建,图标素材,仅在「指向独立文件」的备选方案里用到)

代码实现

index.phpourMenu() 里把图标换成 Base64 内嵌 SVG,并挂载仅限本页的 CSS

function ourMenu() {
  $mainPageHook = add_menu_page('Words To Filter', 'Word Filter', 'manage_options', 'ourwordfilter', array($this, 'wordFilterPage'), 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4...(后略,完整字符串是整份 SVG 文件转出来的 Base64 编码)', 100);
  add_submenu_page('ourwordfilter', 'Words To Filter', 'Words List', 'manage_options', 'ourwordfilter', array($this, 'wordFilterPage'));
  add_submenu_page('ourwordfilter', 'Word Filter Options', 'Options', 'manage_options', 'word-filter-options', array($this, 'optionsSubPage'));
  add_action("load-{$mainPageHook}", array($this, 'mainPageAssets'));
}

function mainPageAssets() {
  wp_enqueue_style('filterAdminCss', plugin_dir_url(__FILE__) . 'styles.css');
}

图标的另一种写法(指向独立 SVG 文件,会保留原始配色,不被 WordPress 重新上色)

// 备选方案:直接给文件路径,而不是内嵌 Base64
plugin_dir_url(__FILE__) . 'custom.svg'

wordFilterPage():手写表单 HTML

function wordFilterPage() { ?>
  <div class="wrap">
    <h1>Word Filter</h1>
    <form method="POST">
      <label for="plugin_words_to_filter"><p>Enter a <strong>comma-separated</strong> list of words to filter from your site's content.</p></label>
      <div class="word-filter__flex-container">
        <textarea name="plugin_words_to_filter" id="plugin_words_to_filter" placeholder="bad, mean, awful, horrible"></textarea>
      </div>
      <input type="submit" name="submit" id="submit" class="button button-primary" value="Save Changes">
    </form>
  </div>
<?php }

styles.css(新建,只给这一个页面用)

.word-filter__flex-container {
  display: flex;
}

.word-filter__flex-container textarea {
  flex: 1;
  height: 200px;
  margin-bottom: 20px;
}

关键改动点:

  • Base64 内嵌图标的生成方式:把 SVG 源文件内容复制出来,在浏览器控制台执行 btoa(\...SVG 原始内容...\)(用反引号包裹避免 SVG 里的引号跟 JS 字符串引号冲突),把返回结果(去掉首尾多余的引号)粘贴进 data:image/svg+xml;base64, 后面——这段编码本质上就是整份 SVG 文件内容的另一种文本表示
  • 两种图标写法的核心差异:Base64 内嵌方式让 WordPress 拿到完整的 SVG 数据,所以能像对待内建 dashicon 一样动态改写 fill 颜色(默认灰色,hover 高亮蓝色);而「指向独立文件路径」的写法,浏览器把 SVG 当成普通图片加载,WordPress 没法伸手进去改颜色,图标会保留设计时定的原始颜色(默认半透明,hover 变实色)——两种效果都合法,看设计需求选
  • add_menu_page() 的返回值是这个页面专属的一个 hook 名字符串,存进 $mainPageHook 变量,用来构造 "load-{$mainPageHook}" 这个动态钩子名——load-{页面 hook} 是 WordPress 提供的「只在某个具体后台页面加载时触发」的钩子模式,比全局挂载再手动判断当前页面更精准、更省性能
  • mainPageAssets() 里用标准的 wp_enqueue_style() 加载 CSS 文件,plugin_dir_url(__FILE__) 拿到当前插件目录对应的 URL,拼上文件名即可
  • 表单目前只是纯 HTML 骨架,method="POST" 但没写 action(省略时默认提交到当前页面自己)——处理提交数据的 PHP 逻辑留到下一讲
  • CSS 用 Flexbox 让 <textarea> 占满除了周围间距外的全部宽度、给固定高度 200px、底部留白 20px,让文本域看起来更像一个正式的大输入框,而不是浏览器默认的小方框

[截图:wp-admin 侧边栏 Word Filter 菜单的自定义 SVG 图标显示效果(灰色默认态/蓝色 hover 态)]

[截图:wp-admin Words List 页面,敏感词逗号分隔文本域与 Save Changes 按钮的样式]


Hook / Function 速查

名称类型用途
add_menu_page() 返回值该顶级页面专属的 hook 名,用于构造 load-{hook} 这类只在该页面生效的动态钩子
load-{page_hook}WP 动态 hook只在指定的某个具体后台页面加载时触发
wp_enqueue_style($handle, $src)WP 内建 function注册并加载一个 CSS 文件
plugin_dir_url(__FILE__)WP 内建 function获取当前文件所在插件目录对应的完整 URL

常见坑

  • CSS/JS 资源不区分页面、全局加载——后台每个页面都会被迫加载跟自己无关的样式/脚本,浪费带宽也可能造成样式冲突,应该用 load-{page_hook} 这类精确的钩子只在需要的页面加载
  • 想要「图标跟随 WordPress 自动配色」却选了「指向独立文件」的写法——浏览器只把它当普通图片处理,WordPress 无法动态改色,达不到预期效果,要用 Base64 内嵌的方式才行

延伸 / 后续讲座会用到

下一讲开始处理表单提交:手写 PHP 接收 $_POST 数据、做安全校验(nonce)、存进数据库。


Sources

Udemy:

  • Become a WordPress Developer: Unlocking Power With Code — Section 23, EP131