The opening

First, we all know that throttle and debounce are great tools for performance optimization.

In this article, we will briefly introduce the two concepts, but we will not talk about the principle of the two functions, but the love and hate between the two functions and vue, but before we get into the topic, we need to know a little about it.

Function throttling means that js methods run only once in a certain period of time.

Throttling Throttling means to save the flow of water, like a faucet running water. We can manually reduce the flow of water (for a certain period of time), but it will always flow.

When the function is throttled, the function is executed every n seconds. The common scenarios are as follows:

  • DOM element drag and drop implementation (Mousemove)
  • Search for Associations (KeyUp)
  • Calculate the distance of mouse movement (Mousemove)
  • Canvas Drawing Board (Mousemove)
  • Shooter mouseDown/KeyDown events (only one shot per unit of time)

Function debounce runs the code only once when there is enough free time.

Take a bus in life, for example, is a certain period of time, if someone has to swipe the card to get on the bus, the driver will not drive. A driver only drives when someone else’s card is gone. (In fact, as long as you remember the idea of throttling can be determined by elimination method throttling and anti-shake)

When a function is buffered, execution of the function will always be delayed, resulting in the effect that it will not be executed. The common scenarios are as follows:

  • The resize/scroll statistics event is triggered every time
  • Validation of text input (send AJAX request for validation after continuous text input, once validation is fine)

vue throttle

So what sparks do they erase when combined with vUE? Recently, I had a discussion with my girlfriend about vue throttle. I thought it was simple at first, but it really lasted for an hour…. High energy ahead of the core, layers of progressive involving VUE source code.

For the first time to explore

