Vue 3 未检测到对在 Vue 组件之外创建的对象所做的更改

saq*_*dev 3 typescript vue.js vuejs3

我有一个类字符: Character.ts

    /// This is called when server responds
    public setAttributeByType(type: StatsTypes, value: number): void {
        switch (type) {
            case StatsTypes.STRENGTH:
            case StatsTypes.DEXTERITY:
            case StatsTypes.VITALITY:
            case StatsTypes.INTELIGENCE:
                this.stats[type] = value;
                break;
            default: break;
        }
    }
    ....
Run Code Online (Sandbox Code Playgroud)

类实例是在我的“网络代码”中的 Vue 组件之外创建的:

    public static onStartGame(data:any):void {
        Player.character = new Character(data.data);
        Game.displayGamePage(PagesIndex.GAME_PAGE);
        requestAnimationFrame(Game.loop);
    }

Run Code Online (Sandbox Code Playgroud)

并用于主要组件: Game.vue

import { defineComponent } from 'vue'
import Player from '@/Player/player';
import SceneManager, { Scenes } from '@/Render/scene_manager';
import Scene from '@/Render/scene';
import MainScene from "@/Render/scenes/main";
import MapScene from "@/Render/scenes/map";
import Game from '@/game/game';

// Components
import VplayerBar from "@/vue/subs/playerBar.vue"
import Vcharacter from "@/vue/subs/character.vue"

export enum GamePages {
    MAIN_PAGE = 1,
    MAP_PAGE,
}

export default defineComponent({
    name: "game",
    components: {
        VplayerBar,
        Vcharacter,
    },
    data() {
        return {
            page: GamePages.MAIN_PAGE,
            scenes: Scenes,
            gamePages: GamePages,
            player: Player,
            character: Player.character, /* <------------ Reference to class */
            pages: {
                character: false,
            }
        }
    },
})
Run Code Online (Sandbox Code Playgroud)

...将其作为道具传递给character.vue组件

    /// This is called when server responds
    public setAttributeByType(type: StatsTypes, value: number): void {
        switch (type) {
            case StatsTypes.STRENGTH:
            case StatsTypes.DEXTERITY:
            case StatsTypes.VITALITY:
            case StatsTypes.INTELIGENCE:
                this.stats[type] = value;
                break;
            default: break;
        }
    }
    ....
Run Code Online (Sandbox Code Playgroud)

问题是,每当我在 vue 组件之外(在我的网络代码中)更改字符类实例中的任何内容时,例如character.setAttributeByType(attribute, value),Vue 都看不到更改。如果我直接在character.vue组件内部执行此操作,它会起作用(请参阅 中的注释代码addPoint

我尝试使用 Proxy & Watch 但它没有帮助。

Mic*_*evý 5

您的问题是此处描述的“身份”问题

Vue 3 使用 ES6 代理使对象具有反应性。如果你这样做了const data = reactive(payload),那么它data就是不同的对象payload(不像在 Vue 2 中,对象只是用反应式 setter/getter 修改过)。

同样适用于 Options API(您正在使用)。如果你character: Player.characterdata()结果是this.character(Vue的组件内)是不同的对象,然后Player.character。您可以通过执行console.log(this.character === Player.character)...轻松测试它,例如在mounted()- 结果将是false

因此,如果您使用this.character(Vue 反应式代理)进行任何更改,Vue 将检测更改并重新渲染(并将更改传播到原始对象),但是如果您更改原始对象Player.character,则 Vue 不会检测到更改...

简单的解决方法是使用 Vue 的Composition API,它允许您在 Vue 组件之外使用 Vue 反应性。

import { reactive } from `vue`

Player.character = reactive(new Character(data.data));
Run Code Online (Sandbox Code Playgroud)

现在当你在 Vue 组件中使用Player.character初始化data()时,Vue 看到它已经是一个响应式代理并且不再将它包装在代理中