从一个开源项目看到一个自定义指令挺好的,但是vue2的,想改成vue3,但是vue3没有extend了。导致一些列问题。
自定义指令v-empty,程序如下
empty目录下
index.js
empty.vue
index.js
import Vue from 'vue'
import Empty from './empty.vue'
import {
addClass,
removeClass,
getStyle
} from 'element-ui/src/utils/dom'
const EmptyMask = Vue.extend(Empty)
/**
* xs-empty-text
* 在绑定了v-empty指令的元素上添加xs-empty-text属性,其值会被渲染为加载文案,并显示在加载图标的下方
* xs-empty-icon
* 定义了几个类型 none 就是不展示icon 其他或者无值 就是一种效果
* xs-empty-background
* 背景色
* xs-empty-custom-class
* 类选择器样式 多个以空格分开
*/
const emptyDirective = {}
emptyDirective.install = Vue => {
if (Vue.prototype.$isServer) return
const toggleEmpty = (el, binding) => {
/** 如果是数组 判断数组长度 否则 判断是否存在 当做Boolean */
if ((Object.prototype.toString.call(binding.value) === '[object Array]' && binding.value.length === 0) ||
(Object.prototype.toString.call(binding.value) !== '[object Array]' && binding.value)) {
Vue.nextTick(() => {
el.wkEmptyOriginalPosition = getStyle(el, 'position')
insertDom(el, el, binding)
})
} else { // 移除效果
el.wkEmptyVisible = false
removeClass(el, 'xs-empty-parent--relative')
removeClass(el, 'xs-empty-parent--hidden')
el.wkEmptyInstance.visible = false
}
}
const insertDom = (parent, el, binding) => {
if (!el.wkEmptyVisible && getStyle(el, 'display') !== 'none' && getStyle(el, 'visibility') !== 'hidden') {
Object.keys(el.wkEmptyMaskStyle).forEach(property => {
el.wkEmptyMask.style[property] = el.wkEmptyMaskStyle[property]
})
if (el.wkEmptyOriginalPosition !== 'absolute' && el.wkEmptyOriginalPosition !== 'fixed') {
addClass(parent, 'xs-empty-parent--relative')
}
el.wkEmptyVisible = true
parent.appendChild(el.wkEmptyMask)
Vue.nextTick(() => {
el.wkEmptyInstance.visible = true
})
el.wkEmptyInserted = true
}
}
Vue.directive('empty', {
bind: function(el, binding, vnode) {
const textExr = el.getAttribute('xs-empty-text')
const iconExr = el.getAttribute('xs-empty-icon')
const backgroundExr = el.getAttribute('xs-empty-background')
const customClassExr = el.getAttribute('xs-empty-custom-class')
const vm = vnode.context
const mask = new EmptyMask({
el: document.createElement('div'),
data: {
text: vm && vm[textExr] || textExr,
icon: vm && vm[iconExr] || iconExr,
background: vm && vm[backgroundExr] || backgroundExr,
customClass: vm && vm[customClassExr] || customClassExr
}
})
el.wkEmptyInstance = mask
el.wkEmptyMask = mask.$el
el.wkEmptyMaskStyle = {}
binding.value && toggleEmpty(el, binding)
},
update: function(el, binding) {
el.wkEmptyInstance.setText(el.getAttribute('xs-empty-text'))
el.wkEmptyInstance.setIcon(el.getAttribute('xs-empty-icon'))
if (binding.oldValue !== binding.value) {
toggleEmpty(el, binding)
}
},
unbind: function(el, binding) {
if (el.wkEmptyInserted) {
el.wkEmptyMask &&
el.wkEmptyMask.parentNode &&
el.wkEmptyMask.parentNode.removeChild(el.wkEmptyMask)
toggleEmpty(el, {
value: false,
modifiers: binding.modifiers
})
}
}
})
}
export default emptyDirective
empty.vue
<template>
<div v-show="visible" :style="{ backgroundColor: background || '' }" :class="[customClass]" class="empty-mask">
<div class="empty-content">
<img v-if="iconUrl" :src="iconUrl" class="empty-icon">
<p v-if="showText" class="empty-text">{{ showText }}</p>
</div>
</div>
</template>
<script>
export default {
data() {
return {
text: null,
background: null,
visible: false,
icon: null,
customClass: "",
};
},
computed: {
iconUrl: function () {
/** 内置几个类型 当时none的时候 不展示 */
if (this.icon) {
if (this.icon === "none") {
return "";
} else if (this.icon === "nopermission") {
return require("@/assets/img/nopermission.png");
} else {
return require("@/assets/img/empty.png");
}
} else {
return require("@/assets/img/empty.png");
}
},
showText: function () {
/** 内置几个类型 当时none的时候 不展示 */
if (this.text) {
return this.text;
} else {
return "没有找到数据";
}
},
},
methods: {
setText(text) {
if (text) {
this.text = text;
}
},
setIcon(icon) {
this.icon = icon;
},
},
};
</script>
<style lang="scss" scoped>
.empty-mask {
position: absolute;
z-index: 5;
background-color: rgba(255, 255, 255, 0.98);
margin: 0;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.empty-content {
top: 50%;
width: 100%;
text-align: center;
position: absolute;
}
.empty-text {
margin: 3px 0;
color: #aaa;
font-size: 13px;
}
.empty-icon {
display: block;
width: 150px;
margin: 0 auto 20px auto;
}
</style>
想要升级为vue3,是不是这么改
index.js
import { createVNode, render, nextTick } from "vue"
import Empty from './empty.vue'
import {
addClass,
removeClass,
getStyle
} from 'element-plus/lib/utils/dom'
import isServer from 'element-plus/lib/utils/isServer'
/**
* xs-empty-text
* 在绑定了v-empty指令的元素上添加xs-empty-text属性,其值会被渲染为加载文案,并显示在加载图标的下方
* xs-empty-icon
* 定义了几个类型 none 就是不展示icon 其他或者无值 就是一种效果
* xs-empty-background
* 背景色
* xs-empty-custom-class
* 类选择器样式 多个以空格分开
*/
const emptyDirective = {}
emptyDirective.install = app => {
if (isServer) return
const toggleEmpty = (el, binding) => {
/** 如果是数组 判断数组长度 否则 判断是否存在 当做Boolean */
if ((Object.prototype.toString.call(binding.value) === '[object Array]' && binding.value.length === 0) ||
(Object.prototype.toString.call(binding.value) !== '[object Array]' && binding.value)) {
nextTick(() => {
el.wkEmptyOriginalPosition = getStyle(el, 'position')
insertDom(el, el, binding)
})
} else { // 移除效果
el.wkEmptyVisible = false
removeClass(el, 'xs-empty-parent--relative')
removeClass(el, 'xs-empty-parent--hidden')
el.wkEmptyInstance.props.visible = false
}
}
const insertDom = (parent, el, binding) => {
if (!el.wkEmptyVisible && getStyle(el, 'display') !== 'none' && getStyle(el, 'visibility') !== 'hidden') {
Object.keys(el.wkEmptyMaskStyle).forEach(property => {
el.wkEmptyMask.style[property] = el.wkEmptyMaskStyle[property]
})
if (el.wkEmptyOriginalPosition !== 'absolute' && el.wkEmptyOriginalPosition !== 'fixed') {
addClass(parent, 'xs-empty-parent--relative')
}
el.wkEmptyVisible = true
parent.appendChild(el.wkEmptyMask)
nextTick(() => {
// 这里修改没生效
el.wkEmptyInstance.props.visible = true
})
el.wkEmptyInserted = true
}
}
app.directive('empty', {
beforeMount: function (el, binding, vnode) {
const textExr = el.getAttribute('xs-empty-text')
const iconExr = el.getAttribute('xs-empty-icon')
const backgroundExr = el.getAttribute('xs-empty-background')
const customClassExr = el.getAttribute('xs-empty-custom-class')
const vm = binding.instance
const div = document.createElement('div')
const mask = createVNode(Empty, {
text: vm && vm[textExr] || textExr,
icon: vm && vm[iconExr] || iconExr,
background: vm && vm[backgroundExr] || backgroundExr,
customClass: vm && vm[customClassExr] || customClassExr
})
// mask.appContext = app._context
render(mask, div)
el.wkEmptyInstance = mask
el.wkEmptyMask = mask.el
el.wkEmptyMaskStyle = {}
binding.value && toggleEmpty(el, binding)
},
updated: function (el, binding) {
el.wkEmptyInstance.setText(el.getAttribute('xs-empty-text'))
el.wkEmptyInstance.setIcon(el.getAttribute('xs-empty-icon'))
if (binding.oldValue !== binding.value) {
toggleEmpty(el, binding)
}
},
unmounted: function (el, binding) {
if (el.wkEmptyInserted) {
el.wkEmptyMask &&
el.wkEmptyMask.parentNode &&
el.wkEmptyMask.parentNode.removeChild(el.wkEmptyMask)
toggleEmpty(el, {
value: false,
modifiers: binding.modifiers
})
}
}
})
}
export default emptyDirective
empty.vue
<template>
<div v-show="maskShow" :style="{ backgroundColor: background || '' }" :class="[customClass]" class="empty-mask">
<div class="empty-content">
<img v-if="iconUrl" :src="iconUrl" class="empty-icon">
<p v-if="showText" class="empty-text">{{ showText }}</p>
</div>
</div>
</template>
<script>
export default {
data() {
return {
maskShow: false
}
},
props: {
text: {
type: String,
default: null,
},
background: {
type: String,
default: null,
},
visible: {
type: Boolean,
default: false,
},
icon: {
type: String,
default: null,
},
customClass: {
type: String,
default: "",
},
},
computed: {
iconUrl: function () {
/** 内置几个类型 当时none的时候 不展示 */
if (this.icon) {
if (this.icon === "none") {
return "";
} else if (this.icon === "nopermission") {
return require("@/assets/img/nopermission.png");
} else {
return require("@/assets/img/empty.png");
}
} else {
return require("@/assets/img/empty.png");
}
},
showText: function () {
/** 内置几个类型 当时none的时候 不展示 */
if (this.text) {
return this.text;
} else {
return "没有找到数据";
}
},
},
watch: {
visible: {
immediate: true,
handler(val) {
console.log('empty props visible', val)
this.maskShow = val
}
},
},
methods: {
setText(text) {
if (text) {
// this.text = text;
}
},
setIcon(icon) {
// this.icon = icon;
},
},
};
</script>
<style lang="scss" scoped>
.empty-mask {
position: absolute;
z-index: 5;
background-color: rgba(255, 255, 255, 0.98);
margin: 0;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.empty-content {
top: 50%;
width: 100%;
text-align: center;
position: absolute;
}
.empty-text {
margin: 3px 0;
color: #aaa;
font-size: 13px;
}
.empty-icon {
display: block;
width: 150px;
margin: 0 auto 20px auto;
}
</style>
但是这么改后,一直没生效主要是该指令是extend一个empty模板后使用,但是没有extend,所以使用了createVNode来生成一个VNode,传入了props但是怎么修改这些props呢?