Node.js を使用してリアルタイム スポーツ アプリケーションを作成する

PHPz
リリース: 2023-09-03 12:41:09
オリジナル
847 人が閲覧しました

今日の記事では、NHL のライブ ゲーム スコアを表示する Web アプリケーションの作成方法を説明します。ゲームの進行に応じてスコアは自動的に更新されます。

これは、開発とスポーツという私の大好きな 2 つの趣味を組み合わせる機会を与えてくれる、私にとって非常にエキサイティングな記事です。

アプリケーションの作成に使用されるテクノロジは次のとおりです:

  1. Node.js
  2. ソケット.io
  3. MySportsFeeds.com
  4. Preact (React と同様)
  5. HTM

Node.js をまだインストールしていない場合は、続行する前に今すぐダウンロード ページにアクセスしてセットアップしてください。

Socket.io とは何ですか?

Socket.io は、WebSocket を使用してクライアントをサーバーに接続するテクノロジーです。この例では、クライアントは Web ブラウザー、サーバーは Node.js アプリケーションです。サーバーには、いつでも複数のクライアントを接続できます。

接続が確立されると、サーバーはすべてのクライアントまたは単一のクライアントにメッセージを送信できます。その代わりに、クライアントはサーバーにメッセージを送信できるため、双方向のリアルタイム通信が可能になります。

Socket.io 以前は、Web アプリケーションは通常 AJAX を使用し、クライアントとサーバーの両方が相互にイベントをポーリングしていました。たとえば、AJAX 呼び出しは 10 秒ごとに発生し、処理する必要のあるメッセージがあるかどうかを確認します。

メッセージのポーリングは、メッセージが存在しないときに常にメッセージを検索するため、クライアントとサーバーの両方に多大なオーバーヘッドを生じます。

Socket.io を使用すると、メッセージを探す必要がなく即座にメッセージを受信できるため、オーバーヘッドが削減されます。

Socket.io アプリケーションの例

リアルタイム モーション データを扱う前に、Socket.io がどのように機能するかを示すサンプル アプリケーションを作成しましょう。

まず、新しい Node.js アプリケーションを作成します。プロジェクトを配置するフォルダーに移動し、アプリケーション用に新しいフォルダーを作成してから、新しいアプリケーションを作成します。 リーリー

すべてデフォルト設定を使用しました。

Web アプリケーションを作成しているため、セットアップを簡素化するために Express という NPM パッケージを使用します。コマンド プロンプトで、次のようにインストールします:

npm installexpress - 保存###### もちろん、Socket.io パッケージをインストールする必要があります: npm install ソケット.io --save

Web サーバーを作成することから始めましょう。 index.js

という名前の新しいファイルを作成し、次のコードをそのファイルに挿入して、Express を使用して Web サーバーを作成します。 リーリー

