ใช้ React ในแอพหลายหน้า


122

ฉันได้เล่นกับ React และจนถึงตอนนี้ฉันชอบมันมาก ฉันกำลังสร้างแอปด้วย NodeJS และต้องการใช้ React สำหรับส่วนประกอบแบบโต้ตอบบางส่วนในแอปพลิเคชัน ฉันไม่ต้องการให้เป็นแอปหน้าเดียว

ฉันยังไม่พบสิ่งใดในเว็บที่ตอบคำถามต่อไปนี้:

ฉันจะแยกหรือรวมส่วนประกอบการตอบสนองของฉันในแอปแบบหลายหน้าได้อย่างไร

ขณะนี้ส่วนประกอบทั้งหมดของฉันอยู่ในไฟล์เดียวแม้ว่าฉันจะไม่เคยโหลดเลยในบางส่วนของแอป

จนถึงตอนนี้ฉันกำลังพยายามใช้คำสั่งเงื่อนไขในการแสดงผลส่วนประกอบโดยค้นหา ID ของคอนเทนเนอร์ที่ React จะแสดงผล ฉันไม่แน่ใจ 100% ว่าแนวทางปฏิบัติที่ดีที่สุดคืออะไรกับ React มันจะเป็นแบบนี้

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')
    );
}

ฉันยังคงอ่านเอกสารและไม่พบสิ่งที่ต้องการสำหรับแอปหลายหน้า

ขอบคุณล่วงหน้า.


ลองใช้ปลั๊กอิน requirejs
amit_183

2
หากคุณไม่ทราบว่า ReactJs เป็นไลบรารี JS ขนาดใหญ่มากที่จะต้องเริ่มต้นสำหรับแต่ละหน้า (อย่างที่คุณบอกว่าคุณไม่ได้สร้างแอปหน้าเดียว) ฉันไม่แน่ใจว่ามันสำคัญสำหรับคุณ ได้รวมส่วนประกอบทั้งหมดไว้ในไฟล์เดียว มันจะถูกแคชบนไคลเอนต์ เมื่อหน้าเว็บโหลดให้renderจัดองค์ประกอบที่ถูกต้องในscriptบล็อก
WiredPrairie

ฉันมีปัญหาเดียวกัน: ฉันมีแอพที่โหลดไลบรารีขนาดใหญ่อื่น ๆ ในหน้าต่างๆและฉันอยากจะโหลด react + หนึ่งไลบรารีขึ้นอยู่กับความต้องการของผู้เยี่ยมชมแทนที่จะเป็นสี่ไลบรารีขนาดใหญ่ในกรณี
AJFarkas

คำตอบ:


83

ตอนนี้ฉันกำลังทำบางอย่างที่คล้ายกัน

แอปพลิเคชันไม่ใช่แอป React แบบเต็มฉันใช้ React สำหรับ Dynamic Stuff เช่น CommentBox ซึ่งเป็น autark และสามารถรวมไว้ที่ Point ใดก็ได้ที่มีพารามิเตอร์พิเศษ ..

อย่างไรก็ตามแอปย่อยทั้งหมดของฉันจะโหลดและรวมอยู่ในไฟล์เดียวall.jsดังนั้นเบราว์เซอร์จึงสามารถแคชในหน้าต่างๆได้

เมื่อฉันต้องการรวมแอปลงในเทมเพลต SSR ฉันต้องรวม DIV กับคลาส "__react-root" และ ID พิเศษ (ชื่อของ React App ที่จะแสดงผล)

ตรรกะนั้นง่ายมาก:

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>

แก้ไข

เนื่องจาก webpack รองรับการแยกโค้ดและ LazyLoading อย่างสมบูรณ์แบบฉันคิดว่ามันสมเหตุสมผลแล้วที่จะรวมตัวอย่างที่คุณไม่จำเป็นต้องโหลดแอพทั้งหมดของคุณในชุดเดียว แต่แยกออกและโหลดตามความต้องการ

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);
    });
  }
}

1
ดูดีมีใครบ้างที่รันเมื่อใช้ npm create-react-app ฉันไม่คิดว่ามันจะได้ผลสำหรับฉัน ... ตอนนี้ฉันใช้งานกับแอพ 1 และสามารถดูว่ามันทำงานอย่างไร แต่บิลด์ของฉันจะไม่สร้างบิลด์การผลิตที่ทำงานได้อย่างถูกต้อง
Sprose

