Live Share 核心功能

屏幕截图显示了用户在 Teams 会议中玩敏捷扑克游戏的示例,其中展示了实时共享功能。

Live Share SDK 可以很轻松地添加到会议扩展的 sidePanelmeetingStage 上下文。

本文重点介绍如何将 Live Share SDK 集成到应用和 SDK 的关键功能中。

先决条件

安装 JavaScript SDK

Live Share SDK 是在 npm 上发布的 JavaScript 包,可以通过 npm 或 yarn 下载。 还必须安装 Live Share 对等依赖项,其中包括 fluid-framework@fluidframework/azure-client。 如果在选项卡应用程序中使用 Live Share,则还必须安装 @microsoft/teams-js 版本 2.11.0 或更高版本。 如果要使用 TestLiveShareHost 类进行本地浏览器开发,则必须在 中devDependencies安装 @fluidframework/test-client-utilsstart-server-and-test 包。

npm

npm install @microsoft/live-share fluid-framework @fluidframework/azure-client --save
npm install @microsoft/teams-js --save
npm install @fluidframework/test-client-utils start-server-and-test --save-dev

yarn add @microsoft/live-share fluid-framework @fluidframework/azure-client
yarn add @microsoft/teams-js
yarn add @fluidframework/test-client-utils -dev

注册 RSC 权限

若要为会议扩展启用 Live Share SDK,必须首先将以下 RSC 权限添加到应用清单中:

{
  // ...rest of your manifest here
  "configurableTabs": [
    {
        "configurationUrl": "<<YOUR_CONFIGURATION_URL>>",
        "canUpdateConfiguration": true,
        "scopes": [
            "groupchat",
            "team"
        ],
        "context": [
            "meetingSidePanel",
            "meetingStage"
        ]
    }
  ],
  "validDomains": [
    "<<BASE_URI_ORIGIN>>"
  ],
  "authorization": {​
    "permissions": {​
      "resourceSpecific": [
        // ...other permissions here​
        {​
          "name": "LiveShareSession.ReadWrite.Chat",​
          "type": "Delegated"
        },
        {​
          "name": "LiveShareSession.ReadWrite.Group",​
          "type": "Delegated"
        },
        {​
          "name": "MeetingStage.Write.Chat",​
          "type": "Delegated"
        },
        {​
          "name": "ChannelMeetingStage.Write.Group",​
          "type": "Delegated"
        }
      ]​
    }​
  }​
}

加入会议会话

按照步骤加入与用户会议关联的会话:

  1. 初始化 LiveShareClient
  2. 定义要同步的数据结构。 例如,LiveStateSharedMap
  3. 加入容器。

示例:

import { LiveShareClient, LiveState } from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";
import { SharedMap } from "fluid-framework";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: {
    liveState: LiveState,
    sharedMap: SharedMap,
  },
};
const { container } = await liveShare.joinContainer(schema);

// ... ready to start app sync logic

这就是设置容器和加入会议会话所需的全部操作。 现在,让我们回顾一下可与 Live Share SDK 一起使用的不同类型的分布式数据结构

提示

确保在调用 LiveShareHost.create()之前初始化 Teams 客户端 SDK。

Live Share 数据结构

Live Share SDK 包括一组新的分布式数据结构,这些结构扩展 Fluid 的 DataObject 类,提供新类型的有状态和无状态对象。 与 Fluid 数据结构不同,Live Share 的 LiveDataObject 类不会将更改写入 Fluid 容器,从而实现更快的同步。 此外,这些类是针对 Teams 会议中的常见会议方案从头开始设计的。 常见方案包括同步演示者正在查看的内容、显示会议中每个用户的元数据或显示倒计时计时器。

Live 对象 说明
LivePresence 查看哪些用户处于联机状态,为每个用户设置自定义属性,并广播其状态更改。
LiveState 同步任何 JSON 可 state 序列化值。
LiveTimer 同步给定间隔的倒计时计时器。
LiveEvent 使用有效负载中的任何自定义数据属性广播单个事件。
LiveFollowMode 关注特定用户、向会话中的每个人演示,以及开始或结束暂停。

LivePresence 示例

屏幕截图显示了一个示例,显示使用 Live Share 状态在 sessionTeams 中可用的人员。

