V-ați întrebat vreodată cum sunt construite aplicațiile în timp real? Ați observat vreodată importanța și cazurile de utilizare ale aplicațiilor în timp real?

Dacă sunteți curioși de întrebările de mai sus și aveți nevoie de un răspuns, atunci această postare de blog este pentru dvs.

În primul rând, să identificăm câteva cazuri de utilizare care necesită aplicații în timp real:

  1. Obținerea actualizărilor de locație pentru cabina dvs. pe o hartă a aplicației de rezervare a cabinei.
  2. Primești mesaje noi instantaneu pe aplicația ta preferată de chat.
  3. Informații despre comenzi alimentare actualizate în bucătăria restaurantului dvs. preferat.

Toate acestea sunt scenariile comune ale vieții noastre de zi cu zi în care nu putem tolera o întârziere în actualizarea informațiilor și, prin urmare, avem nevoie de comunicare în timp real.

Tehnologii pentru care se poate folosi comunicare în timp real sunt:

  1. Sondaj scurt: AJAX, creează trafic intens.
  2. Sondaj lung: La fel ca AJAX, dar serverul reține răspunsul până când are o actualizare. După ce a primit-o, clientul trimite o altă cerere și are nevoie de antet suplimentar pentru a fi parcurs înainte și înapoi, provocând cheltuieli suplimentare.
  3. Socluri Web: face posibilă deschiderea comunicării interactive între client și server. Se poate trimite o cerere către server și poate primi răspunsuri determinate de evenimente fără a interoga serverul pentru un răspuns, făcând socket-urile web cea mai buna alegere pentru cazul nostru de utilizare.

Puteți citi informații mai detaliate despre cele trei tehnologii de mai sus Aici.

Vom învăța să creăm o aplicație în timp real, acoperind următorul scenariu.

Imaginează-ți că stai la restaurantul tău preferat și ai un meniu digital. Plasați comanda, iar bucătăria este actualizată cu privire la comanda dvs. în timp real. Când bucătăria este terminată cu comanda, o actualizează și în timp real.

Caracteristici în detaliu:

  1. Plasați comanda: Interfață pentru a selecta cantitatea și a plasa comanda pentru un produs alimentar selectat în bucătărie.
  2. Bucătărie: Interfață care poate fi deschisă pe mai multe bucătării și actualizează în timp real bucătarii și bucătarii în ceea ce privește totalul comenzilor create și a prezis cantitatea de produse alimentare, oferindu-le flexibilitatea de a le actualiza. De asemenea, are o funcționalitate pentru a descărca raportul sub forma unei foi Excel.
  3. Schimbarea prezisă: Interfață pentru actualizarea cantității prevăzute de produse alimentare.
Cum sa creati o aplicatie in timp real folosind Socketio

O demonstrație live din acest scenariu se poate găsi Aici.

Pentru o mai bună înțelegere, deschideți-l în diferite file / dispozitive în același timp pentru a vedea schimbarea datelor în timp real.

Codul sursă este Aici. Simțiți-vă liber să creați ceva inovator / util deasupra acestuia.

Asadar, haideti sa începem.

Stiva de tehnologie:

În față: React.js, Reactstrap, Socket.io

Backend: Node.js (Express), MongoDB, Socket.io

Structura folderului:

/*
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 */ 
 ............

Explicația codului sursă:

În față:

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

Pachete utilizate:

  1. Reactstrap: Componente bootstrap4 ușor de utilizat
  2. Socket.io: Socket.io este o bibliotecă care permite comunicarea în timp real, bidirecțională și bazată pe evenimente între browser și server.
  3. react-html-table-to-excel: Oferă o generație de fișier Excel (.xls) din partea clientului din elementul tabelului HTML.
  4. react-router-dom: Legături DOM pentru react router. Este alcătuit din multe componente importante, cum ar fi BrowserRouter, utilizat atunci când există un server pentru a gestiona cererea dinamică, Switch, Route etc.

Componenta aplicației

cale: src / App.js

Această componentă conține logica principală de rutare a frontendului. Acest fișier este utilizat în src / index.js din modulul Router Browser. Codul de mai jos demonstrează una dintre abordările pentru a menține aplicația dvs. modulară.

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 <Route> 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 <Route> element. */
/* The Route component expects a path prop, which is a string that describes the pathname that the route matches */
/* The <Switch> will iterate over routes and only render the first one that matches the current pathname */
class App extends Component {
  render() {
    return (
      <div className="App">
        <Header />
        <Switch>
          <Route exact path="/" component={PlaceOrder} />
          <Route path="/updatepredicted" component={UpdatePredicted} />
          <Route path="/kitchen" component={Kitchen} />
        </Switch>
      </div>
    );
  }
}
export default App;

