最近寫了一個開源項目,有些地方需要二次封裝,需要透傳一些數據,需要注意的是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})}