您现在的位置是:首页 >学无止境 >vue3到vue2组件重构方法笔记网站首页学无止境

vue3到vue2组件重构方法笔记

山雀~ 2024-08-31 12:01:03
简介vue3到vue2组件重构方法笔记

这两天的任务是把一批做好的vue3组件放在vue2项目中使用,将组合式api分散开有一些零散的技巧,所以写一篇转化笔记以供大家参考

先上vue3一个组件的示例代码

<template>
  <div ref="GForms" :style="{background: props.background, border: '1px solid ' + props.borderColor, 'backdrop-filter': props.backBlur? 'blur(10px)': 'blur(0px)'}" style="min-width: 200px; min-height: 60px; padding: 0; margin: 0; position: relative; transition: width .8s, height .8s;">
    <!-- 四个角 -->
    <div v-if="!props.corner" :style="{background: props.cornerColor}" style="position: absolute; left: -1px; top: -1px; width: calc(100% + 2px); height: 2px;"></div>
    <div v-if="props.corner" style="position: absolute; left: -1px; top: -1px; transform: rotateZ(0deg);">
      <div :style="{background: props.cornerColor}" style="width: 6px; height: 2px; position: absolute;"></div>
      <div :style="{background: props.cornerColor}" style="width: 6px; height: 2px; position: absolute; transform: rotateZ(90deg); left: -2px; top: 2px;"></div>
    </div>
    <div style="position: absolute; left: -1px; bottom: -1px; transform: rotateZ(270deg);">
      <div :style="{background: props.cornerColor}" style="width: 6px; height: 2px; position: absolute;"></div>
      <div :style="{background: props.cornerColor}" style="width: 6px; height: 2px; position: absolute; transform: rotateZ(90deg); left: -2px; top: 2px;"></div>
    </div>
    <div v-if="props.corner" style="position: absolute; right: -1px; top: -1px; transform: rotateZ(90deg);">
      <div :style="{background: props.cornerColor}" style="width: 6px; height: 2px; position: absolute;"></div>
      <div :style="{background: props.cornerColor}" style="width: 6px; height: 2px; position: absolute; transform: rotateZ(90deg); left: -2px; top: 2px;"></div>
    </div>
    <div style="position: absolute; right: -1px; bottom: -1px; transform: rotateZ(-180deg);">
      <div :style="{background: props.cornerColor}" style="width: 6px; height: 2px; position: absolute;"></div>
      <div :style="{background: props.cornerColor}" style="width: 6px; height: 2px; position: absolute; transform: rotateZ(90deg); left: -2px; top: 2px;"></div>
    </div>
    <!-- 标题栏 @mouseup="gFormsMouseup" @mousemove="gFormsMousemove"-->
    <div v-if="props.showTitle" @mousedown="gFormsMousedown" :style="{cursor: props.move? 'move': 'default'}" style="user-select: none; padding: 4px 6px; display: flex; flex-direction: row; flex-wrap: nowrap; justify-content: space-between; align-items: center;">
      <div style="margin-top: 2px; display: flex; flex-direction: row; flex-wrap: nowrap; align-items: center;">
        <div style="font-size: 16px; font-weight: bold; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">{{props.title}}</div>
        <img :src="data.icon" alt="logo" style="margin: -3px 0 0 3px;"/>
      </div>
      <div @click.stop="gFormsCloseWindows" class="g-forms-close-btn" style="position: relative; width: 22px; height: 22px;">
        <div class="g-forms-close-btn-bg" style="position: relative; width: 20px; height: 20px; margin: 1px;">
          <div style="position: absolute; width: 14px; height: 2px; transform: rotateZ(45deg); transform-origin: center 50%; background: #fff; top: 9px; left: 3px;"></div>
          <div style="position: absolute; width: 14px; height: 2px; transform: rotateZ(-45deg); transform-origin: center 50%; background: #fff; top: 9px; left: 3px;"></div>
        </div>
        <div style="position: absolute; left: -1px; top: -1px; width: 2px; height: 2px; background: #fff;"></div>
        <div style="position: absolute; left: -1px; bottom: -1px; width: 2px; height: 2px; background: #fff;"></div>
        <div style="position: absolute; right: -1px; top: -1px; width: 2px; height: 2px; background: #fff;"></div>
        <div style="position: absolute; right: -1px; bottom: -1px; width: 2px; height: 2px; background: #fff;"></div>
      </div>
    </div>
    <!-- 分割条 -->
    <div v-if="props.showTitle" style="display: flex; flex-direction: row; flex-wrap: nowrap; align-items: center; padding: 0 4px;">
      <div style="width: 90%; background: #4F728E; height: 3px;"></div>
      <div style="width: 10%; background: #DEE3E9; height: 3px; margin-left: 2px;"></div>
    </div>
    <slot></slot>
  </div>
