다중 페이지 앱에서 반응 사용
나는 리액션과 놀아왔고 지금까지 정말 좋아해.나는 NodeJS로 앱을 만들고 있는데, 어플리케이션 전체의 일부 인터렉티브 컴포넌트에 대해 리액션을 사용하고 싶다.나는 한 페이지 앱으로 만들고 싶지 않아.
나는 아직 웹에서 다음 질문에 대답할 만한 것을 찾지 못했다.
React 구성 요소를 여러 페이지 앱에서 분리하거나 번들로 묶는 방법
앱의 일부 섹션에서는 로드하지 않아도 현재 내 모든 구성 요소가 하나의 파일에 있다.
지금까지 나는 리액션이 렌더링할 컨테이너의 ID를 검색하여 구성요소를 렌더링하기 위해 조건문을 사용하려고 한다.리액션의 모범적 실무 지침이 무엇인지 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')
);
}
나는 아직 설명서를 읽고 있는데 멀티 페이지 앱에 필요한 것을 찾지 못했어.
미리 고맙다.
현재 나는 비슷한 일을 하고 있다.
어플리케이션은 완전한 리액션 앱이 아니라 오토마크인 CommentBox와 같이 다이내믹한 Stuff를 위한 리액션을 사용하고 있다.그리고 어느 지점에서나 특별한 매개체가 포함될 수 있다.
그러나 모든 하위 앱이 로드되어 단일 파일에 포함됨all.js
페이지에 걸쳐 브라우저에 의해 캐시될 수 있다.
SSR 템플릿에 앱을 포함시켜야 하는 경우 "_react-root" 클래스와 특수 ID(렌더링할 리액트 앱 이름)가 포함된 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>
편집
웹팩은 코드 분할 & 게으름부담을 완벽하게 지원하므로, 모든 앱을 한 묶음으로 로드하지 않고 분할하여 온디맨드로 로딩하는 예를 포함하는 것이 타당하다고 생각했다.
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 폴더에서 각각의 js 파일과 함께 3개의 다른 html 파일을 가질 수 있다 (1페이지에 대한 설명).
<!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'))
그런 다음 각 단일 페이지에 대해 서로 다른 리액션 구성 요소를 로드할 수 있다.
나는 원점에서 원서를 만들고 있고 내가 가면서 배우고 있지만, 네가 찾고 있는 것은 리액션 로터라고 생각해.반응 라우터는 당신의 구성 요소를 특정 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의 속성으로 'term'에 액세스할 수 있음:
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, array's, ints, floats 등과 같은 다른 유형을 전달하기 위한 추가 옵션이 있다.
이 질문을 받은 지 꽤 오래됐다는 것을 알지만 이것이 누군가에게 도움이 되기를 바란다.
@Cocomico에서 언급한 바와 같이 webpack.config.js 파일에서 응용 프로그램에 대한 몇 가지 진입점을 제공할 수 있다.정적 페이지에 반응 구성요소를 추가할 수 있는 간단한 웹 팩 설정(여러 진입점 아이디어에 기반)을 찾고 있는 경우 다음 웹 팩 설정을 사용하십시오. https://github.com/przemek-nowicki/multi-page-app-with-react
나는 내 욕구를 충족시킬 수 있는 답을 찾지 못한 채, 같은 처지였기 때문에 이 오래된 질문을 되살린다.그래서 @webdeb의 대답을 바탕으로 CRA를 (꺼내지 않고) 사용하여 CRA의 장점을 모두 보존하면서 원하는 만큼의 구성요소를 HTML 페이지에 주입하는 미니프레임워크를 작성했다.
TL;DR
필요한 모든 파일과 이 모든 것을 완벽하게 설명하는 미디엄 기사에 대한 링크가 들어 있는 내 공개 리포(repo)를 여기에서 확인할 수 있다.
일반적인 생각
비결은 평소처럼 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_ATTRS
CRA의 개발 서버를 사용하는 동안 구성 요소를 테스트하기 위한 환경 변수.당신의.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
그리고.css
대응 구성요소를 로드해야 하는 다중 페이지 앱의 각 페이지에 포함해야 하는 파일.추가하는 것을 잊지 마십시오.INLINE_RUNTIME_CHUNK=false
당신 안에.env
인라인 자바스크립트를 피하기 위해 파일을 작성하십시오!
그런 다음 표시할 HTML 페이지에 구성요소의 컨테이너를 추가하십시오.예를 들어,
<div class="__react-cmp" data-react-component="TestComponent1"></div>
그parseComponents()
크레이즈로 선언된index.js
파일을 실행해야 하며, 파일을 움켜쥐어야 함div
…과 함께.__react-cmp
class, 그리고 나서 그것을 당신의 컨테이너로 사용한다.TestComponent1
구성 요소를 반응하십시오.
전용 리포와 기사에서는 CRA의 도움으로 빌드 경로를 변경할 수 있는 방법을 설명한다.BUILD_PATH
환경 변수(서버 또는 CDN에서 빌드 파일을 호스팅할 수 있음)와 나는 빌드된 파일을 구문 분석할 로더를 제공한다.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/
관성 기능을 사용하면 선택한 서버측 웹 프레임워크에서 항상 그랬던 것처럼 애플리케이션을 구축할 수 있다.라우팅, 컨트롤러, 미들웨어, 인증, 권한 부여, 데이터 가져오기 등에 프레임워크의 기존 기능을 사용하는 경우.
다만 다른 점은 시야층이다.서버 측 렌더링을 사용하는 대신(예:블레이드 또는 ERB 템플릿), 보기는 JavaScript 페이지 구성 요소임.이렇게 하면 리액션, 부에 또는 스벨트를 사용하여 전체 프런트엔드를 만들 수 있다.
참조URL: https://stackoverflow.com/questions/31933359/using-react-in-a-multi-page-app
'Programing' 카테고리의 다른 글
관찰 가능>에서 특정 요소를 제거하는 방법 (0) | 2022.03.19 |
---|---|
다른 항목을 클릭하여 요소의 클래스를 추가 및 제거하는 방법리액트-리플렉스로 (0) | 2022.03.19 |
Angular NgRx - 처음 호출된 서비스 폴링을 계속하는 효과 (0) | 2022.03.19 |
구성 요소가 리듀렉스 저장소에 연결되지 않는 이유 (0) | 2022.03.19 |
Python 유니코드 문자열에서 억양을 제거하는 가장 좋은 방법은 무엇인가? (0) | 2022.03.18 |