在 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