自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Vue.js設(shè)計(jì)與實(shí)現(xiàn)之十三-渲染器的核心功能:掛載與更新02

開發(fā) 前端
在Vue.js的事件處理先要解決的問題,就是如何在虛擬節(jié)點(diǎn)中描述事件,事件是一種特殊的屬性,在vnode.props對(duì)象中以字符串on開頭的屬性都被視作事件。

1、寫在前面

在上篇文章中介紹了虛擬節(jié)點(diǎn)的掛載與更新,以及虛擬DOM節(jié)點(diǎn)上的屬性設(shè)置,封裝了新的卸載函數(shù)unmount。那么,虛擬節(jié)點(diǎn)上的事件又是如何處理的呢,同一個(gè)事件設(shè)置多個(gè)處理函數(shù),同一個(gè)元素綁定多個(gè)事件,觸發(fā)事件和綁定事件的時(shí)機(jī)問題應(yīng)該如何處理?

2、事件的處理

在Vue.js的事件處理先要解決的問題,就是如何在虛擬節(jié)點(diǎn)中描述事件,事件是一種特殊的屬性,在vnode.props對(duì)象中以字符串on開頭的屬性都被視作事件。

const vnode = {
type:"p",
props:{
// 同一個(gè)事件多個(gè)事件處理函數(shù)
onClick:[
()=>{
//...
},
()=>{
//...
}
],
// 同一個(gè)元素綁定多個(gè)事件
onContextMenu(){
//...
}
},
children:"text"
}
renderer.render(vnode, document.querySelector("#app"));

在上面代碼中,我們看到同一的DOM元素上可以綁定多個(gè)事件,同一個(gè)事件上又可以有多個(gè)事件處理函數(shù)。多次我們修改patchProps函數(shù)中事件處理相關(guān)代碼得到:

patchProps(el, key, prevValue, nextValue){
if(/^on/.test(key)){
const invokers = el._vei || (el._vei = {});
let invoker = invokers[key];
const name = key.slice(2).toLowerCase();
if(nextValue){
if(!invoker){
invoker = el._vei[key] => {
//invoker.value是數(shù)組時(shí),遍歷逐個(gè)調(diào)用事件處理函數(shù)
if(Array.isArray(invoker.value)){
invoker.value.forEach(fn=>fn(e));
}else{
invoker.value(e);
}
}
invoker.value = nextValue;
el.addEventListener(name, invoker);
}else{
invoker.value = nextValue;
}
}else if(key === "class"){
//...
}else if(shouleSetAsProps(el, key, nextValue)){
//...
}else{
//...
}
}
}

在上面代碼中,先通過/^on/.test(key)檢測(cè)元素上以on開頭的屬性,在綁定事件時(shí)偽造事件處理函數(shù)invoker

  • 如果invoker不存在時(shí),將invoker作為事件處理函數(shù),緩存到el._vei屬性中
  • 將真正的事件處理函數(shù)設(shè)置為invoker.value屬性的值,偽造的事件處理函數(shù)invoker綁定到元素上

將el._vei的數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)為一個(gè)對(duì)象,鍵即為事件名稱,值為對(duì)應(yīng)的事件處理函數(shù),這樣就不會(huì)出現(xiàn)事件覆蓋的現(xiàn)象。當(dāng)上面invoker.value的類型是數(shù)組時(shí),數(shù)組中的每個(gè)元素都是一個(gè)獨(dú)立的事件處理函數(shù),且這些事件處理函數(shù)都能夠正確綁定到對(duì)應(yīng)元素上。

3、事件冒泡與更新時(shí)機(jī)問題

在事件處理中,需要注意處理事件冒泡和更新時(shí)機(jī)結(jié)合導(dǎo)致的問題,事件觸發(fā)的時(shí)間會(huì)早于事件處理函數(shù)被綁定的時(shí)間。

const {effect, ref} = VueReactivity;
const bol = ref(false);
effect(()=>{
//創(chuàng)建vnode
const vnode = {
type:"div",
props:bol.value ? {
onClick(){
//...
}
}:{},
children:[{
type:"p",
props:{
onClick(){
bol.value = true;
}
},
children:"pingping"
}]
}
//渲染vnode
renderer.render(vnode, document.querySelector("#app"));
})

在上面代碼中進(jìn)行理論分析,首次渲染后由于bol.value的初始值為false,對(duì)此渲染器并不會(huì)給div元素綁定點(diǎn)擊事件。在鼠標(biāo)點(diǎn)擊p元素后,bol.value的值變更為true,看到點(diǎn)擊事件會(huì)從子元素p冒泡到父元素div上,但是div元素又沒有綁定事件,因此啥也不發(fā)生。

