• Vue3 封装第三方组件(一)做一个合格的传声筒


    各种UI库的功能都是非常强大的,尤其对于我这种不会 css 的人来说,就更是帮了大忙了。

    只是嘛,如果再封装一下的话,那么用起来就会更方便了。

    那么如何封装呢?

    封装三要素 —— 属性、插槽、事件、方法

    可以封装,但是原生UI库提供的强大功能不能给封装没了吧,吃了回扣可是不好滴。
    那么如何做到不遗漏呢?先做一个合格的传声筒。

    传递属性

    先看看 el-input 提供的属性:

    el-input的属性

    太长了,这里只截了一半。
    这么多的属性,如果一个一个都弄到 props 里面,然后再一个一个绑定上去,这就太麻烦了。

    我们可以分成两部分,重要的属性做到 props 里面,其他的可以放到 $attrs 里面。

    定义一个简单的组件

    模板

    <template>
      <div>
        <el-input
          v-model="value" // 不能直接帮的属性
          v-bind="$attrs"  // 绑定其他属性。
        >
        </el-input> 
      </div>
    </template>
    

    代码

    export default {
      name: 'test-text',
      inheritAttrs: false,
      props: {
        modelValue: [String, Number]
      },
      setup(props, ctx) {
        const value = debounceRef(props, ctx.emit)
    
        return {
          value
        }
      }
    }
    

    父组件的调用代码:
    模板

    <inputtext
        v-model="value"
        v-bind="attrs"
      >
      </inputtext>
    

    代码

    const value = ref('222')
    
    const attrs = reactive({
      maxlength: 10,
      'show-word-limit': true,
      clearable: true
    })
    

    这里 modelValue 就是 props ,maxlength、show-word-limit、clearable 就变成了 $attrs 。

    然后要看 el-input 是否是根元素,如果是跟元素的话,那么会自动绑定上,不需要我们手动写 v-bind="$attrs"

    如果像上面的例子不是根元素的话,需要手动写 v-bind="$attrs"

    inheritAttrs

    这个是指定组件是否自动绑定 $attrs 。
    默认是 true,会自动把 $attrs 绑定到根元素上面,不管根元素是啥。
    这里设置为 false,那么$attrs 就不会自动绑定到 div 上面了。

    插槽

    这个稍微复杂一点,插槽本来就有一点绕,官网的介绍又比较含混。

    我们可以找到 $slots 这个东东,但是官网的介绍(https://www.vue3js.cn/docs/zh/api/instance-properties.html#slots )却是 使用 h,这个就……

    不过想要传递插槽,还是需要这个。

    我们先看看 el-input 的插槽的使用。

      <el-input
        placeholder="请输入内容"
        v-model="input3"
        class="input-with-select"
      >
        <template #prepend>
          <el-select v-model="select" placeholder="请选择">
            <el-option label="餐厅名" value="1"></el-option>
            <el-option label="订单号" value="2"></el-option>
            <el-option label="用户电话" value="3"></el-option>
          </el-select>
        </template>
        <template #append>
          <el-button icon="el-icon-search"></el-button>
        </template>
      </el-input>
    

    那么想要传递插槽的话,是不是可以这样?

       <!--传递插槽-->
        <template  v-slot:prepend> // 给递给el-input 的插槽
          <slot name="prepend"></slot> // 接收父组件传递进来的插槽
        </template> 
    

    测试可以。

    那么总不会一个一个写吧,这也太麻烦了。如果能够for就好了。

    等等, for?那么我们是不是可以这样。

      <!--传递插槽-->
      <template 
        v-for="(item, key, index) in $slots"
          :key="index"
          v-slot:[key]
      >
          <slot :name="key"></slot>
      </template>  
    

    测试可以。

    完整代码

        <el-input
          ref="refInput"
          v-model="value"
          v-bind="$attrs"
        >
          <!--传递插槽-->
          <template 
            v-for="(item, key, index) in $slots"
            :key="index"
            v-slot:[key]
          >
            <slot :name="key"></slot>
          </template> 
        </el-input> 
    

    传递事件

    这个就简单了,啥都不用做,自动就传递出去了。el-input 是否是跟元素都可以。
    测试一下:

     <inputtext
        ref="refInput"
        v-model="value"
        v-bind="props"
        @clear="clear"
        @my-change="myChange"
      >
    
    • clear 是 el-input 提供的事件,外部可以直接得到这个事件,组件内部不用做操作。
    • my-change 是自定义的事件。

    方法

    一直都忽略了,还有方法这个事,因为基本没用过。

    使用方法嘛,就需要使用 ref,这个此 ref 非彼 ref,说不清了,还是写代码吧。

    直接使用的方法

    直接使用UI库组件的方法,比如 el-input 的 提供的 select:

    el-input的方法

    <el-input
      ref="refInput" // 注意这里的 ref 
      v-model="value"
      v-bind="$attrs" >
    </el-input> 
    

    ref 的写法,不要加冒号。

    const refInput = ref(null) // 先放一个null
    onMounted(() => { // 然后在 onMounted 里面才能得到值。
      console.log('refinput', refInput) // 看看啥样。
      refInput.value.select() // 调用方法,文本框的内容会被选中
    })
    

    先定义一个 ref,然后交给模板里的 ref,好像有点绕,这里必须使用 ref,reactive是不行滴。

    在渲染后才能生效,也就是说必须在 onMounted 里面才能得到值,我们看看打印结果:(太长只能截取一部分)

    ref

    很长吧。

    父组件里面怎么用方法

    <inputtext
        ref="refInput"
        v-model="value">
    </el-input> 
    
    // 测试方法
    const refInput = ref(null)
    
    onMounted(() => {
      console.log('refinput', refInput)
      // refInput.value.$refs.refInput.select()
      refInput.value.refInput.select()
    })
    

    父组件里面的用法是一样的,只是需要再套一层,才能拿到自定义组件内部的UI库组件。

    看看结构:
    父组件调用方法一

    太长了,还在下面。

    父组件调用方法二

    这个就比较近了。

    话说为啥弄得这么多属性和方法事件呀?

    父组件调用子组件内部的方法

    上面那种方式,还可以让父组件调用子组件内部定义的方法,比如内部定义一个

       const setInput = () => {
          value.value = new Date()
        }
    

    父组件可以这样调用

    refInput.value.setInput()
    

    总结

    其实事件和方法,并没有封装,而是直接就可以使用的。
    这是 element-plus 测试的结果,其他UI库没有测试。

    插槽需要写一个 v-for 就可以实现传递,而且是通用的代码。
    属性 就需要规划一下了,看设计要求,哪些放在 props里面,哪些放在attrs 里面。

  • 相关阅读:
    关于金蝶K3服务器更换硬件重新注册问题
    vCenter 忘记网页登录密码 / 重置密码
    首次使用docker有感
    NBI可视化平台快速入门教程(一)数据准备
    Mac版VSCode安装教程
    mysql学习笔记(二)
    mysql学习笔记(三)
    mysql学习笔记(四)
    vs版本对应关系
    TensorRT 记录
  • 原文地址:https://www.cnblogs.com/jyk/p/14669630.html
Copyright © 2020-2023  润新知