LivePresence 使跟踪会话中的人员比以往更加容易。 调用 .initialize().updatePresence() 方法时,可以为该用户分配自定义元数据,例如个人资料图片、他们正在查看的内容的标识符等。 通过侦 presenceChanged 听事件,每个客户端接收最新的 LivePresenceUser 对象,将所有状态更新折叠到每个唯 userId一的 记录中。

下面是一些可在应用程序中使用的示例 LivePresence

  • 获取会话中每个用户的 Microsoft Teams userIddisplayNameroles
  • 显示有关连接到会话的每个用户的自定义信息,例如个人资料图片 URL。
  • 同步每个用户头像所在的 3D 场景中的坐标。
  • 报告文本文档中每个用户的光标位置。
  • 在组活动期间发布每个用户对破冰问题的答案。
import {
  LiveShareClient,
  LivePresence,
  PresenceState,
} from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: {
    presence: LivePresence,
  },
};
const { container } = await liveShare.joinContainer(schema);
const presence = container.initialObjects.presence;

// Register listener for changes to each user's presence.
// This should be done before calling `.initialize()`.
presence.on("presenceChanged", (user, local) => {
  console.log("A user presence changed:")
  console.log("- display name:", user.displayName);
  console.log("- state:", user.state);
  console.log("- custom data:", user.data);
  console.log("- change from local client", local);
  console.log("- change impacts local user", user.isLocalUser);
});

// Define the initial custom data for the local user (optional).
const customUserData = {
  picture: "DEFAULT_PROFILE_PICTURE_URL",
  readyToStart: false,
};
// Start receiving incoming presence updates from the session.
// This will also broadcast the user's `customUserData` to others in the session.
await presence.initialize(customUserData);

// Send a presence update, in this case once a user is ready to start an activity.
// If using role verification, this will throw an error if the user doesn't have the required role.
await presence.update({
  ...customUserData,
  readyToStart: true,
});

从单个设备加入会话的用户有一 LivePresenceUser 条共享到其所有设备的记录。 若要访问每个活动连接的最新 datastate ,可以使用 类中的 getConnections() API LivePresenceUser 。 这将返回对象的列表 LivePresenceConnection 。 可以使用 属性查看给定 LivePresenceConnection 实例是否来自本地设备 isLocalConnection

每个 LivePresenceUserLivePresenceConnection 实例都有一个 state 属性,该属性可以是 onlineofflineawaypresenceChanged当用户的状态发生更改时,将发出事件。 例如,如果用户离开会议,其状态将更改为 offline

注意

在离开会议后,最多可能需要 20 秒LivePresenceUserstate才能更新到 offline

LiveState 示例

屏幕截图显示了实时共享状态的示例,用于同步太阳系中的行星主动呈现给会议。

LiveState 支持为会议中的每个人同步简单的应用程序状态。 LiveState 同步单个 state 值,以便同步任何 JSON 可序列化值,例如 stringnumberobject

下面是一些可在应用程序中使用的示例 LiveState

  • 设置当前演示者的用户标识符以生成 采取控制 功能。
  • 同步应用程序的当前路由路径,以确保每个人都位于同一页上。 例如,/whiteboard/:whiteboardId
  • 维护当前演示者正在查看的内容标识符。 例如, taskId 任务板上的 。
  • 同步多轮组活动中的当前步骤。 例如,敏捷扑克游戏中的猜测阶段。
  • 使 “关注我” 功能的滚动位置保持同步。

注意

与 不同 SharedMapstate 中的所有 LiveState 用户从会话断开连接后,中的值将重置。

示例:

import { LiveShareClient, LiveState } from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: { appState: LiveState },
};
const { container } = await liveShare.joinContainer(schema);
const { appState } = container.initialObjects;

// Register listener for changes to the state.
// This should be done before calling `.initialize()`.
appState.on("stateChanged", (planetName, local, clientId) => {
  // Update app with newly selected planet.
  // See which user made the change (optional)
  const clientInfo = await appState.getClientInfo(clientId);
});

// Set a default value and start listening for changes.
// This default value will not override existing for others in the session.
const defaultState = "Mercury";
await appState.initialize(defaultState);

// `.set()` will change the state for everyone in the session.
// If using role verification, this will throw an error if the user doesn't have the required role.
await appState.set("Earth");

LiveEvent 示例

