搜尋
首頁web前端js教程使用 Node.js 創建即時體育應用程序

在今天的文章中,我將示範如何製作一個 Web 應用程式來顯示 NHL 的現場比賽得分。分數將隨著遊戲的進展自動更新。

這對我來說是一篇非常令人興奮的文章,因為它讓我有機會將我最喜歡的兩個愛好結合在一起:發展和運動。

將用於創建應用程式的技術是:

  1. Node.js
  2. Socket.io
  3. MySportsFeeds.com
  4. Preact(如 React)
  5. HTM

如果您尚未安裝 Node.js,請立即訪問其下載頁面並進行設置,然後再繼續。

什麼是 Socket.io?

Socket.io 是一種使用 WebSocket 將客戶端連接到伺服器的技術。在此範例中,客戶端是 Web 瀏覽器,伺服器是 Node.js 應用程式。伺服器可以在任何給定時間有多個客戶端連接到它。

一旦建立連接,伺服器就可以向所有客戶端或單一客戶端發送訊息。作為回報,客戶端可以向伺服器發送訊息,從而實現雙向即時通訊。

在 Socket.io 之前,Web 應用程式通常使用 AJAX,客戶端和伺服器都會互相輪詢以查找事件。例如,每 10 秒就會發生一次 AJAX 調用,以查看是否有任何訊息需要處理。

輪詢訊息會為客戶端和伺服器帶來大量開銷,因為當沒有訊息時,它會不斷地尋找訊息。

使用 Socket.io,可以即時接收訊息,無需查找訊息,從而減少了開銷。

範例 Socket.io 應用程式

在我們使用即時運動資料之前,讓我們先建立一個範例應用程式來示範 Socket.io 的工作原理。

首先,我將建立一個新的 Node.js 應用程式。導航到您想要專案的資料夾,為應用程式建立一個新資料夾,然後建立一個新應用程式:

cd ~/Documents/Nodejs
mkdir SocketExample
cd SocketExample
npm init

我使用了所有預設設定。

因為我們正在製作一個 Web 應用程序,所以我將使用一個名為 Express 的 NPM 套件來簡化設定。在命令提示字元中,如下安裝: npm installexpress --save

#當然,我們需要安裝 Socket.io 套件: npm install socket.io --save

#讓我們從建立 Web 伺服器開始。建立一個名為 index.js 的新文件,並將以下程式碼放入其中以使用 Express 建立 Web 伺服器:

const app = require("express")();
const http = require("http").Server(app);

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

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

如果您不熟悉 Express,上面的程式碼範例包含 Express 庫並建立一個新的 HTTP 伺服器。在此範例中,HTTP 伺服器正在偵聽連接埠 3000,例如https://localhost:3000。路由在網站的根目錄“/”處建立。路由結果回傳一個 HTML 檔案:index.html。

在建立index.html檔案之前,讓我們先透過設定Socket.io來完成伺服器。將以下內容附加到您的 index.js 檔案中以建立 Socket 伺服器:

const io = require('socket.io')(http);

io.on('connection', function(socket){
    console.log('Client connection received');
});

與 Express 類似,程式碼首先匯入 Socket.io 函式庫。它儲存在名為 io 的變數中。接下來,使用 io 變量,透過 on 函數建立事件處理程序。正在監聽的事件是連線。每次客戶端連接到伺服器時都會呼叫此事件。

現在讓我們創建我們非常基本的客戶端。建立一個名為 index.html 的新文件,並將以下程式碼放入其中:

<!DOCTYPE html>
<html>
    <head>
		<title>Socket.IO Example</title>
	</head>
	<body>
		<script src="/socket.io/socket.io.js"></script>
		<script type="module">
			const socket = io();
		</script>
	</body>
</html>

上面的 HTML 載入 Socket.io 客戶端 JavaScript 並初始化與伺服器的連線。若要查看範例,請啟動 Node 應用程式: node index.js

然後,在瀏覽器中導航至 http://localhost:3000。頁面上不會出現任何內容;但是,如果您查看執行 Node 應用程式的控制台,則會記錄兩個訊息:

  1. HTTP 伺服器在連接埠 3000 上啟動
  2. 收到客戶端連線

現在我們已經成功建立了套接字連接,讓我們使用它。讓我們先從伺服器向客戶端發送訊息。然後,當客戶端收到訊息時,它可以將回應發送回伺服器。

讓我們來看看縮寫的index.js 檔案:

io.on("connection", function (socket) {
    console.log("Client connection received");
	socket.emit("sendToClient", { hello: "world" });
	socket.on("receivedFromClient", function (data) {
		console.log(data);
	});
});

之前的 io.on 函數已更新,包含幾行新程式碼。第一個 socket.emit 將訊息傳送到客戶端。 sendToClient 是事件的名稱。透過命名事件,您可以發送不同類型的訊息,以便客戶端可以以不同的方式解釋它們。第二個新增是 socket.on,還包含一個事件名稱:receivedFromClient。這將建立一個接受來自客戶端的資料的函數。在這種情況下,資料將記錄到控制台視窗。

