The cause of the matter is such, Xiao Ming attended the goose factory interview, with the interviewer battle of wits and courage N many back and forth fruitless, the interviewer unexpectedly released fishing questions! Ask directly: tell me how Vue implements event delegation… Xiaoming: It’s… There are? There are? “Or how about you tell me how you use V-for for long lists to bind events? Xiao Ming: I **🔥 (omitted here)
😜 well, if you are also successful fishing, then continue to read! 👇
There is very little discussion of Vue event delegation in nuggets, so let me be the first one. This article will use cases to source debug the binding events in the V-for list! Start with @click in our list; Explore what the render function looks like after compiling; Finally, how does the whole new Vue bind events to elements when it is mounted to the DOM
Let’s explore whether Vue implements event delegation for list-bound events! At the end of the article, the author will combine his own understanding, discuss with the big 🔥, we usually develop, whether it is necessary to list binding event event delegate optimization processing!
1. Ask questions
Take a look at the following questions and answer them yourself:
- In daily development, in
Vue
A long list ofWhen you bind events, what does it say? - Is there a performance problem if you bind events directly to each item in the list?
- If there’s a performance problem with that, then
Vue
Is it handled internally? After all, any entry-level front-end would know thatEvent delegationIs it possible for Utah to ignore this?
Start with the code: it is simple to bind the click event handleClick to each LI
// Template code
<ul>
<li
v-for="(item, i) in listArr"
:key="item + i"
@click="handleClick(item + (i + 1))"
>
{{item + (i + 1)}}
</li>
</ul>
/ / js code
methods: {
handleClick (params) {
console.log('Trigger bind event' + params)
}
}
Copy the code
Finally, the page looks like this. Let’s compare the differences between these two images:
- As you can see from the first picture,
ul
Tag is not bound toclick
Events. Up againdiv #app
、body
, untilhtml
Tag, no bindingclick
Events. Instead of one tile, they are the same as picture 1. The conclusion is:li
None of the parent elements are boundclick
Events.
- Then we look at Figure 2, and we see that
li
The tag is boundclick
Events. eachli
All can beEvent Listeners
Found in theclick
The binding. It’s not the same. It’s the same.
From a result-oriented perspective, the click event bound in V-for is not internally delegated to the upper element in Vue. Next, let’s take a look at Vue’s event implementation from the source level!
Second,@click
How do I bind to the DOM?
-
Let’s first look at what this code looks like when it’s packaged
li
的render
In the function, the second argument has oneon
Property, there is aclick
The event points to our codehandleClick
n._l
Is tov-for
Implementation, in the source codecore/instance/render-helpers/render-list.js
I’m not going to expand it here
-
Recall how the Vue component gets onto the DOM? 👀
- As shown in the figure:
new Vue
、init
、$mount
、render
getVNode
- Finally,
VNode
forpatch
, to the realDOM
On the
- As shown in the figure:
-
See how @click gets into the DOM through code debugging.
-
A.Directly from the
createELm
Began to read. (This is creatingReal DOMThe method, big 🔥 need not delve into, know on the line). As shown below, you can see the currentli
的DOM
It’s already created, let’s go from there! -
B. After all child elements are created, invokeCreateHooks and updateDOMListeners are implemented. (The recursive process of creating li child elements will not be expanded. Children familiar with Vue Patch can imagine it by themselves.)
-
C.
updateDOMListeners
The implementation. Let’s say noon
Property, returns directly; It will then callupdateListeners
This is the key function. (We just need to focus on the core point, likenormalizeEvents
Is dealing withv-model
And the rest we can ignore) -
You can see that createFnInvoker was assigned to on.click on your machine. CreateFnInvoker is a new function (higher order function) wrapped around handleClick. Why do you do that? Here’s a hint
-
E. See ⭐️ in the figure above, where the add function performs event binding on the real DOM. Look directly at the figure below to see what the Add function does. (The current execution stack shows that the add function is executing)
- This is going to be executed as many times as there are list items
add
Function for event binding. We have eight of them in our caseli
This is going to be executed eight times, given eightli
The bindingclick
The event
- This is going to be executed as many times as there are list items
OK, this is how the DOM binding event is handled during the component initialization phase. Based on the above debugging results, it is clear that Vue does not delegate events for the bound events of v-for list items.
I’m going to change the length of the list to 3, get rid of the other debuggers, and keep only the addEventListener, and record a screen for you. Notice that addEventListener 👇 is executed three times
Conclusion: Vue has no event delegate for the click event bound to the list item!
Three, that Vue really does not have any processing?
The createFnInvoker function is useful here. 👇 has seen the Vue error handling implementation and knows that this method wraps our handleClick with a try-catch for error catching. Is there any other use for it? (For more information on error catching, see Vue error handling here.)
invoker
Function. Focus oncreateFnInvoker
Core processing of- Core point 1:To receive a
fns
Function or array of functions, returns oneFunction
- Core point two:The returned
invoker
Static properties of a methodfns
And thenEvery timeinvoker
And when we do that, we get this againinvoker.fns
- Core point three:We developers wrote it
handleClick
Will be wrapped ininvokeWithErrorHandling
In, the callback to the click event is executed internally. As mentioned at the beginning of the function, Vue makes a layerError trappingProcessing. (Check out another article by me.)
- Core point 1:To receive a
export function createFnInvoker (fns: Function | Array<Function>, vm: ? Component) :Function {
function invoker () {
// Get invoker's static property FNS
const fns = invoker.fns
if (Array.isArray(fns)) {
const cloned = fns.slice()
for (let i = 0; i < cloned.length; i++) {
// 1. The empirical verification [I] is the implementation of a function, such as handleClick, which is input into a function
// 2. This is executed inside invokeWithErrorHandling (wrapped in try-catch)
invokeWithErrorHandling(cloned[i], null.arguments, vm, `v-on handler`)}}else {
// Only one function goes here, the logic is exactly the same as above
return invokeWithErrorHandling(fns, null.arguments, vm, `v-on handler`)
}
}
invoker.fns = fns
return invoker
}
Copy the code
invoker
What is the use of packing this layer?
- First to see
updateListeners
In theelse if
Logic in:old.fns = cur
- That’s how Vue handles our list binding event! When a component is updated: Just replace the function!
- Note here that component initialization,Component updatesAre all go
updateListeners
Of this method
function updateListeners () {...for (name in on) {
...
if (isUndef(cur)) {
...
} else if (isUndef(old)) {
if (isUndef(cur.fns)) {
Cur = invoker
cur = on[name] = createFnInvoker(cur, vm);
}
if (isTrue(event.once)) {
cur = on[name] = createOnceHandler(event.name, cur, event.capture);
}
// Review step D. Draw the pentagram ⭐️ section, where the function will be added for the first time
add(event.name, cur, event.capture, event.passive, event.params);
} else if(cur ! == old) {// When the component updates, go here and assign the new Invoker to the previous invoker static property FNSold.fns = cur; on[name] = old; }}... }Copy the code
- Go back to the source code for binding events. Here,
handler
In fact, it isinvoker
Function. Now, why not just bind to ourshandleClick
Function, big 🔥get it? Here I make a bold conclusion:Is to avoid component updates when each itemli
Events need to be rebound
target$1.addEventListener(
name, // click
handler, // The DOM is bound to the invoker function, not directly to handleClick
supportsPassive
? { capture: capture, passive: passive }
: capture
);
Copy the code
Now, let’s see if this is the case by looking at old.fns = cur; Logical execution, Let’s go!
Do component updates require rebinding events?
As we all know, whenever a reactive data change is triggered, an update of the current component is triggered. Component update is nothing more than the process of rendering and patch again (with a diff in the middle). Reexecuting render means that our handleClick bindings on Li will be a completely new function.
Here is the Render function diagram of the App component again to deepen your understanding
Next, through the debugging of the two cases, verify whether the Vue component updates are rebound events
The case code is as follows:
/* template */
<div id="app">
// Add a flag
<p>{{flag}}</p>
<ul>
<li
v-for="(item, i) in listArr"
:key="item + i"
@click="handleClick(item + (i + 1))"
>
{{item + (i + 1)}}
</li>
</ul>
// Click the button to change the flag
<button @click="flag = ! flag">change</button>
</div>
/* script */
data () {
return {
flag: true.listArr: new Array(3).fill('list-item')}},methods: {
handleClick (params) {
console.log('Trigger bind event' + params)
}
}
Copy the code
1. What happens to common component updates?
The page of the code above looks like this. Now let’s clickchange
Button. Check to see ifDirect substitution function!
Straight to the results:
- As shown, component updates exist
old
, so it does not go to the initialization logic (screenshots at ❌) - three
li
includingChange button
There are four of themclick
Events all follow the logic of static attributesfns
Switch to a new click functionold.fns = cur
Will not go nativeaddEventListener
Rebind the event!
2. Newpush
ali
What happens?
Everybody can think according to the case before oneself! It’s actually very simple
We changed the button click event slightly in the above example code:
<button @click="listArr.push('list-item')">pushItem</button>
Copy the code
The page looks like 👇
Let’s hit the pushItem button! Barring accidents, the first three times should be the same as above!
No surprises! Let’s move on! Until we enter the addEventListener, sure enough, it is the li element of our new push! I took his innerHTML and showed it to 🔥
Conclusion: When the Vue component is updated, the element is not re-event bound! To expand, we use invoker wrapping (higher-order function thinking) to put our event callbacks in the invoker.fns static property. The DOM event binding callback binds invoker. When the event callback is executed, it actually finds the function in invoker. FNS to execute, which is our handleClick
So, in fact, there is some internal optimization of the event, but there is no internal optimization of the event delegate!!
Five, talk about whether the daily development needs event delegation
First up:
- All optimization processes are generally considered after the occurrence of performance problems.
- Optimizations don’t need to be considered in every development, after all, complete the business and leave work early.
- This is not to say that optimization thinking and execution are not good, but to take into account the cost of time, and the necessity of optimization. If you don’t need to optimize the smooth duck, then I feel there is no need to optimize.
From the practical point of view:
- There are certainly performance benefits to event delegation for long lists, but it’s up to you to decide if you want to write event delegates. If there is no performance problem, directly
@click
It works in each of these terms - Notice how the binding function is written. Don’t be fooled by some interview questions. Some interview questions are more expensive to write but can be avoided. Look at the pseudocode below
for (let i = 0; i < xxx; i++) { // Assign a new function each time li.onclick = function () {... }// The function will not be generated n times li.onclick = myCick } function myClick () {... }Copy the code
Vue
Internal optimization, reduce a lot of re-binding, unbinding overhead
So, don’t worry about tying events directly to list items. You can fix performance problems later, right?
In fact, I write this topic for several reasons:
- First: mainly because there is really little discussion about Vue long list binding events and whether Vue has internal help to implement event delegation. Anyway, I did not find a suitable ~ so I will do the first to eat the first one 🦀️
- Second: the author was asked this question before the interview ~ “🙈 the interviewer do personal” was also asked meng. It is also taken for granted that such a basic point can not be taken into account by Utah university, right? In fact, for the implementation of the source code, if you do not see that piece, really do not know, there is no way ~
- The third: be familiar with
React
Students may be able to think of it naturallySynthetic eventsFeeling,Vue
There are ways to deal with events. In a word, it is really endless learning ah ~
If you think something is not well written or wrong, please point it out in time. If you think it is well written and can understand it, please give a small praise to the author 👍👍👍.
Finally, please allow me to say one more word!! Please be a good person (bold!). ! 🙄