@Sprose ควรทำงานร่วมกับ create-react-app ด้วยฉันขาดอะไรไปหรือเปล่า? บางทีคุณสามารถแบ่งปันตัวอย่างเล็ก ๆ บน github ได้ในที่ที่ไม่เห็นฉันไม่เห็นเหตุผลว่าทำไมจึงไม่ควร
webdeb

1
@Sprose สำหรับคำตอบที่ล่าช้า แต่ฉันคิดว่าคุณกำลังลองสิ่งที่แตกต่างไปจากเดิมอย่างสิ้นเชิงสิ่งที่วิธีนี้พยายามแก้ไข IMO จะช่วยให้คุณขับรถบางง่ายต่อการสร้างcreate react app bundle.jsดังนั้นจุดประสงค์ของคำตอบของฉันคือใช้แบบเดียวกันbundle.jsและใช้กับไซต์ SSR หลายไซต์และโหลดแอปพลิเคชัน React ที่แตกต่างกันจำนวนมากลงในหน้าเดียว ขออภัยหากคำตอบของฉันไม่ตรงกับความต้องการของคุณอย่าลังเลที่จะสร้างโพสต์ใหม่และอธิบายสิ่งที่คุณกำลังพยายามทำฉันจะพยายามช่วยคุณ
webdeb

1
@SorcererApprentice cra ใช้ webpack คุณต้องดีดออกแล้วเช็คเอาต์พื้นฐานwebpack.js.org/guides/getting-started/#creating-a-bundle
webdeb

1
@SorcererApprentice โดยทั่วไปมีคนโพสต์การกำหนดค่า webpack ในหน้านี้: stackoverflow.com/a/41857199/5004923
webdeb

52

คุณสามารถระบุจุดเข้าใช้งานได้หลายจุดในไฟล์ webpack.config.js:

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'
  },
}

จากนั้นคุณสามารถมีไฟล์ html ที่แตกต่างกันสามไฟล์ในโฟลเดอร์ src ของคุณพร้อมไฟล์ js ตามลำดับ (ตัวอย่างเช่น 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>

ไฟล์ 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'))

จากนั้นสามารถโหลดส่วนประกอบการตอบสนองที่แตกต่างกันสำหรับแต่ละหน้า


1
ฉันอ่านเกี่ยวกับด้านล่างในหน้า 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.ดังนั้นตัวอย่างนี้ควรได้รับการอัปเดตสำหรับ webpack 4+ หรือไม่?
Ramesh

32

ฉันกำลังสร้างแอพลิเคชันจากพื้นดินขึ้นและกำลังเรียนรู้ที่ผมไป แต่ผมคิดว่าสิ่งที่คุณกำลังมองหามีปฏิกิริยา-Router React-Router จะแมปส่วนประกอบของคุณกับ URL เฉพาะ ตัวอย่างเช่น:

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)

ในกรณีการค้นหา 'term' สามารถเข้าถึงได้เป็นคุณสมบัติใน AnimalSearchBox:

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

ลองใช้งาน บทช่วยสอนนี้เป็นบทช่วยสอนที่ทำให้ฉันอยู่ในอันดับต้น ๆ ในแง่ของความเข้าใจในเรื่องนี้และหัวข้ออื่น ๆ ที่เกี่ยวข้อง


คำตอบเดิมมีดังนี้:

ฉันพบทางของฉันที่นี่และกำลังมองหาคำตอบเดียวกัน ดูว่าโพสต์นี้สร้างแรงบันดาลใจให้คุณหรือไม่ หากแอปพลิเคชันของคุณเป็นอะไรที่เหมือนกับของฉันแอปพลิเคชันจะมีพื้นที่ที่เปลี่ยนแปลงน้อยมากและแตกต่างกันไปเฉพาะในเนื้อหาหลัก คุณสามารถสร้างวิดเจ็ตที่มีความรับผิดชอบในการแสดงผลวิดเจ็ตที่แตกต่างกันตามสถานะของแอปพลิเคชัน ด้วยการใช้สถาปัตยกรรมฟลักซ์คุณสามารถส่งการดำเนินการนำทางที่เปลี่ยนสถานะวิดเจ็ตร่างกายของคุณจะเปิดขึ้นและอัปเดตเนื้อหาของเพจเท่านั้น

