在 Vue3 的项目中使用了 JS 对象的 setter 和 getter 属性,打包后失效是怎么回事?
问题概述:
由于前端 Vue 组件需要绑定 v-model 的数据格式和接口返回和上传的格式不一样,为了方便使数据“双向同步”,我想到使用 JS 对象的 setter 和 getter 属性来实现,事实证明这也是很方便的.
一切在我本地开发时都正常,最近打包后发布在测试环境后发现出了 Bug,好像是 setter 和 getter 没有生效. 
如果打包前正常,打包后出了 Bug 是不是打包这里出问题了呢,所以想问问有没有了解 Vue 和 Vite 打包机制的大佬,看能不能帮忙定位下问题.
开发环境:
使用了 Vue3 开发,并配套使用了 Vite.js 打包(被Vue主页各种忽悠使用的)
Vue 3.0.5
Vite 2.1.5
element-plus: ^1.0.2-beta.39
涉案代码:
组件使用了 ElementUI 的 time-picker 的 range 模式,v-model 需要绑定 Date 数组.
<el-form-item prop="_timeRange" label="营业时间:">    <el-time-picker is-range 
        v-model="localForm._timeRange" 
        range-separator="至" start-placeholder="开始时间"
        end-placeholder="结束时间" 
        placeholder="选择时间范围">
    </el-time-picker>
</el-form-item>
服务端返回和要求上传的数据格式中,这两个时间是两个字符串字段:
{  "openingTimeEnd": "string",
  "openingTimeStart": "string"
}
于是我从服务端获取表单后,做了如下处理:
const localForm = {    ...form,
    set _timeRange(value) {
      console.log(value)
      this.openingTimeStart = moment(value[0]).format('YYYY-MM-DD HH:mm:ss')
      this.openingTimeEnd = moment(value[1]).format('YYYY-MM-DD HH:mm:ss')
    },
    get _timeRange() {
      this.openingTimeEnd = this.openingTimeEnd || "1949-10-01 17:00:00"
      this.openingTimeStart = this.openingTimeStart || "1949-10-01 09:00:00"
      return [new Date(this.openingTimeStart), new Date(this.openingTimeEnd)]
    },
}
这样一来,每当修改 _timeRange 的时候,都会自动设置 localForm 中的 openingTimeStart 和 openingTimeEnd 字段.
这一效果在我本地调试时也是正常的,但打包后就失效了,表现为我设置时间后,openingTimeStart 和 openingTimeEnd 字段并不更新
请教!
这个 Bug 我没有头绪,难道要放弃这种思路,然后去监听事件手动修改字段吗,请大佬们不吝赐教
!
补充
在我本地调试时,打印我处理后的表单如下:

可以看到我设置的多个 set 和 get
打包后,打印的结果如下:

所有的 get 和 set 消失了!
回答:
更新
在 Github 上找到了该 issue#3247,具体的原因如下:
Bug 原因
为了定位 Bug,我想到去查看打包后,代码到底发生了什么变化。阅读打包后的代码,我发现问题出在打包后,对 ES6 解构语法的转译,即对以下代码的转译:
const localForm = {    ...obj,
    property1: 'somevalue1',
    property2: 'somevalue2',
    set _value(value) {
        \\ some code
    },
    get _value() {
        \\ some code
    }
}
打包过程中,会把以上涉及 ... 解构符号的代码转化为一个函数,类似以下代码:(其中 obj1 就是被解构的对象,obj2 就是把剩下的属性包装成一个对象)
var mergeObj = (obj1, obj2) => {  for (var key in obj2 || {}) {
    if (hasOwnProperty.call(obj2, key)) setValue(obj1, key, obj2[key])
  }
  if (getOwnPropertySymbols) console.log('hanld symbols property')
  return obj1
}
代码考虑了普通属性和 Symbols 属性,却没考虑 setter 和 getter. 问题就出在这里,在 for in 循环中,虽然 _value 作为属性可以被拷贝,但无法拷贝他的 getter 和 setter,这就导致了,_value 被拷贝后的值只是一个从 obj2 中的 setter 获取的一个静态值. 所以 bug 就出现了.
解决办法
问题出现在打包时对 ... 解构符的转译,那么可以避免在使用 setter 和 getter 的同时避免使用 ... 符号,可以避免该问题.
但仍然感觉这应该是 vitejs 打包时的一个 Bug,不知道 vite.js 底层使用了什么工具打包,还是自己新写的,总之应该被解决.
Demo
根据边城的建议,我做了一个 bug 复现的Demo,(需要打包后才能复现 Bug)
其实原理上面应该介绍的差不多了,Demo 的关键代码如下:
const getFormData = () => {  setTimeout(() => {
    /* 该写法不会产生bug
    formData.value = {
      openingTimeStart: '1949-10-01 09:00:00',
      openingTimeEnd: '1949-10-01 18:00:00',
      set _timeRange(value: | string[] | Date[]) {
        this.openingTimeStart = moment(value[0]).format('YYYY-MM-DD HH:mm:ss')
        this.openingTimeEnd = moment(value[1]).format('YYYY-MM-DD HH:mm:ss')
      },
      get _timeRange() {
        this.openingTimeEnd = this.openingTimeEnd || "1949-10-01 17:00:00"
        this.openingTimeStart = this.openingTimeStart || "1949-10-01 09:00:00"
        return [new Date(this.openingTimeStart), new Date(this.openingTimeEnd)]
      },
    }
    */
    // 下面写法产生 Bug
    const res = {
      openingTimeStart: '1949-10-01 09:00:00',
      openingTimeEnd: '1949-10-01 18:00:00',
    }
    formData.value = {
      ...res,
      set _timeRange(value: | string[] | Date[]) {
        this.openingTimeStart = moment(value[0]).format('YYYY-MM-DD HH:mm:ss')
        this.openingTimeEnd = moment(value[1]).format('YYYY-MM-DD HH:mm:ss')
      },
      get _timeRange() {
        this.openingTimeEnd = this.openingTimeEnd || "1949-10-01 17:00:00"
        this.openingTimeStart = this.openingTimeStart || "1949-10-01 09:00:00"
        return [new Date(this.openingTimeStart), new Date(this.openingTimeEnd)]
      },
    }
  }, 100)
}
以上是 在 Vue3 的项目中使用了 JS 对象的 setter 和 getter 属性,打包后失效是怎么回事? 的全部内容, 来源链接: utcz.com/p/935482.html