</template>

<script lang="ts" setup>
// 边框
import { defineProps, reactive, defineEmits, ref, onMounted } from 'vue'

const props = defineProps({
  title: { type: String, default: "G-Forms" }, // 窗口标题
  showTitle: { type: Boolean, default: true }, // 显示标题
  move: { type: Boolean, default: true }, // 是否可以移动
  inArea: { type: Boolean, default: true }, // 是否在区域内移动
  corner: { type: Boolean, default: true }, // 四个角是否为尖角
  cornerColor: { type: String, default: "#FFF" }, // 四角装饰颜色
  backBlur: { type: Boolean, default: true }, // 开启背景模糊
  background: { type: String, default: "#001820ab" }, // 背景颜色
  borderColor: { type: String, default: "#2F739A" }, // 边框线条颜色
})

const GForms = ref<HTMLElement | null>()
let gflag = ref(false)
const data = reactive({
  icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAAAZCAYAAAC2JufVAAABYElEQVRIx2P4//8/w2DDDKOOGnXUiHKUqW/cAyD+j4Qb5m478QCI/yPhBkrU/b/k+wCI/yPhBkKOckAz5H/5hEUpaJb9jy5uSSFX3bZFwSlojgJhA7zRB9Q4Ac2gC0ALJqBZeIESdUBHTEBz1AVCjhJAD3bHyKwO9OiZuGpPB7nqfGNjOghFI0Z8DoZoxJr6BzoacTlqQKMRZ1kB1JiAHuxACxLQo4cSdUBHJGBEI4GQ+oBsgFN0TiXQ8A/IFk1YsauSXHX+cTGVQEd8QHNUAT5HHUDz1Qag4QfQfL+BEnVABxxAc9AGfLmvAc2AB9M3HOpAs+iBdXBKB7nqPp/y60BzEChtCeAqpwLQDPhQN31lIZpFH0JyagvJVXdkdVAhmoM+oJfqyA5SQI/3oKzqEvT00TBzdQm56nKKIkuwpKMEfHXfBTRfLQCVMWi+X0CJOnBZhOqgBaPtqVFHjVhHAQDOW7xzUODHCgAAAABJRU5ErkJggg==",
  // 鼠标位置
  gfMX: 0,
  gfMY: 0,
  // 偏移量
  deltaX: 0,
  deltaY: 0,
  // 页面尺寸
  pageW: 0,
  pageH: 0
})

const emit = defineEmits(['close'])

onMounted(() => {
  data.pageW = window.innerWidth
  data.pageH = window.innerHeight
  window.onresize = () => {
    data.pageW = window.innerWidth
    data.pageH = window.innerHeight
  }
  if (data.deltaX != 0 || data.deltaY != 0) setPosition()
})

// 设置窗体位置
const setPosition = () => setTimeout(() => {
  if (GForms.value == null || GForms.value == undefined) return;
  if (props.inArea) {
    if (data.deltaX < 1) data.deltaX = 1
    if (data.deltaY < 80) data.deltaY = 80
    if (data.deltaX + GForms.value?.offsetWidth > data.pageW) data.deltaX = data.pageW - GForms.value?.offsetWidth
    if (data.deltaY + GForms.value?.offsetHeight > data.pageH) data.deltaY = data.pageH - GForms.value?.offsetHeight
  }
  GForms.value.style.top = data.deltaY + "px"
  GForms.value.style.left = data.deltaX + "px"
}, 0)