屏幕截图显示了 Teams 客户端在事件发生更改时显示通知的示例。

LiveEvent 是一种将简单事件发送到会议中的其他客户端的好方法,这些事件仅在传递时才需要。 它适用于发送会话通知或实现自定义反应等方案。

import { LiveEvent, LiveShareClient } from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: { customReactionEvent: LiveEvent },
};
const { container } = await liveShare.joinContainer(schema);
const { customReactionEvent } = container.initialObjects;

// Register listener to receive events sent through this object.
// This should be done before calling `.initialize()`.
customReactionEvent.on("received", async (kudosReaction, local, clientId) => {
  console.log("Received reaction:", kudosReaction, "from clientId", clientId);
  // See which user made the change (optional)
  const clientInfo = await customReactionEvent.getClientInfo(clientId);
  // Display notification in your UI
});

// Start listening for incoming events
await customReactionEvent.initialize();

// `.send()` will send your event value to everyone in the session.
// If using role verification, this will throw an error if the user doesn't have the required role.
const kudosReaction = {
  emoji: "❤️",
  forUserId: "SOME_OTHER_USER_ID",
};
await customReactionEvent.send(kudosReaction);

LiveTimer 示例

屏幕截图显示了剩余 9 秒的倒计时计时器示例。

LiveTimer 提供为会议中的所有参与者同步的简单倒计时计时器。 对于具有时间限制的方案(例如,组冥想计时器或游戏的轮次计时器)非常有用。 还可以使用它为会话中的每个人安排任务,例如显示提醒提示。

import { LiveShareClient, LiveTimer } from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: { timer: LiveTimer },
};
const { container } = await liveShare.joinContainer(schema);
const { timer } = container.initialObjects;

// Register listeners for timer changes
// This should be done before calling `.initialize()`.

// Register listener for when the timer starts its countdown
timer.on("started", (config, local) => {
  // Update UI to show timer has started
});

// Register listener for when a paused timer has resumed
timer.on("played", (config, local) => {
  // Update UI to show timer has resumed
});

// Register listener for when a playing timer has paused
timer.on("paused", (config, local) => {
  // Update UI to show timer has paused
});

// Register listener for when a playing timer has finished
timer.on("finished", (config) => {
  // Update UI to show timer is finished
});

// Register listener for the timer progressed by 20 milliseconds
timer.on("onTick", (milliRemaining) => {
  // Update UI to show remaining time
});

// Start synchronizing timer events for users in session
await timer.initialize();

// Start a 60 second timer for users in the session.
// If using role verification, this will throw an error if the user doesn't have the required role.
const durationInMilliseconds = 1000 * 60;
await timer.start(durationInMilliseconds);

// Pause the timer for users in session
// If using role verification, this will throw an error if the user doesn't have the required role.
await timer.pause();

// Resume the timer for users in session
// If using role verification, this will throw an error if the user doesn't have the required role.
await timer.play();

LiveFollowMode 示例

图像显示了具有三个不同视图的三个客户端:一个演示者、一个关注演示者的用户,以及一个具有其自己的专用视图的用户,其中包含同步回演示者的选项。

注意

LiveFollowMode 处于 Beta 版中,仅作为预览版提供。 请勿在生产环境中使用此 API。

LiveFollowMode将 和 LiveState 合并LivePresence为单个类,使你能够轻松地在应用程序中实现追随者和演示者模式。 这允许你从热门协作应用(如 PowerPoint Live、Excel Live 和 Whiteboard)实现熟悉的模式。 与屏幕共享不同, LiveFollowMode 你可以以高质量、改进的辅助功能和增强的性能呈现内容。 用户可以轻松地在其专用视图之间切换并关注其他用户。

可以使用 函数startPresenting()控制会话中所有其他用户的应用程序。 或者,你可以允许用户使用 followUser() 函数单独选择要关注的特定用户。 在这两种情况下,用户都可以使用 函数暂时进入专用视图, beginSuspension() 或者使用 endSuspension() 函数同步回演示者。 同时, update() 函数允许本地用户在会话中通知其他客户端他们自己的个人 stateValue。 与 LivePresence类似,可以通过事件侦听器侦听每个用户的 stateValuepresenceChanged 更改。

