Prosty i Nowoczesny Framework dla Vue.js
7 godz. 2 min · Vue.js · Full-stack i Programowanie
Wojciech PołowniakSr. Fullstack EngineerW tym projekcie tworzyć będziemy praktyczny projekt - klona Wikipedii! Minipedia pobierze dane z prawdziwego źrodła danych, zewnętrznego API WikiMedia - będzie łudząco podobna do oryginału oraz współdzieliła najważniejsze funkcjonalności, takie jak dostarczanie treści w różnych wersjach językowych, możliwość wyszukiwania artykułów, czy pobieranie najpopularniejszych wpisów dla danego języka. Stworzymy także podstronę wyświetlającą za każdym razem losowy artykuł w danej wersji językowej.Zobaczysz też, jak dokonać migracji ze statycznej strony HTML na Nuxt - oraz jak tworzyć przykładowe komponenty podczas tego procesu.Dzięki temu wszystkiemu, dowiesz się jak wykorzystać nabytą wiedzę w praktyce.
Dzięki Nuxtowi dowiesz się jak optymalizować Twoje strony www z duchem idei progressive enhancement, dostarczając coraz to lepsze doświadczenia dla swoich użytkowników oraz tworząc aplikację napisaną w 100% w JavaScript, która będzie działała nawet wtedy, gdy użytkownik jawnie wyłączy go w przeglądarce! To wszystko za SSR (Server Side Rendering), dzięki któremu treści będą gotowe do konsumpcji już przy wstępnej, pierwszej odpowiedzi z serwera WWW.Oczywiście, ma to swoje implikacje na wydajność naszej aplikacji - sprawiając że Lighthouse czy optymalizacja pod SEO staje się coraz prostsza!
Czymże jest ekosystem danego frameworka bez wsparcia społeczności! Dlatego podczas tego kursu nauczysz się instalować i wykorzystywać różne pluginy i moduły, które zaoszczędzą Ci dużo czasu. Dowiesz się też, jak tworzyć własne moduły - które rozszerzą działanie Twojej aplikacji Nuxt w sposób czysty i zarządzalny.
Nuxt jest frameworkiem w którym pracujemy zarówno po stronie front-endu, jak i back-endu. W związku z czym, stworzymy swoje własne API (bazujące na express.js) które będzie pewnego rodzaju pośrednikiem pomiędzy API WikiMedia oraz Twoją aplikacją internetową.
W tym kursie, dowiesz się także jak tworzyć swoje własne Middleware'y - czyli fragmenty kodu wywoływane pomiędzy przechodzeniem między stronami, czy podczas innych zapytań HTTP.Dodatkowo, zobaczysz jakie narzędzia daje nam Nuxt aby tworzyć progresywne aplikacje internetowe, oferując wsparcie dla takich funkcjonalności jak np. animacje między przejściami pojedynczymi podstronami naszego serwisu.
Tajemnicze i18n - czyli numeronim odpowiadający angielskiemu słowu "internalisation" - odnosi się w kontekście web developmentu do tłumaczenia danych na różne języki. Podczas realizacji projektu Minipedii, nie tylko wykorzystamy standardowe podejście do tłumaczeń stałych elementów naszego interfejsu użytkownika, ale także przetłumaczymy same dane zwracane z API, wykorzystując inną wersję językową w zależności od preferencji użytkownika!
Poznasz też kilka ciekawych pluginów i rozwiązań dotyczących optymalizacji pod SEO - poza faktem, że podejście spełniające intencje użytkownika jest najlepszym, co możemy zrobić - pluginy generujące nam mapę stron automatycznie na podstawie zadeklarowanych przez nas podstron, czy generatory danych jsonld pomogą w pozycjonowaniu Twojej wydajnej aplikacji jeszcze wyżej w hierarchii wyszukiwarek.
Aby móc efektywnie korzystać z tego kursu, powinieneś posiadać średnio zaawansowaną wiedzę z kategorii Front End, z naciskiem na umiejętność posługiwania się JavaScriptem oraz solidne podstawy NodeJS. Konieczna jest znajomość Vue, jako że Nuxt jest frameworkiem korzystającym z niego u swoich podstaw. Jeśli czujesz się dość swobodnie w wyżej wymienionych aspektach web developmentu, ten kurs będzie idealny dla Ciebie.
Cześć! Wiemy już gdzie możemy podpiąć się pod
różne dane pochodzące z innych API z innych baz danych.
W poziomie Nexta i w tej lekcji będziemy faktycznie kontynuować integrację z wiki.
To jest i przy okazji troszkę posprzątamy też ten kod.
Czyli moglibyśmy zostawić to tak jak jest.
Zwłaszcza ten endpoint pobierający
informacje dotyczące pojedynczej właściwości, ale możemy też przepisać go
na async, który jest bardziej współczesną formą pisania kodu asynchronicznego.
Ponieważ możemy go pisać w sposób
synchroniczny, to znaczy, że bez tutaj tych callback ów jakby
promise to nie kropki oczywiście, ale jest tutaj przekazany funkcja jako parametr.
W związku z czym co możemy zrobić to na naszym.
Wiki page.
Możemy to przypisać do zmiennej i np.
opisać to jako.
Page Promise i jeżeli piszemy wewnątrz funkcji
synchronicznej, to możemy też użyć tak zwanego wita i robi tyle, że
rezerwuje wartość danego promise i przypisuje ją tutaj do tej zmiennej.
W związku z czym to co tutaj sobie
zwracaliśmy do tej pory będzie przypisane od razu w Page Promise.
Pomimo tego, że Page Promise dalej.
Będziemy musieli tutaj
wywołać na nim funkcję info, która też jest promise, w związku z czym robimy to
na takiej zasadzie, że po prostu przypisujemy tutaj info promise.
Nazwa zmiennej oczywiście jest dowolna.
Ja tutaj staram się wykorzystać taką w miarę podobną.
Klawiszem myszy wyłączmy funkcje
wait page info i tutaj musimy użyć oczywiście Page Promise albo po prostu
zostawię to jako page i tutaj zostawmy to jako info, bo już nie będzie.
W momencie gdy zaczniemy przypisanie więc page kropka info property outside i
tutaj będziemy mieli tą samą informację, której wykorzystujemy tutaj,
w związku z czym możemy pozbyć się tych błędów całkowicie.
I zwrócić tutaj jako vario info albo info info.
Nie ma to znaczenia w tym momencie
jest to o wiele prostszy i czytelniejszy kod niż wszystkie komórki.
Jest faktycznie mniej znaków.
Możemy tutaj też spróbować uruchomić ponownie.
I jak widzimy dalej to wszystko działa, w
związku z czym możemy to wpisać w taki sposób.
Kolejna rzecz, którą możemy zrobić to pozbyć się tego przedrostka wiki, zapisać.
No i oczywiście w tym momencie ten element nie zadziała.
Czyli dostajemy czwórkę.
Ale jesteśmy w stanie tutaj przy A+.
Wewnątrz Raus index jest.
Zapisać sobie właśnie ten teleskop tak jakby, czyli to co tam wpisaliśmy wiki.
To będzie dodatkowym sklepem ponad nasze API.
Slash API slash wiki i dopiero później
wchodzą roboty typu właśnie info, page id property.
No więc stworzymy więcej tutaj tych
skrótów, zapiszemy ten kod jeszcze raz, no i zobaczmy dokumentację wiki czy jest.
Tutaj jest kilka opisów różnych funkcji.
My będziemy korzystali z kilku.
Na pewno podstawowymi i takimi, których potrzebujemy użyć jest content,
czyli tutaj to będzie faktycznie ta sekcja bibliografii.
Inaczej jak przejdziemy do naszego local hosta, odświeżamy to.
To będą te nasze sekcje bibliografii.
To będzie content.
Na pewno będzie potrzebowali.
Robimy reset, ponieważ musimy dostać się do do tego obrazka.
Tutaj jest oczywiście jeszcze taka zmienna jak main image, ale niestety aby
wyświetlić ten obrazek tak czy siak będziemy musieli wykorzystać images image.
Po prostu wskaże nam, który z tych obrazków jest głównym obrazkiem samary,
czyli takie podsumowanie tego, o czym jest ta strona.
Czyli na przykład ta sekcja będzie mogła
być w Summary, no i full info będzie nam zwracało
te informacje na temat danej osoby, na temat, na temat danej postaci.
Natomiast tak jak wspominałem, format będzie się różnił w zależności od
danego patcha, w związku z czym to będzie trzeba też zrobić.
Więc ja sobie tylko po kopiuję różne tutaj
metody, z których będziemy korzystać w tym momencie.
Oczywiście będziemy korzystać z większej
ich ilości w przyszłości, więc summary full info.
Images.
I content. Powiedzmy, że to są takie podstawowe.
Będziemy dodawać ich oczywiście więcej.
Więc ja tutaj sobie wkleję wszystkie te metody.
Tutaj nam się Ewka skopiowała.
Możemy użyć
Option albo Alta, aby publikować nasz kursor i pozbyć się tych elementów.
No i możemy też pisać równolegle na kilku liniach.
Czyli jeśli sobie napiszemy router kropka get.
I tutaj weźmiemy.
Content robimy sam, ale full info.
I dalej przekażemy nazwę Page'a.
Podobnie ID.
I tutaj już nie potrzebujemy property, bo
będziemy zwracać całą zawartość, a nie pojedyncze informacje, w
związku z czym tworzymy następnie callback.
Tutaj zrobimy go także callback.
Ten asynchroniczny przekazujemy funkcję,
która przyjmuje jako parametr request i response.
Jest to funkcja strzałka.
Zamykamy callback i możemy zacząć implementację wszystkich z tych metod.
Co możemy zrobić dalej to skopiować sobie
jeszcze raz tutaj tą nazwę tych metod i już widzę, że niestety to nie wygląda
tak ładnie, ale możemy to sobie skopiować ręcznie.
No i to co możemy zrobić dalej to przede wszystkim w każdym z tych
requestów będziemy musieli zrobić request do naszego page'a.
Więc to jest taką troszkę powtarzalną częścią.
Moglibyśmy to oczywiście wydzielić,
ale nie będziemy spędzać na tym tak dużo czasu.
Więc kolejne co będziemy robić, to
faktycznie wykorzystać metody, które tutaj sobie zadeklarowaliśmy.
Czyli jeśli chcielibyśmy pobrać z tego kontent, to robimy wita Wiki.
Kropka.
Content niewielki, tylko page content zapiszemy i tutaj zwrócimy.
Tak samo adres.
Status 200.
Czasem powinniśmy jakoś to ujednolicić,
ale ja po prostu zwrócę tutaj chyba cały całą treść tego contentu tak jak jest.
Jako że to już będzie wrzucone jako
content, a nie pojedyncze, zapiszmy to, zobaczmy co się stanie.
Czyli tutaj zmienię po prostu troszkę to co się tutaj dzieje.
Czyli zamiast wiki info pobierzemy sobie wiki content.
Patrz Janusz Korczak.
Zobaczmy, czy ze spacją nam zadziała.
Cudem Korczak, bo tutaj musimy to przekazać jako
stringa, jeżeli są w tym białe znaki albo jedno.
Bezpiecznego ciągu znaków.
Tutaj niestety terminal jest troszkę za ciasne i z jakiegoś powodu.
Brzydko to się wyświetla.
Jeszcze raz.
Z.
Wysyłamy zapytanie, nie ma odpowiedzi.
Zobaczmy, czy w ogóle to się udało uruchomić konsolowy content.
Przygotujmy serwer dla pewności.
Albo po prostu możemy też zrobić zapytanie
o inną postać w tym momencie, tylko żeby sprawdzić czy to działa.
Zobaczmy Batmana w międzyczasie.
Viki content.
Tutaj oczywiście jest błąd, nie ma takiego endpoint.
Zróbmy sobie tutaj Janusz Korczak Content slash.
Poczekamy aż się nam zbudują, a syty serwer już odpowiada.
Informacji dalej niestety nie ma, więc zobaczymy.
Batman.
I Batman.
Też niestety nie zadziałał.
Zobaczmy content page.
Batman.
Czy my zapisaliśmy?
Nie zapisaliśmy tego w wiki.
Jeszcze raz spróbujmy.
OK, mamy błąd.
Czyli w tym momencie dopiero co kliknęło it is not defined.
Czyli prawdopodobnie nie wyjąłem.
Oczywiście, że nie wyjąłem naszych properties ów z parametrów, czyli
tak naprawdę tutaj ten parametr jest zadeklarowany, ale ja z niego nie
korzystałem, bo po prostu nie przypisałem do zmiennej wewnątrz tego callback.
W związku z czym wstawiam go tutaj. Zapiszmy ponownie.
Mam nadzieję, że.
Nas się przewiduje.
Jeszcze raz Batman.
Widzimy, że teraz request wisi troszkę
dłużej, więc mam nadzieję, że zwróci nam te informacje.
Prawdopodobnie będziemy musieli troszkę to skasować.
To znaczy zrobimy sobie po prostu kopię lokalną tych danych zwracanych przez
Wikipedię, bo API potrafi długo nie odpowiadać.
Jestem ciekawy, czy to faktycznie trwa tak
długo, czy dobrze zabijemy ten proces i spróbujmy ponownie Janusza Korczaka.
I nie ma żadnej zwrotki.
Nie ma też nic wewnątrz
naszego console loga, więc aby przechwycić wyjątki spośród pracy z promieniami
poprzez async musimy użyć tricks tak jakbyśmy obsługiwali zwykłe exception.
I tutaj możemy zrobić, że normalnie będzie nam się zwracało gwiazdka z informacjami.
Tak tylko że teraz edytuje nie ten endpoint co trzeba.
Stała, że zostawiam, chociaż już mogłem
też zostawić to jako lepiej obsłużony wyjątek.
Więc jeszcze raz przesuwamy całą zawartość do jego środka.
Ch.
Konsolowe.
Albo console error.
Error.
I tutaj return.
Status 500 jako błąd internal error
i możemy też wyświetlić sobie tutaj ten błąd.
Zobaczymy co się stanie po zapisaniu ponownie.
Widać API content.
Pan Janusz Korczak nie działa.
Batman.
Batman nam się zwrócił i faktycznie
wychodzi na to, że po prostu ID nie jest dobrze przekazywane.
Zobaczmy co się dzieje.
I ponownie Janusz Korczak.
Jeszcze raz.
Niestety nie widzę tutaj tej zwrotki, ale może to po prostu występować.
Czyli spacja w Juri komponencie to jest 20.
I wysłaniu faktycznie działa.
Czyli nie możemy.
Wysłać tego w ten sposób, bo nam to niestety po prostu nie zadziała.
Zapiszemy.
I jeszcze jedną istotną rzecz, którą
powinniśmy zrobić, zanim przejdziemy do kolejnych wpisów, jest sprawienie, aby
nasze wiki korzystało jawnie z danej wersji Wikipedii z danym nagłówkiem.
Co to znaczy?
W tym momencie nie pokazujemy żadnej wartości tutaj do konstruktora wiki.
Który pobraliśmy sobie z Wiki.
Teraz nie jest to w sumie konstrukcja, jest to po prostu funkcja
i powinniśmy zrobić sobie tutaj nową instancję albo po prostu kopię.
Tego wiki to możemy zmienić nazwę na wiki.
Czy jest.
To może być z wielkiej litery.
Wiki nie ma znaczenia, możemy to sobie później ujednolicić.
I takie właśnie widzi teraz.
Powinniśmy mu przekazać pewien obiekt.
Czyli tutaj ten obiekt.
Tutaj coś tam się skopiowała.
Będzie zawierał w sobie takie informacje.
Jest to widoczne w dokumentacji.
Możemy do tego dojść jak to zrobić?
I kwestia jest taka, że Wikipedia jest dosyć publicznym zasobem
i jest wiele osób, które korzystają z tego API, w związku z czym Wikipedia
ogranicza czasami zasoby, którymi możemy dysponować w ramach.
Danego ID i danych zapytań, w związku z czym tutaj jawne określenie
naszego usera agenta, który może być naszym własnym opisem.
Nasz adres email Możemy tu przepisać naszą
stronę www, nazwę naszego bota to możemy to w nagłówkach do wiki przekazać.
I tutaj.
API URL też możemy zmieniać w zależności, np.
jeżeli chcemy użyć innej wersji językowej Wikipedii.
No to w ten sposób to robimy.
Więc ja to tylko zapiszę.
To powinno dalej działać w taki sam sposób.
Zobaczmy co to jest za błąd na linijce ósmej.
Prawdopodobnie tutaj.
Tutaj jest Real, tutaj nie ma jednego nawiasu i tutaj kolejny nawias okrągły.
W tym momencie to będzie działać.
Zapiszemy.
Jeszcze raz zapiszmy.
Chyba trzeba zrestartować serwer
i tak jak widzimy tutaj nie jest to zbyt dobrze widoczne, bo niestety
pracujemy tutaj z Carlem, ale to co jest nam zwracane jest to
format title content, który już przygotowaliśmy wcześniej.
Jak widzimy jest tutaj title, gdzieś tam
niżej jest content właśnie i jest to sekcja graficzna, którą
faktycznie mieliśmy wyświetlaną już na stronie.
W związku z czym będziemy w stanie bezpośrednio użyć tych
danych w naszych komponentach, gdy najdzie na to czas.
I teraz analogicznie wykorzystajmy to nowo stworzone wiki.
Aby nie wywołać nowej instancji, tylko
wykorzystać tą, która już istnieje i po zapisaniu.
Trzymamy kciuki.
Dalej to wszystko działa.
Więc mamy obsłużone już dwa.
Endpoint pierwszy jest to nasz klasyczne
info page, kolejne to jest content page, no i następne robimy rest.
Możemy też analogicznie zrobić na tej samej zasadzie kopiuj wklej.
I zamiast content po prostu wywołać images.
Zamiast tej właściwości wykorzystać summary i zamiast tutaj tego.
Bierzemy full info, zapiszemy i możemy to jeszcze szybciutko przetestować.
Czyli zamiast content wpisujemy full info.
I dostajemy informacje
w takim formacie, że dostajemy General i jest tam obiekt z różnymi informacjami.
Analogicznie Samary.
Tutaj dostajemy to jako stringa.
I ostatnie nad które tutaj stworzyliśmy, bo robimy.
I faktycznie jest to jakiś tutaj duży obiekt z wieloma informacjami, z których
najprawdopodobniej będziemy korzystać tylko z jakiegoś małego fragmentu.
Więc tak wygląda potencjalna integracja z różnymi API.
Nie jest to oczywiście produkcyjne rozwiązanie.
Powinniśmy to wszystko skasować.
Powinniśmy ładnie to rozbijać, ale jest to
najprostsza implementacja, jaką mogliśmy zrobić, nie ustępując
pewnym dobrym praktykom, jak na przykład podział odpowiedzialności na chociażby
różne pliki i stosowanie w miarę spójnych wzorców dla naszych kształtów.
Więc to wszystko w tej lekcji, w
następnych sekcjach tego kursu będziemy wykorzystywać to API, które tutaj już
sobie stworzyliśmy wewnątrz nas i podpinać je pod gotowe komponenty, które
już przygotowaliśmy, by następnie stworzyć dynamiczne
podstrony, które będą ładowały treści dynamicznie.
Zmiany wprowadzone w lekcji · 16 min
diff --git a/15/minipedia/api/index.js b/15/minipedia/api/index.js
index 619d0b7..ec72f3b 100644
--- a/15/minipedia/api/index.js
+++ b/15/minipedia/api/index.js
@@ -5,6 +5,6 @@ import Wiki from './routes/wiki';
const app = Express();
app.use(Ping);
-app.use(Wiki);
+app.use('/wiki/', Wiki);
export default app;
\ No newline at end of file
diff --git a/15/minipedia/api/routes/wiki.js b/15/minipedia/api/routes/wiki.js
index 5683060..a657fc5 100644
--- a/15/minipedia/api/routes/wiki.js
+++ b/15/minipedia/api/routes/wiki.js
@@ -1,21 +1,74 @@
import { Router } from 'express';
-import Wiki from 'wikijs';
-
+import WikiJs from 'wikijs';
const router = Router();
+const Wiki = WikiJs({
+ apiUrl: 'https://en.wikipedia.org/w/api.php',
+ headers: { 'User-Agent': 'api-test (https://api-test; my@email) wiki.js'}
+});
-router.get('/wiki/info/page/:id/property/:property', (req, res) => {
- const id = req.params.id;
+router.get('/info/page/:id/property/:property', async (req, res) => {
+ const id = req.params.id;
const property = req.params.property;
- Wiki()
- .page(id)
- .then(page => {
- return page.info(property);
- })
- .then(value => {
- return res.status(200).json({ value });
- });
+
+ const page = await Wiki.page(id);
+ const info = await page.info(property);
+
+ return res.status(200).json({ info });
+});
+
+router.get('/content/page/:id', async (req, res) => {
+ try {
+ const id = req.params.id;
+ const page = await Wiki.page(id);
+ const content = await page.content();
+ return res.status(200).json(content);
+
+ } catch (e) {
+ console.error(e);
+ return res.status(500).json(e);
+ }
+});
+
+router.get('/rawImages/page/:id', async (req, res) => {
+ try {
+ const id = req.params.id;
+ const page = await Wiki.page(id);
+ const content = await page.rawImages();
+ return res.status(200).json(content);
+
+ } catch (e) {
+ console.error(e);
+ return res.status(500).json(e);
+ }
});
+router.get('/summary/page/:id', async (req, res) => {
+ try {
+ const id = req.params.id;
+ const page = await Wiki.page(id);
+ const content = await page.summary();
+ return res.status(200).json(content);
+
+ } catch (e) {
+ console.error(e);
+ return res.status(500).json(e);
+ }
+});
+
+router.get('/fullInfo/page/:id', async (req, res) => {
+ try {
+ const id = req.params.id;
+ const page = await Wiki.page(id);
+ const content = await page.fullInfo();
+ return res.status(200).json(content);
+
+ } catch (e) {
+ console.error(e);
+ return res.status(500).json(e);
+ }
+});
+
+
export default router;
\ No newline at end of file