做电商站的人应该都有过这种纠结:产品价格到底是亮出来,还是藏起来?我自己做过的几个WooCommerce项目里,至少有三四个客户明确提出“尽量不要让访客看到价格”。理由五花八门,有做B2B批发不想把底价亮给终端客户的,有做定制类产品价格本来就得一单一议的,还有纯粹是想走“询盘制”路线的。但说实话,“隐藏产品价格”这四个字听起来像个几行代码就能搞定的小需求,真正动手的时候才知道坑不少。你以为把价格删掉就完事了?列表页的价格还在、变体切换时价格又从JavaScript里冒出来、购物车的小计还写着金额、连搜索引擎的富媒体摘要里都还挂着价格。这篇文章就专门说清楚,如何在WooCommerce里把产品价格藏得干净、藏得彻底,并且不影响正常业务流程。
文章适合三种人看:一是正在搭建外贸站或B2B询盘站的开发者,二是用WooCommerce做展示型品牌站、不想突出价格的运营者,三是那些已经被“价格隐藏不彻底”问题折磨到想放弃的站长。我会从业务判断讲起,再把纯代码实现、插件选型、边角细节和进阶玩法全部过一遍。
1. 为什么要把产品价格藏起来:比“想藏”更重要的业务判断
多数人搜索“隐藏产品价格”时,脑子里想的都是“把那几个数字弄掉就行”,但真正的问题不是怎么藏,而是为什么藏、藏到什么程度、藏完之后客户怎么询价。这三个问题想不清楚,就算代码写得再漂亮,业务上也会翻车。
1.1 最典型的几类隐藏价格场景
我接触过的隐藏价格需求,基本可以归成四类。
第一类是B2B批发站。这类站点往往面对经销商、贸易商,价格体系本身就带有谈判空间,不同采购量对应的折扣完全不一样。把价格公开放出去,等于把自己的底牌亮给了竞争对手,而且终端消费者看到批发价还可能直接绕过经销商来问,扰乱渠道。这种站点的隐藏需求是最强烈的。
第二类是定制产品或非标品。比如机械设备、印刷包装、定制家具,价格取决于材质、尺寸、工艺和数量,标一个默认价格反而误导用户。客户看到“价格面议”或者“获取报价”按钮,心理预期反而是合理的。
第三类是品牌展示型站点。有些品牌做WooCommerce并不是为了在线交易,更多是展示产品线和设计理念,价格会拉低品牌调性,也会让用户把注意力放在钱上而不是产品本身。
第四类是会员制或内部采购站。价格只对登录用户可见,游客访问时完全屏蔽。这种场景在代理商体系里很常见。
搞清楚自己属于哪一类,后面所有技术取舍都会清晰很多。比如B2B批发站,通常还需要保留“加入购物车”功能,只是价格要按登录用户显示;展示型品牌站则干脆把整个购买流程都关掉,换成询盘表单。
1.2 隐藏前先想清楚的三件事
第一,是全部产品隐藏,还是部分隐藏。全局隐藏和分类隐藏的代码难度差别很大,后面第五节会专门讲分类控制和角色控制。
第二,是彻底不显示,还是用“价格面议”替代。直接把价格区域变成空白,用户往往以为自己打开了个坏掉的网页;给出“价格面议”“获取报价”这样的提示,反而能引导用户进入询盘流程。这里的设计决策,直接影响用户体验。
第三,隐藏价格之后,用户怎么得到价格。如果你没有准备好报价流程,比如询盘表单、在线客服、邮件回复机制,那隐藏价格就等于把客户往门外推。价格是信息的核心,你把核心信息藏起来了,就必须提供一个更便捷的获取路径。
1.3 隐藏价格不等于隐藏成本:边界意识
这里说句可能不太好听的:隐藏价格的做法本身是在增加用户的决策成本。原来用户扫一眼价格,能买就买,不能买就走,整个决策链路很短。现在用户必须先发起询盘、等待回复、再跟进沟通,流程变长了,如果你的响应速度不够快,客户可能已经跑到竞争对手那里去了。
所以做隐藏价格之前,最好先在客服或者销售侧做好承接方案。很多站点隐藏了价格但询盘表单没人看,结果流量进来了、询盘也发了,几天没人回复,客户直接流失。这个问题的杀伤力,比“价格没藏干净”严重得多。技术只是工具,业务闭环才是关键。
2. 纯代码实现:直接调用WooCommerce钩子把价格“摘掉”
如果你有子主题,或者愿意在functions.php里加代码,纯代码方案是最干净、最可控的。不需要装额外插件,不会引入兼容性问题,也方便后续按角色、按分类做精细控制。
2.1 先搞懂WooCommerce的价格显示流程
WooCommerce 显示价格,不是模板里写死一个echo $price,而是通过一系列模板钩子(hook)来驱动的。理解了这个机制,你就知道该从哪里“摘”。
默认情况下:
- 产品列表页(店铺页、分类页、标签页)的价格,是在
woocommerce_after_shop_loop_item_title这个钩子位置,调用woocommerce_template_loop_price函数渲染的,优先级是10。 - 单品详情页的价格,是在
woocommerce_single_product_summary这个钩子位置,调用woocommerce_template_single_price函数渲染的,优先级也是10。 - 相关产品、最近浏览产品等小部件,如果是用标准
WC_Widget渲染的,也会调用同一个循环价格模板函数。
所以最基本的两行代码就是把这俩函数从钩子上摘掉:
add_action('after_setup_theme', 'custom_hide_product_price_setup'); function custom_hide_product_price_setup() { // 移除列表页价格 remove_action('woocommerce_after_shop_loop_item_title', 'woocommerce_template_loop_price', 10); // 移除单品页价格 remove_action('woocommerce_single_product_summary', 'woocommerce_template_single_price', 10); }这里用after_setup_theme而不是init,是因为after_setup_theme在主题加载完成后立即执行,比 WooCommerce 注册模板钩子的时机更早,能确保remove_action生效。有些主题会在自己的functions.php里重新挂载价格钩子,这种情况下你需要调整挂载时机或者优先级,这一节后面会补充。
2.2 用过滤器兜底:woocommerce_get_price_html
用remove_action摘钩子的方案有个盲区:如果某个模板或者小部件是直接调用$product->get_price_html()方法来获取价格的,那remove_action就管不着了。比如某些主题的搜索结果模板、某些自定义短代码,可能会绕过标准钩子自己输出价格。
这时候就需要一个更底层的过滤器:
add_filter('woocommerce_get_price_html', 'custom_hide_price_html', 10, 2); function custom_hide_price_html($price_html, $product) { // 这里可以根据业务条件判断,例如根据产品分类 return ''; }这个过滤器挂在价格 HTML 字符串生成的最后一道关卡上,任何地方调用get_price_html(),只要经过 WooCommerce 标准流程,都会被这个过滤器拦截。返回空字符串,价格区域就会变成空白。
不过要注意,全部产品直接返回空字符串会显得突兀。更好的做法是返回一段提示文字,比如“价格面议”:
add_filter('woocommerce_get_price_html', 'custom_hide_price_text', 10, 2); function custom_hide_price_text($price_html, $product) { if (custom_should_hide_price($product)) { return '<span class="price-inquiry">' . __('价格面议', 'your-text-domain') . '</span>'; } return $price_html; } function custom_should_hide_price($product) { // 这里写业务条件,例如特定分类 return has_term('custom-quote', 'product_cat', $product->get_id()); }这个方案的优势是统一,不管是列表页、详情页还是小部件,都会被覆盖到。remove_action和get_price_html过滤器这两个方案可以叠加使用:前者负责摘掉默认的模板钩子,后者负责兜底那些“不走寻常路”的输出位置。
2.3 难点:可变产品的价格泄漏问题
实践中最容易翻车的是可变产品(Variable Product)。你按照上面的代码把单品页价格摘了,列表页价格也摘了,看上去干干净净。但是用户在前台选择了一个变体(比如颜色、尺寸),页面上的价格区域又突然冒出来一个价格。
这是怎么回事?原因在于 WooCommerce 的变体价格是通过 JavaScript 动态传递的。单品页加载时,WooCommerce 会把所有变体的价格数据封装在一个woocommerce_variation对象里,然后前端脚本根据用户选择实时更新价格显示。你摘掉了初始渲染的钩子,但没有拦截 JS 末尾传递的数据。
解决方案是过滤woocommerce_available_variation钩子,把每个变体里的price_html字段清空:
add_filter('woocommerce_available_variation', 'custom_hide_variation_price_data', 10, 3); function custom_hide_variation_price_data($variation_data, $product, $variation) { if (custom_should_hide_price($product)) { $variation_data['price_html'] = ''; } return $variation_data; }这样前端在选择变体时,价格区域就不会被 JS 更新。这个细节如果不处理,隐藏价格就会功亏一篑。
2.4 相关产品区和“从XX元起”的区间价
你以为处理完变体就完了?还有两个隐蔽位置。
一个是相关产品(Related Products)。它们是用WC_Widget或者模板循环渲染的,如果主题的模板里直接调用了get_price_html(),那remove_action拦不住,只有get_price_html过滤器能拦住。所以我在实战中通常两个方案一起上。
另一个是区间价。如果可变产品的最低价格和最高价格不同,WooCommerce 会显示“从XX元起”这样的价格区间,这个区间价的生成逻辑也在woocommerce_get_price_html过滤器之前,所以只要你用了上面的过滤器方案,区间价也会一并消失。
至于产品页里显示“库存:有货”这类信息,不属于价格范畴,通常不需要处理,但在某些场景下“有货”会和价格一起形成购买暗示,要不要一起调整取决于你的业务设计。
3. 不想写代码?插件方案的选型与使用
不是每个站长都愿意碰代码,尤其是拿现成主题做站的用户。好消息是,隐藏价格这个需求太常见了,插件市场里有一堆现成解决方案。
3.1 免费插件怎么选
免费插件层面,搜索“Hide Price”能出来一大堆。比较常见的有WooCommerce Hide Price、Hide Price and Add to Cart Button这类,功能大多是把价格和加入购物车按钮一起隐藏,有的还自带“联系我们”按钮,可以填链接。
选免费插件时要警惕三点。
第一,看最后更新时间。WooCommerce 迭代很快,插件如果超过一两年没更新,大概率跟新版有不兼容问题。一个很常见的现象是插件代码写得早,用的还是旧的钩子,在新版 WooCommerce 下根本不生效。
第二,看能不能按分类控制。很多免费插件只有全局开关,要么全部隐藏,要么全部显示。如果你只想隐藏部分分类,这种插件就用不了。
第三,看是否影响手机端。这个比较玄学,但有些插件的隐藏逻辑依赖 CSS 类名,而移动端主题可能没有输出这些类名,结果桌面端藏住了、手机端露馅了。测试时一定要两个端都看一眼。
我用免费插件的原则是:能用代码解决的问题尽量不装插件,因为每多一个插件就多一份维护成本和未知冲突风险。但如果你急着上线、不熟悉代码,免费插件作为过渡是完全OK的。
3.2 付费插件和主题自带功能的差异
付费插件方面,比较有代表性的有WooCommerce Catalog Mode、WooCommerce Wholesale Pricing这类。它们通常不只是隐藏价格,还包含按用户角色显示价格、询盘表单、一键切换“商店模式”和“展示模式”等能力。
另外很多主流付费主题本身就内置了隐藏价格功能。Flatsome、Woodmart、Astra Pro这些主题在商品设置里就有“隐藏价格”开关,或者叫“Catalog Mode”。如果你用的主题自带这个功能,优先用主题的,因为主题作者已经适配过自己模板的输出位置,通常比第三方插件更少出现“这里藏住了那里没藏住”的问题。
我做个简单的对比表,方便你根据自己的情况选:
| 方案 | 适用人群 | 优点 | 缺点 |
|---|---|---|---|
| 纯代码(子主题) | 开发者和愿意折腾的站长 | 完全可控,无额外插件负担,可按角色/分类精细控制 | 需要一定代码基础,维护成本在自己身上 |
| 免费插件 | 非技术型站长 | 安装即用,省事 | 功能较粗,灵活度低,存在兼容风险 |
| 付费插件 | 有预算、需要进阶功能 | 功能全面,售后更新有保障 | 增加成本,可能有冗余功能 |
| 主题自带功能 | 使用对应主题的用户 | 适配最好,无冲突 | 只能在特定主题下使用,换主题就失效 |
3.3 为什么不建议用CSS把价格“藏”起来
网上有一些教程让你用CSS直接隐藏价格元素,比如:
.woocommerce div.product .price { display: none; }这种方案我极其不推荐,尤其不推荐作为长期方案。
原因很简单:CSS 隐藏只是视觉隐藏,HTML 里价格数据依然存在。搜索引擎的爬虫读取的是 HTML 源码,而不是渲染后的视觉效果,所以 Google 和百度依然能从你的页面里提取到价格,并且可能在搜索结果里展示价格富媒体摘要。这等于你的价格只是“看不见”,并没有“不存在”。
另外,CSS 隐藏依赖选择器,而不同主题、不同插件的 HTML 结构不一样,这套 CSS 换个主题就失效了。更麻烦的是,如果某些用户把浏览器里的 CSS 禁用,价格就会全部显示出来。所以 CSS 只适合做临时调试,不适合作为正式方案。
4. 真正容易翻车的B端细节:购物车按钮、缓存与结构化数据
价格藏住了只是第一步,真正的翻车点往往在“价格的下游”。很多站长隐藏价格之后,用户确实在产品页看不到价格了,但加购、购物车、搜索摘要、邮件这些地方还在把价格暴露出去,整个方案就功亏一篑了。
4.1 把“加入购物车”换掉,否则隐藏价格形同虚设
如果只是隐藏价格,但又保留“加入购物车”按钮,用户点了加购之后跳转到购物车页,购物车列表里会显示商品单价、小计金额,结算页也会显示订单总额。前面藏得再好,这里一秒破功。
所以隐藏价格的站点,几乎都必须同步处理“加入购物车”按钮。常见的做法是移除默认的加购按钮,换成“获取报价”或“联系我们”链接。
先移除默认加购按钮:
// 移除列表页的加购按钮 remove_action('woocommerce_after_shop_loop_item', 'woocommerce_template_loop_add_to_cart', 10); // 移除单品页的加购按钮 remove_action('woocommerce_single_product_summary', 'woocommerce_template_single_add_to_cart', 30);然后在相应的钩子位置挂上自己的按钮。比如列表页:
add_action('woocommerce_after_shop_loop_item', 'custom_add_quote_button', 10); function custom_add_quote_button() { echo '<a class="button" href="' . esc_url(home_url('/contact-us/')) . '">获取报价</a>'; }单品页同理:
add_action('woocommerce_single_product_summary', 'custom_add_quote_button_single', 30); function custom_add_quote_button_single() { echo '<a class="button" href="' . esc_url(home_url('/contact-us/')) . '">获取报价</a>'; }如果是全站都隐藏价格、不做在线交易,还有一个更彻底的方案:直接把产品设为不可购买。因为“不可购买”会让所有加购按钮、购物车校验逻辑都失效,从根源上掐断交易链路:
add_filter('woocommerce_is_purchasable', 'custom_disable_purchase', 10, 2); function custom_disable_purchase($purchasable, $product) { return false; }如果只有部分产品做询盘制,可以在这个函数里用产品ID或者分类做条件判断,只对指定的产品返回false。
这里还有个小技巧:按钮的后半段可以带上产品信息,方便客服快速定位询盘对象。比如弹出一个询盘表单,把product_id和product_name作为隐藏字段一并提交。这个我在第五节展开讲。
4.2 页面缓存导致代码不生效:排查思路
这是隐藏价格实践中我遇到过的最普遍的坑。代码明明写对了,functions.php也确认无误,但前台就是不生效。问题往往出在缓存。
WooCommerce 商品页是动态页面,但很多站长会安装WP Rocket、LiteSpeed Cache或者服务器级别的页面缓存,把渲染好的 HTML 静态化。你改了functions.php里的逻辑,但用户访问的依然是缓存的旧 HTML,价格自然还在。
排查思路我整理成一条链路:
- 先确认你改代码的位置是子主题的
functions.php,而不是父主题。父主题一更新,你的代码就没了。 - 清空所有缓存插件的页面缓存,不只是后台的“清空缓存”按钮,还要检查服务器端的缓存层,比如 Nginx FastCGI Cache、Redis、Memcached。
- 用浏览器的“无痕模式”访问前台,排除浏览器本地缓存干扰。
- 如果还不行,安装
Query Monitor插件,在商品页查看当前页面到底执行了哪些钩子,woocommerce_template_single_price是否还在钩子列表里。这一步能直接告诉你,到底是你的代码没生效,还是缓存还在兜底。
有一个容易被忽略的点:after_setup_theme这个钩子的执行时机是在主题加载之后,但有些主题自带的价格逻辑是在init之后才挂载的,如果你的remove_action在价格钩子挂载之前执行,那是有效的;但如果主题是在你之后执行挂载,那价格又被加回来了。遇到这种情况,你可以在wp钩子(页面请求解析之后)里再执行一次remove_action:
add_action('wp', 'custom_remove_price_late', 99); function custom_remove_price_late() { remove_action('woocommerce_single_product_summary', 'woocommerce_template_single_price', 10); }优先级设为99,确保在你所有主题逻辑之后跑。
4.3 WooCommerce结构化数据中的价格泄漏
这是隐藏价格方案里最容易忽略的一环。
WooCommerce 默认会在商品页输出schema.org的结构化数据(JSON-LD),其中Product类型的offers属性里包含价格和币种。这部分数据是给搜索引擎看的,Google 和百度都能读取,并且有可能在搜索结果里以富媒体摘要的形式展示价格。你网站上藏得干干净净,但搜索结果里价格还在,用户还没进站就已经看到价格了,这不就是白藏了吗?
处理方式是把offers数据清空。用这个过滤器:
add_filter('woocommerce_structured_data_product_offers', 'custom_remove_structured_data_offers', 10, 2); function custom_remove_structured_data_offers($offers, $product) { if (custom_should_hide_price($product)) { return array(); } return $offers; }如果全站统一隐藏,直接返回空数组即可。做完这一步,再去看页面源码,确认offers已经不在 JSON-LD 数据里了。
顺带说一句,JSON-LD 数据除了价格,还包含评分、评论、库存状态等,如果你对这些也不想公开,可以用同样的思路过滤整个数组。不过一般业务场景下,只需要处理价格即可。
4.4 邮件、订阅RSS、订单附件等隐蔽出口
结构化数据只是“不给搜索引擎看”,但还有几类更隐蔽的出口,经常被忽视。
邮件通知。WooCommerce 的新订单邮件会列出每个商品的价格和订单总金额。如果你的站点还允许在线下单,那邮件里的价格是藏不掉的,也不建议藏,因为客户需要对账。但如果你的业务是纯询盘制,根本不应该让用户走到下单那一步,而是前面说的把产品设为不可购买,邮件问题才会从根本上消失。
RSS 订阅。WooCommerce 商品页面的 RSS Feed 默认会输出商品价格。这个监听器挂在woocommerce_price_in_rss_feed钩子上。
add_filter('woocommerce_price_in_rss_feed', '__return_false');这是我见过第二多被忽略的地方,因为大多数人根本不会想到有 RSS 这个东西。但很多第三方采集工具、价格监测工具,恰恰是通过 RSS 来抓取电商站价格的。既然要藏,就得连这些后门一起堵上。
PDF 订单发票。如果你安装了WooCommerce PDF Invoices & Packing Slips这类插件,发票上是必然有金额的,这个不建议强行隐藏,因为发票本身是交易凭证。如果你不想暴露价格,那就不要让客户走到“生成订单”这一步,这又回到了业务闭环的问题上。
5. 进阶玩法:按用户角色、产品分类做精细化控制
前面讲的都是“全站统一隐藏”的做法。但实际业务里,更常见的是“对一部分人隐藏、对另一部分人显示”,或者“这个分类隐藏、那个分类不隐藏”。这一节就专门讲精细化控制。
5.1 登录用户可见,访客不可见
会员制、渠道价、内部采购这类场景,需要做到“未登录看不到价格,登录后看到价格”。实现方式是在之前的woocommerce_get_price_html过滤器里加一个登录判断:
add_filter('woocommerce_get_price_html', 'custom_price_by_login_status', 10, 2); function custom_price_by_login_status($price_html, $product) { if (!is_user_logged_in()) { // 访客看到的是“登录后查看价格” return '<a href="' . esc_url(wc_get_page_permalink('myaccount')) . '">登录后查看价格</a>'; } return $price_html; }这里有个体验层面的小建议:别直接用空字符串,给出一个“登录后查看价格”的引导链接,用户知道下一步该干什么,转化率会比干巴巴的空白好得多。
如果你还要细分角色,比如只对“批发客户”角色显示价格,可以用current_user_can:
add_filter('woocommerce_get_price_html', 'custom_price_by_role', 10, 2); function custom_price_by_role($price_html, $product) { // 管理员和批发客户角色显示价格 if (current_user_can('administrator') || current_user_can('wholesale_customer')) { return $price_html; } return '<span class="price-inquiry">价格面议</span>'; }WooCommerce 默认的用户角色只有管理员、编辑、客户、订阅者这类,wholesale_customer这类自定义角色一般要通过插件(比如WooCommerce Wholesale Suite)或者代码来创建。用角色做价格控制的好处是灵活,而且可以在用户后台里直接手动调整用户的角色,运营起来非常直观。
5.2 指定分类或指定产品隐藏价格
另一类常见需求是“大部分产品正常展示,但某些系列需要询盘”。这种情况下,用分类作为判断条件是最省心的。
先说按分类:
add_filter('woocommerce_get_price_html', 'custom_price_by_category', 10, 2); function custom_price_by_category($price_html, $product) { if (has_term('custom-quote', 'product_cat', $product->get_id())) { return '<span class="price-inquiry">价格面议</span>'; } return $price_html; }这里的custom-quote是产品分类的 slug。如果你有好几个分类都需要隐藏,把 slug 放在数组里循环判断即可。
按单个产品控制更简单,直接用产品ID:
add_filter('woocommerce_get_price_html', 'custom_price_by_product_id', 10, 2); function custom_price_by_product_id($price_html, $product) { if (in_array($product->get_id(), array(123, 456, 789))) { return '<span class="price-inquiry">价格面议</span>'; } return $price_html; }不过产品ID这种方式在维护上很差,产品多了之后连自己都记不住哪些ID被控了。更专业的做法是给产品加一个自定义字段(如_hide_price),后台编辑产品时勾选一个复选框,然后前台判断这个字段。这需要一段简单的自定义字段代码,篇幅有限不在这里展开了,但思路是清晰的:判断条件尽量走业务语义,而不是散落的ID清单。
5.3 隐藏价格后接入询盘表单的完整流程
隐藏价格之后,询盘链路的设计直接决定业务转化率。我见过不少站点就是把价格换成了一句“联系我们”,但根本没有配置联系方式,用户只能自己去翻页脚找邮箱,这种体验非常糟糕。
一个比较成熟的询盘链路是这样的:
第一步,在产品页把价格替换成“获取报价”按钮,同时隐藏掉加购按钮。
第二步,点击按钮之后,跳转到联系页面或者弹出表单,表单里预填产品的名称和ID。这样用户不用重复说明自己要问哪个产品,客服收到询盘也知道对方在问什么。
产品名称可以用 URL 参数传递。比如按钮链接写成:
$product_title = get_the_title($product->get_id()); $contact_url = add_query_arg( array( 'product' => urlencode($product_title), 'product_id' => $product->get_id(), ), home_url('/contact-us/') ); echo '<a class="button" href="' . esc_url($contact_url) . '">获取报价</a>';在联系页的表单里,用Contact Form 7的动态字段钩子把product参数读取出来,填进隐藏字段。或者用一段小代码在表单字段里输出当前的产品名。
第三步是客服的响应机制。这个不用代码,是业务流程:询盘进来之后,尽量在当个工作日内回复报价,否则隐藏价格带来的决策成本就会被竞争对手捡漏。我自己给客户做方案时,一般还会建议在询盘表单里加上“回复时间承诺”,比如“一个工作日内回复”,这能显著提高用户填表的意愿。
6. 我踩过的坑和推荐组合方案
文章最后,分享几个我做这个功能时真实踩过的坑,以及现在比较推荐的组合方案。这些经验不是从文档里看来的,是线上项目一个坑一个坑踩出来的。
6.1 三个踩坑记录
第一个坑:只处理了单品页,忘了列表页。那是一个B2B定制包装站点,我当时想着“价格主要是在产品详情页展示”,就在woocommerce_single_product_summary上把价格摘了,没管列表页。结果分类页上“从500元起”这类区间价照样显示,客户截图给我看,我才意识到列表页的价格其实才是最先被用户看到的地方。后来我把woocommerce_after_shop_loop_item_title的移除逻辑也加上,才算消停。
第二个坑:缓存让代码“假装”不生效。有一回我在一个客户站点上调试,代码反复检查没毛病,前台就是没变化。折腾了大半天,最后发现是服务器安装了页面缓存插件,把旧的HTML页面缓存住了。清掉缓存之后,代码立刻生效。从此以后我调试这种功能的第一件事,就是先清缓存,而不是先查代码。
第三个坑:变体价格在JS里漏出来。这个前面详细讲过了。那是我给一个服装定制站做隐藏价格,列表页和单品页都处理干净了,结果用户一旦选择尺寸和颜色,价格区域就自动刷新出变体价格。排查之后发现是woocommerce_available_variation里的price_html没有被清空。从那以后,我的隐藏价格代码里永远都会带上过滤变体数据的这一段。
6.2 我的推荐组合方案
根据不同的站点情况,我把隐藏价格的方案组合分成三种。
第一种,快速上线型。适合已经用Flatsome、Woodmart这类主题的站长,直接用主题的“Catalog Mode”功能把价格隐藏,再用主题的面板替换按钮文案。全程不需要写代码,十分钟搞定。缺点是比较依赖主题,换主题后要重新配置。
第二种,标准开发型。适合有子主题的开发者。用remove_action摘掉标准钩子,使用woocommerce_get_price_html过滤器兜底,清空变体数据,隐藏加购按钮,禁用结构化数据里的offers,再关掉RSS价格输出。这套组合我用下来,基本没有漏网之鱼,代码全部放在子主题的functions.php里,也方便交接。
第三种,精细控制型。在标准开发型基础上,加入角色判断或者分类判断,并且集成询盘表单链路。适合B2B批发站、会员制站点。虽然代码量多一些,但业务灵活性最高。
我个人的偏好是:能用子主题代码解决的,绝不装插件。不是情怀,而是插件在WooCommerce月月更新的背景下,很容易变成兼容性雷区。但如果站点要求快速上线、又不想在后端折腾,选一款维护积极的免费插件或者直接用主题自带功能是完全合理的,业务目标才是第一优先级。
做隐藏价格这件事,说到底是让“价格”这个信息从公开转为按需释放。技术手段都不复杂,真正拉开差距的是细节:列表页、单品页、变体、搜索摘要、RSS、结构化数据,还有那根始终挂着的“加入购物车”按钮。把这些位置都处理干净,用户看到的是一个设计完整、风格统一的询盘站,而不是一个“好像哪里坏了”的商店。这套方案我已经在多个项目里验证过,照着做基本不会翻车。