但是,事實(shí)上在點(diǎn)擊p元素時(shí),父元素div的click事件觸發(fā)了執(zhí)行函數(shù)的執(zhí)行。這是因?yàn)閎ol是個(gè)響應(yīng)式數(shù)據(jù),在點(diǎn)擊p元素后,bol.value的值發(fā)生改變,會(huì)觸發(fā)副作用函數(shù)的重新執(zhí)行。而在更新階段,渲染器會(huì)給div元素綁定click事件,在更新完后點(diǎn)擊事件才從p元素冒泡到div元素。

觸發(fā)事件的時(shí)機(jī)與事件綁定的時(shí)機(jī)的聯(lián)系

在一個(gè)事件觸發(fā)時(shí),目標(biāo)元素上還沒有綁定相關(guān)的事件處理函數(shù),因此屏蔽所有綁定事件時(shí)機(jī)要晚于觸發(fā)時(shí)間的事件處理函數(shù)的執(zhí)行。

patchProps(el, key, prevValue, nextValue){
if(/^on/.test(key)){
const invokers = el._vei || (el._vei = {});
let invoker = invokers[key];
const name = key.slice(2).toLowerCase();
if(nextValue){
if(!invoker){
invoker = el._vei[key] => {
//e.timeStamp是事件發(fā)生的時(shí)間,如果事件觸發(fā)的時(shí)機(jī)早于事件綁定的時(shí)間,則不執(zhí)行事件處理函數(shù)
if(e.timeStamp < invoker.attached) return;
//invoker.value是數(shù)組時(shí),遍歷逐個(gè)調(diào)用事件處理函數(shù)
if(Array.isArray(invoker.value)){
invoker.value.forEach(fn=>fn(e));
}else{
invoker.value(e);
}
}
invoker.value = nextValue;
// 添加invoker.attached屬性,存儲(chǔ)事件處理函數(shù)被綁定的時(shí)間
invoker.attached = performance.now();
el.addEventListener(name, invoker);
}else{
invoker.value = nextValue;
}
}else if(key === "class"){
//...
}else if(shouleSetAsProps(el, key, nextValue)){
//...
}else{
//...
}
}
}

在上面代碼中,給偽造的事件處理函數(shù)添加了invoker.attached屬性,用于存儲(chǔ)事件處理函數(shù)被綁定的時(shí)間。在invoker執(zhí)行的時(shí)候,通過事件對(duì)象e.timeStamp獲取事件發(fā)生的時(shí)間,比較兩者的時(shí)間,如果事件觸發(fā)的時(shí)機(jī)早于事件綁定的時(shí)間,則不執(zhí)行事件處理函數(shù)。

4、寫在最后

在本文中主要討論了事件的處理,介紹了在虛擬節(jié)點(diǎn)上綁定事件,如何綁定和更新事件。同時(shí),還介紹了如何處理觸發(fā)事件與更新時(shí)機(jī)的問題,屏蔽所有綁定事件時(shí)機(jī)要晚于觸發(fā)時(shí)間的事件處理函數(shù)的執(zhí)行。

責(zé)任編輯:姜華 來源: 前端一碼平川
相關(guān)推薦

2022-04-19 23:01:54

Vue.jsDOM節(jié)點(diǎn)DOM樹

2022-04-18 08:09:44

渲染器DOM掛載Vue.js

2022-04-01 08:08:27

Vue.js框架命令式

2022-04-04 16:53:56

Vue.js設(shè)計(jì)框架

2022-04-12 08:08:57

watch函數(shù)options封裝

2022-04-25 07:36:21

組件數(shù)據(jù)函數(shù)

2022-04-03 15:44:55

Vue.js框架設(shè)計(jì)設(shè)計(jì)與實(shí)現(xiàn)

2022-04-11 08:03:30

Vue.jscomputed計(jì)算屬性

2022-05-03 21:18:38

Vue.js組件KeepAlive

2022-04-14 09:35:03

Vue.js設(shè)計(jì)Reflect

2022-04-05 16:44:59

系統(tǒng)Vue.js響應(yīng)式

2022-04-26 05:55:06

Vue.js異步組件

2022-04-17 09:18:11

響應(yīng)式數(shù)據(jù)Vue.js

2022-04-09 17:53:56

Vue.js分支切換嵌套的effect

2022-04-16 13:59:34

Vue.jsJavascript

2022-04-12 08:09:22

Nodejs前端面試題

2019-04-01 19:38:28

Vue.jsJavascript前端

2010-08-13 11:02:27

Flex渲染器

2016-11-01 19:10:33

vue.js前端前端框架

2009-07-15 13:48:26

Swing模型和渲染器
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)