LiveFollowMode 还会公开一个 state 对象,该对象根据本地用户所关注的用户动态更新。 例如,如果本地用户不关注任何人,则 state.value 属性与本地用户最近 stateValue 通过 update()广播的 匹配。 但是,如果本地用户关注演示者,则 state.value 属性与演示用户的最新 stateValue匹配。 与 LiveState类似,可以使用事件侦听器侦听对值的stateChanged更改state。 有关 对象的详细信息 state ,请参阅 IFollowModeState 接口参考

下面是一些可在应用程序中使用 LiveFollowMode 的示例:

  • 在设计评审期间,同步 3D 场景中的相机位置以共同浏览。
  • slideId更新 以在旋转木马中打开,以便进行富有成效的演示和讨论。
  • path广播 以在应用程序的路由器中打开。

示例:

import {
  LiveShareClient,
  LiveFollowMode,
  FollowModeType,
} from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: {
    followMode: LiveFollowMode,
  },
};
const { container } = await liveShare.joinContainer(schema);
const followMode = container.initialObjects.followMode;

// As an example, we will assume there is a button in the application document
const button = document.getElementById("action-button");
// As an example, we will assume there is a div with text showing the follow state
const infoText = document.getElementById("info-text");

// Register listener for changes to the `state` value to use in your app.
// This should be done before calling `.initialize()`.
followMode.on("stateChanged", (state, local, clientId) => {
  console.log("The state changed:");
  console.log("- state value:", state.value);
  console.log("- follow mode type:", state.type);
  console.log("- following user id:", state.followingUserId);
  console.log("- count of other users also following user", state.otherUsersCount);
  console.log("- state.value references local user's stateValue", state.isLocalValue);
  // Can optionally get the relevant user's presence object
  const followingUser = followMode.getUserForClient(clientId);
  switch (state.type) {
    case FollowModeType.local: {
        // Update app to reflect that the user is not currently following anyone and there is no presenter.
        infoText.innerHTML = "";
        // Show a "Start presenting" button in your app.
        button.innerHTML = "Start presenting";
        button.onclick = followMode.startPresenting;
        // Note: state.isLocalValue will be true.
        break;
    }
    case FollowModeType.activeFollowers: {
        // Update app to reflect that the local user is being followed by other users.
        infoText.innerHTML = `${state.otherUsersCount} users are following you`;
        // Does not mean that the local user is presenting to everyone, so you can still show the "Start presenting" button.
        button.innerHTML = "Present to all";
        button.onclick = followMode.startPresenting;
        // Note: state.isLocalValue will be true.
        break;
    }
    case FollowModeType.activePresenter: {
        // Update app to reflect that the local user is actively presenting to everyone.
        infoText.innerHTML = `You are actively presenting to everyone`;
        // Show a "Stop presenting" button in your app.
        button.innerHTML = "Stop presenting";
        button.onclick = followMode.stopPresenting;
        // Note: state.isLocalValue will be true.
        break;
    }
    case FollowModeType.followPresenter: {
        // The local user is following a remote presenter.
        infoText.innerHTML = `${followingUser?.displayName} is presenting to everyone`;
        // Show a "Take control" button in your app.
        button.innerHTML = "Take control";
        button.onclick = followMode.startPresenting;
        // Note: state.isLocalValue will be false.
        break;
    }
    case FollowModeType.suspendFollowPresenter: {
        // The local user is following a remote presenter but has an active suspension.
        infoText.innerHTML = `${followingUser?.displayName} is presenting to everyone`;
        // Show a "Sync to presenter" button in your app.
        button.innerHTML = "Sync to presenter";
        button.onclick = followMode.endSuspension;
        // Note: state.isLocalValue will be true.
        break;
    }
    case FollowModeType.followUser: {
        // The local user is following a specific remote user.
        infoText.innerHTML = `You are following ${followingUser?.displayName}`;
        // Show a "Stop following" button in your app.
        button.innerHTML = "Stop following";
        button.onclick = followMode.stopFollowing;
        // Note: state.isLocalValue will be false.
        break;
    }
    case FollowModeType.suspendFollowUser: {
        // The local user is following a specific remote user but has an active suspension.
        infoText.innerHTML = `You were following ${followingUser?.displayName}`;
        // Show a "Resume following" button in your app.
        button.innerHTML = "Resume following";
        button.onclick = followMode.endSuspension;
        // Note: state.isLocalValue will be true.
        break;
    }
    default: {
        break;
    }
  }
  const newCameraPosition = state.value;
  // TODO: apply new camera position
});

