كيفية إنشاء تطبيق في الوقت الفعلي باستخدام Socket.io و React و Node & MongoDB

هل تساءلت يومًا عن كيفية إنشاء تطبيقات الوقت الفعلي؟ هل لاحظت من قبل أهمية واستخدام حالات تطبيقات الوقت الحقيقي؟

إذا كنت مهتمًا بالأسئلة المذكورة أعلاه وتحتاج إلى إجابة ، فإن منشور المدونة هذا يناسبك.

أولاً ، دعنا نحدد بعض حالات الاستخدام التي تتطلب تطبيقات في الوقت الفعلي:

  1. الحصول على تحديثات موقع الكابينة الخاصة بك على خريطة لتطبيق حجز سيارة أجرة.
  2. تلقي رسائل جديدة على الفور على تطبيق الدردشة المفضل لديك.
  3. تحديث معلومات طلب الطعام إلى مطبخ مطعمك المفضل.

هذه كلها سيناريوهات شائعة لحياتنا اليومية حيث لا يمكننا تحمل التأخير في تحديث المعلومات وبالتالي نحتاج إلى اتصال في الوقت الفعلي.

التقنيات التي يمكن استخدامها للاتصال في الوقت الفعلي هي:

  1. استطلاع قصير : AJAX ، يخلق حركة مرور كثيفة.
  2. استقصاء طويل : مثل AJAX ، لكن الخادم يحتفظ بالاستجابة حتى يتم تحديثه. بعد استلامه ، يرسل العميل طلبًا آخر ، ويحتاج إلى رأس إضافي ليتم اجتيازه ذهابًا وإيابًا مما يتسبب في زيادة الحمل.
  3. مآخذ الويب : تجعل من الممكن فتح اتصال تفاعلي بين العميل والخادم. يمكن للمرء إرسال طلب إلى الخادم وتلقي ردود مدفوعة بالأحداث دون استطلاع رأي الخادم للرد ، مما يجعل مآخذ الويب الخيار الأفضل لحالة الاستخدام الخاصة بنا.

يمكن قراءة المزيد من المعلومات المتعمقة حول التقنيات الثلاث المذكورة أعلاه هنا.

سنتعلم كيفية إنشاء تطبيق في الوقت الفعلي من خلال تغطية السيناريو التالي.

تخيل أنك جالس في مطعمك المفضل ولديك قائمة طعام رقمية. أنت تضع الطلب ويتم تحديث المطبخ فيما يتعلق بطلبك في الوقت الفعلي. عندما ينتهي المطبخ من الطلب ، يقومون بتحديثه في الوقت الفعلي أيضًا.

الميزات بالتفصيل:

  1. مكان الطلب : واجهة لتحديد الكمية ووضع الطلب لعنصر غذائي محدد في المطبخ.
  2. المطبخ : واجهة يمكن فتحها عبر مطابخ متعددة وتحديثات في الوقت الفعلي للطهاة والطهاة فيما يتعلق بإجمالي الطلبات التي تم إنشاؤها والكمية المتوقعة من المواد الغذائية ، مما يمنحهم المرونة لتحديثها. يحتوي أيضًا على وظيفة لتنزيل التقرير في شكل ورقة إكسل.
  3. توقع التغيير : واجهة لتحديث الكمية المتوقعة من المواد الغذائية.

يمكن العثور على عرض توضيحي مباشر لهذا السيناريو هنا.

لفهم أفضل ، افتحه في علامات تبويب / أجهزة مختلفة في نفس الوقت لرؤية تغير البيانات في الوقت الفعلي.

شفرة المصدر هنا. لا تتردد في صنع شيء مبتكر / مفيد فوقه.

اذا هيا بنا نبدأ.

كومة التكنولوجيا:

الواجهة الأمامية : React.js ، Reactstrap ، Socket.io

الخلفية : Node.js (Express) ، MongoDB ، Socket.io

هيكل المجلد:

/* Go to the root directory in the source code and find out the below-mentioned files. This architecture helps in creating a big modular App. */ backend-my-app/ /* Backend code of the app */ server.js /* Socket and backend code resides here*/ build/ /* Optional for deployment of Frontend Build */ package.json /* Backend dependency */ ... public/ src/ /* Frontend Sourcecode */ global/ /* Components getting used everywhere */ header.css header.js main/ Kitchen.js PlaceOrder.js UpdatePredicted.js App.js /* Routing logic and component assembly part */ package.json /* Frontend dependency */ ............

شرح كود المصدر:

نهاية المقدمة:

git clone //github.com/honey93/OrderKitchen.git cd OrderKitchen npm install npm start

