@@ -55,12 +55,18 @@ export default {
5555 prefix,
5656 classPrefix: name,
5757 timeDataUnit: TimeDataUnit,
58- timeData: parseTimeData (0 ),
58+ // 用 Object.freeze 阻断 Vue3 对 timeData 内部 5 个字段的深度响应化,
59+ // 减少鸿蒙 V8 下 Proxy.set 的次数(高频 setTimeout 写 reactive 会触发 GC race -> SIGSEGV)
60+ timeData: Object .freeze (parseTimeData (0 )),
5961 formattedTime: ' 0' ,
6062 tools,
6163 timeoutId: null ,
6264 isInitialTime: false ,
6365 timeRange: [],
66+ // 组件是否已销毁标记(非响应式,仅作为闭包标志使用)
67+ // 用于防止 setTimeout 回调在组件卸载后访问已销毁的 reactive 实例
68+ // 鸿蒙 (HarmonyOS) 端 V8 在该场景下会触发 SIGSEGV(SEGV_MAPERR)
69+ _destroyed: false ,
6470 };
6571 },
6672 watch: {
@@ -71,8 +77,12 @@ export default {
7177 immediate: true ,
7278 },
7379 },
74- mounted () {},
80+ mounted () {
81+ this ._destroyed = false ;
82+ },
7583 beforeUnmount () {
84+ this ._destroyed = true ;
85+ this .counting = false ;
7686 if (this .timeoutId ) {
7787 clearTimeout (this .timeoutId );
7888 this .timeoutId = null ;
@@ -92,7 +102,10 @@ export default {
92102
93103 pause () {
94104 this .counting = false ;
95- this .timeoutId && clearTimeout (this .timeoutId );
105+ if (this .timeoutId ) {
106+ clearTimeout (this .timeoutId );
107+ this .timeoutId = null ;
108+ }
96109 },
97110
98111 reset () {
@@ -119,10 +132,14 @@ export default {
119132
120133 const { timeText } = parseFormat (remain, format);
121134
122- const timeRange = format .split (' :' );
123-
124- this .timeRange = timeRange;
125- this .timeData = timeData;
135+ // timeRange 仅由 format(prop) 决定,初次或 format 变化时才赋值,
136+ // 避免每帧都写 reactive 数组造成鸿蒙端 V8 GC race。
137+ if (! this ._lastFormat || this ._lastFormat !== format) {
138+ this .timeRange = format .split (' :' );
139+ this ._lastFormat = format;
140+ }
141+ // freeze 一下,禁止 Vue 深度响应化每帧的新对象
142+ this .timeData = Object .freeze (timeData);
126143 this .formattedTime = timeText .replace (/ :/ g , ' : ' );
127144
128145 if (remain === 0 && (this .counting || this .isInitialTime )) {
@@ -133,7 +150,17 @@ export default {
133150 },
134151
135152 doCount () {
153+ // 毫秒模式下从 33ms (≈30fps) 降到 100ms (≈10fps),肉眼仍流畅,
154+ // 但 Proxy.set 频率降低 3 倍,显著降低鸿蒙 V8 GC race 概率。
155+ let interval = this .millisecond ? 100 : 200 ;
156+ // #ifdef APP-HARMONY
157+ interval = this .millisecond ? 100 : 500 ;
158+ // #endif
136159 this .timeoutId = setTimeout (() => {
160+ // 组件已销毁,直接返回,避免对已失效的 reactive 实例做 set 操作
161+ // 修复鸿蒙端因 Vue3 Proxy set 已销毁对象触发 V8 SIGSEGV 崩溃
162+ if (this ._destroyed ) return ;
163+
137164 const time = this .getTime ();
138165
139166 if (this .millisecond ) {
@@ -142,10 +169,10 @@ export default {
142169 this .updateTime (time);
143170 }
144171
145- if (time !== 0 ) {
172+ if (time !== 0 && ! this . _destroyed ) {
146173 this .doCount ();
147174 }
148- }, 33 ); // 30 帧,因此 1000 / 30 = 33
175+ }, interval);
149176 },
150177 },
151178 }),
0 commit comments