// Register listener for changes to each user's personal state updates.
// This should be done before calling `.initialize()`.
followMode.on("presenceChanged", (user, local) => {
  console.log("A user presence changed:");
  console.log("- display name:", user.displayName);
  console.log("- state value:", user.data?.stateValue);
  console.log("- user id user is following:", user.data?.followingUserId);
  console.log("- change from local client", local);
  console.log("- change impacts local user", user.isLocalUser);
  // As an example, we will assume there is a button for each user in the session.
  document.getElementById(`follow-user-${user.userId}-button`).onclick = () => {
    followMode.followUser(user.userId);
  };
  // Update 3D scene to reflect this user's camera position (e.g., orb + display name)
  const userCameraPosition = user.data?.stateValue;
});

// Define the initial stateValue for the local user (optional).
const startingCameraPosition = {
  x: 0,
  y: 0,
  z: 0,
};
// Start receiving incoming presence updates from the session.
// This will also broadcast the user's `startingCameraPosition` to others in the session.
await followMode.initialize(startingCameraPosition);

// Example of an event listener for a camera position changed event.
// For something like a camera change event, you should use a debounce function to prevent sending updates too frequently.
// Note: it helps to distinguish changes initiated by the local user (e.g., drag mouse) separately from other change events.
function onCameraPositionChanged(position, isUserAction) {
    // Broadcast change to other users so that they have their latest camera position
    followMode.update(position);
    // If the local user changed the position while following another user, we want to suspend.
    // Note: helps to distinguish changes initiated by the local user (e.g., drag mouse) separately from other change events.
    if (!isUserAction) return;
    switch (state.type) {
      case FollowModeType.followPresenter:
      case FollowModeType.followUser: {
        // This will trigger a "stateChanged" event update for the local user only.
        followMode.beginSuspension();
        break;
      }
      default: {
        // No need to suspend for other types
        break;
      }
    }
}

实时数据结构的角色验证

Teams 中的会议包括通话、全手会议和在线教室。 会议参与者可能跨组织、具有不同的权限或具有不同的目标。 因此,在会议期间尊重不同用户角色的特权非常重要。 实时对象旨在支持角色验证,允许你定义允许为每个单独的实时对象发送消息的角色。 例如,你选择了仅允许会议演示者和组织者控制视频播放的选项。 但是,来宾和与会者仍可以请求下一个视频watch。

在以下示例中,只有演示者和组织者可以控制, LiveState 用于同步哪个用户是活动的演示者:

import {
  LiveShareClient,
  LiveState,
  UserMeetingRole,
} from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: { appState: LiveState },
};
const { container } = await liveShare.joinContainer(schema);
const { appState } = container.initialObjects;

// Register listener for changes to state
appState.on("stateChanged", (state, local) => {
  // Update local app state
});

// Set roles who can change state and start listening for changes
const initialState = {
  documentId: "INITIAL_DOCUMENT_ID",
};
const allowedRoles = [UserMeetingRole.organizer, UserMeetingRole.presenter];
await appState.initialize(initialState, allowedRoles);

async function onSelectEditMode(documentId) {
  try {
    await appState.set({
      documentId,
    });
  } catch (error) {
    console.error(error);
  }
}

async function onSelectPresentMode(documentId) {
  try {
    await appState.set({
      documentId,
      presentingUserId: "LOCAL_USER_ID",
    });
  } catch (error) {
    console.error(error);
  }
}

在将角色验证实现到应用之前(特别是组织者角色),请倾听客户以了解其应用场景。 不能保证会议组织者将出席会议。 作为一般经验法则,在组织内协作时,所有用户都是 组织者演示者 。 如果用户是与会者,这通常是代表会议组织者的有意决定。

在某些情况下,用户可能有多个角色。 例如, 组织者 也是 演示者。 此外,位于托管会议的租户外部的会议参与者具有 “来宾” 角色,但也可能具有 “演示者 ”权限。 这为如何在应用程序中使用角色验证提供了更大的灵活性。

注意

频道会议中的 来宾 用户不支持 Live Share SDK。