الحزم المستخدمة:

  1. Reactstrap: سهولة استخدام مكونات bootstrap4
  2. Socket.io: Socket.io هي مكتبة تتيح الاتصال في الوقت الحقيقي ، ثنائي الاتجاه والقائم على الأحداث بين المتصفح والخادم.
  3. رد فعل-html-table-to-excel: يوفر إنشاء جانب العميل من ملف Excel (.xls) من عنصر جدول HTML.
  4. رد فعل جهاز التوجيه دوم: روابط DOM لجهاز التوجيه المتفاعل. يتكون من العديد من المكونات المهمة مثل BrowserRouter المستخدم عندما يكون هناك خادم للتعامل مع الطلب الديناميكي ، والتبديل ، والمسار ، وما إلى ذلك.

مكون التطبيق

المسار : src / App.js

يحتوي هذا المكون على منطق التوجيه الرئيسي للواجهة الأمامية. يتم استخدام هذا الملف في src / index.js داخل وحدة جهاز التوجيه المستعرض. يوضح الكود أدناه أحد الأساليب للحفاظ على تطبيقك معياريًا.

import React, { Component } from "react"; import "./App.css"; import { Header } from "./global/header"; import { Switch, Route } from "react-router-dom"; import PlaceOrder from "./main/PlaceOrder"; import UpdatePredicted from "./main/UpdatePredicted"; import Kitchen from "./main/Kitchen"; /*The  component is the main part of React Router. Anywhere that you want to only render content based on the location’s pathname, you should use a  element. */ /* The Route component expects a path prop, which is a string that describes the pathname that the route matches */ /* The  will iterate over routes and only render the first one that matches the current pathname */ class App extends Component { render() { return ( ); } } export default App;

مكون الرأس

المسار : src / global / header.js

سيكون هذا المكون شائعًا ويتم استخدامه عبر الأقسام مثل Place Order و Change Predicted و Kitchen. يساعد هذا الأسلوب في تجنب تكرار الكود ويحافظ على وحدات التطبيق.

import React, { Component } from "react"; import { NavLink } from "react-router-dom"; import socketIOClient from "socket.io-client"; import "./header.css"; // The Header creates links that can be used to navigate // between routes. var socket; class Header extends Component { /* Creating a Socket client and exporting it at the end to be used across the Place Order, Kitchen, etc components*/ constructor() { super(); this.state = { endpoint: '//localhost:3001/' }; socket = socketIOClient(this.state.endpoint); } render() { return (   
    
  • Place Order
  • Change Predicted
  • Kitchen
); } } export { Header, socket };

مكون المطبخ

المسار : src / main / Kitchen.js

يوجد منطق واجهة مستخدم شاشة المطبخ ورمز html في هذا المكون:

import React, { Component } from "react"; import { Button, Table, Container } from "reactstrap"; import { socket } from "../global/header"; import ReactHTMLTableToExcel from "react-html-table-to-excel"; class Kitchen extends Component { constructor() { super(); this.state = { food_data: [] // this is where we are connecting to with sockets, }; } getData = foodItems => { console.log(foodItems); this.setState({ food_data: foodItems }); }; changeData = () => socket.emit("initial_data"); /*As soon as the component gets mounted ie in componentDidMount method, firing the initial_data event to get the data to initialize the Kitchen Dashboard */ /* Adding change_data listener for listening to any changes made by Place Order and Predicted Order components*/ componentDidMount() { var state_current = this; socket.emit("initial_data"); socket.on("get_data", this.getData); socket.on("change_data", this.changeData); } /* Removing the listener before unmounting the component in order to avoid addition of multiple listener at the time revisit*/ componentWillUnmount() { socket.off("get_data"); socket.off("change_data"); } /* When Done gets clicked, this function is called and mark_done event gets emitted which gets listened on the backend explained later on*/ markDone = id => { // console.log(predicted_details); socket.emit("mark_done", id); }; getFoodData() { return this.state.food_data.map(food => { return (  {food.name}  {food.ordQty}  {food.prodQty}  {food.predQty}   this.markDone(food._id)}>Done  ); }); } render() { return (  

Kitchen Area

{this.getFoodData()}
Name Quantity Created Till Now Predicted Status
); } } export default Kitchen;

مكوّن الطلب

المسار : src / main / PlaceOrder.js

