❝随着技术的快速发展,前端为了快速开发,咱们通常会接入像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