// 关闭方法
const gFormsCloseWindows = () => {
  emit('close')
}
// 窗体移动方法
const gFormsMousedown = (e:any) => {
  if (!props.move) return

  gflag.value = true
  data.gfMX = e.offsetX
  data.gfMY = e.offsetY
  // 防抖
  if (data.deltaX != 0 || data.deltaY != 0) setPosition()
  // 移动过程
  document.onmousemove = (e: any) => {
    if (!gflag.value) return;
    data.deltaX = e.clientX - data.gfMX
    data.deltaY = e.clientY - data.gfMY
    setPosition()
  }
  // 鼠标抬起
  document.onmouseup = (e: any) => {
    setPosition()
    gflag.value = false
    document.onmousemove = null
    document.onmouseup = null
  }
}


</script>

<style scoped>

.g-forms-close-btn-bg {
  background: #2E454C;
}
.g-forms-close-btn:hover .g-forms-close-btn-bg {
  background: #4b96a3;
  cursor: pointer;
}
.g-forms-close-btn:active .g-forms-close-btn-bg {
  background: #2E454C;
  cursor: pointer;
}

.div-l-shape-s {
  border-style: solid;
  border-width: 0 0 3px 3px;
  position: relative;
  right: -3px;
  top: -3px;
  background: white;
  width: 4em;
  height: 4em;
}
</style>

上面是其中一个vue3组件样式表,在vue3的项目中展示效果为下图所示:
在这里插入图片描述
组件中还有一些其他的功能都要完整保留,所以现在开始整理:

要将 Vue 3 组件转换为 Vue 2 组件,需要注意一些语法和功能上的差异。下面是我总结的 Vue 3 组件转换为 Vue 2 组件的一般步骤:
1、首先是template里的内容,可以看到在vue3模板里使用属性要这么写:

:style="{background: props.background}" //取出在props里面的值

在 Vue 3 中,使用 props 选项来声明组件的属性,而Vue 3 中的 props 默认是不响应式的,所以可以看到代码中套了一层defineProps方法,defineProps 方法是 Vue 3 中用于声明组件属性的函数。它的作用是定义组件接收的属性,并使其在组件内部成为响应式的。

而在vue2中可以直接使用props里面的值,语法为下面代码:

:style="{background: background}"
export default {
props: {
      title: { type: String, default: "G-Forms" }, // 窗口标题
      showTitle: { type: Boolean, default: true }, // 显示标题
      move: { type: Boolean, default: true }, // 是否可以移动
      inArea: { type: Boolean, default: true }, // 是否在区域内移动
      corner: { type: Boolean, default: true }, // 四个角是否为尖角
      cornerColor: { type: String, default: "#FFF" }, // 四角装饰颜色
      backBlur: { type: Boolean, default: true }, // 开启背景模糊
      background: { type: String, default: "#001820ab" }, // 背景颜色
      borderColor: { type: String, default: "#2F739A" }, // 边框线条颜色
    },
}

因为里面的数据都是可以直接使用的

2、在vue2中要去掉<script lang="ts" setup>里面的lang="ts" setup
书写vue2中的逻辑
删掉import { defineProps, reactive, defineEmits, ref, onMounted } from 'vue'这类语句因为不需要

3、在vue3中,data的书写方式为下面代码:

const data = reactive({
  icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAAAZCAYAAAC2JufVAAABYElEQVRIx2P4//8/w2DDDKOOGnXUiHKUqW/cAyD+j4Qb5m478QCI/yPhBkrU/b/k+wCI/yPhBkKOckAz5H/5hEUpaJb9jy5uSSFX3bZFwSlojgJhA7zRB9Q4Ac2gC0ALJqBZeIESdUBHTEBz1AVCjhJAD3bHyKwO9OiZuGpPB7nqfGNjOghFI0Z8DoZoxJr6BzoacTlqQKMRZ1kB1JiAHuxACxLQo4cSdUBHJGBEI4GQ+oBsgFN0TiXQ8A/IFk1YsauSXHX+cTGVQEd8QHNUAT5HHUDz1Qag4QfQfL+BEnVABxxAc9AGfLmvAc2AB9M3HOpAs+iBdXBKB7nqPp/y60BzEChtCeAqpwLQDPhQN31lIZpFH0JyagvJVXdkdVAhmoM+oJfqyA5SQI/3oKzqEvT00TBzdQm56nKKIkuwpKMEfHXfBTRfLQCVMWi+X0CJOnBZhOqgBaPtqVFHjVhHAQDOW7xzUODHCgAAAABJRU5ErkJggg==",
  // 鼠标位置
  gfMX: 0,
  gfMY: 0,
  // 偏移量
  deltaX: 0,
  deltaY: 0,
  // 页面尺寸
  pageW: 0,
  pageH: 0
})