import React, { Component } from "react"; import { Button, Table, Container } from "reactstrap"; import { socket } from "../global/header"; class PlaceOrder extends Component { constructor() { super(); this.state = { food_data: [] // this is where we are connecting to with sockets, }; } getData = foodItems => { console.log(foodItems); foodItems = foodItems.map(food => { food.order = 0; return food; }); this.setState({ food_data: foodItems }); }; componentDidMount() { socket.emit("initial_data"); var state_current = this; socket.on("get_data", state_current.getData); } componentWillUnmount() { socket.off("get_data", this.getData); } //Function to place the order. sendOrder = id => { var order_details; this.state.food_data.map(food => { if (food._id == id) { order_details = food; } return food; }); console.log(order_details); socket.emit("putOrder", order_details); var new_array = this.state.food_data.map(food => { food.order = 0; return food; }); this.setState({ food_data: new_array }); }; // Changing the quantity in the state which is emitted to the backend at the time of placing the order. changeQuantity = (event, foodid) => { if (parseInt(event.target.value)  { if (food._id == foodid) { food.order = parseInt(event.target.value); } return food; }); this.setState({ food_data: new_array }); }; // To get the initial data getFoodData() { return this.state.food_data.map(food => { return (  {food.name}   this.changeQuantity(e, food._id)} value={food.order} type="number" placeholder="Quantity" />  this.sendOrder(food._id)}>Order  ); }); } render() { return (  

Order Menu

{this.getFoodData()}
Product Quantity Order
); } } export default PlaceOrder;

قسم آخر يسمى Update Predicted Path: src / main / UpdatePredicted.js المماثل للقسم أعلاه يوجد في مستودع الكود.

الخلفية

بدء الخلفية:

cd backend-my-app npm install node server.js

الحزم المستخدمة:

  1. Monk : طبقة صغيرة توفر تحسينات بسيطة ولكنها جوهرية في الاستخدام لاستخدام MongoDB داخل Node.JS.
  2. Socket.io : Socket.io هي مكتبة تتيح الاتصال في الوقت الحقيقي ، ثنائي الاتجاه والقائم على الأحداث بين المتصفح والخادم.

3. Express : إطار ويب سريع ومبسط للعقدة.

كود الرئيسي

المسار : backend-my-app / server.js

const express = require("express"); const http = require("http"); const socketIO = require("socket.io"); // Connection string of MongoDb database hosted on Mlab or locally var connection_string = "**********"; // Collection name should be "FoodItems", only one collection as of now. // Document format should be as mentioned below, at least one such document: // { // "_id": { // "$oid": "5c0a1bdfe7179a6ca0844567" // }, // "name": "Veg Roll", // "predQty": 100, // "prodQty": 295, // "ordQty": 1 // } const db = require("monk")(connection_string); const collection_foodItems = db.get("FoodItems"); // our localhost port const port = process.env.PORT || 3000; const app = express(); // our server instance const server = http.createServer(app); // This creates our socket using the instance of the server const io = socketIO(server); io.on("connection", socket => { // console.log("New client connected" + socket.id); //console.log(socket); // Returning the initial data of food menu from FoodItems collection socket.on("initial_data", () => { collection_foodItems.find({}).then(docs => { io.sockets.emit("get_data", docs); }); }); // Placing the order, gets called from /src/main/PlaceOrder.js of Frontend socket.on("putOrder", order => { collection_foodItems .update({ _id: order._id }, { $inc: { ordQty: order.order } }) .then(updatedDoc => { // Emitting event to update the Kitchen opened across the devices with the realtime order values io.sockets.emit("change_data"); }); }); // Order completion, gets called from /src/main/Kitchen.js socket.on("mark_done", id => { collection_foodItems .update({ _id: id }, { $inc: { ordQty: -1, prodQty: 1 } }) .then(updatedDoc => { //Updating the different Kitchen area with the current Status. io.sockets.emit("change_data"); }); }); // Functionality to change the predicted quantity value, called from /src/main/UpdatePredicted.js socket.on("ChangePred", predicted_data => { collection_foodItems .update( { _id: predicted_data._id }, { $set: { predQty: predicted_data.predQty } } ) .then(updatedDoc => { // Socket event to update the Predicted quantity across the Kitchen io.sockets.emit("change_data"); }); }); // disconnect is fired when a client leaves the server socket.on("disconnect", () => { console.log("user disconnected"); }); }); /* Below mentioned steps are performed to return the Frontend build of create-react-app from build folder of backend Comment it out if running locally*/ app.use(express.static("build")); app.use("/kitchen", express.static("build")); app.use("/updatepredicted", express.static("build")); server.listen(port, () => console.log(`Listening on port ${port}`));

قاعدة البيانات : MongoDB

Mlab : قاعدة البيانات كخدمة لـ MongoDB

اسم المجموعة : FoodItems

تنسيق المستند : هناك حاجة إلى مستند واحد على الأقل في مجموعة FoodItems بالتنسيق المذكور أدناه.

{ "name": "Veg Roll", // Food Name "predQty": 100, // Predicted Quantity "prodQty": 295, // Produced Quantity "ordQty": 1 // Total Order Quantity }

آمل أن تكون قد فهمت كيفية إنشاء تطبيق معياري في الوقت الفعلي باستخدام مكدس MERN الرائج. إذا وجدت أنه من المفيد التصفيق أدناه ، فامنح النجوم لمشروع إعادة الشراء وشاركه مع أصدقائك أيضًا.