programing

React를 사용하여 여러 페이지 앱을 구축하는 방법

oldcodes 2023. 4. 4. 22:15
반응형

React를 사용하여 여러 페이지 앱을 구축하는 방법

NodeJs로 앱을 만들고 있는데 애플리케이션 전체의 인터랙티브 컴포넌트 중 몇 가지에 React를 사용하고 싶습니다.한 페이지 앱으로 만들고 싶지 않아요.

여러 페이지 앱에서 React 구성 요소를 분리하거나 번들하려면 어떻게 해야 합니까?

현재 앱의 일부 섹션에 로드하지 않아도 모든 컴포넌트가 한 파일에 저장되어 있습니다.

지금까지 조건문을 사용하여 React가 렌더링할 컨테이너의 ID를 검색하여 컴포넌트를 렌더링하려고 합니다.React의 베스트 프랙티스가 무엇인지 100% 확신할 수 없습니다.이렇게 생겼어요.

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

아직 매뉴얼을 읽고 있는데 아직 멀티페이지 앱에 필요한 것을 찾지 못했습니다.

저는 지금 비슷한 일을 하고 있어요.

어플리케이션은 완전 리액트 앱이 아닙니다.자동 마크인 Comment Box와 같이 React for dynamic Stuff를 사용하고 있습니다.또한 특별한 매개 변수가 있는 모든 지점에 포함할 수 있습니다.

모든 에 포함됩니다.all.js여러 페이지에 걸쳐 브라우저에서 캐시할 수 있습니다.

SSR 템플릿에 앱을 포함해야 할 경우 클래스 "_react-root"와 특수 ID( 렌더링할 React App 이름)를 가진 DIV를 포함하면 됩니다.

논리는 매우 간단합니다.

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>

편집

웹 팩은 코드 분할과 Lazy Loading을 완벽하게 지원하므로 모든 앱을 하나의 번들에 로드할 필요가 없고 분할하여 온 디맨드로 로드할 필요가 있는 예를 포함하는 것이 타당하다고 생각했습니다.

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

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

그러면 src 폴더에 3개의 html 파일과 각각의 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'))

그런 다음 각 페이지마다 다른 React 구성 요소를 로드할 수 있습니다.

저는 원점에서 어플리케이션을 만들어 가면서 배우고 있습니다만, 당신이 찾고 있는 것은 React-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)

검색 사례에서 '용어'는 AnimalSearchBox의 속성으로 액세스할 수 있습니다.

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

한번 써보세요. 튜토리얼은 이 토픽이나 다른 관련 토픽에 대한 저의 이해에 있어서 저를 최고의 자리에 올려놓은 것입니다.


최초 답변은 다음과 같습니다.

같은 답을 찾아 여기까지 왔어요이 게시물이 영감을 주는지 확인하십시오.만약 당신의 어플리케이션이 제 것과 같은 것이라면, 그것은 거의 변하지 않고 본체만 다를 것입니다.애플리케이션 상태에 따라 다른 위젯을 렌더링하는 위젯을 작성할 수 있습니다.플럭스 아키텍처를 사용하면 본문 위젯이 전환되는 상태를 변경하는 네비게이션 작업을 디스패치하여 페이지 본문만 효과적으로 업데이트할 수 있습니다.

그게 내가 지금 시도하고 있는 방법이야.

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, 어레이, ints, floats 등의 다른 유형을 전달하기 위한 추가 옵션이 있습니다.

이 질문이 나온지 꽤 됐지만 누군가 도움이 됐으면 좋겠다.

@Cocomico에서 언급한 바와 같이 webpack.config.js 파일에 응용 프로그램의 몇 가지 엔트리 포인트를 제공할 수 있습니다.React 컴포넌트를 스태틱페이지에 추가할 수 있는 간단한 웹팩 셋업(복수의 엔트리 포인트에 근거)을 찾고 있는 경우는, https://github.com/przemek-nowicki/multi-page-app-with-react 를 사용해 주세요.

