Vue 组件二次封装透传slots、refs、attrs、listeners

发布于:2025-08-17 ⋅ 阅读:(13) ⋅ 点赞:(0)

最近写了一个开源项目,有些地方需要二次封装,需要透传一些数据,需要注意的是ref,我这里使用俩种方式间接传递ref,具体如下:

使用:

import VideoPlayer from './index.js'

Vue.use(VideoPlayer)

index.js因为是Vue 2.7 老项目,所以这里用Vue.extend构造函数构造组件这里和 Vue3 不太一样,Vue3 要用 defineComponent创建组件支持hook式和选项式,废弃了Vue.extend(),这里属性继承用$attrs, 事件用 $listeners(Vue3 不需要,在attrs里面),插槽这里固定指定了一个ocr作用域插槽,refs后面说

import Player from "video-player"
import OcrResult from "@/components/ocrResult"
import Vue from "vue"

let P = Vue.extend({
	data() { 
		return { 
			isEnableOcr: true, // 是否启用ocr
			isEnableWaterMarker: true, // 是否启动水印
			waterMarkerContent: '水印' // 水印内容
	 	} 
	}template: `
	    <Player 
	        ref="player"
	        v-bind="$attrs"
	        v-on="$listeners"
	        :isEnableOcr="isEnableOcr"
	        :isEnableWaterMarker="isEnableWaterMarker"
	        :waterMarkerContent="waterMarkerContent">
	      <template v-if="isEnableOcr" #ocr="{ show_ocr, curPlayTime, cancel }">
	        <ocrResult
	            v-model="show_ocr"
	            :curPlayTime="curPlayTime * 1000"
	            @cancel="cancel"/>
	      </template>
	    </Player >`,
})

export default {
    install(Vue) {
        Vue.component('VideoPlayer', VideoPlayer)
    }
}

这里如果涉及到多个slot插槽,利用#[name]动态插槽,scope返回作用域插槽数据:

 <template v-for="(_, name) in $slots" #[name]="scope">
   <slot :name="name" v-bind="scope"></slot>
 </template>

这里涉及到 refs,因为 refs``Vue没有做处理,不想 reactforwardRef,我的处理方式,提前已知组件暴露出什么数据,遍历获取,反之全部遍历获取:

// 透传ref属性,这里劫持下,不然会报错
let attributes = ['_register_emits', 'current', 'duration']

attributes.forEach(key => {
  Object.defineProperty(this, key, {
    get() {
      return Reflect.get(this.$refs.player, key, this)
    },
    set(v) {
      throw new Error('Not Allow!')
    }
  })
})

// for (let key in this.$refs.player) {
//   this[key] = this.$refs.player[key]
// }

还有一种方式,利用render函数渲染 vnode,这样做少去了编译过程,Vue拿到节点compireAST,进而打标记最后生成render函数,render函数渲染时当前利用Vue.extend()生成的组件实例会自动绑定this,而template不会这么做

render(h) {
    let scopedSlots = {}
    if (this.isEnableOcr) {
      scopedSlots.ocr = ({ show_ocr, curPlayTime, cancel }) => {
        return h(ocrResult, {
          props: {
            value: show_ocr,
            curPlayTime: curPlayTime * 1000
          },
          on: {
            input: (val) => { show_ocr = val; },
            cancel
          }
        });
      };
    }
    return h(Player, {
      attrs: this.$attrs,
      on: this.$listeners, // 添加 $listeners
      props: {
        isEnableOcr: this.isEnableOcr,
        isEnableWaterMarker: this.isEnableWaterMarker,
        waterMarkerContent: this.waterMarkerContent
      },
      scopedSlots
    })
  }

网站公告

今日签到

点亮在社区的每一天
去签到