Leveling up with React: React Router

ten samouczek jest pierwszą z trzyczęściowej serii o Reaccie autorstwa Brada Westfalla. Kiedy Brad mi to powiedział, zwrócił uwagę, że istnieje wiele samouczków na temat rozpoczęcia pracy w Reaccie, ale nie tyle o tym, gdzie iść stamtąd. Jeśli jesteś nowicjuszem w Reaccie, polecam obejrzenie tego wideo intro. Ta seria rozpoczyna się tam, gdzie kończą się podstawy.

seria artykułów:

  1. React Router (jesteś tutaj !
  2. Komponenty Kontenerowe
  3. Redux

Uwaga! Ten artykuł został napisany przed Reactowym routerem 4, który stał się bardziej standardowym wyborem dla routingu w Reaccie. Jest tu nowy artykuł dotyczący React Router 4, który zdecydowanie powinieneś przeczytać.

kiedy uczyłem się po raz pierwszy, znalazłem wiele poradników dla początkujących (tj. 1, 2, 3, 4), które pokazywały, jak tworzyć pojedyncze komponenty i renderować je do drzewa DOM. Wykonali świetną robotę, ucząc podstaw, takich jak JSX i props, ale zmagałem się z odkryciem, jak działa React w szerszym ujęciu – jak prawdziwa jednostronicowa aplikacja (SPA). Ponieważ ta seria obejmuje wiele materiałów, nie obejmie absolutnych pojęć dla początkujących. Zamiast tego zacznie się od założenia, że już rozumiesz, jak utworzyć i renderować co najmniej jeden komponent.

co to jest warte, oto kilka innych świetnych przewodników, które mają na celu początkujących:

  • reaguj.js i jak to pasuje do wszystkiego innego?
  • Przemyślenia (Branża) Najlepsze Praktyki
  • Wprowadzenie do js dla ludzi, którzy znają wystarczająco dużo jQuery, aby przejść przez

Kod serii

ta seria zawiera również kod do zabawy na GitHub. W trakcie całej serii będziemy budować podstawowe SPA skupione wokół użytkowników i widżetów.

aby wszystko było proste i krótkie, przykłady z tej serii zaczną się od założenia, że React i React Router są pobierane z CDN. Więc nie zobaczysz require() lub importw poniższych przykładach. Pod koniec tego samouczka przedstawimy Webpack i Babel dla przewodników GitHub. W tym momencie to wszystko ES6!

React-Router

React to nie framework, to biblioteka. Dlatego nie rozwiązuje wszystkich potrzeb aplikacji. Świetnie sprawdza się w tworzeniu komponentów i dostarczaniu systemu zarządzania stanem, ale stworzenie bardziej złożonego SPA będzie wymagało obsady wspierającej. Pierwszym, na który przyjrzymy się jest React Router.

jeśli używałeś wcześniej dowolnego routera front-end, wiele z tych pojęć będzie znanych. Ale w przeciwieństwie do innych routerów, których używałem wcześniej, React Router używa JSX, co na początku może wyglądać trochę dziwnie.

jako podkład tak wygląda renderowanie pojedynczego komponentu:

var Home = React.createClass({ render: function() { return (<h1>Welcome to the Home Page</h1>); }});ReactDOM.render(( <Home />), document.getElementById('root'));

oto jak komponent Home będzie renderowany z Reactowym routerem:

...ReactDOM.render(( <Router> <Route path="/" component={Home} /> </Router>), document.getElementById('root'));

zauważ, że <Router> i <Route> to dwie różne rzeczy. Technicznie są to Komponenty Reactowe, ale w rzeczywistości nie same tworzą DOM. Chociaż może to wyglądać tak, jakby sam <Router> był renderowany do 'root', to właśnie definiujemy reguły dotyczące działania naszej aplikacji. Idąc dalej, często zobaczysz tę koncepcję: komponenty czasami istnieją nie po to, aby same tworzyć DOM, ale po to, aby koordynować inne komponenty, które to robią.

w przykładzie, <Route> definiuje regułę, w której odwiedzając stronę główną / renderuje komponent Home do 'root'.

wiele tras

w poprzednim przykładzie pojedyncza trasa jest bardzo prosta. Nie daje nam to dużej wartości, ponieważ mieliśmy już możliwość renderowania komponentu Home bez udziału routera.

moc Reactowego routera pojawia się, gdy używamy wielu tras do zdefiniowania, który komponent ma być renderowany w oparciu o bieżącą ścieżkę:

ReactDOM.render(( <Router> <Route path="/" component={Home} /> <Route path="/users" component={Users} /> <Route path="/widgets" component={Widgets} /> </Router>), document.getElementById('root'));

każdy <Route> wyrenderuje odpowiedni komponent, gdy jego ścieżka pasuje do adresu URL. Tylko jeden z tych trzech komponentów będzie renderowany do 'root' w danym momencie. Dzięki tej strategii montujemy router do DOM 'root' raz, a następnie Router zamienia komponenty w I Na zewnątrz ze zmianami trasy.

warto również zauważyć, że router będzie przełączał trasy bez wysyłania żądań do serwera, więc wyobraź sobie, że każdy komponent może być zupełnie nową stroną.

układ wielokrotnego użytku

zaczynamy dostrzegać skromne początki pojedynczej aplikacji strony. Jednak nadal nie rozwiązuje rzeczywistych problemów. Oczywiście, moglibyśmy zbudować te trzy komponenty, aby były pełnymi stronami HTML, ale co z ponownym użyciem kodu? Są szanse, że te trzy komponenty mają wspólne zasoby, takie jak nagłówek i pasek boczny, więc jak zapobiec powtarzaniu HTML w każdym komponencie?

wyobraźmy sobie, że tworzyliśmy aplikację internetową, która przypominała tę makietę:

prosta makieta strony internetowej.

kiedy zaczniesz myśleć o tym, jak tę makietę można podzielić na sekcje wielokrotnego użytku, możesz skończyć z tym pomysłem:

jak możesz podzielić prostą makietę internetową na sekcje.

myślenie w kategoriach zagnieżdżonych komponentów i układów pozwoli nam tworzyć części wielokrotnego użytku.

nagle dział sztuki informuje, że aplikacja potrzebuje strony do wyszukiwania widżetów, która przypomina stronę do wyszukiwania użytkowników. Ponieważ Lista użytkowników i lista widżetów wymagają tego samego „wyglądu” dla swojej strony wyszukiwania, pomysł, aby układ wyszukiwania jako osobny komponent miał teraz jeszcze większy sens:

Szukaj widżetów teraz, zamiast użytkowników, ale sekcje nadrzędne pozostają takie same.

układ wyszukiwania może być szablonem nadrzędnym dla wszystkich rodzajów stron wyszukiwania. Podczas gdy niektóre strony mogą wymagać układu wyszukiwania, inne mogą bezpośrednio korzystać z układu głównego bez niego:

układ odsprzęgnięty.

jest to powszechna strategia i jeśli korzystałeś z dowolnego systemu szablonów, prawdopodobnie zrobiłeś coś bardzo podobnego. Teraz popracujmy nad HTML. Na początek zrobimy statyczny HTML bez uwzględniania JavaScript:

<div> <!-- Main Layout --> <div class="app"> <header class="primary-header"><header> <aside class="primary-aside"></aside> <main> <!-- Search Layout --> <div class="search"> <header class="search-header"></header> <div class="results"> <!-- User List --> <ul class="user-list"> <li>Dan</li> <li>Ryan</li> <li>Michael</li> </ul> </div> <div class="search-footer pagination"></div> </div> </main> </div></div>

pamiętaj, że element 'root' zawsze będzie obecny, ponieważ jest to jedyny element, który ma początkowe ciało HTML przed uruchomieniem JavaScript. Słowo „root” jest właściwe, ponieważ cała nasza aplikacja Reactowa będzie do niego montowana. Ale nie ma „właściwej nazwy” ani konwencji, jak to nazywasz. Wybrałem „korzeń”, więc będziemy go nadal używać we wszystkich przykładach. Tylko uważaj, że montaż bezpośrednio do elementu <body> jest wysoce odradzany.

po utworzeniu statycznego HTML-a przekonwertuj go na komponenty Reactowe:

var MainLayout = React.createClass({ render: function() { // Note the `className` rather than `class` // `class` is a reserved word in JavaScript, so JSX uses `className` // Ultimately, it will render with a `class` in the DOM return ( <div className="app"> <header className="primary-header"><header> <aside className="primary-aside"></aside> <main> {this.props.children} </main> </div> ); }});var SearchLayout = React.createClass({ render: function() { return ( <div className="search"> <header className="search-header"></header> <div className="results"> {this.props.children} </div> <div className="search-footer pagination"></div> </div> ); }});var UserList = React.createClass({ render: function() { return ( <ul className="user-list"> <li>Dan</li> <li>Ryan</li> <li>Michael</li> </ul> ); }});

nie rozpraszaj się zbytnio między tym, co nazywam „układem” a „komponentem”. Wszystkie trzy są komponentami Reactowymi. Po prostu wybrałem nazwanie dwóch z nich „układami”, ponieważ to jest rola, którą pełnią.

ostatecznie użyjemy” zagnieżdżonych tras”, aby umieścić UserList wewnątrz SearchLayout, a następnie wewnątrz MainLayout. Ale najpierw zauważ, że gdy UserList zostanie umieszczona wewnątrz rodzica SearchLayout, rodzic użyje this.props.children do określenia jego lokalizacji. Wszystkie komponenty mają this.props.children jako atrybut, ale tylko wtedy, gdy komponenty są zagnieżdżone, komponent nadrzędny otrzymuje ten atrybut wypełniony automatycznie przez Reacta. Dla komponentów, które nie są komponentami nadrzędnymi, this.props.children będzie null.

zagnieżdżone trasy

więc jak zmusić te komponenty do zagnieżdżenia? Router robi to za nas, gdy zagnieżdżamy trasy:

ReactDOM.render(( <Router> <Route component={MainLayout}> <Route component={SearchLayout}> <Route path="users" component={UserList} /> </Route> </Route> </Router>), document.getElementById('root'));

komponenty będą zagnieżdżane zgodnie ze sposobem zagnieżdżania tras przez router. Gdy użytkownik odwiedzi trasę /users, React Router umieści komponent UserList wewnątrz SearchLayout, a następnie oba komponenty wewnątrz MainLayout. Efektem końcowym wizyty /usersbędą trzy zagnieżdżone komponenty umieszczone wewnątrz 'root'.

zauważ, że nie mamy reguły, gdy użytkownik odwiedza ścieżkę do strony głównej (/) lub chce przeszukać widżety. Te zostały pominięte dla uproszczenia, ale dodajmy je do nowego routera:

ReactDOM.render(( <Router> <Route component={MainLayout}> <Route path="/" component={Home} /> <Route component={SearchLayout}> <Route path="users" component={UserList} /> <Route path="widgets" component={WidgetList} /> </Route> </Route> </Router>), document.getElementById('root'));

prawdopodobnie zauważyłeś już, że JSX przestrzega reguł XML w tym sensie, że komponent Route może być zapisany jako jeden tag: <Route />lub dwa: <Route>...</Route>. Dotyczy to wszystkich JSX, w tym niestandardowych komponentów i zwykłych węzłów DOM. Na przykład <div /> jest poprawnym JSX i konwertuje do <div></div> po wyrenderowaniu.

dla zwięzłości, wyobraź sobie, że WidgetListprzypomina UserList.

ponieważ <Route component={SearchLayout}> ma teraz dwie trasy potomne, użytkownik może odwiedzić /users lub /widgets, a odpowiedni <Route> załaduje odpowiednie komponenty wewnątrz komponentu SearchLayout.

zwróć również uwagę, jak komponent Home zostanie umieszczony bezpośrednio wewnątrz MainLayout bez udziału SearchLayout — ze względu na sposób zagnieżdżania s <Route>. Możesz sobie wyobrazić, że łatwo jest zmienić sposób zagnieżdżania układów i komponentów, zmieniając trasy.

IndexRoutes

React Router jest bardzo ekspresyjny i często jest więcej niż jeden sposób na zrobienie tego samego. Na przykład mogliśmy również napisać powyższy router w ten sposób:

ReactDOM.render(( <Router> <Route path="/" component={MainLayout}> <IndexRoute component={Home} /> <Route component={SearchLayout}> <Route path="users" component={UserList} /> <Route path="widgets" component={WidgetList} /> </Route> </Route> </Router>), document.getElementById('root'));

pomimo odmiennego wyglądu, oba działają dokładnie w ten sam sposób.

opcjonalne atrybuty trasy

czasami <Route> będzie miał atrybut component bez path, jak w SearchLayout trasa z góry. Innym razem może być konieczne posiadanie <Route> z pathi bez component. Aby zobaczyć dlaczego, zacznijmy od tego przykładu:

<Route path="product/settings" component={ProductSettings} /><Route path="product/inventory" component={ProductInventory} /><Route path="product/orders" component={ProductOrders} />

/product część path jest powtarzalna. Możemy usunąć powtórzenie, owijając wszystkie trzy trasy w Nowy <Route>:

<Route path="product"> <Route path="settings" component={ProductSettings} /> <Route path="inventory" component={ProductInventory} /> <Route path="orders" component={ProductOrders} /></Route>

ponownie, React Router pokazuje swoją ekspresję. Quiz: Czy zauważyłeś problem z obu rozwiązań? W tej chwili nie mamy reguł dotyczących odwiedzania ścieżki /product przez użytkownika.

aby to naprawić, możemy dodać IndexRoute:

<Route path="product"> <IndexRoute component={ProductProfile} /> <Route path="settings" component={ProductSettings} /> <Route path="inventory" component={ProductInventory} /> <Route path="orders" component={ProductOrders} /></Route>

użyj <Link> nie <a>

podczas tworzenia kotwic dla tras musisz użyć <Link to="">zamiast <a href="">. Nie martw się jednak, gdy używasz komponentu <Link>, React Router ostatecznie da ci zwykłe zakotwiczenie w drzewie DOM. Użycie <Link> jest jednak konieczne, aby React Router wykonał część swojej magii routingu.

dodajmy jakiś link (anchory) do naszego MainLayout:

var MainLayout = React.createClass({ render: function() { return ( <div className="app"> <header className="primary-header"></header> <aside className="primary-aside"> <ul> <li><Link to="/">Home</Link></li> <li><Link to="/users">Users</Link></li> <li><Link to="/widgets">Widgets</Link></li> </ul> </aside> <main> {this.props.children} </main> </div> ); }});

atrybuty komponentów <Link> zostaną przekazane do kotwicy, którą tworzą. Więc to JSX:

<Link to="/users" className="users">

Will become this in DOM:

<a href="/users" class="users">

jeśli chcesz utworzyć zakotwiczenie dla ścieżek innych niż router, takich jak zewnętrzna strona internetowa, użyj zwykłych znaczników zakotwiczenia. Więcej informacji można znaleźć w dokumentacji IndexRoute i Link.

aktywne linki

fajną cechą komponentu <Link> jest możliwość poznania, kiedy jest aktywny:

<Link to="/users" activeClassName="active">Users</Link>

jeśli użytkownik znajduje się na ścieżce /users, router wyszukuje pasujące kotwice, które zostały utworzone za pomocą <Link> i przełącza ich klasę active. Zobacz więcej na temat tej funkcji.

historia przeglądarki

aby zapobiec zamieszaniu, pominąłem ważny szczegół do tej pory. <Router> musi wiedzieć, której strategii śledzenia historii użyć. React Router Docs zaleca przeglądarkę, która jest zaimplementowana w następujący sposób:

var browserHistory = ReactRouter.browserHistory;ReactDOM.render(( <Router history={browserHistory}> ... </Router>), document.getElementById('root'));

w poprzednich wersjach Reactowego routera atrybut history nie był wymagany, a domyślnym atrybutem było używanie hashHistory. Jak sama nazwa wskazuje, używał znaku hashowego # w adresie URL do zarządzania trasowaniem w stylu spa front-end, podobnym do tego, czego można oczekiwać od szkieletu.router js.

z hashHistory adresy URL będą wyglądać tak:

  • example.com
  • example.com/#/users?_k=ckuvup
  • example.com/#/widgets?_k=ckuvup

o co chodzi z tymi brzydkimi ciągami zapytań?

po wdrożeniu browserHistory ścieżki wyglądają bardziej organicznie:

  • example.com
  • example.com/users
  • przykład.com / widgets

istnieje jednak zastrzeżenie na serwerze, gdy browserHistory jest używany na interfejsie. Jeśli użytkownik rozpoczyna swoją wizytę na example.com, a następnie przechodzi do /users i /widgets, React router obsługuje ten scenariusz zgodnie z oczekiwaniami. Jeśli jednak użytkownik rozpocznie swoją wizytę wpisując example.com/widgets bezpośrednio w przeglądarce lub jeśli odświeży się na example.com/widgets, przeglądarka musi wysłać co najmniej jedno żądanie do serwera dla /widgets. Jeśli jednak nie ma routera po stronie serwera, zapewni to 404:

ostrożnie z adresami URL. Będziesz potrzebował routera po stronie serwera.

aby rozwiązać problem 404 z serwera, React Router zaleca Router wieloznaczny po stronie serwera. Dzięki tej strategii, bez względu na to, jaka jest wywoływana trasa po stronie serwera, serwer powinien zawsze obsługiwać ten sam plik HTML. Następnie, jeśli użytkownik zaczyna bezpośrednio od example.com/widgets, mimo że zwracany jest ten sam plik HTML, React Router jest na tyle inteligentny, aby załadować właściwy komponent.

użytkownik nie zauważy niczego dziwnego, ale możesz mieć obawy, że zawsze będziesz obsługiwał ten sam plik HTML. W przykładach kodu, ta seria będzie nadal używać strategii” Router wieloznaczny”, ale to do ciebie należy obsługa routingu po stronie serwera w sposób, który uznasz za odpowiedni.

czy React Router może być używany zarówno po stronie serwera, jak i po stronie klienta w sposób izomorficzny? Oczywiście, że może, ale to znacznie wykracza poza zakres tego samouczka.

przekierowanie za pomocą browserHistory

obiekt browserHistory jest singletonem, więc możesz dołączyć go do dowolnego pliku. Jeśli chcesz ręcznie przekierować użytkownika w dowolnym kodzie, możesz użyć metody push , aby to zrobić:

browserHistory.push('/some/path');

dopasowywanie tras

React router obsługuje dopasowywanie tras podobnie jak inne routery:

<Route path="users/:userId" component={UserProfile} />

ta trasa będzie pasować, gdy użytkownik odwiedzi dowolną ścieżkę, która zaczyna się od users/ i ma później dowolną wartość. Będzie pasował do /users/1, /users/143 lub nawet /users/abc (które musisz sprawdzić samodzielnie).

React Router przekaże wartość :userIdjako właściwość do UserProfile. Ten atrybut jest dostępny jako this.props.params.userId wewnątrz UserProfile.

Demo routera

w tym momencie mamy wystarczająco dużo kodu, aby pokazać demo.

Zobacz demo Pen React-Router autorstwa Brada Westfalla (@bradwestfall) na CodePen.

jeśli klikniesz na kilka tras w przykładzie, możesz zauważyć, że przyciski Wstecz i do przodu przeglądarki działają z routerem. Jest to jeden z głównych powodów, dla których istnieją te history strategie. Pamiętaj również, że przy każdej odwiedzanej trasie nie ma żadnych żądań do serwera, z wyjątkiem pierwszego, który uzyska początkowy kod HTML. Czy to nie fajne?

ES6

w naszym przykładzie CodePen, React, ReactDOM i ReactRouter są globalnymi zmiennymi z CDN. Wewnątrz obiektu ReactRouter znajdują się wszelkiego rodzaju rzeczy, których potrzebujemy, takie jak komponenty Router i Route. Więc możemy użyć ReactRouter w ten sposób:

ReactDOM.render(( <ReactRouter.Router> <ReactRouter.Route ... /> </ReactRouter.Router>), document.getElementById('root'));

tutaj, musimy przedrostek wszystkie nasze komponenty routera z ich obiektu nadrzędnego ReactRouter. Albo możemy użyć nowej składni destrukcji ES6, takiej jak ta:

var { Router, Route, IndexRoute, Link } = ReactRouter

to „wyodrębnia” części ReactRouter do normalnych zmiennych, abyśmy mogli uzyskać do nich bezpośredni dostęp.

począwszy od teraz, przykłady z tej serii będą używać różnych składni ES6, w tym destrukcji, operatora spreadu, importu / eksportu i być może innych. Będzie tam krótkie wyjaśnienie każdej nowej składni, jak się pojawią, a repozytorium GitHub, które pochodzi z tej serii, ma również wiele wyjaśnień ES6.

w pakiecie z webpack i Babel

jak wspomniano wcześniej, ta seria jest dostarczana z repo GitHub, dzięki czemu możesz eksperymentować z kodem. Ponieważ będzie przypominał zadatki prawdziwego SPA, użyje narzędzi takich jak webpack i Babel.

  • pakiet webpack łączy wiele plików JavaScript w jeden plik dla przeglądarki.
  • Babel przekonwertuje kod ES6 (ES2015) na ES5, ponieważ większość przeglądarek nie rozumie jeszcze wszystkich ES6. W miarę starzenia się tego artykułu przeglądarki będą obsługiwać ES6, a Babel może nie być potrzebny.

jeśli nie czujesz się jeszcze zbyt komfortowo z tymi narzędziami, nie martw się, przykładowy kod ma już wszystko ustawione, więc możesz skupić się na Reaccie. Ale pamiętaj, aby przejrzeć przykładowy kod README.md plik dla dodatkowej dokumentacji workflow.

uważaj na przestarzałą składnię

wyszukiwanie w Google informacji na temat routera Reactowego może wylądować na jednym z wielu artykułów lub stron StackOverflow, które zostały napisane, gdy React Router był w wersji przed 1.0. Wiele funkcji z pre-1.0 release są teraz przestarzałe. Oto krótka lista:

  • <Route name="" /> jest przestarzały. Zamiast tego użyj <Route path="" />.
  • <Route handler="" /> jest przestarzały. Zamiast tego użyj <Route component="" />.
  • <NotFoundRoute /> jest przestarzały. Zobacz alternatywa
  • <RouteHandler /> jest przestarzała.
  • willTransitionTo jest przestarzały. Zobacz onEnter
  • willTransitionFrom jest przestarzały. Zobacz onLeave
  • ” lokalizacje „są teraz nazywane”historiami”.

Zobacz pełną listę dla wersji 1.0.0 i 2.0.0

podsumowanie

w Reactowym routerze jest jeszcze więcej funkcji, które nie zostały pokazane, więc zapoznaj się z dokumentami API. Twórcy Reactowego routera stworzyli również samouczek krok po kroku dla Reactowego routera, a także checkout this React.js Conf wideo o tym, jak powstał Router React.

Specjalne podziękowania dla Lynn Fisher za pracę @lynnandtonic

seria artykułów:

  1. React Router (jesteś tutaj !)
  2. Komponenty Kontenerowe
  3. Redux

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.