นั่นคือแนวทางที่ฉันกำลังพยายามอยู่ตอนนี้


8
จะเกิดอะไรขึ้นถ้าเส้นทาง URL ถูกสร้างขึ้นโดยรหัสแบ็กเอนด์ของฉัน (nodejs ในกรณีนี้) จะRouterทำงานเหมือนกับในแอปหน้าเดียวหรือไม่
Jose Carrillo

1
@Scott จะเกิดอะไรขึ้นถ้าฉันไม่ต้องการเปิดเผยฟังก์ชันการทำงานของหน้าผู้ดูแลระบบที่มีวิดเจ็ตพิเศษ การใช้ react เป็นไปได้ที่จะสร้างรูปลักษณ์ใหม่โดยไม่ต้องมีการพิสูจน์ตัวตนจริง
Kairat Kempirbaev

11

คุณใช้ CMS หรือไม่? พวกเขามักจะชอบเปลี่ยน URL ซึ่งอาจทำให้แอปพลิเคชันของคุณเสียหายได้

อีกวิธีหนึ่งคือการใช้สิ่งที่ต้องการตอบสนองที่อยู่อาศัย

ด้วยวิธีนี้คุณสามารถลงทะเบียนส่วนประกอบและพวกมันจะได้สัมผัสกับโดมโดยอัตโนมัติ

ตัวอย่าง

ลงทะเบียนส่วนประกอบ:

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

จากนั้นพวกเขาจะพร้อมใช้งานในโดเมนของคุณดังนี้:

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

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

ข้างต้นจะถูกแทนที่ด้วยส่วนประกอบปฏิกิริยาของคุณโดยอัตโนมัติ

จากนั้นคุณสามารถส่งคุณสมบัติ (หรืออุปกรณ์ประกอบฉาก) ไปยังส่วนประกอบของคุณได้โดยอัตโนมัติ:

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

สิ่งนี้จะแสดงให้เห็นsizeว่าเป็นส่วนประกอบของส่วนประกอบของคุณ มีตัวเลือกเพิ่มเติมสำหรับการส่งผ่านประเภทอื่น ๆ เช่น json, array's, ints, floats เป็นต้น


2

ฉันรู้ว่าคำถามนี้ถูกถามมาระยะหนึ่งแล้ว แต่หวังว่าจะช่วยใครสักคนได้

ดังที่ @Cocomico กล่าวถึงคุณสามารถระบุจุดเข้าใช้งานได้หลายจุดในไฟล์ webpack.config.js หากคุณกำลังมองหาการตั้งค่า Webpack แบบง่ายๆ (ตามแนวคิดของจุดเข้าใช้งานหลายจุด) ที่ช่วยให้คุณสามารถเพิ่มส่วนประกอบ React ไปยังเพจแบบคงที่คุณอาจพิจารณาใช้สิ่งนี้: https://github.com/przemek-nowicki/multi-page -App ที่มีการตอบสนอง


-1

ฉันขอแนะนำให้คุณดู InertiaJS: https://inertiajs.com/

ด้วยความเฉื่อยคุณสามารถสร้างแอปได้เหมือนกับที่คุณเคยทำกับเว็บเฟรมเวิร์กฝั่งเซิร์ฟเวอร์ที่คุณเลือก คุณใช้ฟังก์ชันการทำงานที่มีอยู่ของเฟรมเวิร์กสำหรับการกำหนดเส้นทางตัวควบคุมมิดเดิลแวร์การพิสูจน์ตัวตนการอนุญาตการดึงข้อมูลและอื่น ๆ

สิ่งเดียวที่แตกต่างคือเลเยอร์มุมมองของคุณ แทนที่จะใช้การแสดงผลฝั่งเซิร์ฟเวอร์ (เช่นเทมเพลต Blade หรือ ERB) มุมมองเป็นส่วนประกอบของหน้า JavaScript สิ่งนี้ช่วยให้คุณสร้างส่วนหน้าทั้งหมดโดยใช้ React, Vue หรือ Svelte

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.