vue3的h函数创建的VNode怎么修改props

从一个开源项目看到一个自定义指令挺好的,但是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呢?

阅读 3.1k
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题