学习产品属性选择以及购物车前端业务流程,即:add-to-cart-variation.js,记录一些知识点,笔记备忘。
1:关于Woo原型链模式
发现调用更新产品变体属性功能onUpdateAttributes
var VariationForm = function( $form ) {
var self = this;
self.$form = $form;
...
$form.on( 'update_variation_values.wc-variation-form', { variationForm: self }, self.onUpdateAttributes );
通过以下方式调用:
VariationForm.prototype.onUpdateAttributes = function( event ) {
var form = event.data.variationForm,
而不是直接在构造函数里创建方法,如下:
var VariationForm = function( $form ) {
// 将方法直接定义在构造函数内部
this.onUpdateAttributes = function( event ) {
// 方法实现
};
};
进一步了解,得知第一种调用方式是原型链方式,结构如下:
var VariationForm = function( $form ) {
// 初始化代码
};
// 将方法添加到原型链
VariationForm.prototype.onUpdateAttributes = function( event ) {
// 方法实现
};
这种方式的最大用处是节省内存
例如在电商页面中,可能同时存在多个可变产品表单(如产品列表页)。如果每个表单实例都复制一份方法,会显著增加内存占用。例如:
// 创建1000个表单实例
for (let i = 0; i < 1000; i++) {
new VariationForm($(`.form-${i}`));
}
采用原型链方式,所有实例共享同一个 onUpdateAttributes 方法。而基于构造函数内部方法,则需要创建 1000 个独立的方法副本。
2:js事件绑定在 DOM 元素上,而非对象
Woo 产品属性变体选择默认是下拉菜单,我把它改为单选按钮方式。add-to-cart-variation.js中的部分功能受到影响,尤其是那些直接绑定到下拉菜单事件处理函数。
常见的作法是将原始下拉菜单隐藏但保留在 DOM 中,然后用用户操作单选按钮时,事件转发给隐藏的下拉按钮,再由下拉按钮触发原生add-to-cart-variation.js相关功能。代码如下:
$(".radio-variation-wrapper input[type=\"radio\"]").on("change", function() {
// 获取当前被点击的单选按钮
var $radio = $(this);
// 从父容器获取属性名称(例如:attribute_pa_color)
var attributeName = $radio.closest('.radio-variation-wrapper').data('attribute_name');
// 找到对应的隐藏下拉选择器
var $select = $('select[name="' + attributeName + '"]');
// 更新下拉选择器的值并触发change事件
$select.val($radio.val()).trigger('change');
// 手动触发WooCommerce的购物车更新事件
$(document.body).trigger('update_checkout', { update_shipping: true });
});
而在Woo种,已经使用原型模式 创建 VariationForm 对象,并在初始化时绑定事件监听器:
// WooCommerce 核心代码(位于 add-to-cart-variation.js)
function VariationForm( $form ) {
this.$form = $form;
this.init();
}
VariationForm.prototype.init = function() {
// 监听下拉菜单的 change 事件
this.$form.on( 'change', '.variations select', this.onChange );
// 其他初始化逻辑...
};
VariationForm.prototype.onChange = function( event ) {
// 处理变体选择的核心逻辑
this._reset_variations();
this._maybe_update_variation();
this._display_price();
// 其他逻辑...
};
因此,$select.val($radio.val()).trigger(‘change’); 该操作它模拟了用户直接操作原生下拉菜单的行为,触发了 WooCommerce 核心 JavaScript 已经监听的 DOM 事件。
注意:我上面的代码可以与原生监听交互。即执行
$select.val($radio.val()).trigger(‘change’); 时,这里的 $select 是原生下拉菜单的 jQuery 对象,与 WooCommerce 监听的是同一个 DOM 元素。所以,这个操作会触发该元素上所有绑定的 change 事件监听器,包括 WooCommerce 通过 VariationForm 绑定的监听器。
所以关键知识点:
JavaScript 中的事件监听是基于 DOM 元素 的,而非创建监听器的对象。即使 VariationForm 创建了事件监听器,这些监听器实际上是绑定到 DOM 元素(下拉菜单)上的。
所以,我自定义的代码可以通过触发同一个 DOM 元素的 change 事件,间接调用了 WooCommerce 的原生功能。
3:关于form.$form.trigger功能
form.$form.trigger( 'woocommerce_update_variation_values' );
这个功能类似于wordpress动作钩子,用户自定义的事件可以在该代码执行后触发。比如我现在把Woo 产品属性变体选择默认是下拉菜单,我把它改为单选按钮方式。其中add-to-cart-variation.js中功能
VariationForm.prototype.onUpdateAttributes
不起作用,得重写该功能。该功能也预留了自定义事件接口,如下:
VariationForm.prototype.onUpdateAttributes = function( event ) {
var form = event.data.variationForm,
attributes = form.getChosenAttributes(),
...
form.$form.trigger( 'woocommerce_update_variation_values' );//预留接口
我新建个js文件add-to-cart-custom.js ,监听woocommerce_update_variation_values 事件,如下:
jQuery( document ).on( 'woocommerce_update_variation_values', function() {
console.log( '变体值已更新1' );
});
只要trigger触发该事件,则会被document 上绑定了该事件的监听器所监听到,触发对应的回调函数。
为什么会被监听到呢?原理如下:
document
└── body
└── form (form.$form)
└── select (变体选择器)
上面是页面结构。当用户在 <select>
或 <input>
中选择变体选项时,VariationForm 实例捕获到选择变化,执行onUpdateAttributes功能,最后的form.$form.trigger(‘woocommerce_update_variation_values’) 事件被触发,然后该事件冒泡到 document
,于是所有通过 jQuery(document).on() 注册的监听器被触发,从而执行自定义逻辑。
重点:jQuery(document).on()是全局事件监听,用于捕获在整个文档中触发的特定事件。无论事件是在哪个元素上触发的,只要它冒泡到 document,所有绑定在 document 上的该事件监听器都会被触发。这就是为什么不同 JS 文件中定义的监听器都能响应同一事件的原因。