流体分布式数据结构

Live Share SDK 支持 Fluid Framework 中包含的任何分布式数据结构。 这些功能充当一组基元,可用于构建可靠的协作方案,例如实时更新任务列表或在 HTML <textarea>中共同创作文本。

LiveDataObject与本文中提到的类不同,Fluid 数据结构在应用程序关闭后不会重置。 这非常适合会议侧面板等场景,用户经常在会议中使用其他选项卡(例如聊天)时关闭并重新打开应用。

Fluid Framework 正式支持以下类型的分布式数据结构:

共享对象 说明
SharedMap 分布式键值存储。 为给定键设置任何 JSON 可序列化对象,以便为会话中的每个人同步该对象。
SharedSegmentSequence 一种类似于列表的数据结构,用于在集位置存储一组项(称为段)。
SharedString 针对编辑文档或文本区域的文本进行了优化的分布式字符串序列。

让我们看看 SharedMap 的工作原理。 在此示例中,我们使用 SharedMap 生成了播放列表功能。

import { LiveShareClient } from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";
import { SharedMap } from "fluid-framework";

// Join the Fluid container
const host = LiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: { playlistMap: SharedMap },
};
const { container } = await liveShare.joinContainer(schema);
const playlistMap = container.initialObjects.playlistMap;

// Register listener for changes to values in the map
playlistMap.on("valueChanged", (changed, local) => {
  const video = playlistMap.get(changed.key);
  // Update UI with added video
});

function onClickAddToPlaylist(video) {
  // Add video to map
  playlistMap.set(video.id, video);
}

注意

核心 Fluid Framework DDS 对象不支持会议角色验证。 会议中的每个人都可以更改通过这些对象存储的数据。

本地浏览器测试

可以使用 类在浏览器中本地测试 Live Share SDK, TestLiveShareHost 而无需在 Teams 中安装应用。 这对于在熟悉 localhost 的环境中测试应用程序的核心协作功能非常有用。

示例:

import { LiveShareClient, TestLiveShareHost, LiveState } from "@microsoft/live-share";
import { LiveShareHost } from "@microsoft/teams-js";
import { SharedMap } from "fluid-framework";

/**
 * Detect whether you are in Teams or local environment using your preferred method.
 * Options for this include: environment variables, URL params, Teams FX, etc.
 */
const inTeams = process.env.IN_TEAMS;
// Join the Fluid container
const host = inTeams
  ? LiveShareHost.create()
  : TestLiveShareHost.create();
const liveShare = new LiveShareClient(host);
const schema = {
  initialObjects: {
    liveState: LiveState,
    sharedMap: SharedMap,
  },
};
const { container } = await liveShare.joinContainer(schema);

// ... ready to start app sync logic

TestLiveShareHost 利用 tinylicious Fluid Framework 中的测试服务器,而不是我们的生产 Azure Fluid Relay 服务。 为此,必须向 添加几个脚本 package.json 才能启动测试服务器。 还必须将 @fluidframework/test-client-utilsstart-server-and-test 包添加到 devDependencies 中的 package.json

{
    "scripts": {
        "start": "start-server-and-test start:server 7070 start:client",
        "start:client": "{YOUR START CLIENT COMMAND HERE}",
        "start:server": "npx tinylicious@latest"
    },
    "devDependencies": {
        "@fluidframework/test-client-utils": "^1.3.6",
        "start-server-and-test": "^2.0.0"
    }
}

以这种方式启动应用程序时, LiveShareClient 如果 URL 不存在,则会将其添加到 #{containerId} URL。 然后,可以将 URL 复制并粘贴到新的浏览器窗口中,以连接到同一 Fluid 容器。

注意

默认情况下,通过 TestLiveShareHost 连接的所有客户端都将具有 presenterorganizer 角色。

代码示例

示例名称 Description JavaScript TypeScript
投掷骰子 启用所有连接的客户端以投掷骰子并查看结果。 View View
Agile Poker 使所有连接的客户端都能够玩 Agile Poker 游戏。 View 不适用
3D 模型 允许所有连接的客户端一起查看 3D 模型。 不适用 View
Timer 启用所有连接的客户端以查看倒计时计时器。 不适用 View
状态头像 显示所有已连接客户端的状态头像。 不适用 View

后续步骤

另请参阅