但是在vue2中

data() {
      return {
        icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACUAAAAZCAYAAAC2JufVAAABYElEQVRIx2P4//8/w2DDDKOOGnXUiHKUqW/cAyD+j4Qb5m478QCI/yPhBkrU/b/k+wCI/yPhBkKOckAz5H/5hEUpaJb9jy5uSSFX3bZFwSlojgJhA7zRB9Q4Ac2gC0ALJqBZeIESdUBHTEBz1AVCjhJAD3bHyKwO9OiZuGpPB7nqfGNjOghFI0Z8DoZoxJr6BzoacTlqQKMRZ1kB1JiAHuxACxLQo4cSdUBHJGBEI4GQ+oBsgFN0TiXQ8A/IFk1YsauSXHX+cTGVQEd8QHNUAT5HHUDz1Qag4QfQfL+BEnVABxxAc9AGfLmvAc2AB9M3HOpAs+iBdXBKB7nqPp/y60BzEChtCeAqpwLQDPhQN31lIZpFH0JyagvJVXdkdVAhmoM+oJfqyA5SQI/3oKzqEvT00TBzdQm56nKKIkuwpKMEfHXfBTRfLQCVMWi+X0CJOnBZhOqgBaPtqVFHjVhHAQDOW7xzUODHCgAAAABJRU5ErkJggg==",
        // 鼠标位置
        gfMX: 0,
        gfMY: 0,
        // 偏移量
        deltaX: 0,
        deltaY: 0,
        // 页面尺寸
        pageW: 0,
        pageH: 0,
        gflag:false,
        GFormsRef:0,
        gFormsMouseup: false,
      };
    },

利用函数式的data存放数据

4、const GForms = ref<HTMLElement | null>()
这是vue3组件中的一句代码,这句话的意思是创建一个响应式引用(ref),其初始值为 null,类型为 HTMLElement 或 null。

在 Vue 3 中,ref 是一个用于创建响应式数据的函数。它接受一个初始值作为参数,并返回一个包含 .value 属性的响应式引用对象。.value 属性用于访问和修改引用的值。当引用的值发生变化时,相关的组件会自动重新渲染。

在上述代码中,GForms 是一个名为 GForms 的变量,它是一个响应式引用。我们可以使用 GForms.value 来访问和修改它的值。在初始状态下,GForms.value 的值为 null。可以通过将其赋值为一个 HTMLElement 对象来将其引用到特定的 DOM 元素。

通过使用 ref 创建的响应式引用,可以轻松地追踪和响应 DOM 元素的变化,并在 Vue 3 组件中进行操作。

而在vue2中,这句话要被拆分为几部分
1、在组件的 data 选项中定义一个名为 GForms 的属性,并将其初始值设置为 null。例如:

data() {
  return {
    GForms: null,
  };
},

2、在模板中使用 ref 属性将 GForms 和相应的 DOM 元素进行绑定。例如:

<div ref="GForms">...</div>

3、在组件的生命周期钩子函数(例如 mounted)中,使用 $refs 来访问 GForms 引用的 DOM 元素,并将其赋值给组件的 GForms 属性。例如:

mounted() {
  this.GForms = this.$refs.GForms;
},

在这样的转换后,您可以在组件中使用 this.GForms 来访问和操作引用的 DOM 元素。

4、vue3传值

const emit = defineEmits(['close'])

在Vue 3中,const emit = defineEmits(['close'])这句话的作用是定义一个名为emit的常量,它使用defineEmits函数来声明组件的自定义事件。

defineEmits是Vue 3中的一个全局辅助函数,用于声明组件可以触发的自定义事件。通过传递一个字符串数组,你可以定义组件可以发出的事件名称。

在我的例子中,['close']是一个包含单个字符串'close'的数组,它表示组件可以触发名为'close'的自定义事件。通过使用defineEmits(['close']),可以确保组件在使用$emit方法触发’close’事件时,不会产生警告。

在组件中,可以使用emit常量来触发声明的自定义事件,例如:

<script>
import { defineEmits } from 'vue';

const emit = defineEmits(['close']);

export default {
  methods: {
    handleClose() {
      // 触发 'close' 事件
      emit('close');
    },
  },
};
</script>