Express を初めて使用する場合は、上記のコード例に Express ライブラリが含まれており、新しい HTTP サーバーが作成されます。この例では、HTTP サーバーはポート 3000 (https://localhost:3000 など) でリッスンしています。ルートはサイトのルート ディレクトリ「/」に作成されます。ルーティング結果は HTML ファイル、index.html を返します。 Index.html ファイルを作成する前に、Socket.io をセットアップしてサーバーを完成させましょう。以下を

index.js

ファイルに追加して、ソケット サーバーを作成します:

リーリー

Express と同様に、コードは最初に Socket.io ライブラリをインポートします。これは、io という名前の変数に保存されます。次に、

io

変数を使用して、on 関数を通じてイベント ハンドラーを作成します。リッスンされているイベントは接続です。このイベントは、クライアントがサーバーに接続するたびに呼び出されます。 次に、非常に基本的なクライアントを作成しましょう。 index.html という名前の新しいファイルを作成し、その中に次のコードを追加します。 リーリー

上記の HTML は、Socket.io クライアント JavaScript をロードし、サーバーへの接続を初期化します。例を確認するには、Node アプリケーションを起動します:

nodeindex.js

次に、ブラウザで http://localhost:3000 に移動します。ページには何も表示されませんが、Node アプリケーションを実行しているコンソールを見ると、次の 2 つのメッセージが記録されます。

HTTPサーバーがポート3000で起動しました

クライアント接続の受信

    ソケット接続が正常に確立されたので、それを使用してみましょう。まずサーバーからクライアントにメッセージを送信しましょう。その後、クライアントはメッセージを受信すると、サーバーに応答を送り返すことができます。
  1. 短縮されたindex.jsファイルを見てみましょう:
  2. リーリー
  3. 以前の
io.on

関数は、いくつかの新しいコード行を含むように更新されました。最初の

socket.emit

はメッセージをクライアントに送信します。

sendToClient

はイベントの名前です。イベントに名前を付けることで、クライアントがメッセージをさまざまな方法で解釈できるように、さまざまなタイプのメッセージを送信できます。 2 番目の追加は socket.on で、これにはイベント名 receivedFromClient も含まれています。これにより、クライアントからデータを受け取る関数が作成されます。この場合、データはコンソール ウィンドウに記録されます。 <p>服务器端修改完成;它现在可以从任何连接的客户端发送和接收数据。</p> <p>让我们通过更新客户端以接收 <code class="inline">sendToClient 事件来完成此示例。当它接收到该事件时,它可以将 receivedFromClient 事件响应回服务器。

这是在 HTML 的 JavaScript 部分中完成的,因此在 index.html 文件中,我更新了 JavaScript,如下所示:

const socket = io();

socket.on('sendToClient', function (data) {
    console.log(data);
    socket.emit('receivedFromClient', { my: 'data' });
});
ログイン後にコピー

使用实例化的套接字变量,我们在服务器上具有与 socket.on 函数非常相似的逻辑。对于客户端,它正在监听 sendToClient 事件。一旦客户端连接,服务器就会发送此消息。当客户端收到它时,它会记录到浏览器中的控制台。然后,客户端使用与服务器发送原始事件相同的 socket.emit 。在本例中,客户端将 receivedFromClient 事件发送回服务器。当服务器收到消息时,会将其记录到控制台窗口。

亲自尝试一下。首先,在控制台中运行 Node 应用程序:node index.js。然后在浏览器中加载 http://localhost:3000。

检查 Web 浏览器控制台,您应该会看到记录以下 JSON 数据: {hello: “世界”}

然后,在运行 Node 应用程序的命令提示符中,您应该看到以下内容:

HTTP server started on port 3000
Client connection received
{ my: 'data' }
ログイン後にコピー

客户端和服务器都可以使用接收到的 JSON 数据来执行特定任务。一旦我们连接到实时体育数据,我们将了解更多信息。

运动数据

现在我们已经掌握了如何向客户端和服务器发送和接收数据,可以利用它来提供实时更新。我选择使用体育数据,尽管同样的理论并不限于体育。在开始这个项目之前,我研究了不同的运动数据。我选择的是 MySportsFeeds,因为他们提供免费的开发者帐户(我与他们没有任何关系)。为了访问实时数据,我注册了一个帐户,然后做了一笔小额捐款。捐款起价为 1 美元,数据每 10 分钟更新一次。这对于示例来说是有好处的。

您的帐户设置完毕后,您就可以继续设置对其 API 的访问权限。为了帮助实现这一点,我将使用他们的 NPM 包: npm install mysportsfeeds-node --save

安装包后,可以按如下方式进行 API 调用:

const MySportsFeeds = require("mysportsfeeds-node");

const msf = new MySportsFeeds("1.2", true);
msf.authenticate("********", "*********");

const today = new Date();

msf.getData('nhl', '2017-2018-regular', 'scoreboard', 'json', { 
    fordate: today.getFullYear() + 
    	('0' + parseInt(today.getMonth() + 1)).slice(-2) + 
		('0' + today.getDate()).slice(-2),
	force: true
});
ログイン後にコピー

在上面的示例中,请务必将对验证函数的调用替换为您的用户名和密码。

以下代码执行 API 调用以获取今天的 NHL 记分牌。 fordate 变量是今天指定的。我还将 force 设置为 true ,以便始终返回响应,即使数据没有更改。

使用当前设置,API 调用的结果将写入文本文件。在最后一个例子中,这将被改变;但是,出于演示目的,可以在文本编辑器中查看结果文件以了解响应的内容。结果包含一个记分板对象。该对象包含一个名为 gameScore 的数组。该对象存储每场比赛的结果。每个对象都包含一个名为 game 的子对象。该对象提供有关谁正在玩的信息。

在游戏对象之外,还有一些变量提供游戏的当前状态。数据根据游戏状态而变化。例如,当游戏还没有开始时,只有几个变量告诉我们游戏没有进行并且还没有开始。

当游戏进行时,会提供有关得分、游戏进行的时间段以及剩余时间的附加数据。当我们进入下一节中显示游戏的 HTML 时,我们将看到这一点。

实时更新

我们已经掌握了拼图的所有碎片,所以现在是时候将拼图拼凑起来以揭示最终图片了。目前,MySportsFeeds 对向我们推送数据的支持有限,因此我们必须从他们那里轮询数据。幸运的是,我们知道数据每 10 分钟只更改一次,因此我们不需要通过过于频繁地轮询更改来增加开销。一旦我们轮询它们的数据,我们就可以将这些更新从服务器推送到所有连接的客户端。

为了执行轮询,我将使用 JavaScript setInterval 函数每 10 分钟调用一次 API(在我的例子中)以查找更新。收到数据后,会将一个事件发送到所有连接的客户端。当客户端收到事件时,游戏分数将在网络浏览器中使用 JavaScript 进行更新。

当 Node 应用程序首次启动时,MySportsFeeds 也会被调用。此数据将用于在第一个 10 分钟间隔之前连接的任何客户端。这存储在全局变量中。这个相同的全局变量作为间隔轮询的一部分进行更新。这将确保当任何新客户端在轮询后连接时,他们将拥有最新的数据。

为了帮助主 index.js 文件中的一些代码整洁,我创建了一个名为 data.js 的新文件。该文件将包含一个导出的函数(可在 index.js 文件中找到),该函数执行先前对 MySportsFeeds API 的调用。以下是该文件的完整内容:

const MySportsFeeds = require("mysportsfeeds-node");

const msf = new MySportsFeeds("1.2", true, null);
msf.authenticate("*******", "******");

const today = new Date();

exports.getData = function () {
    return msf.getData("nhl", "2017-2018-regular", "scoreboard", "json", {
		fordate:
			today.getFullYear() +
			("0" + parseInt(today.getMonth() + 1)).slice(-2) +
			("0" + today.getDate()).slice(-2),
		force: true,
	});
};
ログイン後にコピー

导出 getData 函数并返回调用结果,在本例中是一个将在主应用程序中解析的 Promise。

现在让我们看看index.js文件的最终内容:

const app = require("express")();
const http = require("http").Server(app);
const io = require("socket.io")(http);
const data = require("./data.js");

// Global variable to store the latest NHL results
let latestData;

// Load the NHL data for when client's first connect
// This will be updated every 10 minutes
data.getData().then((result) => {
    latestData = result;
});

app.get("/", function (req, res) {
	res.sendFile(__dirname + "/index.html");
});

http.listen(3000, function () {
	console.log("HTTP server started on port 3000");
});

io.on("connection", function (socket) {
	// when clients connect, send the latest data
	socket.emit("data", latestData);
});

// refresh data
setInterval(function () {
	data.getData().then((result) => {
		// Update latest results for when new client's connect
		latestData = result;

		// send it to all connected clients
		io.emit("data", result);

		console.log("Last updated: " + new Date());
	});
}, 300000);
ログイン後にコピー

上面的前七行代码实例化了所需的库和全局 latestData 变量。最终使用的库列表是:Express、HTTP、Socket.io 以及刚刚创建的上述 data.js 文件。

完成必要的处理后,应用程序会为服务器首次启动时将连接的客户端填充 latestData

// Global variable to store the latest NHL results
const latestData;

// Load the NHL data for when client's first connect
// This will be updated every 10 minutes
data.getData().then((result) => { 
    latestData = result;
});
ログイン後にコピー

接下来的几行设置了网站根页面(http://localhost:3000/)的路由,并启动HTTP服务器监听3000端口。

接下来,设置 Socket.io 来查找连接。当收到新连接时,服务器会发出一个名为 data 的事件,其中包含 latestData 变量的内容。

最后,最后一段代码创建轮询间隔。当间隔发生时,latestData 变量将使用 API 调用的结果进行更新。然后,该数据向所有客户端发出相同的数据事件。

// refresh data
setInterval(function() {
    data.getData().then((result) => { 
		// Update latest results for when new client's connect
		latestData = result; 
	
		// send it to all connected clients
		io.emit('data', result);
		
		console.log('Last updated: ' + new Date());
	});
}, 300000);
ログイン後にコピー

您可能会注意到,当客户端连接并发出事件时,它会使用套接字变量发出事件。此方法将仅将事件发送到连接的客户端。在该间隔内,全局 io 用于发出事件。这会将事件发送给所有客户端。

服务器就完成了。让我们在客户端前端工作。在前面的示例中,我创建了一个基本的 index.html 文件,该文件设置客户端连接,该连接将记录来自服务器的事件并将事件发回。我将扩展该文件以包含已完成的示例。

因为服务器正在向我们发送一个 JSON 对象,所以我将使用 Preact,它就像 React 的优化版本(如果您不熟悉 React,那也没关系)。此外,我将使用 HTM。 HTM 将允许我使用像 React 的 JSX 这样的语法,而无需构建工具。此外,它还包括与 Preact 的集成。

首先,我需要创建一个 id 为 games 的 div

<div id="games"></div>
ログイン後にコピー

然后,我将创建模板。以下是模板的完整脚本(您需要将其放入主要 HTML 脚本中):

import { html, render } from "https://esm.sh/htm/preact";
import { signal } from "https://esm.sh/@preact/signals";
const games = signal([]);
const socket = io();
socket.on("data", function (data) {
    games.value = data;
});

function ordinalSuffix(input) {
	const tenRemainder = input % 10,
		hundredRemainer = input % 100;
	if (tenRemainder == 1 && hundredRemainer != 11) {
		return input + "st";
	}
	if (tenRemainder == 2 && hundredRemainer != 12) {
		return input + "nd";
	}
	if (tenRemainder == 3 && hundredRemainer != 13) {
		return input + "rd";
	}
	return input + "th";
}
function timeLeft(time) {
	const minutes = Math.floor(time / 60);
	const seconds = time - minutes * 60;

	return minutes + ":" + ("0" + seconds).slice(-2);
}
function stats() {
	return html`${games.value.forEach(
		(game) =>
			html`<div class="game">
				<div>
					${game.game.awayTeam.City} ${game.game.awayTeam.Name} at at
					${game.game.homeTeam.City} ${game.game.homeTeam.Name}
				</div>
				<div>
					${(() => {
						if (game.isUnplayed) {
							return `Game Starts at ${game.game.time}`;
						} else if (game.isCompleted === "false") {
							return html`<div>
									Current Score: ${game.awayScore} - ${game.homeScore}
								</div>
								<div>
									${(() => {
										if (game.currentIntermission) {
											return `${ordinalPrefix(
												game.currentIntermission
											)} Intermission`;
										} else if (game.currentPeriod) {
											return html`${ordinalPrefix(
													game.currentPeriod
												)}<br />${timeLeft(
													game.currentPeriodSecondsRemaining
												)}`;
										} else {
											return `1st`;
										}
									})()}
								</div>`;
						} else {
							return `Final Score: ${game.awayScore} - ${game.homeScore}`;
						}
					})()}
				</div>
			</div>`
	)}`;
}
render(stats, document.getElementById("games"));
ログイン後にコピー

这已经很多了!让我们一步步来看看。首先,我们导入 Preact、HTM 和称为 Preact Signals 的东西。我们稍后会详细讨论这一点。

接下来,我们建立 WebSocket 连接。此代码与我们之前的代码相同,除了事件名称和我们对数据执行的操作不同之外。您可能会注意到我们分配数据的对象是一个信号。这是在 Preact 中管理状态的快速方法。您可以在 Preact Signals 页面上阅读更多相关信息。

接下来,我们有一些辅助函数,稍后我们将在实际模板中使用它们。之后,我们就有了模板组件。首先,我们迭代所有游戏并返回每个游戏的标记。

标记的第一部分显示团队。然后,我们进入游戏数据的主要部分。

在下一节中,我们首先检查游戏是否已经开始。如果没有,我们将显示比赛何时开始。如果已经开始,我们会显示当前分数,以及当前时段和剩余时间。这是使用辅助函数的地方。

最后,如果比赛结束,我们只显示最终得分。脚本的最后一行只是在我们之前创建的 div 中渲染模板。

下面是混合了已完成的游戏、正在进行的游戏和尚未开始的游戏时的情况的示例。我不是一个很好的设计师,所以当开发人员制作自己的用户界面时,它看起来就像你所期望的那样。如果需要,您可以创建自己的 CSS 样式。

使用 Node.js 创建实时体育应用程序

这是 HTML 和 JavaScript 的结合体。



    
		Socket.IO Example
	
	
		
		
		<div id="games"></div>
	

ログイン後にコピー

启动 Node 应用程序并浏览到 http://localhost:3000 亲自查看结果!

X 分ごとに、サーバーはクライアントにイベントを送信します。クライアントは更新されたデータを使用してゲーム要素を再描画します。したがって、Web サイトを開いたままにして定期的にチェックすると、ゲームの進行中にゲーム データが更新されるのがわかります。

###結論は###

最終製品では、Socket.io を使用して、クライアントが接続するサーバーを作成します。サーバーはデータを取得してクライアントに送信します。クライアントがデータを受信すると、表示をシームレスに更新できます。これにより、クライアントはサーバーからイベントを受信した場合にのみ作業を実行するため、サーバーの負荷が軽減されます。

ソケットは一方向に限定されず、クライアントはサーバーにメッセージを送信することもできます。サーバーはメッセージを受信すると、いくつかの処理を実行できます。

チャット アプリケーションは通常、このように動作します。サーバーはクライアントからメッセージを受信し、接続されているすべてのクライアントにブロードキャストして、誰かが新しいメッセージを送信したことを示します。

この投稿が気に入っていただけたなら幸いです。私はお気に入りのスポーツの 1 つのためにこのライブ スポーツ アプリを作成しました。

この記事は、Jacob Jackson からの寄稿により更新されました。 Jacob は、Web 開発者、テクニカル ライター、フリーランサー、オープンソースの貢献者です。

以上がNode.js を使用してリアルタイム スポーツ アプリケーションを作成するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!