服务器端修改完成;它现在可以从任何连接的客户端发送和接收数据。

让我们通过更新客户端以接收 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 分鐘,伺服器就會傳送一個事件到客戶端。客戶端將使用更新的資料重新繪製遊戲元素。因此,當您保持網站打開並定期查看它時,您會看到遊戲正在進行時遊戲資料刷新。

結論

最終產品使用 Socket.io 建立客戶端連接的伺服器。伺服器獲取資料並將其發送給客戶端。當客戶端收到資料時,可以無縫更新顯示。這減少了伺服器上的負載,因為客戶端僅在從伺服器接收到事件時才執行工作。

套接字不限於一個方向;客戶端也可以向伺服器發送訊息。當伺服器收到訊息後,可以進行一些處理。

聊天應用程式通常會以這種方式運作。伺服器將從客戶端接收訊息,然後廣播到所有連接的客戶端以顯示有人發送了新訊息。

希望您喜歡這篇文章,因為我為我最喜歡的運動之一創建了這個即時運動應用程式!

這篇文章已根據 Jacob Jackson 的貢獻進行了更新。 Jacob 是 Web 開發人員、技術作家、自由工作者和開源貢獻者。

#

以上是使用 Node.js 創建即時體育應用程序的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
Python vs. JavaScript:開發環境和工具Python vs. JavaScript:開發環境和工具Apr 26, 2025 am 12:09 AM

Python和JavaScript在開發環境上的選擇都很重要。 1)Python的開發環境包括PyCharm、JupyterNotebook和Anaconda,適合數據科學和快速原型開發。 2)JavaScript的開發環境包括Node.js、VSCode和Webpack,適用於前端和後端開發。根據項目需求選擇合適的工具可以提高開發效率和項目成功率。

JavaScript是用C編寫的嗎?檢查證據JavaScript是用C編寫的嗎?檢查證據Apr 25, 2025 am 12:15 AM

是的,JavaScript的引擎核心是用C語言編寫的。 1)C語言提供了高效性能和底層控制,適合JavaScript引擎的開發。 2)以V8引擎為例,其核心用C 編寫,結合了C的效率和麵向對象特性。 3)JavaScript引擎的工作原理包括解析、編譯和執行,C語言在這些過程中發揮關鍵作用。

JavaScript的角色:使網絡交互和動態JavaScript的角色:使網絡交互和動態Apr 24, 2025 am 12:12 AM

JavaScript是現代網站的核心,因為它增強了網頁的交互性和動態性。 1)它允許在不刷新頁面的情況下改變內容,2)通過DOMAPI操作網頁,3)支持複雜的交互效果如動畫和拖放,4)優化性能和最佳實踐提高用戶體驗。

C和JavaScript:連接解釋C和JavaScript:連接解釋Apr 23, 2025 am 12:07 AM

C 和JavaScript通過WebAssembly實現互操作性。 1)C 代碼編譯成WebAssembly模塊,引入到JavaScript環境中,增強計算能力。 2)在遊戲開發中,C 處理物理引擎和圖形渲染,JavaScript負責遊戲邏輯和用戶界面。

從網站到應用程序:JavaScript的不同應用從網站到應用程序:JavaScript的不同應用Apr 22, 2025 am 12:02 AM

JavaScript在網站、移動應用、桌面應用和服務器端編程中均有廣泛應用。 1)在網站開發中,JavaScript與HTML、CSS一起操作DOM,實現動態效果,並支持如jQuery、React等框架。 2)通過ReactNative和Ionic,JavaScript用於開發跨平台移動應用。 3)Electron框架使JavaScript能構建桌面應用。 4)Node.js讓JavaScript在服務器端運行,支持高並發請求。

Python vs. JavaScript:比較用例和應用程序Python vs. JavaScript:比較用例和應用程序Apr 21, 2025 am 12:01 AM

Python更適合數據科學和自動化,JavaScript更適合前端和全棧開發。 1.Python在數據科學和機器學習中表現出色,使用NumPy、Pandas等庫進行數據處理和建模。 2.Python在自動化和腳本編寫方面簡潔高效。 3.JavaScript在前端開發中不可或缺,用於構建動態網頁和單頁面應用。 4.JavaScript通過Node.js在後端開發中發揮作用,支持全棧開發。

C/C在JavaScript口譯員和編譯器中的作用C/C在JavaScript口譯員和編譯器中的作用Apr 20, 2025 am 12:01 AM

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。1)C 用于解析JavaScript源码并生成抽象语法树。2)C 负责生成和执行字节码。3)C 实现JIT编译器,在运行时优化和编译热点代码,显著提高JavaScript的执行效率。

JavaScript在行動中:現實世界中的示例和項目JavaScript在行動中:現實世界中的示例和項目Apr 19, 2025 am 12:13 AM

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中