Używanie Reacta w wielostronicowej aplikacji

123

Bawiłem się z Reactem i jak dotąd bardzo mi się to podoba. Buduję aplikację za pomocą NodeJS i chciałbym użyć Reacta dla niektórych interaktywnych komponentów w całej aplikacji. Nie chcę, aby była to aplikacja jednostronicowa.

Nie znalazłem jeszcze w sieci niczego, co odpowiadałoby na następujące pytania:

Jak rozdzielić lub połączyć moje komponenty React w wielostronicową aplikację?

Obecnie wszystkie moje komponenty znajdują się w jednym pliku, chociaż mogę ich nigdy nie załadować w niektórych sekcjach aplikacji.

Do tej pory próbuję używać instrukcji warunkowych do renderowania komponentów, wyszukując identyfikator kontenera, w którym zrenderuje React. Nie jestem w 100% pewien, jakie są najlepsze praktyki w React. Wygląda mniej więcej tak.

if(document.getElementById('a-compenent-in-page-1')) {
    React.render(
        <AnimalBox url="/api/birds" />,
        document.getElementById('a-compenent-in-page-1')
    );
}

if(document.getElementById('a-compenent-in-page-2')) {
    React.render(
        <AnimalBox url="/api/cats" />,
        document.getElementById('a-compenent-in-page-2')
    );
}

if(document.getElementById('a-compenent-in-page-3')) {
    React.render(
        <AnimalSearchBox url="/api/search/:term" />,
        document.getElementById('a-compenent-in-page-3')
    );
}

Wciąż czytam dokumentację i nie znalazłem jeszcze tego, czego potrzebuję do aplikacji wielostronicowej.

Z góry dziękuję.

Jose Carrillo
źródło
spróbuj użyć wtyczki requirejs.
amit_183
2
Jeśli nie masz nic przeciwko temu, że ReactJs to bardzo duża biblioteka JS, która będzie musiała zostać zainicjowana dla każdej strony (jak powiedziałeś, że nie tworzysz aplikacji jednostronicowej), to nie jestem pewien, czy ma to znaczenie, że ty połączyliśmy wszystkie komponenty w jeden plik. Zostanie zbuforowany na kliencie. Po załadowaniu strony ustaw jej renderwłaściwy komponent w scriptbloku.
WiredPrairie
Mam ten sam problem: mam aplikację, która ładuje inne duże biblioteki na różnych stronach i wolałbym po prostu załadować reagowanie + jedną bibliotekę w zależności od potrzeb odwiedzającego, zamiast czterech dużych bibliotek na wszelki wypadek.
AJFarkas

Odpowiedzi:

83

Obecnie robię coś podobnego.

Ta aplikacja nie jest pełną aplikacją React, używam Reacta do dynamicznych rzeczy, takich jak CommentBox, który jest autark. I może zostać uwzględniony w dowolnym punkcie ze specjalnymi parametrami.

Jednak wszystkie moje aplikacje podrzędne są ładowane i umieszczane w jednym pliku all.js, więc przeglądarka może je buforować na różnych stronach.

Kiedy muszę dołączyć aplikację do szablonów SSR, wystarczy, że dołączę DIV z klasą „__react-root” i specjalnym identyfikatorem (nazwa renderowanej aplikacji React)

Logika jest naprawdę prosta:

import CommentBox from './apps/CommentBox';
import OtherApp from './apps/OtherApp';

const APPS = {
  CommentBox,
  OtherApp
};

function renderAppInElement(el) {
  var App = APPS[el.id];
  if (!App) return;

  // get props from elements data attribute, like the post_id
  const props = Object.assign({}, el.dataset);

  ReactDOM.render(<App {...props} />, el);
}

document
  .querySelectorAll('.__react-root')
  .forEach(renderAppInElement)

<div>Some Article</div>
<div id="CommentBox" data-post_id="10" class="__react-root"></div>

<script src="/all.js"></script>

Edytować

