React는 기본적으로 서버 측 렌더링을 구현합니다.

React는 기본적으로 서버 측 렌더링을 구현합니다.

기사 출처 : Lagou 빅 프런트 엔드 고소득 교육 캠프

연습 코드 주소

1. 서버 측 렌더링이 빠르게 시작됩니다.

1. React SSR 구현

  1. 렌더링 할 React 컴포넌트 소개
  2. renderToString 메서드를 통해 React 구성 요소를 HTML 문자열로 변환
  3. 결과 HTML 문자열을 클라이언트에 응답

renderToStringHTML 문자열을 변환하기위한 React 어셈블리 방법을 react-dom/server소개합니다.

2. Webpack 패키징 구성

문제점 : 노드 환경은 ESModule 모듈 시스템을 지원하지 않으며 JSX 구문을 지원하지 않습니다.

3. 프로젝트 시작 명령 구성

  1. 서버 측 패키징 명령을 구성합니다. "dev:server-build": "webpack --config webpack.server.js --watch"
  2. 서버 시작 명령을 구성하십시오. "dev:server-run": "nodemon --watch build --exec\"node build/bundler.js\""

클라이언트 측 React 추가 이벤트 2 개

1. 실현 분석

클라이언트 측에서 구성 요소의 보조 "렌더링"을 수행하고 구성 요소 요소에 이벤트를 첨부합니다.

2. 클라이언트 측은 수화물을 "렌더링"합니다.

hydrate 메서드를 사용하여 구성 요소를 렌더링하고 구성 요소에 이벤트를 첨부합니다.
hydrate 메서드는 렌더링 할 때 기존 DOM 노드를 재사용하여 노드 재생성 및 원래 DOM 노드 삭제의 오버 헤드를 줄입니다.
react-dom을 통해 수화물 가져 오기

ReactDOM.hydrate(<Home/>, document.getElementById('#root'))

3. Client React 패키징 구성

  1. webpack 구성
    패키징 목적 : JSX 구문 변환, 브라우저에서 인식되지 않는 고급 JavaScript 구문 변환
    패키지 대상 : 공용 폴더

  2. 패키지 시작 명령 구성

"dev:client-build": "webpack --config webpack.client.js --watch"

4. 클라이언트 패키지 파일 요청 링크 추가

클라이언트에 대한 응답의 HTML 코드에 스크립트 태그를 추가하여 클라이언트 JavaScript에 파일을 패키징하도록 요청합니다.

  <html>
    <head>
      <title> React SSR</title>
    </head>
    <body>
      <div id="root">${content}</div>
      <script src="bundle.js"></script>
    </body>
  </html>

5. 정적 리소스에 대한 서버 측 액세스

서버 측 프로그램은 정적 리소스 액세스 기능을 구현하고 클라이언트 측 JavaScript 패키지 파일은 정적 리소스로 사용됩니다.

app.use(express.static('public'))

셋, 최적화

1. 웹팩 구성 병합

서버 측 웹팩 구성 및 클라이언트 측 웹팩 구성이 복제되고 중복 구성이 webpack.base.js 구성 파일로 추상화됩니다.

2. 프로젝트 시작 명령 병합

목적 : npm-run-all 도구를 통해 하나의 명령을 사용하여 프로젝트를 시작하고 여러 명령을 시작하는 번거로운 문제를 해결합니다.

"dev": "npm-run-all --parallel dev:*"

3. 서버 측 패키지 파일 볼륨 최적화

문제 : 서버 측 패키지 파일에 노드 시스템 모듈이 포함되어있어 패키지 파일 자체가 커집니다.
솔루션 : 웹팩 구성을 통해 패키지 파일에서 노드 모듈을 제거하십시오.

const nodeExternals = require('webpack-node-externals')
const config = {
    
    
  externals: [nodeExternals()]
}
module.exports = merge(baseConfig, config)

4. 시작 서버 코드와 렌더링 코드를 모듈 식으로 분할

코드 구성 최적화, React 컴포넌트 코드 렌더링은 독립적 인 기능이므로 서버 측 항목 파일에서 추출됩니다.

네, 라우팅 지원

1. 실현 분석

React SSR 프로젝트에서는 양쪽에서 라우팅을 구현해야합니다.
클라이언트 측 라우팅은 사용자가 링크를 클릭하여 페이지로 이동할 수 있도록 지원하는 데 사용됩니다.
서버 측 라우팅은 사용자가 브라우저 주소 표시 줄에서 직접 페이지에 액세스 할 수 있도록 지원하는 데 사용됩니다.
클라이언트와 서버는 일련의 라우팅 규칙을 공유합니다.