Componenta antet

cale: src / global / header.js

Această componentă va fi obișnuită și utilizată în secțiunile cum ar fi Comandă de plasare, Modificare anticipată, Bucătărie. Această abordare ajută la evitarea duplicării codului și menține aplicația modulară.

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: 'http://localhost:3001/'
    };
socket = socketIOClient(this.state.endpoint);
  }
render() {
    return (
      <header>
        <nav>
          <ul className="NavClass">
            <li>
              <NavLink exact to="/">
                Place Order
              </NavLink>
            </li>
            <li>
              <NavLink to="/updatepredicted">Change Predicted </NavLink>
            </li>
            <li>
              <NavLink to="/kitchen"> Kitchen </NavLink>
            </li  >
          </ul>
        </nav>
      </header>
    );
  }
}
export { Header, socket };

Componenta bucatariei

cale: src / main / Kitchen.js

Logica UI a ecranului bucătăriei și codul html se află în această componentă:

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 (
        <tr key={food._id}>
          <td> {food.name} </td>
          <td> {food.ordQty} </td>
          <td> {food.prodQty} </td>
          <td> {food.predQty} </td>
          <td>
            <button onClick={() => this.markDone(food._id)}>Done</button>
          </td>
        </tr>
      );
    });
  }
render() {
    return (
      <Container>
        <h2 className="h2Class">Kitchen Area</h2>
        <ReactHTMLTableToExcel
          id="test-table-xls-button"
          className="download-table-xls-button"
          table="table-to-xls"
          filename="tablexls"
          sheet="tablexls"
          buttonText="Download as XLS"
        />
<Table striped id="table-to-xls">
          <thead>
            <tr>
              <th>Name</th>
              <th>Quantity</th>
              <th>Created Till Now</th>
              <th>Predicted</th>
              <th>Status</th>
            </tr>
          </thead>
          <tbody>{this.getFoodData()}</tbody>
        </Table>
      </Container>
    );
  }
}
export default Kitchen;

Componenta Comandă de plasare

cale: 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) < 0) {
      event.target.value = 0;
    }
    var new_array = this.state.food_data.map(food => {
      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 (
        <tr key={food._id}>
          <td> {food.name} </td>
          <td>
            <input
              onChange={e => this.changeQuantity(e, food._id)}
              value={food.order}
              type="number"
              placeholder="Quantity"
            />
          </td>
          <td>
            <button onClick={() => this.sendOrder(food._id)}>Order</button>
          </td>
        </tr>
      );
    });
  }
render() {
    return (
      <Container>
        <h2 className="h2Class">Order Menu</h2>
        <Table striped>
          <thead>
            <tr>
              <th>Product</th>
              <th>Quantity</th>
              <th>Order</th>
            </tr>
          </thead>
          <tbody>{this.getFoodData()}</tbody>
        </Table>
      </Container>
    );
  }
}
export default PlaceOrder;

Încă o secțiune numită Actualizare cale predusă: src / main / UpdatePredicted.js similară secțiunii de mai sus se află în depozitul de coduri.

Backend

Pornirea backend-ului:

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

Pachete utilizate:

  1. Călugăr: Un strat mic care oferă îmbunătățiri de utilizare simple, dar substanțiale, pentru utilizarea MongoDB în cadrul Node.JS.
  2. Socket.io: Socket.io este o bibliotecă care permite comunicarea în timp real, bidirecțională și bazată pe evenimente între browser și server.

3. Expres: Cadru web rapid, minimalist pentru nodul.

Codul principal

cale: 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}`));

Bază de date: MongoDB

Mlab: Baza de date ca serviciu pentru MongoDB

Numele colecției: Produse alimentare

Format document: Este necesar cel puțin un document în colecția FoodItems cu formatul menționat mai jos.

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

Sper că ați înțeles cum să creați o aplicație modulară în timp real folosind stiva MERN în tendințe. Dacă ți s-a părut de ajutor bate mai jos, dă stele la proiect repo și împărtășește și cu prietenii tăi.