Ponieważ pakiet internetowy doskonale obsługuje dzielenie kodu i LazyLoading, pomyślałem, że sensowne jest zawarcie przykładu, w którym nie musisz ładować wszystkich aplikacji w jednym pakiecie, ale dziel je i ładuj na żądanie.

import React from 'react';
import ReactDOM from 'react-dom';

const apps = {
  'One': () => import('./One'),
  'Two': () => import('./Two'),
}

const renderAppInElement = (el) => {
  if (apps[el.id])  {
    apps[el.id]().then((App) => {
      ReactDOM.render(<App {...el.dataset} />, el);
    });
  }
}
webdeb
źródło
1
Wygląda świetnie, czy ktoś ma to uruchomione podczas korzystania z aplikacji npm create-respons-app? Nie sądzę, żeby to zadziałało dla mnie ... Mam ją teraz uruchomioną z aplikacją 1 i widzę, jak to może działać, ale moja kompilacja nie utworzy wersji produkcyjnej, która działa poprawnie.
Sprose
@Sprose powinien również działać z aplikacją create-react-app, czy czegoś mi brakuje? Może możesz udostępnić mały przykład na githubie, gdzie nie ma, nie widzę powodu, dla którego nie powinien
webdeb
1
@Sprose proszą o późną odpowiedź, ale myślę, że próbujesz czegoś zupełnie innego, co to podejście próbuje rozwiązać. IMO create react appzapewnia proste narzędzia do tworzenia bundle.js. Tak więc celem mojej odpowiedzi jest użycie tego samego bundle.jsi użycie go w wielu witrynach SSR oraz załadowanie wielu różnych aplikacji React na jednej stronie. Przepraszam, jeśli moja odpowiedź nie odpowiada Twoim potrzebom, nie krępuj się stworzyć nowego posta i opisać co próbujesz zrobić, postaram się Ci pomóc.
webdeb
1
@SorcererApprentice cra używa webpacka, musisz wysunąć i sprawdzić
webdeb
1
@SorcererApprentice w zasadzie ktoś opublikował konfigurację webpacka na tej stronie: stackoverflow.com/a/41857199/5004923
webdeb
52

W pliku webpack.config.js możesz podać kilka punktów wejścia dla aplikacji:

var config = {
  entry: {
    home: path.resolve(__dirname, './src/main'),
    page1: path.resolve(__dirname, './src/page1'),
    page2: path.resolve(__dirname, './src/page2'),
    vendors: ['react']
  },
 output: {
    path: path.join(__dirname, 'js'),
    filename: '[name].bundle.js',
    chunkFilename: '[id].chunk.js'
  },
}

wtedy możesz mieć w swoim folderze src trzy różne pliki html z odpowiadającymi im plikami js (przykład dla page1):

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Page 1</title>
</head>
<body>
  <div id="app"></div>
  <script src="./vendors.js"></script>
  <script src="./page1.bundle.js"></script>
</body>
</html>

Plik JavaScript:

import React from 'react'
import ReactDom from 'react-dom'
import App from './components/App'
import ComponentA from './components/ReactComponentA'
ReactDom.render(<div>
                  <App title='page1' />
                  <ReactComponentA/>
                 </div>, document.getElementById('app'))

Następnie dla każdej strony można załadować różne komponenty Reacta.

Cocomico
źródło
1
O tym przeczytałem poniżej na stronie webex. webpack version < 4 it was common to add vendors as separate entrypoint to compile it as separate file (in combination with the CommonsChunkPlugin). This is discouraged in webpack 4. Instead the optimization.splitChunks option takes care of separating vendors and app modules and creating a separate file. Do not create a entry for vendors or other stuff which is not the starting point of execution.Czy zatem ta próbka powinna zostać zaktualizowana dla pakietu webpack 4+?
Ramesh
32

Buduję aplikację od podstaw i uczę się na bieżąco, ale myślę, że to, czego szukasz, to React-Router . React-Router mapuje twoje komponenty na określone adresy URL. Na przykład:

render((
    <Router>
        <Route path="/" component={App}>
            <Route path="api/animals" component={Animals}>
               <Route path="birds" component={Birds}/>
               <Route path="cats" component={Cats}/>
            </Route>
        </Route>
        <Route path="api/search:term" component={AnimalSearchBox}>
    </Router>
), document.body)

