【问题标题】:Typescript data modeling shared by both front-end and back-end - Classes or interfaces?前端和后端共享的打字稿数据建模 - 类还是接口?
【发布时间】:2020-04-10 16:49:47
【问题描述】:

我想知道在客户端 (React) 和服务器 (Express + Socket.IO) 之间共享相同数据类型的最佳方式是什么。

在我的游戏中,我有不同的房间,每个房间都会保存当前状态,例如:

class GameRoom {
    players: Player[];
    started: boolean;
    currentPlayerTurn; Player;
    dices: [number, number];

    constructor({players = [], started = false, currentPlayerTurn = null, dices = [1,1]) {
        this.players = players;
        this.started = started;
        this.currentPlayerTurn = currentPlayerTurn;
        this.dices = dices;
    }

    startGame() {
        this.currentPlayerTurn = this.players[0];
        this.started = true;
    }

    // etc..
}

房间正在服务器中生成,以 JSON 格式发送到客户端,然后在客户端中重建。我将数据与套接字事件同步,一切都很完美。

但故事的 React 方面存在问题:更改 GameRoom 属性不会导致重新渲染。这意味着每次编辑某些内容或收听课程更改时,我都必须forceRerender()。这两个选项都是一团糟,我described it deeply in this question。

这个烂摊子让我觉得上课可能不是最好的方式。使用接口将完全解决这个问题,但我确实丢失了像 GameRoom.startGame() 这样的实例函数,必须将其转换为实用函数,比如:

export function startGame(gameRoom: GameRoom) {
    gameRoom.currentPlayerTurn = gameRoom.players[0];
    gameRoom.started = true;
}

这又是一团糟,因为它们隐藏在代码中,开发人员需要知道它们存在,而不是直接编辑gameRoom。

如果你们对如何为我的数据类型建模有任何想法,我会非常乐意听到。

谢谢!

【问题讨论】:

    标签: javascript reactjs typescript express


    【解决方案1】:

    我会采用功能性方法。我不是 JS 中的类的忠实粉丝。

    为您的游戏定义一个类型,但将属性设置为只读。这将告诉开发者他们不应该改变 GameRoom。

    export type GameRoom = {
        readonly players: Player[];
        readonly started: boolean;
        readonly currentPlayerTurn: Player;
        readonly dices: [number, number];
    }
    

    然后,您可以将所有更改定义为纯函数,因此您可以定义输入并创建一个新对象,即更新后的 GameRoom。这使得测试和跟踪更改变得容易。

    export function startGame(gameRoom: GameRoom): GameRoom {
        return {
            ...gameRoom,
            currentPlayerTurn: gameRoom.players[0],
            started: true
        }
    }
    

    或者,您可以使用 Redux 之类的工具,并将您的更改定义为一组操作。 (例如 add-player、start-game 等)然后,您可以从代码中的任何位置调度这些操作。

    使用不变性的好处是每次对对象进行更改时,都会返回一个新对象,因此 React 将始终适当地重新渲染。

    【讨论】:

    • 实际上这是一个不错的方法,我有三个问题: 1. ...gameRoom 在这种情况下是否有效?因为例如,这就像在做players: gameRoom.players, dices: gameRoom.dices - 这些是相同的数组,它可以与readonly 一起使用吗? 2. 当gameRoom 有100 个属性(有些像其他对象/数组一样复杂)时,效率如何? 3. 为什么是type 而不是interface?有关系吗?谢谢!
    • 是的,readonly 适用于数组。您可以使用数组中的扩展运算符来添加播放器。 ...gameroom 将仅应用 gameRoom 的所有属性,并且仅更改以下属性。效率很好,如果您有一个未更改的嵌套数组或对象,它只会在引擎盖下添加对它的引用,因此您只会创建已更改的新对象。这取决于您使用类型或接口的用例。接口有利于扩展,类型有利于组合,可以很好地与可区分联合之类的功能思想配合使用。
    猜你喜欢
    • 2022-11-20
    • 1970-01-01
    • 2019-07-23
    • 2019-09-23
    • 1970-01-01
    • 2021-11-18
    • 2019-04-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多