2. 라우팅 규칙 작성

share / routes.js

import Home from './pages/Home'
import List from './pages/List'

export default [
  {
    
    
    path: '/',
    component: Home,
    exact: true
  }, {
    
    
    path: '/list',
    component: List,
  }
]

3. 서버 측 라우팅 구현

  1. 익스프레스 라우팅은 모든 요청을 수락
    익스프레스 라우팅은 모든 요청을 수락

Express 라우팅은 모든 Get 요청을 수락하고 서버 측 React 라우팅은 요청 경로를 통해 렌더링되는 구성 요소와 일치합니다.

  1. 서버 측 라우팅 구성
import React from 'react'
import {renderToString} from 'react-dom/server'
import { StaticRouter } from "react-router-dom";
import routes from '../share/routes'
import { renderRoutes } from "react-router-config";

export default (req) => {
  const content = renderToString(
    <StaticRouter location={req.path}>
      {renderRoutes(routes)}
    </StaticRouter>
  )
  return `
  <html>
    <head>
      <title> React SSR</title>
    </head>
    <body>
      <div id="root">${content}</div>
      <script src="bundle.js"></script>
    </body>
  </html>
  `
}

4. 클라이언트 라우팅 구현

클라이언트 라우팅 구성 추가

import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter } from "react-router-dom"
import { renderRoutes } from "react-router-config";
import routes from '../share/routes'

ReactDOM.hydrate(
  <BrowserRouter>
    {renderRoutes(routes)}
  </BrowserRouter>
  , document.getElementById('root'))

다섯, Redux 지원

1. 실현 분석

React SSR을 구현하는 프로젝트에서 Redux를 양쪽에 구현해야합니다.
클라이언트 Redux는 클라이언트 JavaScript를 통해 Store의 데이터를 관리하는 것입니다.
서버 Redux는 서버에 Redux 코드 세트를 구축하여 관리합니다. 구성 요소. 데이터
클라이언트는 서버와 서버 공유 감속기 코드의 집합 두.의 코드
저장소를 만드는 인해 서로 다른 매개 변수 전달에 공유 할 수 없습니다.

브라우저가 기본적으로 비동기 함수를 지원하지 않기 때문에 비동기 디스패치를 ​​만들 때 오류가보고됩니다.


포착 되지 않은 ReferenceError : invokePassiveEffectCreate (react-dom.development.js : 23482
)
의 eval (List.js : 16)
에서 fetchUser ( user.action.js : 44)의 eval (user.action.js : 17) regeneratorRuntime이 정의되지 않았습니다. )
at HTMLUnknownElement.callCallback (react-dom.development.js : 3945)
at Object.invokeGuardedCallbackDev (react-dom.development.js : 3994)
at invokeGuardedCallback (react-dom.development.js : 4056)
at flushPassiveEffectsImpl (react-dom .development.js : 23569)
at unstable_runWithPriority (scheduler.development.js : 646)
at runWithPriority $ 1 (react-dom.development.js : 11276)

Babel은 polyfill 지원을 엽니 다.

{
    
    
  test: /\.js$/,
  exclude: /node_modules/,
  use: {
    
    
    loader: 'babel-loader',
    options: {
    
    
      presets: [
        [
          '@babel/preset-env',
          {
    
    
            useBuiltIns: 'usage'
          }
        ],
        '@babel/preset-react'
      ]
    }
  }
}

2. 서버 측 Redux 실현

  1. 스토어 만들기

server / createStore.js

import {
    
     createStore, applyMiddleware } from "redux";
import thunk from 'redux-thunk'
import reducer from '../share/store/reducers'

export default () => createStore(reducer, {
    
    }, applyMiddleware(thunk))
  1. 스토어 구성

server / index.js

import app from './http'
import renderer from './renderer'
import createStore from './createStore'

app.get('*', (req, res) => {
    
    
  const store = createStore()
  res.send(renderer(req, store))
})

server / renderer.js

import React from 'react'
import {
    
    renderToString} from 'react-dom/server'
import {
    
     StaticRouter } from "react-router-dom";
import routes from '../share/routes'
import {
    
     renderRoutes } from "react-router-config";
import {
    
     Provider } from "react-redux";