W przypadku wyszukiwania „termin” jest dostępny jako właściwość w AnimalSearchBox:

componentDidMount() {
    // from the path `/api/search/:term`
    const term = this.props.params.term
}

Wypróbuj to. Ten samouczek jest tym, który dał mi przewagę, jeśli chodzi o zrozumienie tego i innych powiązanych tematów.


Oryginalna odpowiedź jest następująca:

Trafiłem tutaj, szukając tej samej odpowiedzi. Sprawdź, czy ten post Cię inspiruje. Jeśli twoja aplikacja jest podobna do mojej, będzie miała obszary, które zmieniają się bardzo nieznacznie i różnią się tylko w głównej części. Możesz stworzyć widżet, którego zadaniem jest renderowanie innego widżetu na podstawie stanu aplikacji. Korzystając z architektury flux, możesz wywołać akcję nawigacji, która zmienia stan, na który włącza się twój widget body, skutecznie aktualizując tylko treść strony.

To podejście, które teraz próbuję.

Scott
źródło
8
Co się stanie, jeśli ścieżka URL jest tworzona przez mój kod zaplecza (w tym przypadku nodejs)? Czy będzie Routerdziałać tak samo, jak w aplikacji z pojedynczą stroną?
Jose Carrillo
1
@Scott A co, jeśli nie chcę udostępniać funkcji strony administratora, która ma specjalne widżety? Za pomocą reakcji można odtworzyć wygląd i styl bez prawdziwego uwierzytelnienia.
Kairat Kempirbaev
11

Czy korzystasz z CMS? Zwykle lubią zmieniać adresy URL, które mogą spowodować uszkodzenie aplikacji.

Innym sposobem jest użycie czegoś takiego jak React Habitat .

Dzięki niemu możesz zarejestrować komponenty i automatycznie zostaną one ujawnione w domenie.

Przykład

Zarejestruj składnik (i):

container.register('AnimalBox', AnimalBox);
container.register('AnimalSearchBox', AnimalSearchBox);

Wtedy są one dostępne w Twoim domu w ten sposób:

<div data-component="AnimalBox"></div>

<div data-component="AnimalSearchBox"></div>

Powyższe zostaną automatycznie zastąpione komponentami reagującymi.

Następnie możesz automatycznie przekazywać właściwości (lub rekwizyty) również do swoich komponentów:

<div data-component="AnimalBox" data-prop-size="small"></div>

To ujawni się sizejako rekwizyt dla twojego komponentu. Istnieją dodatkowe opcje przekazywania innych typów, takich jak json, tablice, int, zmiennoprzecinkowe itp.

jennas
źródło
2

Wiem, że minęło trochę czasu, odkąd zadano to pytanie, ale mam nadzieję, że to komuś pomoże.

Jak wspomniał @Cocomico, możesz podać kilka punktów wejścia dla aplikacji w pliku webpack.config.js. Jeśli szukasz prostej konfiguracji Webpacka (opartej na idei wielu punktów wejścia), która pozwala dodawać komponenty Reacta do stron statycznych, możesz rozważyć użycie tego: https://github.com/przemek-nowicki/multi-page -app-with-respond

Przemek Nowicki
źródło
-1

Proponuję przyjrzeć się InertiaJS: https://inertiajs.com/

Dzięki Inertia tworzysz aplikacje tak, jak zawsze robiłeś to z wybraną platformą internetową po stronie serwera. Używasz istniejących funkcji swojej platformy do routingu, kontrolerów, oprogramowania pośredniego, uwierzytelniania, autoryzacji, pobierania danych i nie tylko.

Jedyną różnicą jest warstwa widoku. Zamiast korzystać z renderowania po stronie serwera (np. Szablony Blade lub ERB), widoki są komponentami strony JavaScript. Dzięki temu możesz zbudować cały front-end za pomocą React, Vue lub Svelte.

Agu Dondo
źródło