Ionic SSR with React: 모험



React는 UI를 위한 훌륭한 라이브러리이며 UI를 더 쉽게 구축하고 개발 시간을 크게 줄여줍니다. Ionic은 크로스 플랫폼 하이브리드 앱 개발을 위한 훌륭한 라이브러리이기도 합니다. Ionic은 React와 쉽게 통합될 수 있습니다. 실제로 React는 Ionic 팀에서 공식적으로 지원합니다.

모든 꽃과 장미가 아닙니다.



하지만 문제가 있습니다. React는 JS에서 UI를 작성하는 JS 라이브러리입니다. 이는 HTML이 브라우저에서 생성됨을 의미합니다. 이로 인해 귀하의 웹 사이트를 색인화하는 크롤러에게는 적합하지 않은 경험이 됩니다.

모험



접근법 1: nodejs 환경에서 ReactDOMServer 사용



React.js 앱에 대해 SSR을 활성화하는 표준 방법은 서버에서 ReactDOMServer를 사용하고 클라이언트에서 render() 메서드를 hydrate()로 교체하여 애플리케이션이 다시 렌더링되지 않도록 하는 것입니다(이미 렌더링된 구성 요소 ) 브라우저에 제공될 때.

그러나 문제가 있습니다. Ionic 구성 요소는 ReactDOMServer에서 지원하지 않는 웹 구성 요소를 사용하는 stencil.js를 사용합니다. 그래서 우리는 다른 해결책이 필요합니다.

예시 스니펫:

import express from "express";
import ReactDOMServer from "react-dom/server";
import { App } from "./App";

const app = express();
app.use(express.static(STATIC_ASSETS_DIR));

app.get("*", (req, res) => {
  const appHtml = ReactDOMServer.renderToString(<App />);
  // Assume that we have an html template
  const html = htmlTemplate.replace("^_^__APP_SHOULD_BE_HERE__^_^", appHtml);
  res.send(html);
});


접근법 2: Next.js 사용



대부분의 경우 이 방법이 효과가 있습니다. 다음과 같이 작동합니다.
  • @ionic/react 대신 @ionic/core 사용
  • React 구성 요소(예: <IonHeader /> )를 사용하는 대신 웹 구성 요소(예: <ion-header /> )를 사용합니다
  • .
  • 호출하여 웹 구성 요소 초기화defineCustomeElements
  • 그게 다입니다(Next.js를 사용하도록 프로젝트를 구성했다고 가정). 시작 저장소의 경우 이 저장소moonbase-vercel-ionic를 확인하십시오.

  • 그러나 당시 우리 코드베이스에는 @ionic/core에서 지원되지 않는 일부 구성 요소가 있었습니다. 그래서 우리는 이 방법을 사용할 수 없었습니다.

    접근법 3: 총을 칼싸움에 가져오기



    무엇이든 작동하는 마지막 접근 방식은 브라우저를 사용하는 것입니다. 브라우저는 웹 구성 요소를 지원하며 특정 라이브러리(예: 반응, 각도 또는 vue)에 제한되지 않습니다. 그래서 저는 크롬과 파이어폭스를 제어하기 위한 Node.js API를 제공하는 라이브러리인 puppeteer를 사용했습니다.

    따라서 최종 작업 흐름은 다음과 같습니다.
  • 클라이언트가 서버에 GET 요청을 보냅니다
  • .
  • 서버는 필요한 리소스가 페이지인지 또는 정적 자산인지 확인합니다.
  • 정적 자산인 경우 익스프레스로 제공하고 Nginx가 파일 압축과 함께 캐시 헤더를 추가하도록 합니다.
    그렇지 않으면 페이지가 이미 렌더링되었고 캐시에 있는지 확인하십시오.
  • 캐시에 있는 경우 일반 정적 자산으로 제공합니다.
  • 그렇지 않으면 Puppeteer를 실행하여 페이지 URI를 로드합니다.
  • 페이지가 로드될 때까지 기다리십시오.
  • Puppeteer에서 페이지의 HTML을 가져와 캐시에 저장합니다.
  • HTML을 보냅니다.

  • 이는 Node.js에서 다음과 같이 작성할 수 있습니다.

    const app = express();
    
    // Static files handler
    app.get("*.*", (req, res) => res.sendFile(req.url));
    
    // Dynamic Page handler
    app.get("*", async (req, res) => {
      if (cache.has(req.url)) {
        res.send(cache.get(req.url));
        return;
      }
      const browser = await puppeteer.launch();
      const page = await browser.newPage();
      await page.goto(`http://localhost:${CLIENT_PORT}`, { waitUntil: "networkidle0" });
      const html = await page.content();
      res.send(html);
      cache.put(req.url, html);
      await browser.close();
    });
    
    app.listen(SERVER_PORT);
    
    // React Server
    
    app.use(express.static(STATIC_ASSETS_DIR));
    
    app.get("*", (_, res) => res.sendFile("index.html"));
    
    app.listen(CLIENT_PORT);
    


    이 예제에서는 두 개의 서버를 사용했습니다. 하나는 클라이언트(즉, 사용자의 브라우저)가 캐시에 요청을 보내고 캐시를 처리하고 정적 자산을 제공하는 반면 다른 하나는 Puppeteer가 애플리케이션을 로드하기 위해 연결하는 서버입니다.

    이것은 트래픽이 많은 웹사이트에 무거운 인프라가 필요하기 때문에 최상의 솔루션이 아닙니다.

    This article was originally posted on my own blog so if you find that it has been copied, don't worry I copied it from my self.

    좋은 웹페이지 즐겨찾기