나는 같은 상황에 있었기 때문에, 나의 요구를 만족시킬 수 있는 답을 찾지 못하고, 이 오래된 질문을 되살린다.그래서 @webdeb의 답변을 바탕으로 (이젝트 없이) CRA를 사용하여 CRA의 장점을 모두 유지하면서 HTML 페이지에 원하는 만큼의 컴포넌트를 삽입할 수 있는 미니 프레임을 작성했습니다.

TL;DR

필요한 모든 파일이 포함된 공개 리포와 이 모든 내용을 자세히 설명하는 중간 기사에 대한 링크를 여기에서 확인할 수 있습니다.

대략적인 생각

해, 「CRA」를 합니다.index.js

import React, { Suspense } from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import reportWebVitals from './reportWebVitals';

//list here all the components that could be inserted in a web page
const apps = {
  'App': React.lazy(() => import('./App')),
  'TestComponent1': React.lazy(() => import('./TestComponent1')),
  'TestComponent2': React.lazy(() => import('./TestComponent2')),
}

//event manager to communicate between the components
const bridgeEvent = new EventTarget();
//common fallback for all the components
function Fallback() {
  return <div>Loading...</div>;
}
const renderAppInElement = (el) => {
  if(apps[el.dataset.reactComponent] && !el.dataset.rendered){
    //get the component's name stored in the data-react-component attribute
    const App = apps[el.dataset.reactComponent];
    //render the component, inject all the HTML attributes and the Event bridge
    ReactDOM.render(
      <Suspense fallback={<Fallback />}>
        <App  {...el.dataset} bridgeEvent={bridgeEvent}/>
      </Suspense>
    , el);
    el.dataset.rendered = true;
  }
  else if(el.dataset.rendered){
    console.log('el', el, 'is already rendered')
  }
}

//ONLY FOR THE DEV PHASE
const rootEl = document.getElementById('root');
//generate components without attributes
if(process.env.REACT_APP_RENDER_CMP){
  const components = process.env.REACT_APP_RENDER_CMP.split(',');
  
  components.forEach(item => {
    const componentEl = document.createElement('div');
    componentEl.setAttribute("data-react-component", item);
    componentEl.className = "__react-cmp";
    rootEl.append(componentEl);
  });
}
//generate components with attributes
if(process.env.REACT_APP_RENDER_CMP_WITH_ATTRS){
  let componentsWithAttrs;
  try{
    componentsWithAttrs = JSON.parse(process.env.REACT_APP_RENDER_CMP_WITH_ATTRS);
  }
  catch(e){
    console.log('fail to parse REACT_APP_RENDER_CMP_WITH_ATTRS', e);
  }
  if(componentsWithAttrs){
    componentsWithAttrs.forEach(cmp => {
      const componentEl = document.createElement('div');
      componentEl.setAttribute("data-react-component", cmp.class);
      componentEl.className = "__react-cmp";
      Object.keys(cmp.data).forEach(attrKey => {
        componentEl.setAttribute(attrKey, cmp.data[attrKey]);
      });
      rootEl.append(componentEl);
    });
  }
}

//the default name of the global object is ReactComponents, but it could be customized via the REACT_APP_NAMESPACE environment variable
const appNamespace = process.env.REACT_APP_NAMESPACE || "ReactComponents";
window[appNamespace] = {
  ready: false,
  parseComponents(container){
    //parse the container or the whole document and inject all the components in the containers that have a "__react-cmp" class
    (container || document)
    .querySelectorAll('.__react-cmp')
    .forEach(renderAppInElement);
  }
}
window[appNamespace].parseComponents();
window[appNamespace].ready = true;

//if dynamic parsing must be done via the window.ReactComponents.parseComponents() method
//check the availability of window.ReactComponents object via window.ReactComponents.ready property
//or define a window.ReactComponentsAsyncInit() method to be notified of the availability
if(typeof window[`${appNamespace}AsyncInit`] === 'function'){
  window[`${appNamespace}AsyncInit`]();
}

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
reportWebVitals();

다음 이 글자를 붙일 수 있어요.REACT_APP_RENDER_CMP "/"/"REACT_APP_RENDER_CMP_WITH_ATTRSCRA의 개발 서버를 사용하는 동안 컴포넌트를 테스트하기 위한 환경 변수입니다.의 ★★★★★★★★★★★★★★★★★..env.development.local을 사용하다

