【问题标题】:Changes made to an object created outside of Vue component are not detected by Vue 3Vue 3 未检测到对在 Vue 组件之外创建的对象所做的更改
【发布时间】:2021-06-28 21:59:36
【问题描述】:

我有一个班级 Character : 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;
        }
    }
    ....

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

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

并用于主要组件: 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,
            }
        }
    },
})

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

export default defineComponent({
    name: "character",
    props: {
        character: {  // <---- This prop does not change
            type: Character,
            required: true
        },
        toggleCharacter: {
            type: Function, 
            required: true
        }
    },
    components: {
        VBackpack,
        VInventory
    },
    data() {
        return {
            StatsTypes,
        }
    },
    methods: {
        toglePage() {
            this.toggleCharacter()
        },
        getPortrait(isMobile:boolean = false) {
            return Character.getCharacterPortrait(this.character.faction, this.character.gender, isMobile);
        },
        addPoint(attribute:StatsTypes, value: number) {
            if (GKeyHandler.keys[Keys.SHIFT_LEFT])
                value = 10;

            CharacterHandler.addAttributePoint(Player.character, attribute, value);
            //this.character.stats[StatsTypes.STRENGTH] += 1; 
        }
    }
});

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

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

【问题讨论】:

  • 向我们展示您实例化类的代码并将其“放入”Vue(父组件)。
  • @MichalLevý 问题是代码没有在 Vue 组件中实例化。它是在外部实例化的,只有对这个实例的引用放在 vue 组件 Game.vue -> character: Player.character,(Player.character 是对实例的引用)Game.vue 将角色绑定到 character.vue。实例在收到数据包开始游戏时创建,即在自定义处理程序中。
  • @MichalLevý 唯一的解决方法是更改​​ VUe 组件内的 prop 值。然后它正在工作或强制重新渲染,这是不好的。但是 Vue 组件内部或外部的变化没有注册,有没有办法在 Vue 组件外部观察这些变化?
  • 显示代码!它是如何“放置在vue组件Game.vue中”的?
  • @MichalLevý 可能就够了,或者我可以在 github 中提供完整的代码,如果你发给我的话

标签: typescript vue.js vuejs3


【解决方案1】:

您的问题是here 描述的“身份”问题

Vue 3 使用 ES6 代理来使对象具有响应性。如果您使用 const data = reactive(payload),则 data 与 payload 是不同的对象(与 Vue 2 中的对象只是使用响应式 setter/getter 进行修改不同)。

同样适用于 Options API(您正在使用)。如果您在data() 中执行character: Player.character,则结果是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));

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

【讨论】:

  • 嗨@michal,是否可以使用选项API解决这个问题?谢谢
  • @SunuPinasthikaFajar 让对象在 Vue 组件之外响应?不,Options API 不可能。使用 Composition API 对您来说是个问题吗?为什么?
  • 好吧,我们在这里使用 Nuxt 2.*。并且还在观望使用组合API。 (即使有一个图书馆) :) 。但感谢您的关心和回答
  • @SunuPinasthikaFajar 您可以在 Vue 2.x 中使用 Vue.observable 做同样的事情
  • 尽管在 Vue 2.x 中不需要它,因为它使用不同的反应机制 - 它不是创建新对象(代理),而是使用反应性 getter/setter 改变现有对象,因此没有我的回答中提到的身份问题...
猜你喜欢
  • 2021-05-21
  • 2018-09-20
  • 1970-01-01
  • 2020-08-04
  • 1970-01-01
  • 2011-08-19
  • 2021-08-19
  • 2020-03-20
  • 1970-01-01
相关资源
最近更新 更多