035.1 Lecke 1
Tanúsítvány: |
Web Development Essentials |
---|---|
Verzió: |
1.0 |
Témakör: |
035 Node.js szerverprogramozás |
Fejezet: |
035.1 Node.js alapok |
Lecke: |
1/1 |
Bevezetés
A Node.js egy JavaScript futtatókörnyezet, amely a JavaScript kódot webszervereken- az úgynevezett webes backend (szerveroldalon)- futtatja, ahelyett, hogy egy második nyelvet, például Pythont vagy Rubyt használna a szerveroldali programokhoz. A JavaScript nyelvet már használják a webes alkalmazások modern frontend oldalán, interakcióba lépve a HTML-el és a CSS-el, amely felülettel a felhasználó a webböngésző segítségével lép interakcióba. A Node.js és a JavaScript együttes használata a böngészőben lehetőséget nyújt arra, hogy az egész alkalmazáshoz csak egy programozási nyelvet használjunk.
A Node.js létezésének fő oka az a mód, ahogyan a backendben a több egyidejű kapcsolatot kezeli. Az egyik leggyakoribb módja annak, ahogy egy webes alkalmazásszerver kezeli a kapcsolatokat, az, hogy több folyamatot hajt végre. Amikor megnyitunk egy asztali alkalmazást a számítógépen, egy folyamat indul el, ami sok erőforrást használ. Most gondoljunk arra, amikor több ezer felhasználó teszi ugyanezt egy nagy webes alkalmazásban.
A Node.js ezt a problémát az event loop nevű konstrukcióval kerüli el, ami egy belső ciklus, amely folyamatosan ellenőrzi a beérkező feladatok kiszámítását. A JavaScript széleskörű használatának és a webes technológiák mindenütt jelenlévő elterjedésének köszönhetően a Node.js óriási népszerűségnek örvend mind a kisebb, mind a nagyobb alkalmazásokban. A Node.js széleskörű elterjedését más jellemzők is elősegítették, mint például az aszinkron és a nem blokkoló bemeneti/kimeneti (I/O) feldolgozás, amelyet a lecke későbbi részében ismertetünk.
A Node.js környezet egy JavaScript-motort használ a JavaScript-kód értelmezésére és végrehajtására a szerveroldalon vagy az asztali számítógépen. Ilyen körülmények között a programozó által írt JavaScript-kódot elemzi és just-in-time lefordítja az eredeti JavaScript-kód által generált gépi utasítások végrehajtásához.
Note
|
A Node.js-ről szóló leckék során észreveheti, hogy a Node.js JavaScript nem teljesen ugyanaz, mint a böngészőben futó JavaScript (amely az ECMAScript specifikációt követi), de meglehetősen hasonló. |
Első lépések
Ezekben a szekciókban és a következő példákban feltételezzük, hogy a Node.js már telepítve van a Linux operációs rendszerre, és hogy a felhasználó már rendelkezik alapvető ismeretekkel, például azzal, hogyan kell parancsokat végrehajtani a terminálban.
A következő példák futtatásához hozzunk létre egy node_examples
nevű mappát! Nyissunk egy terminál promptot, és írjuk be a node
parancsot! Ha helyesen telepítettük a Node.js-t, akkor megjelenik egy >
prompt, ahol interaktívan tesztelhetjük a JavaScript parancsokat. Az ilyen környezetet REPL-nek hívják, ami a “read, evaluate, print, and loop” (olvasás, kiértékelés, kiíratás és ciklus) kifejezésre utal. Írjuk be a következő bemenetet (vagy bármilyen más JavaScript utasításokat) a >
promptoknál. Nyomjuk meg az Enter billentyűt minden sor után, és a REPL környezet visszaadja a műveletek eredményét:
> let array = ['a', 'b', 'c', 'd']; undefined > array.map( (element, index) => (`Elem: ${element}, index: ${index}`)); [ 'Elem: a, index: 0', 'Elem: b, index: 1', 'Elem: c, index: 2', 'Elem: d, index: 3' ] >
A részletet ES6 szintaxissal írtuk, amely egy map függvényt kínál a tömbön való iterációhoz és az eredmények sztring sablonok használatával történő kiírásához. Gyakorlatilag bármilyen érvényes parancsot meg lehet írni. A Node.js terminálból való kilépéshez írjuk be az .exit
parancsot, és ne feledjük, hogy a kezdőpontot ne hagyjuk ki.
Hosszabb szkriptek és modulok esetén kényelmesebb egy szöveges szerkesztő, például a VS Code, az Emacs vagy a Vim használata. Az imént bemutatott két sor kódot (egy kis módosítással) elmenthetjük egy start.js
nevű fájlba:
let array = ['a', 'b', 'c', 'd'];
array.map( (element, index) => ( console.log(`Elem: ${element}, index: ${index}`)));
Ezután futtathatjuk a parancsfájlt a shellből, hogy ugyanazokat az eredményeket kapjuk, mint korábban:
$ node ./start.js Elem: a, index: 0 Elem: b, index: 1 Elem: c, index: 2 Elem: d, index: 3
Mielőtt belevetnénk magunkat a további kódokba, tekintsük át a Node.js működését az egyszálas végrehajtási környezet és az eseményciklus (event loop) segítségével!
Eseményciklus és egy szál
Nehéz megmondani, hogy egy Node.js programnak mennyi időbe telik egy kérés feldolgozása. Egyes kérések rövidek lehetnek—talán csak a memóriában lévő változókon való átfutás és az értékek visszaadása--, míg mások időigényes tevékenységek lehetnek, mint például egy fájl megnyitása a rendszerben vagy egy adatbázisból való lekérdezés és az eredmények megvárása. Hogyan kezeli a Node.js ezt a bizonytalanságot? A válasz az event loop (eseményciklus).
Képzeljünk el egy szakácsot, aki több feladatot is ellát. A süteménysütés az egyik olyan feladat, amelyhez sok időre van szükség, amíg megsül. A szakács nem marad ott a sütő mellett, amíg a sütemény elkészül és majd csak utána indul el kávét főzni, hanem amíg a sütőben megsül a torta, a szakács párhuzamosan kávét főz és más feladatokat végez el. A szakács azonban mindig ellenőrzi, hogy mikor van itt az ideje annak, hogy átirányítsa a figyelmét egy adott feladatra (a kávékészítésre), vagy arra, hogy kivegye a süteményt a sütőből.
Az event loop olyan, mint a szakács, aki folyamatosan tudatában van a környezetében zajló eseményeknek. A Node.js-ben egy “esemény-ellenőrző” folyamatosan ellenőrzi a JavaScript-motor által befejezett vagy feldolgozásra váró műveleteket.
Ezzel a megközelítéssel egy aszinkron és hosszú művelet nem blokkolja az utána következő gyors műveleteket. Ez azért van így, mert az event loop mechanizmusa mindig ellenőrzi, hogy az adott hosszú feladat, például egy I/O művelet, befejeződött-e már. Ha nem, a Node.js folytathatja a többi feladat feldolgozását. Ha a háttérfeladat befejeződött, visszatér az eredményekkel és a Node.js tetején lévő alkalmazás egy trigger-függvényt (callback) használhat a kimenet további feldolgozására.
Mivel a Node.js más környezetekkel szemben kerüli a több szál használatát, ezért egyszálas környezetnek nevezik, és ezért a nem blokkolós megközelítés rendkívül fontos. Ezért használ a Node.js event loopot. Számításigényes feladatok esetén azonban a Node.js nem tartozik a legjobb eszközök közé: vannak más programozási nyelvek és környezetek, amelyek hatékonyabban kezelik ezeket a problémákat.
A következő szekciókban közelebbről fogjuk megnézni a callback függvényeket. Egyelőre elég annyit értenünk, hogy a callback függvények olyan triggerek, amelyek egy előre meghatározott művelet befejezésekor kerülnek végrehajtásra.
Modulok
A legjobb gyakorlat az összetett funkcionalitás és a kiterjedt kódrészletek kisebb részekre bontása. A modularizálás segít jobban megszervezni a kódbázist, absztrahálni az implementációkat, és elkerülni a bonyolult mérnöki problémákat. E szükségletek kielégítése érdekében a programozók forráskódblokk-csomagokat hoznak létre, amelyeket más belső vagy külső kódrészek használnak.
Vegyünk egy olyan programot, amely egy gömb térfogatát számítja ki. Nyissuk meg a szerkesztőt, és hozzunk létre egy terfogatKalkulator.js
nevű fájlt, amely a következő JavaScriptet tartalmazza:
const gombTerfogat = (sugar) => {
return 4 / 3 * Math.PI * sugar
}
console.log(`Egy gömbnek, aminek 3 a sugara, ${gombTerfogat(3)} a térfogata.`);
console.log(`Egy gömbnek, aminek 6 a sugara, ${gombTerfogat(6)} a térfogata.`);
Futtassuk le a fájlt a Node segítségével:
$ node terfogatKalkulator.js Egy gömbnek, aminek 3 a sugara, 113.09733552923254 a térfogata. Egy gömbnek, aminek 6 a sugara, 904.7786842338603 a térfogata.
Itt egy egyszerű függvényt használtunk egy gömb térfogatának kiszámítására, a gömb sugara alapján. Képzeljük el, hogy egy henger, kúp stb. térfogatát is ki kell számolnunk: gyorsan észrevesszük, hogy ezeket a speciális függvényeket hozzá kell adni a terfogatKalkulator.js
fájlhoz, ami így egy hatalmas függvénygyűjtemény lehet. A struktúra jobb rendszerezéséhez használhatjuk a modulokat, mint elkülönített kódokból álló csomagok mögötti elképzelést.
Ehhez hozzunk létre egy különálló fájlt poliederek.js
néven:
const kupTerfogat = (sugar, magassag) => {
return 1 / 3 * Math.PI * Math.pow(sugar, 2) * magassag;
}
const hengerTerfogat = (sugar, magassag) => {
return Math.PI * Math.pow(sugar, 2) * magassag;
}
const gombTerfogat = (sugar) => {
return 4 / 3 * Math.PI * Math.pow(sugar, 3);
}
module.exports = {
kupTerfogat,
hengerTerfogat,
gombTerfogat
}
Most töröljük ki a régi kódot a terfogatKalkulator.js
fájlból és cseréljük le az alábbi részletre:
const poliederek = require('./poliederek.js');
console.log(`Egy gömbnek, aminek 3 a sugara, ${poliederek.gombTerfogat(3)} a térfogata.`);
console.log(`Egy hengernek, aminek 3 a sugara ás 5 magassága, ${poliederek.hengerTerfogat(3, 5)} a térfogata.`);
console.log(`Egy kúpnak, aminek 3 a sugara és 5 a magassága, ${poliederek.kupTerfogat(3, 5)} a térfogata.`);
Futtassuk le a fájlt a Node.js környezetével:
$ node terfogatKalkulator.js Egy gömbnek, aminek 3 a sugara, 113.09733552923254 a téfogata. Egy hengernek, aminek 3 a sugara ás 5 magassága, 141.3716694115407 a térfogata. Egy kúpnak, aminek 3 a sugara és 5 a magassága, 47.12388980384689 a térfogata.
A Node.js környezetben minden forráskódfájl modulnak minősül, de a “modul” szó a Node.js-ben egy kódcsomagot jelöl, amely az előző példához hasonlóan van csomagolva. A modulok használatával absztraháltuk a térfogatfüggvényeket a főfájlból, a terfogatKalkulator.js
-ből, így csökkentve annak méretét, valamint megkönnyítve a unit tesztek használatát, ami jó gyakorlat a valós alkalmazások fejlesztésénél is.
Most, hogy már tudjuk, hogyan használják a modulokat a Node.js-ben, használhatjuk az egyik legfontosabb eszközt: a Node Package Manager-t (NPM).
Az NPM egyik fő feladata a külső modulok kezelése, letöltése és telepítése a projektbe vagy az operációs rendszerre. A node tárolót az npm init
paranccsal inicializálhatjuk.
Az NPM alapértelmezett kérdéseket tesz fel a repository nevével, verziójával, leírásával, stb., kapcsolatban. Ezeket a lépéseket megkerülhetjük az npm init --yes
paranccsal, ami automatikusan létrehoz egy package.json
fájlt, amely leírja a projekt/modul tulajdonságait.
Nyissuk meg a package.json
fájlt a kedvenc szerkesztőnkben, és egy JSON fájlt fogunk látni, amely olyan tulajdonságokat tartalmaz, mint a kulcsszavak, az NPM-mel használandó szkriptparancsok, név, stb.
Az egyik az ilyen tulajdonságok közül a helyi tárolóba telepített függőségek. Az NPM hozzáadja ezeknek a függőségeknek a nevét és verzióját a package.json
fájlhoz, valamint a package-lock.json
fájlhoz, amelyet az NPM tartalékként használ, ha a package.json
meghibásodik.
Írjuk be a következőt a terminálba:
$ npm i dayjs
Az i
jelző az install
argumentum rövidítése. Ha csatlakoztunk az internethez, az NPM megkeresi a dayjs
nevű modult a Node.js távoli tárolójában, letölti a modult, és helyben telepíti. Az NPM ezt a függőséget hozzáadja a package.json
és package-lock.json
fájlokhoz is. Most már láthatjuk, hogy van egy node_modules
nevű mappa, amely tartalmazza a telepített modult más, esetleg szükséges modulokkal együtt. A node_modules
mappa tartalmazza a tényleges kódot, amelyet a mappa importálásakor és hívásakor használni fogunk. Ez a mappa azonban nem kerül elmentésre a Git-et használó verziókezelő rendszerekben, mivel a package.json
fájl tartalmazza az összes felhasznált függőséget. Egy másik felhasználó foghatja a package.json
fájlt, és egyszerűen futtathatja az npm install
parancsot a saját gépén, ahol az NPM létrehoz egy node_modules
mappát a package.json
mappában található összes függőséggel, így elkerülhető az NPM repositoryban elérhető több ezer fájl verziókezelése.
Most, hogy a dayjs
modul telepítve van a helyi mappába, nyissuk meg a Node.js konzolt, és írjuk be a következő sorokat:
const dayjs = require('dayjs');
dayjs().format('YYYY MM-DDTHH:mm:ss')
A dayjs
modul a require
kulcsszóval töltődik be. A modul egy metódusának meghívásakor a mappa átveszi az aktuális rendszeridőt, és a megadott formátumban adja ki:
2020 11-22T11:04:36
Ez ugyanaz a mechanizmus, mint az előző példában, ahol a Node.js futási ideje betölti a harmadik féltől származó függvényt a kódba.
Szerver funkcionalitás
Mivel a Node.js a webes alkalmazások backendjét vezérli, egyik alapvető feladata a HTTP-kérelmek kezelése.
Az alábbiakban összefoglaljuk, hogyan kezelik a webszerverek a bejövő HTTP-kérelmeket. A szerver funkciója az, hogy meghallgatja a kéréseket, a lehető leggyorsabban meghatározza, hogy az egyes kérésekre milyen válaszra van szükség, és ezt a választ visszaküldi a kérés feladójának. Ennek az alkalmazásnak fogadnia kell a felhasználó által kiváltott bejövő HTTP-kérést, elemeznie kell a kérést, el kell végeznie a számítást, ki kell generálnia a választ, és vissza kell küldenie azt. Egy HTTP-modult, például a Node.js-t azért használják, mert leegyszerűsíti ezeket a lépéseket, és lehetővé teszi a webes programozó számára, hogy magára az alkalmazásra összpontosítson.
Nézzük meg a következő példát, amely ezt a nagyon alapvető funkciót valósítja meg:
const http = require('http');
const url = require('url');
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer((req, res) => {
const queryObject = url.parse(req.url,true).query;
let result = parseInt(queryObject.a) + parseInt(queryObject.b);
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end(`Result: ${result}\n`);
});
server.listen(port, hostname, () => {
console.log(`A szerver a http://${hostname}:${port}/ címen fut`);
});
Mentsük el ezeket egy basic_server.js
nevű fájlba, és futtassuk a node
parancs segítségével. A Node.js-t futtató terminál a következő üzenetet fogja megjeleníteni:
A szerver a http://127.0.0.1:3000/ címen fut
Ezután nyissuk meg a következő URL-t a böngészőkben: http://127.0.0.1:3000/numbers?a=2&b=17
A Node.js a számítógépen egy webszervert futtat, és két modult használ: a http
és az url
modulokat. A http
modul beállít egy alapvető HTTP-szervert, feldolgozza a bejövő webes kéréseket, és átadja azokat az egyszerű alkalmazáskódunknak. Az URL modul elemzi az URL-ben átadott argumentumokat, egész szám formátumba konvertálja őket, és elvégzi az összeadási műveletet. Az http
modul ezután a választ szövegként elküldi a webböngészőnek.
Egy valódi webes alkalmazásban a Node.js-t általában arra használják, hogy feldolgozza és lekérje az adatokat, általában egy adatbázisból, és a feldolgozott információkat visszaadja a frontendnek megjelenítésre. A leckében bemutatott alapalkalmazás azonban tömören bemutatja, hogy a Node.js hogyan használja a modulokat a webes kérések webszerverként történő kezelésére.
Gyakorló feladatok
-
Mi az oka annak, hogy modulokat használjunk egyszerű függvények írása helyett?
-
Miért lett ilyen népszerű a Node.js környezet? Nevezzünk meg egy jellemzőt!
-
Mi a célja a
package.json
fájlnak? -
Miért nem ajánlott a
node_modules
mappát elmenteni és megosztani?
Gondolkodtató feladatok
-
Hogyan lehet Node.js alkalmazásokat futtatni a számítógépen?
-
Hogyan lehet az URL-ben lévő paramétereket elhatárolni a szerveren belüli feldolgozáshoz?
-
Adjunk meg egy olyan forgatókönyvet, amelyben egy adott feladat gondot jelenthet egy Node.js alkalmazás számára!
-
Hogyan valósítanánk meg egy paramétert a két szám szorzására vagy összegzésére a fenti szerver-példában?
Összefoglalás
Ez a lecke áttekintést nyújt a Node.js környezetről, annak jellemzőiről és arról, hogyan lehet egyszerű programokat megvalósítani a segítségével. Ez a lecke a következő fogalmakat veszi át:
-
Mi a Node.js, és miért használjuk.
-
Hogyan futtassuk a Node.js programokat a parancssor segítségével.
-
Az event loop és egy szál.
-
Modulok.
-
A Node csomagkezelő (NPM).
-
Szerver funkcionalitás.
Válaszok a gyakorló feladatokra
-
Mi az oka annak, hogy modulokat használjunk egyszerű függvények írása helyett?
Azzal, hogy hagyományos függvények helyett a modulokat választja, a programozó egyszerűbb kódbázist hoz létre, amely olvasható, karbantartható, és amelyhez automatizált teszteket lehet írni.
-
Miért lett ilyen népszerű a Node.js környezet? Nevezzünk meg egy jellemzőt!
Ennek egyik oka a JavaScript nyelv rugalmassága, amelyet már széles körben használtak a webes alkalmazások frontendjében. A Node.js lehetővé teszi egyetlen programozási nyelv használatát az egész rendszerben.
-
Mi a célja a
package.json
fájlnak?Ez a fájl tartalmazza a projekt metaadatait, például a projekt nevét, verzióját, függőségeit (könyvtárak) stb. A
package.json
fájl birtokában mások is letölthetik és telepíthetik ugyanazokat a könyvtárakat és ugyanúgy futtathatják a teszteket, mint az eredeti készítő. -
Miért nem ajánlott a
node_modules
mappát elmenteni és megosztani?A
node_modules
mappa tartalmazza a távoli repositorykban elérhető mappák implementációit. Így a jobb módja ezen mappák megosztásának, ha apackage.json
fájlban jelezzük őket, majd az NPM segítségével letöltjük ezeket a mappákat. Ez a módszer egyszerűbb és hibamentesebb, mert nem kell a mappákat helyben követni és karbantartani.
Válaszok a gondolkodtató feladatokra
-
Hogyan lehet Node.js alkalmazásokat futtatni a számítógépen?
Ezeket úgy futtathatjuk, hogy a terminál parancssorába beírjuk a
node PATH/FILE_NAME.js
parancsot, aPATH
-t a Node.js fájl elérési útvonalára, aFILE_NAME.js
-t pedig a választott fájlnévre változtatva. -
Hogyan lehet az URL-ben lévő paramétereket elhatárolni a szerveren belüli feldolgozáshoz?
Az
&
jelet a paraméterek elválasztására használjuk, hogy a JavaScript kódban ki lehessen őket emelni és elemezni. -
Adjunk meg egy olyan forgatókönyvet, amelyben egy adott feladat gondot jelenthet egy Node.js alkalmazás számára!
A Node.js nem jó környezet CPU-t intenzíven használó folyamatok futtatására, mivel egyetlen szálat használ. Egy numerikus számítás lelassíthatja és blokkolhatja az egész alkalmazást. Ha numerikus szimulációra van szükség, célszerűbb más eszközöket használni.
-
Hogyan valósítanánk meg egy paramétert a két szám szorzására vagy összegzésére a fenti szerver-példában?
Használjunk ternáris operátort vagy if-else feltételt egy további paraméter ellenőrzésére. Ha a paraméter a
mult
karakterlánc, akkor a számok szorzatát adja vissza, ellenkező esetben az összegét. Cseréljük ki a régi kódot az alábbi kódrészletre! Indítsuk újra a kiszolgálót a parancssorban a kbd:[Ctrl+C] billentyűkombinációval és a parancs újbóli futtatásával a kiszolgáló újraindításához! Most teszteljük le az új alkalmazást a böngészőben ahttp://127.0.0.1:3000/numbers?a=2&b=17&operation=mult
URL-cím meglátogatásával. Ha elhagyjuk vagy megváltoztatjuk az utolsó paramétert, az eredménynek a számok összegének kell lennie.let result = queryObject.operation == 'mult' ? parseInt(queryObject.a) * parseInt(queryObject.b) : parseInt(queryObject.a) + parseInt(queryObject.b);