w Praktyce
10 godz. 44 min · Node.js · Full-stack i Programowanie
Piotr PalarzWeb DeveloperW kursie tym poznasz platformę Node.js od podstaw, aż po bardziej zaawansowane koncepcje. Zaczniemy od omówienia czym jest Node, a także jakie może być zastosowanie tej technologii. Następnie przejdziemy przez proces instalacji i napisanie swojego pierwszego skryptu. Już na samym początku dokładnie omówimy tworzenie własnych modułów, gdyż jest to wiedza niezbędna, by dobrze zrozumieć funkcjonowanie Node. Następnie dowiesz się czym są zdarzenia, jak działa model “publish / subscribe”, a także czym jest “event-driven development”. Chwilę później omówimy pracę z buforami, stream’ami, a także ze standardowym wejściem i wyjściem. Ta wiedza pozwoli Ci zrozumieć jak tworzone aplikacje mogą otrzymywać od użytkownika dane oraz jak je wyświetlają.
Niezwykle ważnym rozdziałem będzie ten, poświęcony pracy z systemem plików. Szczególnie dla dewelopera, pracującego dotychczas z językiem JavaScript wyłącznie w środowisku przeglądarki internetowej. Node pozwala bowiem tworzyć nowe pliki, zapisywać w nich dane oraz odczytywać i usuwać pliki i katalogi. Przy wszystkich tych operacjach, niezwykle ważna jest jednak asynchroniczna natura Node. Dowiesz się jak działa, a także jakie są jej zalety. Na tym etapie nie zabraknie oczywiście praktycznego projektu, którym będzie aplikacja typu CLI (Command Line Interface). Pozwoli nam ona grupowo zmienić nazwy wielu plików, według podanego wzorca.
W kolejnym rozdziale poruszymy fundamentalną dla Node kwestię, mianowicie tworzenie serwerów sieciowych. Zaczniemy od najniższego poziomu, tworząc prosty serwer bazujący na protokole TCP/IP. Wiedza w jaki sposób Node radzi sobie z takim zadaniem, pozwoli później lepiej zrozumieć wyższe warstwy, takie jak np. HTTP czy WebSockets. Chwilę później stworzymy swój pierwszy serwer HTTP, do którego będzie można wysyłać zapytania wprost z przeglądarki internetowej. Omówimy również jak w Node wykorzystać bezpieczne szyfrowanie HTTPS.Na tym etapie będziesz już miał solidną wiedzę jak pracować z Node, jednak zanim przejdziemy dalej, zatrzymamy się, by omówić jak dokładnie działa Node.js, a także jak ta platforma została zbudowana. Dwie dość obszerne lekcje, będą być może jednymi z najważniejszych w tym kursie. Dowiesz się w nich czym jest, a także jak działa jednowątkowa pętla zdarzeń, czym jest proces i wątek, a także zobaczysz kod źródłowy Node.js i wszystkie bloki budulcowe, z jakich Node się składa. To naszym zdaniem niezwykle ważny temat, często jednak pomijany w wielu materiałach. Dzięki dogłębnemu zrozumieniu jednowątkowej natury języka JavaScript i asynchroniczności Node, będziesz mógł tworzyć dużo lepsze aplikacje.
Node.js posiada kilkanaście wbudowanych modułów, z których w dowolnej chwili można skorzystać. Wszystkie jednak dają nam dużą, niskopoziomową kontrolę, ale czasami sporo kodu należy napisać, by zrealizować jakieś zadanie. Jest to jednak świadomy wybór twórców Node, gdyż całą resztę świetnych rozwiązań, dopisuje nieustannie ogólnoświatowa społeczność deweloperów. Zdecydowana większość z modułów ląduje w rejestrze npm. Jest to genialny package manager, który pozwoli nam wyszukiwać, pobierać i aktualizować niezliczoną ilość modułów. Dowiesz się zatem jak korzystać z npm, a chwilę później zaczniemy wykorzystywać zgromadzone tam moduły w dalszej pracy.Jednym z najpopularniejszych modułów jest express.js, który pozwala w bardzo prosty sposób tworzyć serwery HTTP. Grzechem byłoby zatem z niego nie skorzystać. Dowiesz się wszystkiego co niezbędne, by zacząć tworzyć własne aplikacje bazujące na protokole HTTP z użyciem express.js, a także kilku dodatków do tego systemu. Zobaczysz jak routować zapytania, jak korzystać z middleware czy serwować statyczne pliki. Następnie omówimy współpracę z bazą danych MongoDB i z jej wykorzystaniem stworzymy API typu REST. Stworzona aplikacja pozwoli odczytywać dane z bazy i przesyłać je do klienta w formacie JSON, a także dodawać nowe dane, aktualizować i usuwać je. Wszystko to zgodnie z dobrymi praktykami pracy z REST.
Chwilę później czeka Cię kolejny praktyczny projekt - skracacz linków. Stworzymy wspólnie aplikację, która pozwoli skrócić dowolny adres URL do krótkiej formy. Oczywiście będzie działać również w drugą stronę! Kiedy przejdziemy pod skrócony adres, przekieruje nas do odpowiedniej witryny. Node.js to platforma wręcz stworzona do tworzenia aplikacji typu real-time, dlatego w kolejnym rodziale podejmiemy temat technologii WebSockets. Dowiesz się jakie są jej zalety, a także jak pracować z nią po stronie serwera i klienta. Tutaj czeka nas kolejny praktyczny, niezwykle ciekawy projekt. Wykonamy bowiem czat grupowy. Aplikacja ta pozwoli dowolnej ilości użytkowników podłączyć się do czatu podając swój nick, a następnie wysyłać widoczne dla wszystkich wiadomości. Zaimplementujemy nawet takie rozwiązania jak wyświetlanie statusów o dołączeniu kogoś do czatu, a także o jego opuszczeniu.W przedostatnim rodziale tego kursu podejmiemy tematykę dobrych praktyk pracy z Node.js. Na początku omówimy najważniejsze konstrukcje nowej specyfikacji EcmaScript 2015, które są znakomicie wspierane w Node. Chwilę później omówimy jak korzystanie z Promises uprości, a także ulepszy nasz kod. Wśród dobrych praktyk nie zabraknie również informacji o debugowaniu aplikacji. Zobaczysz sprawdzone sposoby, by znaleźć błędy lub lepiej, krok po kroku, zrozumieć jak działa napisany wcześniej kod. Dowiesz się również jak pracować z błędami, by Twoje aplikacje działały w sposób przewidywalny.
Ostatni rozdział w całości został poświęcony temu, co zwykle jest pomijane w innych materiałach, mianowicie wdrażaniu aplikacji do produkcji. Przez cały ten kurs pracować będziemy lokalnie, lecz kiedy aplikacje są już gotowe, wypadałoby udostępnić je światu. Wdrażanie aplikacji napisanych z użyciem Node.js nie jest jednak tak oczywiste, jak np. wgrywanie WordPress’a u wybranego hostingodawcy. Ty będziesz miał jednak możliwość zobaczyć, jak wdrożyć napisany przez nas grupowy czat na platformie Heroku. Jest to bardzo popularny serwis działający jako PaaS (Platform as a Service). Za darmo będziesz mógł w ciągu kilku chwil uruchomić swoją aplikację.Zobaczysz o co należy zadbać, by wszystko poszło gładko. Serwisy typu PaaS dbają o bardzo wiele aspektów wdrażania i serwowania naszych aplikacji, takich jak bezpieczeństwo oraz nieustanną dostępność. Mają jednak pewne ograniczenia. Z tego powodu, dowiesz się również jak wdrożyć swoją aplikację na serwerze wirtualnym VPS z systemem Ubuntu Server. Takie rozwiązanie daje nam całkowitą kontrolę, ale także obarczone jest większą odpowiedzialnością. Zaczniemy od instalacji na serwerze platformy Node.js, systemu kontroli wersji GIT, a także innych niezbędnych modułów
Następnie zobaczysz jak skonfigurować swoje lokalne środowisko tak, by za pomocą GIT’a wysyłać kod do zdalnego serwera, a potem jednym poleceniem wdrażać go do produkcji. Dowiesz się również jak jednocześnie serwować wersję produkcyjną oraz developmencką. W rodziale tym poruszymy również inne kwestie, takie jak procesy potomne czy tworzenie klastrów. Dzięki tej wiedzy, będziesz mógł maksymalnie wykorzystać dostępne zasoby serwera. Na sam koniec tego kursu rzucimy okiem również na inne, nie przedstawione wcześniej zastosowania Node, a także nakreślimy dalszą drogę nauki w tym zakresie.
Kurs ten jest dla wszystkich osób, które dobrze czują się w technologiach frontendowych, tj. HTML, CSS i JavaScript, a teraz chcą rozpocząć swoją przygodę z back-endem. Im zatem lepiej znasz język JavaScript, tym więcej wyciśniesz z Node, natomiast nie jest wymagana bardzo zaawansowana wiedza z zakresu tego języka.
W tej lekcji stworzymy być może najciekawszy projekt w całym tym kursie, mianowicie grupowy czat wykorzystujący
WebSockets.
Zrobimy to na bazie wiedzy, którą już masz, a więc Express, socket.io,
no i do tego wszystkiego dodamy sobie trochę kodu jeszcze po stronie klienta.
Na początek zainstalujmy sobie wszystkie zależności.
Nie ma tutaj nic nowego, czyli npm install. To nam się wgra i będziemy mogli uruchomić serwer za pomocą
nodemona. Ok. Nodemon index i zobaczmy, co mamy w pliku index.
Tutaj nie ma jakichś szczególnych nowości.
Pokażę ci, co do tej pory zrobiłem.
Uruchamiamy sobie io i serwer tak jak w lekcji poprzedniej.
Jedyne co zmieniłem, jest tutaj, czyli renderuję sobie widok home i zauważ, że oprócz title przekazuję mu
jeszcze tablicę z plikami do stylów i ze skryptami.
Tak sobie to wymyśliłem z tego powodu, że jeżeli teraz przejdziemy do views i następnie do partials -
wiesz, że te partials będą kiedy renderujemy sobie home, będzie to wstawione do main, a main będzie się
odwoływał do partials, więc one się będą wczytywać. I w footer
zrobiłem sobie w handlebars takie sprawdzenie i w scripts, czyli jeżeli są podane, to dla każdego z tych skryptów
chcę wywołać taką pętlę.
Jeżeli robisz w handlebars each scripts, to pod this będziesz miał poszczególne elementy, czyli np. jquery, handlebars
i tak dalej.
To samo zrobiłem ze stylami w tym miejscu. I one z kolei są w header.
Czyli dla każdego chcę sobie taki link załadować. W ten sposób łatwiej mi jest pracować, jeżeli chciałbym
dodać tutaj jakieś nowe style czy skrypty.
Dodatkowo to, co tam się pojawiło, to pobrałem bibliotekę socket.io. Nie hostujemy jej już na zewnętrznym
serwerze, ale pobrałem ją i wstawiłem do katalogu public > js. W tym katalogu znajdziesz również bibliotekę
jQuery, którą dołączyłem, i ona jest na stronie ładowana.
Ważne jest, abyś znał przynajmniej podstawy biblioteki jQuery.
Jeżeli nie znasz, to na eduweb.pl znajdziesz obszerny kurs mojego autorstwa na ten temat.
Oprócz tego pobrałem jeszcze ze strony handlebarsjs.com
bibliotekę handlebars do użycia po stronie klienta i również ją ładuję.
Za moment będziemy z tego korzystać.
Czyli jeżeli ten serwer sobie odświeżymy, wygląda to następująco. Poszerzę trochę okno, żeby nam się to
nie rozsypywało. W porządku.
Jeżeli zajrzymy do źródła strony, to to wszystko jest fajnie łączone. Tutaj mamy style, a u dołu będziemy
mieli wszystkie skrypty, które sobie tam wylistowałem. I od tego będziemy chcieli zacząć.
Więc napiszemy kod zarówno po stronie serwera, jak i po stronie klienta. Po stronie klienta poczyniłem
już pewne kroki w pliku scripts.js,
czy w zasadzie w chat.js. Ok, jest w tym miejscu.
Czyli będziemy chcieli standardowo podłączyć się do socketa, na razie go tutaj jednak nie używam. Ale
zrobiłem sobie coś takiego.
Gdybyś zerknąć do views i do home, to mamy tutaj formularz. I ten formularz i ten czat będzie nam przydatny,
Przejrzyj sobie, jak to wygląda.
A ja tutaj w tym pliku chat.js, tym, który będzie po stronie klienta,
zrobiłem tylko takie rzeczy, że kiedy chcemy wysłać taki formularz - ten pierwszy, join, który się wyświetla tutaj,
to będziemy sprawdzać, czy nick został podany.
Jeżeli nie, to dodamy sobie tutaj klasę invalid, która jest czerwona.
Czyli będziemy w tym miejscu.
A jeżeli podamy jakieś nick, bo tak będziemy chcieli na początku zrobić, na przykład czatownik. Dołącz.
To pozwoli nam to wejść na czat. To, co tutaj widzisz, na razie jest na sztywno wpisane, ale jesteśmy w
tym miejscu. Widzisz, że nick był podany,
więc usuwam klasę invalid i robię sobie console.log tego nicku, a także ukrywam formularz, w którym byliśmy,
a pokazuję drugi formularz, który wcześniej domyślnie był ukryty.
Może ja sobie to kliknę tak, żeby widać było. Czyli ten chat-form - teraz go po prostu pokazałem. I za jego
pomocą będziemy mogli wysyłać wiadomości. Przypisałem kolejne zdarzenie do chatForm.on("submit").
Jeżeli będzie message i nie będzie puste, to chcę zrobić jego console.log.
No więc możemy sobie to przetestować. Odświeżę raz jeszcze, żeby od początku zobaczyć.
Jeżeli podam poprawny nick, on się pojawi w konsoli.
Jak widzisz, jest w konsoli. A tutaj jeżeli będę wysyłał wiadomość, to za każdym razem będzie się ona pojawiać
w konsoli. I zamiast tego console.log po prostu wstawimy sobie później odpowiednie zdarzenie, aby wysłać to
do serwera.
Czyli mamy taki już czat przygotowany, natomiast ten czat będzie rozróżniał dwa rodzaje wiadomości.
Pierwsza to będzie wiadomość, tak jak tutaj, gdzie będziemy mieli godzinę, nick oraz wiadomość. Natomiast
druga to będą statusy, czyli np. Tomek opuścił czat
czy Tomek dołączył do czatu.
Będziemy mieli dwa rodzaje takich wiadomości i zajmiemy się tym za moment.
Natomiast najpierw chciałbym przejść na serwer, a później wrócimy do tego kodu, który mamy tutaj. Na serwerze
chciałbym sobie stworzyć osobny plik, w którym będzie znajdowała się nasza aplikacja.
Dlatego tutaj const chat
zrobiłem już require("./chat"), bo
nie chcę tego pisać w tym pliku. I mamy to w osobnej całkiem aplikacji, czyli jest to osobny moduł.
Tutaj będziemy coś pisać i coś eksportować. Jak się domyślasz, będziemy korzystać z io, a io mamy w tym
pliku.
Czyli jeżeli chcę napisać np. io on connection w moim czacie, to nie mam do tego dostępu.
Więc jak zrobić, żeby to było możliwe?
Otóż tutaj będziemy mieli to, co nam zwróci
skrypt chat czy ten moduł.
Będzie to funkcja, bo tak będę chciał zrobić, i wywołam ją, przekazując jej io.
Więc robimy tak: chat(io).
Wygląda to dziwnie, ale to jest po prostu funkcja, którą sobie wyeksportujemy.
Dlatego teraz będę chciał stworzyć funkcję, którą ja nazwę sobie init.
Ona będzie przyjmować io i na końcu zrobimy po prostu module.exports równa się init.
W ten sposób ta funkcja init będzie wyeksportowana tutaj do chat. Wywołamy ją, przekazując jej io.
No i tutaj będziemy mieli do tego io dostęp.
Nic prostszego.
Dlatego teraz tutaj wpiszemy sobie kod, który obsłuży nam taki czat.
Pierwszą rzeczą, którą będziemy chcieli zrobić, to do io, tak jak w lekcji poprzedniej, przypisać za pomocą
on connection. Czyli za każdym razem, kiedy podłączy się jakiś klient, to chcemy wykonać sobie tę funkcję
i klienta mieć pod zmienną socket. I teraz będziemy przypisywać odpowiednie zdarzenia do tego socket
dokładnie tak, jak w lekcji poprzedniej. Natomiast na tym etapie już chcę ci pokazać jedną ciekawą rzecz.
Mianowicie będąc w tym skrypcie, który mamy tutaj, po stronie klienta, będę chciał zrobić rzecz następującą.
Tutaj jeżeli nick został podany, to robiliśmy jego console.log, ale teraz ja nie chciałbym robić console.loga,
ale wysłać go do serwera i wiesz, że możemy się odwołać do socket.emit i wysłać to tam.
Natomiast mówiłem ci, że jako pierwszy parametr możemy podać nazwę zdarzenia, dowolnie możemy sobie go nazwać.
Ja mówię tutaj o zdarzeniach, chociaż może jest to nazwa wiadomości, nie wiem, jak dokładnie to określić.
Natomiast ja sobie ją nazwę join. Mogę ją tak nazwać, nie ma problemu. I tutaj przekażę nick.
W zasadzie to nickName, bo wyżej mamy to w zmiennej w tym miejscu. Czyli do serwera zrobię coś takiego,
że wyślę wiadomość jakby o typie join, która przekaże tam nickName.
Będziemy to mieli wtedy tutaj, ale musimy sobie to przypisać.
I znowu, tak jak w lekcji poprzedniej, socket.on("join"), nazwa tego powiedzmy zdarzenia, funkcja i tutaj powinniśmy
mieć ten przesłany nick.
Pierwszą rzeczą, którą będę chciał zrobić, to do tego socketu, który był utworzony, który jest tutaj, ten
nick sobie przypisać, bo on nam będzie później potrzebny. Więc napiszemy socket, dodamy do niego nick i napiszemy nick.
I teraz skoro ktoś dołączył do czatu, to chciałbym wszystkim innym dać znać, że ktoś dołączył. I teraz
w jaki sposób możemy wysłać informację do wszystkich podłączonych socketów?
Wiesz, że jeżeli wpisałbym socket.emit, to tylko do tego odeślę, ale możemy wpisać io.emit i wtedy odeślemy
wiadomość do wszystkich podłączonych klientów. Bo później zobaczysz, będzie ich więcej. I chciałbym wysłać
do nich wiadomość, którą z kolei nazwiemy sobie status. To znowu jest dowolne i prześlemy tym razem taki
oto obiekt. Biblioteka io.js nam na to pozwala, aby taki json został przesłany. Zostanie to później odpowiednio
sparsowane po stronie klienta również na odpowiedni obiekt.
Chciałbym w każdym takim statusie, tak jak ci pokazałem to tutaj, przesyłać godzinę czy w zasadzie datę. I tę datę
prześlemy sobie w taki sposób, że napiszę time i następnie odwołam się do Date.now. Więc zakładając, że na serwerze mamy
poprawną datę - poprawnie ustawioną -
nie będzie problemu i Date.now zwróci nam ilość milisekund od 1 stycznia 1970 roku i po stronie
klienta sobie to sparsujemy później.
Natomiast jaką drugą rzecz chciałbym przesłać status i tutaj napiszemy nick plus dołączył
do czatu.
Mam nadzieję zatem, że to jest proste i zrozumiałe.
Natomiast nikt tego nie wyświetli, no bo po stronie klienta nigdzie tego nie obsługujemy. Dlatego teraz do socket musimy
przypisać sobie obsługę takiego zdarzenia czy takiej wiadomości status. Zrobimy to poniżej. Socket mamy
na samej górze, jest to io.connect. Odwołujemy się do niego i napiszemy on i status.
Tu będzie funkcja, w niej będzie data. To będzie ten obiekt przysłany z serwera. I na razie sobie będziemy chcieli
tylko wyświetlić.
Więc zrobimy po prostu data.
Teraz to przetestujemy, żeby upewnić się, że ten serwer działa.
Czyli odświeżę tutaj. Podam sobie jakieś nick - czatownik. Dołączam.
Udało się. Dlatego że tutaj mamy jakiś obiekt. On został właśnie odesłany z serwera.
Zobaczmy, co tam jest. Mamy time i status: czatownik dołączył do czatu. Czyli wszystko jest już świetnie.
Wystarczy, że teraz w jakiś sposób z tego obiektu wyświetlimy taką informację w tym miejscu. Natomiast zrobimy
to dopiero za moment, bo chciałbym jeszcze zrobić inne rzeczy na serwerze i pokażę ci, w jaki sposób możemy
się tym zająć.
Chciałbym w momencie kiedy ktoś opuści czat tutaj na serwerze również mieć o tym informację.
I teraz jak można to zrobić?
Otóż oprócz takich wiadomości, które możemy dowolnie nazywać, np. join, to mamy kilka wbudowanych i jedną
z nich jest disconnect.
Dlatego mogę napisać socket.on("disconnect"). Wykona się funkcja. I w tej funkcji będziemy chcieli zrobić
rzecz następującą, mianowicie wysłać do wszystkich, że ta osoba opuściła czat. Więc napiszemy tutaj socket.nick
opuścił czat.
Może na końcu postawimy sobie jeszcze kropki.
Zauważ właśnie tutaj, dlaczego do socket wcześniej dodałem nick. Kiedy się podłączył, dodałem do niego ten nick,
żeby mieć do niego dostęp później, czyli kiedy się rozłączy,
to wszystkim wyślemy taką wiadomość. Natomiast żebyśmy to mogli zobaczyć tutaj,
ja to odświeżę, podłączymy się - czatownik. No to będziemy musieli najpierw podłączyć się jeszcze z innej
przeglądarki.
Dlatego otworzę sobie inną przeglądarkę, przechodzę pod adres localhost 8000, 8080. Podłączymy się do
czatu i napiszemy sobie tutaj
może Kasia. Dołącz. I zauważ, że tutaj pojawił się console.log
od razu, że Kasia dołączył do czatu. Tutaj powinna być dołączyła, ale nie będziemy się tym zajmować.
I teraz jeżeli opuścimy czat, czyli wyjdziemy, przeglądarka zamknie połączenie WebSocket, to zauważ, że tutaj otrzymamy
informację
Kasia opuścił czat.
To jest bardzo istotne, że nam to już wszystko działa.
Informacja do przeglądarki jest przesyłana, więc wystarczy ją za moment odpowiednio wyświetlić.
I teraz jest jeszcze jedna rzecz, którą chcemy zrobić. Mianowicie wyświetlać same wiadomości, czyli takie
jak tutaj, które będą przesyłane. i również nie ma tutaj nic prostszego. Przejdziemy sobie najpierw tutaj, gdzie
wiadomość jest odbierana, czyli jest to tutaj. Robiłem console.log. Pamiętaj - teraz jesteśmy po stronie
klienta. I zamiast console.log zrobimy sobie socket.emit("message") i tutaj przekażemy po prostu message.
W ten sposób wyślemy taką wiadomość do serwera. Na serwerze musimy ją jakoś obsłużyć. Obsługujemy join
czy disconnect.
Więc obsłużmy teraz socket.on("message"). Będzie funkcja, w której będziemy mieli message tutaj.
I znowu co chcemy zrobić?
Otóż za każdym razem gdy ktoś przyśle wiadomość, chcemy ją wysłać do wszystkich innych, w tym również
do tego, który tę wiadomość wysłał. Bo to również jest istotne.
Znowu będziemy mieli time.
Natomiast tutaj zamiast status w tym miejscu napiszemy message.
Tutaj będziemy mieli message. Oczywiście dowolnie sobie możesz to wpisać. Po stronie klienta będziesz
się do tego odwoływał. I tutaj w message nie będziemy mieli oczywiście takiej informacji, tylko wiadomość, którą
ktoś wysłał - msg.
A wcześniej jeszcze chciałbym dodać nick i odwołamy się do socket.nick.
Znowu nam się to przyda.
Ok. Czyli taka wiadomość zostanie do wszystkich wysłana, w tym również do nas.
Więc teraz znowu po stronie klienta musimy na nią nasłuchiwać.
Jeszcze tego nie robiliśmy, więc socket.on("message"). I zauważ, jakie to jest fajne. Kiedy serwer zrobi
emit message, to wtedy po stronie klienta tylko ta funkcja się wykona, a nie ta, bo ta wykona się tylko
dla statusu.
To jest świetna rzecz. Napiszemy tutaj msg, msg. I jeszcze raz będziemy mogli sprawdzić, czy to wszystko
działa. Odświeżę tutaj.
Dołączymy. Mamy tutaj informację, że dołączyliśmy.
I teraz wyślę jakąś wiadomość.
Pamiętaj - ona się wyśle po stronie klienta w tym miejscu. Po stronie serwera zostanie to obsłużone tutaj,
bo mamy nazwę message.
I tutaj nazwę message. I klient odeśle to do wszystkich pod nazwą message. Czyli wszystkie klienty, które
nasłuchują
w ten sposób message, będą to mogły odczytać. Wciskam tutaj Wyślij i pojawiło nam się to w konsoli.
Jak widzisz - message -
hej!
Nick -
czatownik i time.
Dlaczego tutaj podałem sobie np nick?
No właśnie dlatego, że będę chciał go w taki sposób wyświetlić za moment.
To, co jest tutaj, jest na sztywno wstawione w kodzie.
Jak zatem widzisz, skończyliśmy już pisanie naszego serwera.
Wierz mi lub nie, ale jest to cały nasz czat.
Może on nie jest zaawansowany, ale działa poprawnie.
I teraz zajmiemy się jedynie tym, co jest po stronie klienta, abyśmy mogli wszystko poprawnie wyświetlać.
Więc jak będziemy chcieli to zrobić?
Już ci wytłumaczę.
Jeżeli przejdziemy sobie do home.handlebars, to możesz zobaczyć, że te wiadomości, które wystawiłem na sztywno,
są tutaj.
One są w HTML-u, dlatego teraz będę chciał je wykomentować. Ok. Tutaj zrobiło mi taki komentarz handlebarsowy,
więc to zostanie wycięte. Jeżeli teraz bym sobie to zapisał, odświeżymy tutaj, to przechodząc,
możemy to zobaczyć w tym miejscu, do kodu naszej strony
już się nam to nigdzie tutaj nie pojawia. To było tam, gdzie było id chat-window.
Więc mamy tutaj chat-window i nic tam nie ma. Bo pamiętaj, że jesteśmy w pliku handlebars po stronie serwera,
który będzie obsługiwany przez tę bibliotekę właśnie express.handlebars. Więc w porządku, to zostało
wykomentowane.
Natomiast do tego chat-window jesteśmy odwołani tutaj po stronie klienta. W jQuery sobie pobieram odwołanie
do tego elementu, czyli właśnie kiedy strona się wczyta, będzie to ten element i do niego będę chciał
dodawać wiadomości. Ale żebym tych wiadomości nie musiał sklejać w taki brzydki sposób - bo tutaj jeśli raz
jeszcze się podłączymy, to widzisz, że takiego JSONa będziemy dostawać, to już jest sparsowane na obiekt JavaScript -
nie chcę łączyć takiego HTML-a, że np. strong plus object.status plus koniec strong itd.
Dlatego skorzystamy po stronie klienta również z biblioteki handlebars i musimy dla niej utworzyć najpierw
szablony. Teraz ci pokażę, jak to zrobić.
Przejdziemy do pliku home, bo jak wiesz ten plik jest serwowany do naszej przeglądarki. I wstawimy sobie
coś takiego.
Już tłumaczę, o co chodzi.
Otóż zrobiłem skrypt, w którym zamiast wpisać kod JavaScript, to pisałem HTML. Mielibyśmy w przeglądarce
normalnie błąd.
Ale jeżeli wpiszemy type tutaj równa się cokolwiek innego niż text/JavaScript. Możemy wpisać np. text
/
x-handlebars-template -
tak jest to zalecane - to przeglądarka nie będzie tego wczytywać, czyli nie wyskoczy nam, że mamy błąd
w kodzie JavaScript. Bo widzisz, że nie jest to poprawny kod JavaScript.
Przeglądarka to zignoruje, bo musi w ogóle nie mieć type,
to wtedy zakłada, że jest to Javascript albo musi mieć type text/JavaScript. Natomiast do takiego
elementu możemy sobie przypisać ID
i zrobiłem to tutaj - chat-status-template.
Oznacza to tylko tyle, że będę mógł w jQuery znaleźć taki skrypt i pobrać sobie jego kod HTML, a w
tymże kodzie wstawiłem sobie time i status.
Będzie to templatka dla naszego właśnie statusu.
Dlaczego?
No bo właśnie kiedy podłączaliśmy się do serwera, zauważ, czy kiedy był wysyłany status, to otrzymywaliśmy obiekt,
którym jest status i time. Te informacje będziemy chcieli sobie przekazać w tym miejscu.
Ale ten time jeszcze będziemy później odpowiednio formatować. Ok. Zapiszmy to i zobaczmy, co się stanie, kiedy
odświeżę tutaj. A w zasadzie to tutaj, gdzie widzimy kod źródłowy, będziemy mieli problem, bo zauważ, że ten
skrypt, który wstawiłem, się wczytał, ale nie ma tutaj tych informacji, które bym chciał.
Przyznam szczerze, że w momencie kiedy przygotowywałem ten przykład, nie mogłem dojść do tego, co jest
nie tak i dlaczego mi to wszystko nie działa, bo nie zajrzałem tutaj do kodu, a pobierałem sobie tę templatkę
tak, jak to zrobimy za moment w JavaScript.
Otóż okazuje się, że przecież my na serwerze jesteśmy w pliku handlebars, więc każde odwołanie, które tutaj
mamy, np. takie - title, będzie zamieniane przez ten silnik Express na serwerze.
Więc jeżeli ja sobie tutaj wpisałem time i status, tych zmiennych nie było, to handlebars mi to wytnie, a ja
chcę to przesłać do klienta i po stronie klienta z tego korzystać.
Jest proste rozwiązanie. Wystarczy zrobić taki escape przed każdym takim wywołaniem handlebars i wtedy handlebars
po stronie serwera, który ten plik parsuje, zignoruje to. Teraz jeśli odświeżę,
zauważ, że przesłał nam to normalnie do klienta. I teraz po stronie klienta będę mógł się do tego odwołać
i podstawić tutaj odpowiednie rzeczy.
Więc jak to zrobić?
Rzecz jest prosta. Przejdziemy sobie do tej funkcji czy do tego pliku chat i tutaj będę chciał ten skrypt pobrać
po ID. Zapiszę go sobie tutaj w zmiennej.
Natomiast nie tylko go pobrałem, ale jeszcze przepuściłem przez handlebars.compile. Na stronie mamy podłączoną taką
bibliotekę, zobacz na samym dole, przed naszym skryptem chat, więc mam dostęp do handlebars.compile. Tutaj musimy
podać tekst i właśnie w tym tekście musimy mieć takie odwołanie jak tutaj.
Więc ten tekst pobieramy sobie, znajdując po id ten element przez jQuery. Metodą jQuery HTML wycinamy
sobie to, co jest w środku, czyli cały ten tekst. I ten tekst przekazujemy dopiero do handlebars.compile.
To, co nam zwróci handlebars.compile tutaj, to będzie funkcja. Do tej funkcji wystarczy, że teraz przekażemy odpowiednie
dane, właśnie takie jak tutaj nam były odsyłane przed momentem, czyli obiekt JavaScript. I te dane zostaną
wstawione w odpowiednie miejsca dokładnie tutaj. Czyli podobnie jak na stronie serwera robiliśmy,
po stronie serwera.
Przejdźmy sobie zatem niżej.
Tu mamy chat status template. Skopiuję to. W miejscu gdzie przychodził status i data, to zrobimy sobie coś takiego.
Var html, wywołam tę funkcję z data i ona nam zwróci kod HTML
już gotowy, w którym będą zamienione to time i status na to, co było w tym obiekcie. Pamiętaj, że ten obiekt
miał właśnie time i status przysłane z serwera.
I teraz ten HTML wystarczy wstawić na stronę, dlatego nie bez powodu sobie tutaj przypisałem chatWindow pobrane w
jQuery. Odwołamy się do tego obiektu i napiszę append, ale nie w tej funkcji - zaraz do niej wrócimy -
html.
W ten sposób. No i zobaczmy, czy zaczyna to już działać. Mam nadzieję, że zrozumiałe jest to dla ciebie.
Połączymy się, napiszemy czatownik i widzimy: czatownik dołączył do czatu. Czyli taki element został poprawnie
dodany właśnie przez handlebars i jQuery, jak widzisz tutaj. Natomiast abyśmy z tego stringu mogli sobie zrobić odpowiednio
datę, to wrzucę sobie taką funkcję do formatowania daty.
Generalnie wystarczy, że takie cyfry przekażemy do newDate w JavaScript i będziemy mieli obiekt daty, a z
obiektu daty możemy wyłuskać, co nam się podoba.
Dlatego ja tutaj będę chciał zrobić troszkę inaczej.
Przy chatStatusTpl nie przekazywać bezpośrednio tego obiektu, tylko przekażemy taki obiekt i następnie w
tym obiekcie wpiszemy to, co nam tam było potrzebne, czyli time i status.
Więc napiszemy, że status to ma być data.status, czyli nic tutaj nie zmieniamy. Natomiast time to chciałbym,
żeby było formatDate i przekażę tam data.time.
I teraz tę funkcję sobie wrzucę sobie tutaj na samym dole.
Jak widzisz, ona przyjmuje time. I tak jak wspomniałem, new Date(time) stworzy nam nowy obiekt daty i teraz
mogę poprawnie sobie wszystko wyświetlić.
Tutaj robię taką zaawansowaną konstrukcję tylko dlatego, że jeżeli mamy godzinę pierwszą, to chcę wyświetlić
01, tak samo z minutami.
Nie chcę wyświetlić godziny 10:1, tylko 10:01. Więc w ten sposób sobie to sprawdzam. I teraz raz jeszcze jeśli odświeżymy, dołączymy
do czatu, to mamy już poprawną datę. 17:31 i 11 sekund.
Teraz będziemy chcieli zrobić dokładnie to samo, ale dla wiadomości. Dlatego wstawię tutaj już od razu odpowiedni
skrypt. Tym razem ma on id chat-message-template i zauważ, że tutaj robię time, nick oraz message dokładnie w ten
sam sposób. I teraz u góry będę chciał się do niego odwołać, podobnie jak przed momentem wkleję gotowy
kod. Ok. Tym razem będziemy mieli pod zmienną chatMessageTpl funkcję.
Z kolei tę wywołamy sobie tutaj, gdzie przychodzi nam odpowiedź z serwera. Ja też wrzucę gotowy kod, może to zamienimy
sobie po prostu na data.
Czyli znowu nic skomplikowanego. Wywołuję sobie tę funkcję, ona nam zwróci kod HTML, time i formatujemy datę, nick
przekazujemy i message. Czyli to, co nam jest potrzebne: time, nick, message,
dokładnie to jest w tym miejscu i zostanie tu zwrócone.
Wstawimy sobie to do chat-window i nasz czat prawie już będzie gotowy. Dodajmy się znowu do czatu. Mamy:
czatownik dołączył. Napiszmy hej! Wyślemy.
No i widzimy, że wiadomość jest poprawnie wysłana.
Wyślemy jeszcze raz.
Nie ma problemu. Natomiast teraz będę chciał pokazać ci, jak się podłączymy z drugiej przeglądarki, jak
to będzie wyglądać.
Tutaj jestem w Operze i dodamy się może jako Tomek. Zobaczę.
Tutaj się wyświetliło:
Tomek dołączył do czatu, ale również i w tym miejscu. I teraz możemy ze sobą pisać bez żadnego problemu,
nie musimy oczywiście tak jak teraz być na jednym komputerze.
Nie ma to żadnego znaczenia.
Cześć! Pojawi nam się to tutaj, możemy odpisać hej! No i odpisaliśmy, nie ma problemu. Teraz sobie stąd wyjdziemy.
No i widzimy: Tomek opuścił czat.
Oczywiście ten czat należałoby jeszcze bardziej dopracować. Pokażę ci jeszcze tylko jeden problem, jaki
mamy. Jeżeli sobie tutaj odświeżę, a w nowej przeglądarce przejdę sobie raz jeszcze do tego, to tutaj jeszcze
nie dołączyliśmy, a już wiadomości wyświetlamy. Bo zobacz, jeśli tutaj sobie wpiszę np. Tomek. Ok.
I będę coś wysyłał: hejo hejo. Wyślij. To tutaj już jesteśmy podłączeni. I chociaż tego nie widzisz,
to jeśli dołączę, to zauważ, że widzimy te wszystkie wiadomości, które były wcześniej. A chciałbym, żeby ich
tutaj nie było. Żebyśmy nie mogli ich otrzymywać. W zasadzie nie jest to jakiś błąd, ale można tak zrobić.
Więc wróćmy sobie do tego pliku, który mamy po stronie klienta, czyli tutaj, i na samej górze zrobię sobie zmienną
joined i napiszę false. Bo jaki mamy problem? Otóż kiedy ten skrypt się wczyta, to podłączamy się do serwera
i już otrzymujemy informację z tego serwera, a my jeszcze jesteśmy na tym ekranie, gdzie musimy podać
nasz nick, czyli tak naprawdę jeszcze nie dołączyliśmy.
Dlatego będę chciał w miejscu, w którym dołączyliśmy, czyli tutaj, zmienić wartość joined na true i następnie
w każdej funkcji, która przyjmuje jakieś zdarzenie, będziemy sprawdzać if not joined, czyli jeśli jeszcze
nie dołączył, to return - kończymy tę funkcję. Czyli nie będziemy wyświetlać statusu. Jeżeli będzie tam wartość true,
to już będziemy wyświetlać.
Oczywiście to jest takie obejście, bo dobry czat gdybyśmy chcieli napisać, to po stronie klienta warto
skorzystać z różnych frameworków typu np. Angular i byłaby to bardziej poprawnie napisana aplikacja
i nowoczesna, ale musielibyśmy dużo więcej czasu w tym kursie na to poświęcić, abym ci to wytłumaczył.
Dlatego uprościłem to do takiego prostego kodu jQuery.
Teraz już będzie to działać poprawnie.
Możemy zobaczyć. Nie jesteśmy dołączeni jeszcze tutaj.
Jeżeli ktoś tutaj będzie coś wysyłał, to u nas się to nie wyświetla, bo nie możemy tego odbierać.
Nie jest to nigdzie pod spodem wstawiane. Dopiero jeżeli dołączę, to wtedy będą informacje otrzymywane.
Sprawdźmy, czy rzeczywiście. Okazuje się, że tak. Teraz w formie takiej ciekawostki pokażę ci jeszcze jeden
problem, jaki mamy.
Już go widać tutaj.
Otóż musimy skrolować, jak widzisz, jak się pojawi więcej wiadomości.
Więc jeżeli tutaj będę coś wysyłał jeszcze, to te wiadomości są tutaj wstawiane, ale trzeba przeskrolować
na dół, żeby je zobaczyć.
Dlatego pokażę ci prostą funkcję, która pozwoli nam za każdym razem, gdy otrzymamy wiadomość, przeskrolować
tego diva na sam dół i ta funkcja będzie oczywiście po stronie klienta.
Wstawię sobie ją teraz tutaj, gdzie mieliśmy funkcję z datą. I teraz będziemy ją chcieli wywołać, czyli
scrollToBottom - w tym miejscu i w tym miejscu, gdzie wstawiamy te wiadomości. A ona działa tak, że odwołuje
się do chatWindow, które mamy na górze pobrane, czyli właśnie to jest to okno. Zjedźmy niżej. I w jQuery
za pomocą scrollTop ustawia tę wartość na chatWindow,
pobieramy property scrollWindow, czyli w JavaScript możemy sobie sprawdzić - scrollHeight - przepraszam.
W JavaScript można sprawdzić wysokość takiego scrolla maksymalną i następnie przeskrolować za pomocą scrollTop.
Teraz już to będzie działać poprawnie.
Gdybyśmy sobie przeszli tutaj, odświeżyli.
Dołączymy raz jeszcze i przejdziemy do Opery.
Powiedzmy, że tutaj też od nowa wejdziemy.
Tomek i jeżeli ten Tomek będzie ciągle coś wysyłał,
to zauważ, że tutaj już jesteśmy prawie na samym dole.
I zostało to przeskrolowane automatycznie do ostatniego wiersza za pomocą właśnie takiej funkcji.
Jak widzisz, nasz czat jest już w tym momencie gotowy.
Jeżeli ktoś opuści czat, to status zostanie poprawnie również tutaj wysłany.
Nie musieliśmy wiele kodu napisać, by to zrobić.
Ten kod po stronie klienta mógłby być lepszy.
Kod po stronie serwera jest całkiem niezły -
ten, który napisaliśmy tutaj. Jak widzisz, można się już pomieszać, kiedy piszemy kod JavaScript i po stronie
klienta i po stronie serwera.
Ale z drugiej strony jest to swego rodzaju udogodnienie dla nas, bo nie musimy korzystać z wielu języków
programowania.
Polecam ci zatem, abyś spróbował może ten chat troszkę rozwinąć,
może tworzyć prywatne pokoje, bo co się okazuje, to ta biblioteka socket.io pozwala na to, aby tworzyć
takie prywatne pokoje itd.
Na stronie jej dokumentacji pod adresem socket.io
to będziesz mógł znaleźć.
Oczywiście chat to jest tylko jedno z zastosowań, ale za pomocą WebSockets możesz zrobić tak naprawdę dowolne
aplikacje typu real time, do czego serdecznie cię zachęcam.