vue自定義指令意外生效之謎:深入探討
本文探討一個(gè)常見(jiàn)的Vue.JS開(kāi)發(fā)問(wèn)題:自定義指令在未綁定目標(biāo)元素上生效的原因。我們分析一個(gè)案例,解釋這種現(xiàn)象背后的機(jī)制,并提供解決方案。
案例描述
我們創(chuàng)建了一個(gè)全局自定義指令 validatenumber,用于限制輸入框只能輸入數(shù)字:
Vue.directive('validateNumber', { bind(el, binding, vnode) { let lastData = null; el.onkeyup = (e) => { if (String.fromCharCode(e.keyCode) === 'E' || String.fromCharCode(e.keyCode) === 'KeyE') { e.target.value = lastData; return false; } else { lastData = e.target.value; } e.target.value = e.target.value.replace(/[^d]/ig, ''); let _this = vnode.context; _this.AccessNestedObject(_this, vnode.data.model.expression, e.target.value); if (binding.expression && e.target.value) { let obj = (new Function("return " + binding.expression))(); let keys = Object.keys(obj); let values = Object.values(obj); if (keys[0] === 'min') { if (parseInt(e.target.value) < parseInt(values[1])) { e.target.value = values[1]; } } } el.dispatchEvent(new Event('input')); }; } });
在Vue模板中,我們僅在一個(gè)輸入框上使用了該指令:
<el-form-item label="等待時(shí)間" prop="timeInterval"> <el-input id="alarm-rule-time-interval" style="width:100%" type="number" v-model="alertRulesForm.timeInterval" v-validateNumber> <template slot="append">秒</template> </el-input> </el-form-item> <el-form-item label="空間間隔" prop="distanceInterval"> <el-input id="alarm-rule-distance-interval" type="number" v-model="alertRulesForm.distanceInterval"> <template slot="append">米</template> </el-input> </el-form-item>
然而,第二個(gè)輸入框(distanceInterval),盡管未應(yīng)用 v-validateNumber 指令,卻也表現(xiàn)出相同的數(shù)字校驗(yàn)行為。
立即學(xué)習(xí)“前端免費(fèi)學(xué)習(xí)筆記(深入)”;
問(wèn)題分析
關(guān)鍵在于 el.onkeyup 事件監(jiān)聽(tīng)器。該監(jiān)聽(tīng)器綁定在 el 元素上,而 el 指的是所有應(yīng)用了 validateNumber 指令的元素。 由于指令的綁定機(jī)制,事件監(jiān)聽(tīng)器可能會(huì)意外地捕獲到其他元素的 keyup 事件,特別是當(dāng)這些元素在 dom 結(jié)構(gòu)上與已綁定指令的元素存在父子或兄弟關(guān)系時(shí)。 type=”number” 屬性雖然限制了輸入,但這并不能解釋為什么第二個(gè)輸入框會(huì)觸發(fā) validateNumber 指令的邏輯。
解決方案
為了避免這種意外的指令生效,我們需要改進(jìn)指令的實(shí)現(xiàn):
-
更精確的事件監(jiān)聽(tīng): 不要直接監(jiān)聽(tīng) el.onkeyup,而是利用更精確的事件委托機(jī)制,例如,將事件監(jiān)聽(tīng)器綁定到父元素,并使用 e.target 判斷事件源是否為目標(biāo)元素。
-
指令范圍限制: 在指令的 bind 或 inserted 方法中,可以檢查 el 是否為目標(biāo)元素,如果不是,則不執(zhí)行指令邏輯。
修改后的指令:
Vue.directive('validateNumber', { bind(el, binding, vnode) { // 添加判斷,確保指令只在目標(biāo)元素上生效 if (el.id !== 'alarm-rule-time-interval') return; let lastData = null; el.onkeyup = (e) => { // ... (其余邏輯保持不變) }; } });
或者,使用事件委托:
Vue.directive('validateNumber', { bind(el, binding, vnode) { const parent = el.parentElement; // 獲取父元素 parent.addEventListener('keyup', (e) => { if (e.target === el) { // 檢查事件源是否為目標(biāo)元素 // ... (指令邏輯) } }); } });
通過(guò)以上改進(jìn),我們可以確保自定義指令只在目標(biāo)元素上生效,避免了意外的干擾。 記住,在編寫(xiě)自定義指令時(shí),要格外注意事件監(jiān)聽(tīng)器的作用范圍和事件委托的正確使用,以確保指令的可靠性和可預(yù)測(cè)性。