Vue 组件二次封装透传slots、refs、attrs、listeners
最近写了一个开源项目,有些地方需要二次封装,需要透传一些数据,需要注意的是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, // 是否启用ocrisEnableWaterMarker: 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 }"><ocrResultv-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
没有做处理,不想 react
有 forwardRef
,我的处理方式,提前已知组件暴露出什么数据,遍历获取,反之全部遍历获取:
// 透传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
拿到节点compire
为 AST
,进而打标记最后生成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, // 添加 $listenersprops: {isEnableOcr: this.isEnableOcr,isEnableWaterMarker: this.isEnableWaterMarker,waterMarkerContent: this.waterMarkerContent},scopedSlots})}