programing

리액트 renderToString() 퍼포먼스 및 캐시 리액트컴포넌트

megabox 2023. 3. 25. 11:00
반응형

리액트 renderToString() 퍼포먼스 및 캐시 리액트컴포넌트

이 '아까부터'가 에 띄었어요.reactDOM.renderToString()서버에서 큰 컴포넌트 트리를 렌더링할 때 메서드가 크게 느려지기 시작합니다.

배경

약간 배경화면.시스템은 완전 동형 스택입니다. 레벨 ' ' ' '''App구성 요소는 템플릿, 페이지, 돔 요소 및 기타 구성 요소를 렌더링합니다.코드를 의 컴포넌트컴포넌트로 를 렌더링하고 있는 을 알 수 .<p>this is a react component</p>.

개발 시 최대 1500개의 컴포넌트를 렌더링하는 데 최대 200~300ms가 소요됩니다.일부 컴포넌트를 제거함으로써 최대 1200개의 컴포넌트를 175~225ms에 렌더링할 수 있었습니다.

운영 환경에서 최대 1500개의 구성 요소에서 renderToString을 수행하는 데 약 50~200ms가 소요됩니다.

시간이 선형으로 보입니다.느린 컴포넌트는 없습니다.다수의 컴포넌트의 합계한 것입니다.

문제

이로 인해 서버에 몇 가지 문제가 발생합니다.방법이 길면 서버 응답 시간이 길어집니다.TTFB의 경우API는 250ms의 renderToString을 250ms의 renderToString으로 합니다.SEO seo seo seo seo seo seo seo seo seo seo seo seo 。 동기 에, 「동기 방식renderToString()는 노드 서버를 차단하고 후속 요구를 백업할 수 있습니다(이는 2개의 개별 노드 서버(하나는 웹 서버로서, 1개는 반응만을 렌더링하는 서비스로서)를 사용하여 해결할 수 있습니다).

시도

이상적으로는 프로덕션에서 5~50ms가 소요됩니다.몇 가지 아이디어를 생각해 봤는데, 어떤 방법이 최선일지 잘 모르겠어요.

아이디어 1: 컴포넌트 캐싱

'static 컴포넌트는 수 으로 캐시를 으로, 「」는 「」를 참조할 수.renderToString()을 사용법컴포넌트를 찾으면 자동으로 문자열을 가져옵니다.상위 레벨의 컴포넌트에서 이 작업을 수행하면 중첩된 모든 하위 컴포넌트의 마운트가 절약됩니다.마크업의 . ID아이디

아이디어 2: 컴포넌트를 심플/덤으로 표시

컴포넌트를 '심플'로 정의함으로써 리액션은 렌더링 시 모든 라이프 사이클 방법을 건너뛸 수 있습니다. 돔컴포넌트에 대해 이 합니다(React는 react dom).<p/>,<h1/> 하는 것이

아이디어 3: 서버 측 렌더링 컴포넌트 건너뛰기

서버에서 반환할 필요가 없는 컴포넌트(SEO 값 없음)는 서버에서 건너뛰기만 하면 됩니다.로딩되면, 「」를 설정합니다.clientLoaded을 들다true그걸 넘겨주고 재심판을 강요하는 거죠

종료 및 기타 시도

지금까지 구현한 유일한 솔루션은 서버에 렌더링되는 컴포넌트의 수를 줄이는 것입니다.

델이 검토하고 있는 프로젝트에는 다음과 같은 것이 있습니다.

비슷한 문제에 직면한 사람이 있습니까?당신은 무엇을 할 수 있었습니까?감사해요.

react-router 1.0과 react0.14를 사용하여 플럭스 오브젝트를 여러 번 시리얼화하고 있었습니다.

RoutingContext를 호출합니다.createElement이치노을 사용법플럭스도 사용합니다.큰 오브젝트의 시리얼화 버전을 보내드립니다.는 '우리'를 하고 요.flux.serialize()createElement로 합니다.시리얼화 방법에는 최대 20ms가 소요될 수 있습니다.의 템플릿에 됩니다.renderToString()★★★★★★★★★★★★★★★★★★!

이전 코드:

function createElement(Component, props) {
    props = _.extend(props, {
        flux: flux,
        path: path,
        serializedFlux: flux.serialize();
    });
    return <Component {...props} />;
}
var start = Date.now();
markup = renderToString(<RoutingContext {...renderProps} createElement={createElement} />);
console.log(Date.now() - start);

이에 맞게 쉽게 최적화:

var serializedFlux = flux.serialize(); // serialize one time only!

function createElement(Component, props) {
    props = _.extend(props, {
        flux: flux,
        path: path,
        serializedFlux: serializedFlux
    });
    return <Component {...props} />;
}
var start = Date.now();
markup = renderToString(<RoutingContext {...renderProps} createElement={createElement} />);
console.log(Date.now() - start);

에는 이렇게 '아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아.renderToString(), 1x는 120밀리초, 30밀리초)을 해야 합니다.serialize()는 ~은, 「20밀리초」(「20밀리초」)보다 전에 합니다.renderToString()빠른 개선이었습니다.-당장의 영향을 모르더라도 항상 올바르게 하는 것이 중요합니다.