So it looks like the right form. (Note that you are throttle (throttle) and throttle (methods).

<input @input="throttle(download(xxx))">. methods: { download: (xxx) { .. }, throttle: (xx) { } } ...Copy the code

I started to wonder, yes, this is exactly how you throttle correctly, why not, plus I haven’t written vue black magic for a long time, I don’t know how to explain.

Quickly surreptitiously looked it up and silently typed vue Debounce into Google…

I found some correct ways to open it.

I found that it worked, but I couldn’t write it into a template.

Emm. If not, start thinking? Why doesn’t this work? Wait, what did I just say? Go back 3.3 seconds… (Why 3.3 seconds, since the average human speaks at 200 words per minute)

Writing? This is just template syntax for vue. Real browsers don’t work like this.

I feel like I have an idea! Quick, quick, quick find vue template compiled after the appearance

In the browser input the vUE template online these keywords.

Soon we find this address template-explorer.vuejs.org/

We enter our template into the input box on the left.

We get a parsed render function like this.

function render() {
  with(this) {
    return _c('input', {
      on: {
        "input": function ($event) { throttle(download(xxx)); }})}}Copy the code

Here we see that we can get a sense that the input listener method has been wrapped in a layer of functions after parsing. It is also easy to guess that the function that eventually resolves into a true binding will look like this.

xxxx.addEventListener('input'.function ($event) {
		throttle(download(xxx));
})
Copy the code

If you are throttle, I believe that those who know you are throttle will see at a glance that you are not fully effective.

And what about the way we just looked up in the data?

<template> <input @input="click" /> </template> <script> ... click: _.throttle(() => { ... })... </script>Copy the code
function render() {
  with(this) {
    return _c('input', {
      on: {
        "input": click
      }
    })
  }
}
Copy the code

In this way, VUE passes the binding practices directly without any wrapping.

So there’s only one truth

Sure enough is vue template of black magic !!!!!

The advanced

That we through vUE source code to explore, VUE template resolution principle, to deepen some of our impression.

Since this is part of the vUE event-compile-related code, we can easily locate the VUE source code (currently v2.6.12).

Github.com/vuejs/vue/b…

We see that the vue source code contains the following code for event generation.

const fnExpRE = /^([\w$_]+|\([^)]*? \))\s*=>|^function(? :\s+[\w$]+)? \s*\(/
const fnInvokeRE = / \ [^)] *? \); * $/
const simplePathRE = /^[A-Za-z_$][\w$]*(? :\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"] |\[\d+]|\[[A-Za-z_$][\w$]*])*$/.const isMethodPath = simplePathRE.test(handler.value)
const isFunctionExpression = fnExpRE.test(handler.value)
const isFunctionInvocation = simplePathRE.test(handler.value.replace(fnInvokeRE, ' '))

if(! handler.modifiers) {// Return value if it is a method or function expression
  if (isMethodPath || isFunctionExpression) {
    return handler.value
  }
  /* istanbul ignore if */
  if (__WEEX__ && handler.params) {
    return genWeexHandler(handler.params, handler.value)
  }
  // if the above conditions are not met, a layer of methods will be wrapped
  return `function($event){${
    isFunctionInvocation ? `return ${handler.value}` : handler.value
  }} ` // inline statement
} else{... }Copy the code

Since ours doesn’t have modifiers, we comment our code with modifiers to prevent unnecessary interference.

To get a better sense of the situation we call isMethodPath method path, isFunctionExpression function expression, and isFunctionInvocation function call (which is the English version, but for the sake of clarity)

We can see from the code above that if this event is written to satisfy either isMethodPath or isFunctionExpression. So our writing in the event will be returned directly, otherwise it will be wrapped in a layer of function.

Let’s take a look at the scene of the event. Const simplePathRE = /^[a-za-z_ $][\w$]*(? :\.[A-Za-z_$][\w$]*|\[‘[^’]*?’]|\[“[^”]*?”] | | \ [\ d +], [[A Za – z_ $] [$] \ w *]) * $/, at first glance is A little long, we analysis through the analysis of the visualization tools.

jex.im/regulex/

As you can see from the visualization, Our event mode will pass the regular check if it is in the above format (for example,handle,handle[‘xx’], handle[“xx”],handle[XXX],handle[0], console.log) and will not be wrapped in a layer of functions.

There is another kind of circumstance is regular const fnExpRE = / ^ ($_ [/ w] + | \ ((^)) *? \))\s*=>|^function(? :\s+[\w$]+)? \ \ s * (/.

Simply write an anonymous function, (xx) => {} or funciton(){}.

All but two of these cases will contain a layer of methods.

Remember from the official vue tutorial, when we write template syntax, the following two methods are equivalent.

1.<div @click="handler"></div>

2.<div @click="handler()"></div>

Because at compile time, they are compiled separately into the following form.

xxx.onclick = handle

xxx.onclick = function($event) {
	return handler();
}
Copy the code

By wrapping a layer of functions to achieve the same goal, now you get the idea? In VUE, there’s nothing wrong with writing it, sometimes it’s a mistake of your hand, it takes all of that into account, it’s like eating a meal, the meal is on our lips.

In the case of being wrapped in a function, there are two different cases.

isFunctionInvocation ? return ${handler.value} : handler.value

The isfunctionalized Invocation is to remove the function call. If the invocation is removed and the method path is met, a return is generated.

So let’s draw a picture to summarize.

And what about us?

throttle(download(xxx))
Copy the code

Obviously, we didn’t satisfy either method paths or function expressions, so we had the “bug” that we were throttling.

So far, we have been clear about the dark magic in VUE. While VUE brings us convenience, something magical happens when we don’t use it well, or don’t understand some of its principles.

The best

So with all that said, we need to have a best practice.

<template> <input @click="download(xxx)" /> </template> <script> import {debounce} from 'lodash'; . methods: { download: debounce((xxx) => { ... })}... </script>Copy the code

sublimation

So let’s explain another problem, the difference between external import and internal methods?

<template>
	<input @click="debounce(download(xxx))" />
</template>
<script>
import {debounce} from 'lodash';
</script>
Copy the code

I’m going to make a mistake writing this.

Because the methods we write in our template must be methods of methods, otherwise they will not be found.

Maybe we’re going to throttle in the template and have to define that function in methods, which is very unfriendly, because it’s counter-intuitive, and it’s been too long since I’ve written it.

This is also relevant for compiling vue templates, because methods in vue templates are compiled to _vm. XXX, for example.

<template>
	<input @click="debounce(download(xxx))" />
</template>
Copy the code

The above template code will be compiled to look like this.

/* template */
var __vue_render__ = function() {
    var _vm = this;
    var _h = _vm.$createElement;
    var _c = _vm._self._c || _h;
    return _c("input", {
      on: {
        click: function($event) { _vm.debounce(_vm.download(_vm.xxx)); }}}};Copy the code

This is the code that actually executes in the browser, so it is clear that there is no Debounce in _VM, which is why template can only access methods and variables defined in vUE.

Test the edge

Let’s explore whether vUE 3.0 has changed this.

The answer is: no.

I specifically went to @vue/ Compiler-SFC to test it.

const sfc = require('@vue/compiler-sfc');

const template = sfc.compileTemplate({
    filename: 'example.vue'.source: '<input @input="throttle(download(xxx))" />'.id: ' '
});
Copy the code
// output
import { createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock } from "vue"

export function render(_ctx, _cache) {
  return (_openBlock(), _createBlock("input", {
    onInput: _cache[1] || (_cache[1] = $event => (_ctx.throttle(_ctx.download(_ctx.xxx))));
Copy the code

At the end

From this exploration, vUE’s own template language requires a lot of mental models, and in this case, Vue gives us a lot of syntactic sugar to indulge in, which is comfortable, to say the least, but one day we will suffer alone.

This brings us to React JSX, which is cumbersome and cruel to us, but gives us more control over what we do. (Templates is still the official recommended method, though Vue can use JSX.) Because it does a lot of processing for us, in some cases it needs to inject $event, which is our usual event object, but other people handle these things for us, and we gradually forget its original form, if there is a problem, we can’t do anything. JSX requires us to write complete code, which makes everything extra hard to write. Maybe it’s superficial to talk about JSX and VUE Templates, as the vUE documentation says, but it’s something that everyone understands and likes very differently. So I summed it up myself.

All learned si son 🙂

Thank you

This article was first published on the public account “Notes of autumn Wind”. Welcome to follow it.