在上述代码中,handleClose方法通过调用emit('close')来触发’close’事件。这样,其他使用这个组件的地方可以监听该事件并执行相应的逻辑。
在我的代码中:

 <div @click.stop="gFormsCloseWindows"/>

点击触发gFormsCloseWindows函数,

const gFormsCloseWindows = () => {
  emit('close')
}

这样的话就可以在组件外的父组件上进行接收close,
在app组件中

@close="handleClose"

之后在data中定义

  data() {
    return {
      showChildComponent: true
    };
  },

在methods中定义

methods: {
    handleClose() {
      // 执行关闭操作
      this.showChildComponent = false;
    }
  }

前提是在gForm组件中要预留

v-if="showChildComponent"

就可以实现此功能

5、生命周期转化
在vue3中

onMounted(() => {
  data.pageW = window.innerWidth
  data.pageH = window.innerHeight
  window.onresize = () => {
    data.pageW = window.innerWidth
    data.pageH = window.innerHeight
  }
  if (data.deltaX != 0 || data.deltaY != 0) setPosition()
})

而在vue2中进行相应的转化

mounted() {
      this.pageW = window.innerWidth;
      this.pageH = window.innerHeight;
      window.onresize = () => {
        this.pageW = window.innerWidth;
        this.pageH = window.innerHeight;
      };
      if (this.deltaX !== 0 || this.deltaY !== 0) {
        this.setPosition();
      }
    },

6、methods转化
vue3代码示例

// 窗体移动方法
const gFormsMousedown = (e:any) => {
  if (!props.move) return

  gflag.value = true
  data.gfMX = e.offsetX
  data.gfMY = e.offsetY
  // 防抖
  if (data.deltaX != 0 || data.deltaY != 0) setPosition()
  // 移动过程
  document.onmousemove = (e: any) => {
    if (!gflag.value) return;
    data.deltaX = e.clientX - data.gfMX
    data.deltaY = e.clientY - data.gfMY
    setPosition()
  }
  // 鼠标抬起
  document.onmouseup = (e: any) => {
    setPosition()
    gflag.value = false
    document.onmousemove = null
    document.onmouseup = null
  }
}

转化为vue2

methods: {
      gFormsCloseWindows() {
        this.$emit('close');
      },
      // 设置窗体位置
        setPosition() {
          const GFormsRef = this.$refs.GForms;
            setTimeout(() => {
              if (this.GFormsRef == null || this.GFormsRef == undefined) return;
              // 在判断时应该使用 this.GFormsRef,而不是 this.GForms

              if (this.inArea) {
                if (this.deltaX < 1) this.deltaX = 1;
                if (this.deltaY < 1) this.deltaY = 1;
                if (this.deltaX + this.GFormsRef.offsetWidth > this.pageW) {
                  this.deltaX = this.pageW - this.GFormsRef.offsetWidth;
                }
                if (this.deltaY + this.GFormsRef.offsetHeight > this.pageH) {
                  this.deltaY = this.pageH - this.GFormsRef.offsetHeight;
                }
              }

              GFormsRef.style.top = this.deltaY-60 + "px";
              GFormsRef.style.left = this.deltaX + "px";
              // 注意使用 this.GFormsRef.style 来设置元素的样式,而不是 this.GForms.style
            },0);
          },

          // 窗体移动方法
          gFormsMousedown(e) {
            if (!this.move) return;

            this.gflag = true;
            this.gfMX = e.offsetX;
            this.gfMY = e.offsetY;
            // 防抖
            if (this.deltaX !== 0 || this.deltaY !== 0) {
              this.setPosition();
            }
            // 移动过程
            document.onmousemove = (e) => {
              if (!this.gflag) return;
              // console.log(e.clientX);
              // console.log(e.clientY);
              // e.clientX表示鼠标当前的水平坐标
              this.deltaX = e.clientX - this.gfMX;
              this.deltaY = e.clientY - this.gfMY;
              this.setPosition();
            }
            // 鼠标松开
          },
          handleMouseUp() {
            this.setPosition();
            this.gflag = false;
            document.onmousemove = null;
            document.onmouseup = null;
          }
      },
}

大致就是这些,其实熟悉了之后组件之间相互转化就很快了,希望对大家有所帮助

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。