export default (req, store) => {
    
    
  const content = renderToString(
    <Provider store={
    
    store}>
      <StaticRouter location={
    
    req.path}>
        {
    
    renderRoutes(routes)}
      </StaticRouter>
    </Provider>
  )
  return `
  <html>
    <head>
      <title> React SSR</title>
    </head>
    <body>
      <div id="root">${
      
      content}</div>
      <script src="bundle.js"></script>
    </body>
  </html>
  `
}

3. 서버 측 저장소 데이터 채우기

문제 : 서버 측에서 생성 된 저장소가 비어 있고 구성 요소가 저장소에서 데이터를 가져올 수 없습니다.
솔루션 : 서버는 구성 요소를 렌더링하기 전에 구성 요소에 필요한 데이터를 얻습니다.

  1. 구성 요소에 loadData 메서드를 추가합니다.이 메서드는 구성 요소에 필요한 데이터를 가져 오는 데 사용됩니다.이 메서드는 서버에서 호출됩니다.
  2. 현재 구성 요소의 라우팅 정보 개체에 loadData 메서드를 저장합니다.
  3. 서버가 요청을 수신 한 후 요청 주소에 따라 렌더링 할 컴포넌트의 라우팅 정보를 일치시킵니다.
  4. 라우팅 정보에서 컴포넌트의 loadData 메소드를 확보하고 메소드를 호출하여 컴포넌트에 필요한 데이터를 확보하십시오.
  5. 데이터 수집이 완료되면 구성 요소를 렌더링하고 결과로 클라이언트에 응답합니다.

4. 반응 경고 제거

react-dom.development.js : 67 경고 : 서버 HTML에

    • .
      ul
      at div
      at List (webpack : //react-ssr/./src/share/pages/List.js? : 19 : 19)
      at Connect (List) (webpack : //react-ssr/./node_modules/ react-redux / es / components / connectAdvanced.js? : 231 : 68)
      at Route (webpack : //react-ssr/./node_modules/react-router/esm/react-router.js? : 464 : 29)
      at
      라우터 (webpack : //react-ssr/./node_modules/react-router/ ) 에서 스위치 (webpack : //react-ssr/./node_modules/react-router/esm/react-router.js? : 670 : 29) ESM은 / 반응하여-router.js : 93 : 30)
      BrowserRouter에서 (웹팩 : //react-ssr/./node_modules/react-router-dom/esm/react-router-dom.js : 59 : 35)
      에서 공급자 (webpack : //react-ssr/./node_modules/react-redux/es/components/Provider.js? : 16 : 20)

경고 이유 : 클라이언트 저장소에 초기 상태의 데이터가 없습니다. 구성 요소를 렌더링 할 때 빈 ul을 생성하지만 서버 측에서 먼저 데이터를 얻은 다음 구성 요소 렌더링을 수행
하므로 생성 된 ul이 하위 요소와 함께 수화됩니다. 방법을 비교했을 때 두 가지가 일치하지 않아 경고가보고되었습니다
솔루션 : 클라이언트가 초기 데이터를 갖도록 서버에서 얻은 데이터를 클라이언트에 백필하십시오.

  1. 서버가 Store 초기 상태에 응답

server / renderer.js

import React from 'react'
import {
    
    renderToString} from 'react-dom/server'
import {
    
     StaticRouter } from "react-router-dom";
import routes from '../share/routes'
import {
    
     renderRoutes } from "react-router-config";
import {
    
     Provider } from "react-redux";
import serialize from 'serialize-javascript'

export default (req, store) => {
    
    
  const content = renderToString(
    <Provider store={
    
    store}>
      <StaticRouter location={
    
    req.path}>
        {
    
    renderRoutes(routes)}
      </StaticRouter>
    </Provider>
  )
  const initialState = JSON.stringify(JSON.parse(serialize(store.getState())))
  return `
  <html>
    <head>
      <title> React SSR</title>
    </head>
    <body>
      <div id="root">${
      
      content}</div>
      <script>window.INITIAL_STATE = ${
      
      initialState} </script>
      <script src="bundle.js"></script>
    </body>
  </html>
  `
}
  1. 클라이언트는 Store의 초기 상태를 설정합니다.

client / createStore.js

import {
    
     createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import reducer from '../share/store/reducers'

const store = createStore(reducer, window.INITIAL_STATE, applyMiddleware(thunk))

export default store

4. XSS 공격 방지

전환 상태의 악성 코드

let response = {
    
    
  data: [{
    
    id: 1, name: '<script>alert(1)</script>'}]
}
import serialize from 'serialize-javascript'

const initialState = serialize(store.getState())

추천

출처blog.csdn.net/jal517486222/article/details/112798533