❝隨着技術的快速發展,前端爲了快速開發,咱們通常會接入像elementui這樣的庫,以element爲例,一些組件沒法知足咱們的需求,就須要作二次封裝。今天想着嘗試利用vue的函數式組件作一下二次封裝。html
// 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('防抖後:', value) }, inputBlur() { console.log('失去焦點') } } } // 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) } } } 複製代碼
❝若是去運行這段代碼就會發現inputBlur這個函數根本沒有執行,maxlength這個屬性也沒有生效,這是由於@blur和 maxlength是el-input內部方法和屬性。若是想要調用,就須要作透傳,換句話說就是讓el-input知道它的方法或者屬性被調用。其實只要vue提供的\$attrs和$listeners屬性便可。前端
❝咱們來加一下這兩個屬性, 再次去執行的時候發現inputBlur這個函數已經能夠被調用了,maxlength也生效了,因爲太過簡單,就不作過多解釋vue
// deinput.vue
<el-input v-model="inputValue" @input="deInput" v-bind="$attrs" v-on="$listeners"></el-input> 複製代碼
❝其實這已經給咱們提供了大部分的思路,接下來咱們試試用函數式組件的思路是否能知足咱們的需求web
❝定義:咱們能夠將組件標記爲 functional,這意味它無狀態 (沒有響應式數據),也沒有實例 (沒有 this 上下文)。一個函數式組件就像這樣:數組
Vue.component('my-component', {
functional: true, // Props 是可選的 props: { // ... }, // 爲了彌補缺乏的實例 // 提供第二個參數做爲上下文 render: function (createElement, context) { // ... } }) 複製代碼
❝爲何要用函數式組件?編輯器
// 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 // 這裏其實能夠替換爲context.children const time = context.props.time const Switch = context.props.Switch if (!vNodeLists) { console.warn('必需要有一個子元素') return null } const vNode = vNodeLists[0] || {} // 咱們獲取到其input方法進行二次封裝 if (vNode.tag && vNode.tag === 'input') { const funDefault = vNode.data.on && vNode.data.on.input if (!funDefault) { console.warn('請傳入input方法(@input)') return null } const fun = debounce(funDefault, time, Switch) vNode.data.on.input = fun } else { console.warn('僅支持input') return null } return vNode } } 複製代碼
<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('防抖後:', e.target.value) } } } </script> 複製代碼
// debounce.js 關鍵代碼
if (vNode.componentOptions && vNode.componentOptions.tag === 'el-button') { const funDefault = vNode.componentOptions.listeners && vNode.componentOptions.listeners.click if (!funDefault) { console.warn('請傳入click方法(@click)') return null } const fun = debounce(funDefault, time, Switch) vNode.componentOptions.listeners.click = fun } 複製代碼
❝咱們elementui的組件和原生標籤的區別是須要經過vNode.componentOptions獲取,接下來貼出完整的代碼函數
const debounce = (fn, delay=500, Switch=true) => {
let timeout = null; return (params) => { clearTimeout(timeout) if (!Switch) { return fn(params) } timeout = window.setTimeout(() => { // el-button獲取到的是數組,input獲取到的是function 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('必需要有一個子元素') 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('請傳入click方法(@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('請傳入input方法(@input)') return null } const fun = debounce(funDefault, time, Switch) vNode.data.on.input = fun } else { console.warn('僅支持input和el-button') return null } return vNode } } 複製代碼
本文使用 mdnice 排版flex