#this will render the TestComponent1 and TestComponent2 without any attributes
REACT_APP_RENDER_CMP="TestComponent1,TestComponent2"

#this will render TestComponent1 with the data-test-attribute attribute set to "test attribute value"
REACT_APP_RENDER_CMP_WITH_ATTRS="[{"class":"TestComponent1","data":{"data-test-attribute":"test attribute value"}}]"

후, 「 」의 「 」의 「 」의 「 」가 필요합니다.index.html.js ★★★★★★★★★★★★★★★★★」.cssReact 구성 요소를 로드해야 하는 여러 페이지 앱의 각 페이지에 포함해야 하는 파일입니다. 꼭 추가해 주세요.INLINE_RUNTIME_CHUNK=false 안에서.env자바스크립트!

그런 다음 표시할 HTML 페이지에 구성 요소의 컨테이너를 추가합니다.예:

<div class="__react-cmp" data-react-component="TestComponent1"></div>

parseComponents()CRA에서 index.js의 파일을 잡습니다.div.__react-cmp 컨테이너로 수업용 하세요.TestComponent1이치노

전용 보고서 기사에서는 CRA를 사용하여 빌드 경로를 변경하는 방법에 대해 설명합니다.BUILD_PATHvariable 할 수 ) 및 빌드된 합니다.index.html을 파일화하여 합니다..js ★★★★★★★★★★★★★★★★★」.css(모든 파일이 아니라 로더를 포함하기만 하면 됩니다.로더의 파일명은 다음과 같습니다.cmp-loader.js고객의 빌딩 옆에 호스트 되어 있습니다.index.html 삭제:

(async () => {
  const head = document.getElementsByTagName('head')[0];
  const scriptSrcRegexp = new RegExp('<script.*?src="(.*?)"', 'gmi');

  //get the exact script's src as defined in the src attribute
  const scriptSrc = scriptSrcRegexp.exec(document.currentScript.outerHTML);
  //all the resources should be relative to the path of this script
  const resourcesPath = (scriptSrc && scriptSrc.length > 1) ? scriptSrc[1].replace('cmp-loader.js', '') : '';

  //get the index content
  const indexHTML = await (await fetch(resourcesPath+'index.html', {cache:'reload'})).text();

  //assume that all the .js and .css files to load are in the "static" folder
  const reactCSSRegexp = new RegExp(`<link href="${resourcesPath}static\/css\/(.*?)\.css" rel="stylesheet">`, 'gm');
  const reactJSRegexp = new RegExp(`<script (.*?) src="${resourcesPath}static\/js\/(.*?)\.js"><\/script>`, 'gm');

  //grab all the css tags
  const ReactCSS = [].concat(indexHTML.match(reactCSSRegexp)).join('');
  //grab all the js tags
  const ReactJS = [].concat(indexHTML.match(reactJSRegexp)).join('');

  //parse and execute the scripts
  const scriptsDoc = new DOMParser().parseFromString(ReactJS, 'text/html');
  Array.from(scriptsDoc.getElementsByTagName('script')).forEach(item => {
    const script = document.createElement('script');
    [...item.attributes].forEach(attr => {
      script.setAttribute(attr.name, attr.value)
    })
    head.appendChild(script);
  });
  //inject the CSS
  head.insertAdjacentHTML('beforeend', ReactCSS);
})().catch(e => {
  console.log('fail to load react-cmp', e)
});

이너시아를 살펴보세요JS: https://inertiajs.com/

Inertia를 사용하면 서버 측 웹 프레임워크에서 항상 하던 대로 애플리케이션을 구축할 수 있습니다.라우팅, 컨트롤러, 미들웨어, 인증, 인가, 데이터 가져오기 등에 프레임워크의 기존 기능을 사용합니다.

유일한 차이점은 뷰 레이어입니다.서버측 렌더링을 사용하는 대신(예:블레이드 또는 ERB 템플릿) 뷰는 JavaScript 페이지 컴포넌트입니다.이를 통해 React, Vue 또는 Svelte를 사용하여 프런트 엔드 전체를 구축할 수 있습니다.

언급URL : https://stackoverflow.com/questions/31933359/how-to-use-react-to-build-a-multi-page-app

반응형