preface

With the rapid development of technology, front-end libraries such as Elementui are often accessed for rapid development. In the case of Element, some components cannot meet our needs, so we need to do secondary packaging. Today think about trying to use vUE’s functional components to do a secondary wrapper.

Let’s take a look at the simplest demo to supplement the basics

// demo.vue
<template>
  <div class="demo">
    <DeInput @debounce="debounce" maxlength='5' @blur="inputBlur"/>
  </div>
</template> <script> import DeInput from './DeInput' export default {  name: 'Demo'. components: {  DeInput  },  methods: {  debounce(value) {  console.log('After shaking:', value)  },  inputBlur() {  console.log('Out of focus')  }  } }  // deinput.vue <template>  <div>  <el-input v-model="inputValue" @input="deInput"></el-input>  </div> </template> <script> export default {  data() {  return {  inputValue: ' '  }  },  methods: {  deInput() {  this.$emit('debounce', this.inputValue)  }  } } Copy the code

If you run this code, you will see that inputBlur does not execute at all, and maxLength does not take effect either, because @blur and maxLength are internal methods and attributes of el-input. If you want to call, you need to pass through, in other words, let el-Input know that its method or property is being called. $Listeners and $listeners are available on vue.

  • $attrs: contains attribute bindings (except class and style) that are not recognized (and retrieved) as prop in the parent scope. When a component does not declare any prop, all parent-scoped bindings (except class and style) are included, and internal components can be passed in via V-bind =”$attrs” — useful when creating higher-level components.
  • $Listeners: Contains V-on event listeners in the parent scope (without.native modifiers). Internal components can be passed in via V-on =”$Listeners “– useful for creating higher-level components.

InputBlur is now available and maxLength is in effect. It is too simple to explain too much

// deinput.vue
<el-input v-model="inputValue" @input="deInput" v-bind="$attrs" v-on="$listeners"></el-input>
Copy the code

Now that gives us most of the idea, let’s see if the functional component idea meets our needs

Functional component

Definition: We can mark a component as functional, which means it is stateless (no responsive data) and has no instance (no this context). A functional component would look like this:

Vue.component('my-component', {
  functional: true.// Props is optional  props: {
    // ...
 }, // To make up for missing instances// Provide the second argument as context render: function (createElement, context) {  // ...  } }) Copy the code

Why use functional components?

  • Because functional components are just functions, rendering overhead is also much lower.

Try wrapping a functional component with a shake-proof input tag

  • You can refer to my article on anti-shake
// debouce.js
const debounce = (fn, delay=500, Switch=true) = > {    let timeout = null;
    return (params) => {
        clearTimeout(timeout)
  if(! Switch) { return fn(params)  }   timeout = window.setTimeout(() => {  fn(params)  }, Number(delay))  } }  export default {  functional: true. render: function(createElement, context) { Const vNodeLists = context.slots().default // we can substitute context.children here const time = context.props.time  const Switch = context.props.Switch   if(! vNodeLists) { console.warn('There must be a child.')  return null  }   const vNode = vNodeLists[0] || {}  // We get the input method for secondary encapsulation if (vNode.tag && vNode.tag === 'input') {  const funDefault = vNode.data.on && vNode.data.on.input  if(! funDefault) { console.warn('Please pass in the input method (@input)')  return null  }  const fun = debounce(funDefault, time, Switch)   vNode.data.on.input = fun  } else {  console.warn('Input only')  return null  }  return vNode  } } Copy the code

See how this component is used

<template>
  <div class="home">
    <Debounce time='1000' :Switch='true'>
      <input type="text" @input="debounce"/>
    </Debounce>
 </div> </template>  <script> import Debounce from '.. /components/debounce'  export default {  components: {  Debounce  },  methods: {  debounce(e) {  console.log('After shaking:', e.target.value)  }  } } </script> Copy the code

Let’s try again to wrap an ElementUI el-Button component

// debelas.js key codeif (vNode.componentOptions && vNode.componentOptions.tag === 'el-button') {
      const funDefault = vNode.componentOptions.listeners && vNode.componentOptions.listeners.click
      if(! funDefault) {          console.warn('Please pass in the click method (@click)')
 return null  }  const fun = debounce(funDefault, time, Switch)   vNode.componentOptions.listeners.click = fun  } Copy the code

The difference between our elementui components and native tags is that they need to be retrieved via vnode.componentOptions. Post the full code next

const debounce = (fn, delay=500, Switch=true) = > {    let timeout = null;
    return (params) => {
        clearTimeout(timeout)

 if(! Switch) { return fn(params)  }   timeout = window.setTimeout(() => { // El-button gets an array, input gets an arrayfunction  if(! Array.isArray(fn)) { fn = [fn]  }   fn[0](params) }, 1000). } }  export default {  functional: true. render: function(createElement, context) {  const vNodeLists = context.slots().default  const time = context.props.time  const Switch = context.props.Switch   if(! vNodeLists) { console.warn('There must be a child.')  return null  }   const vNode = vNodeLists[0] || {}   if (vNode.componentOptions && vNode.componentOptions.tag === 'el-button') {  const funDefault = vNode.componentOptions.listeners && vNode.componentOptions.listeners.click  if(! funDefault) { console.warn('Please pass in the click method (@click)')  return null  }  const fun = debounce(funDefault, time, Switch)   vNode.componentOptions.listeners.click = fun  } else if (vNode.tag && vNode.tag === 'input') {  const funDefault = vNode.data.on && vNode.data.on.input  if(! funDefault) { console.warn('Please pass in the input method (@input)')  return null  }  const fun = debounce(funDefault, time, Switch)   vNode.data.on.input = fun  } else {  console.warn('Input and el-button only')  return null  }  return vNode  } } Copy the code

Take a quick look at the effect


Consider: Is there a problem with encapsulating el-Input in this way?

This article is formatted using MDNICE