在 Vue3 的项目中使用了 JS 对象的 setter 和 getter 属性,打包后失效是怎么回事?

问题概述:

由于前端 Vue 组件需要绑定 v-model数据格式和接口返回和上传的格式不一样,为了方便使数据“双向同步”,我想到使用 JS 对象的 settergetter 属性来实现,事实证明这也是很方便的.

一切在我本地开发时都正常,最近打包后发布在测试环境后发现出了 Bug,好像是 settergetter 没有生效.

如果打包前正常,打包后出了 Bug 是不是打包这里出问题了呢,所以想问问有没有了解 Vue 和 Vite 打包机制的大佬,看能不能帮忙定位下问题.

开发环境:

使用了 Vue3 开发,并配套使用了 Vite.js 打包(被Vue主页各种忽悠使用的)
Vue 3.0.5
Vite 2.1.5
element-plus: ^1.0.2-beta.39

涉案代码:

组件使用了 ElementUI 的 time-pickerrange 模式,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 中的 openingTimeStartopeningTimeEnd 字段.

这一效果在我本地调试时也是正常的,但打包后就失效了,表现为我设置时间后,openingTimeStartopeningTimeEnd 字段并不更新

请教!

这个 Bug 我没有头绪,难道要放弃这种思路,然后去监听事件手动修改字段吗,请大佬们不吝赐教

补充

在我本地调试时,打印我处理后的表单如下:

在 Vue3 的项目中使用了 JS 对象的 setter 和 getter 属性,打包后失效是怎么回事?

可以看到我设置的多个 setget

打包后,打印的结果如下:

在 Vue3 的项目中使用了 JS 对象的 setter 和 getter 属性,打包后失效是怎么回事?

所有的 getset 消失了!


回答:

更新

在 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 属性,却没考虑 settergetter. 问题就出在这里,在 for in 循环中,虽然 _value 作为属性可以被拷贝,但无法拷贝他的 gettersetter,这就导致了,_value 被拷贝后的值只是一个从 obj2 中的 setter 获取的一个静态值. 所以 bug 就出现了.

解决办法

问题出现在打包时对 ... 解构符的转译,那么可以避免在使用 settergetter 的同时避免使用 ... 符号,可以避免该问题.

但仍然感觉这应该是 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

回到顶部