아이디어 1: 컴포넌트 캐싱

업데이트 1: 하단에 완전한 작업 예를 추가했습니다.메모리에 컴포넌트를 캐시하고 업데이트를 합니다.data-reactid.

이것은 실제로 쉽게 할 수 있다.몽키패치 해야지 ReactCompositeComponent캐시된 버전을 확인합니다.

import ReactCompositeComponent from 'react/lib/ReactCompositeComponent';
const originalMountComponent = ReactCompositeComponent.Mixin.mountComponent;
ReactCompositeComponent.Mixin.mountComponent = function() {
    if (hasCachedVersion(this)) return cache;
    return originalMountComponent.apply(this, arguments)
}

돼요.require('react')앱 내 어디에나 있습니다.

웹 팩 주의:이런 걸 쓰면new webpack.ProvidePlugin({'React': 'react'}) 돼요.new webpack.ProvidePlugin({'React': 'react-override'}).react-override.js 내보내기react (예:)module.exports = require('react'))

에 갱신하는 한 예reactid을 사용하다

import ReactCompositeComponent from 'react/lib/ReactCompositeComponent';
import jsan from 'jsan';
import Logo from './logo.svg';

const cachable = [Logo];
const cache = {};

function splitMarkup(markup) {
    var markupParts = [];
    var reactIdPos = -1;
    var endPos, startPos = 0;
    while ((reactIdPos = markup.indexOf('reactid="', reactIdPos + 1)) != -1) {
        endPos = reactIdPos + 9;
        markupParts.push(markup.substring(startPos, endPos))
        startPos = markup.indexOf('"', endPos);
    }
    markupParts.push(markup.substring(startPos))
    return markupParts;
}

function refreshMarkup(markup, hostContainerInfo) {
    var refreshedMarkup = '';
    var reactid;
    var reactIdSlotCount = markup.length - 1;
    for (var i = 0; i <= reactIdSlotCount; i++) {
        reactid = i != reactIdSlotCount ? hostContainerInfo._idCounter++ : '';
        refreshedMarkup += markup[i] + reactid
    }
    return refreshedMarkup;
}

const originalMountComponent = ReactCompositeComponent.Mixin.mountComponent;
ReactCompositeComponent.Mixin.mountComponent = function (renderedElement, hostParent, hostContainerInfo, transaction, context) {
    return originalMountComponent.apply(this, arguments);
    var el = this._currentElement;
    var elType = el.type;
    var markup;
    if (cachable.indexOf(elType) > -1) {
        var publicProps = el.props;
        var id = elType.name + ':' + jsan.stringify(publicProps);
        markup = cache[id];
        if (markup) {
            return refreshMarkup(markup, hostContainerInfo)
        } else {
            markup = originalMountComponent.apply(this, arguments);
            cache[id] = splitMarkup(markup);
        }
    } else {
        markup = originalMountComponent.apply(this, arguments)
    }
    return markup;
}
module.exports = require('react');

리액션 동형 앱과 같은 문제가 있어서 몇 가지를 사용했습니다.

  1. nodejs 서버 앞에서 Nginx를 사용하여 렌더링된 응답을 잠시 캐시합니다.

  2. 항목 목록을 표시할 경우 목록의 일부만 사용합니다.예를 들어 뷰포트를 채우기 위해 X개의 항목만 렌더링하고 나머지 목록은 Websocket 또는 XHR을 사용하여 클라이언트 측에 로드합니다.

  3. 중 일부 있어 측(「」)에서만됩니다.componentDidMount이러한 컴포넌트는 보통 그래프 또는 프로파일 관련 컴포넌트입니다.에서 볼 때 아무런

  4. SEO에 대해서, 6개월 동안 이형 앱을 사용해 본 경험에서.Google Bot은 클라이언트 측 리액트 웹 페이지를 쉽게 읽을 수 있기 때문에 서버 측 렌더링을 굳이 할 필요가 없습니다.

  5. 「 」는 .<Head> ★★★★★★★★★★★★★★★★★」<Footer>정적 문자열로 사용하거나 템플릿엔진(Reactjs-핸들바)을 사용하여 페이지 내용만 렌더링하면 렌더링된 컴포넌트가 몇 개 저장됩니다.한 페이지 앱의 경우, 각 내비게이션을 통해 제목 설명을 업데이트할 수 있습니다.Router.Run.

빠른 반응 렌더가 도움이 될 것 같아요.서버 렌더링 성능이 3배 향상됩니다.

패키지를 설치하고 ReactDOM.renderToString을 FastReactRender.elementToString으로 치환하기만 하면 됩니다.

var ReactRender = require('fast-react-render');

var element = React.createElement(Component, {property: 'value'});
console.log(ReactRender.elementToString(element, {context: {}}));

또한 fast-react-server를 사용할 수도 있습니다.이 경우 렌더링은 기존 리액트 렌더링보다 14배 빠릅니다.다만, 렌더링 하는 각 컴포넌트는, 그 컴포넌트와 함께 선언할 필요가 있습니다(「fast-react-seed」의 예, 「Webpack에 대해서 실시하는 방법」을 참조해 주세요).

언급URL : https://stackoverflow.com/questions/34728962/react-rendertostring-performance-and-caching-react-components

반응형