Heim  >  Artikel  >  Web-Frontend  >  React führt die Rendering-Transformation auf der Serverseite durch

React führt die Rendering-Transformation auf der Serverseite durch

php中世界最好的语言
php中世界最好的语言Original
2018-06-15 14:57:101190Durchsuche

Dieses Mal werde ich Ihnen die Rendering-Transformation des Servers durch React vorstellen. Hier ist ein praktischer Fall.

Aufgrund des Bedarfs an Webseiten-SEO wollte ich das vorherige React-Projekt in serverseitiges Rendering umwandeln. Nach einigen Recherchen und Recherchen habe ich viele Internetinformationen konsultiert. Erfolgreich in die Falle getreten.

Auswahlidee: Um serverseitiges Rendering zu implementieren, möchte ich die neueste Version von React verwenden und keine größeren Änderungen an der vorhandenen Schreibmethode vornehmen Zu Beginn wird empfohlen, direkt mit dem NEXT-Framework zu schreiben

Projektadresse: https://github.com/wlx200510/react_koa_ssr

Gerüstauswahl: webpack3.11.0 + reagieren Router4 + Redux + koa2 + React16 + Node8.x

Haupterfahrung: Machen Sie sich mit React-bezogenem Wissen vertrauter, erweitern Sie erfolgreich Ihr technisches Feld und sammeln Sie serverseitige Technologie in tatsächlichen Projekten

Hinweis: Vor der Verwendung Stellen Sie beim Framework sicher, dass die aktuelle Webpack-Version 3 ist

Einführung in das Projektverzeichnis

├── assets
│ └── index.css //放置一些全局的资源文件 可以是js 图片等
├── config
│ ├── webpack.config.dev.js 开发环境webpack打包设置
│ └── webpack.config.prod.js 生产环境webpack打包设置
├── package.json
├── README.md
├── server server端渲染文件,如果对不是很了解,建议参考[koa教程](http://wlxadyl.cn/2018/02/11/koa-learn/)
│ ├── app.js
│ ├── clientRouter.js // 在此文件中包含了把服务端路由匹配到react路由的逻辑
│ ├── ignore.js
│ └── index.js
└── src
 ├── app 此文件夹下主要用于放置浏览器和服务端通用逻辑
 │ ├── configureStore.js //redux-thunk设置
 │ ├── createApp.js  //根据渲染环境不同来设置不同的router模式
 │ ├── index.js
 │ └── router
 │  ├── index.js
 │  └── routes.js  //路由配置文件! 重要
 ├── assets
 │ ├── css    放置一些公共的样式文件
 │ │ ├── _base.scss  //很多项目都会用到的初始化css
 │ │ ├── index.scss
 │ │ └── my.scss
 │ └── img
 ├── components    放置一些公共的组件
 │ ├── FloatDownloadBtn 公共组件样例写法
 │ │ ├── FloatDownloadBtn.js
 │ │ ├── FloatDownloadBtn.scss
 │ │ └── index.js
 │ ├── Loading.js
 │ └── Model.js   函数式组件的写法
 │
 ├── favicon.ico
 ├── index.ejs    //渲染的模板 如果项目需要,可以放一些公共文件进去
 ├── index.js    //包括热更新的逻辑
 ├── pages     页面组件文件夹
 │ ├── home
 │ │ ├── components  // 用于放置页面组件,主要逻辑
 │ │ │ └── homePage.js
 │ │ ├── containers  // 使用connect来封装出高阶组件 注入全局state数据
 │ │ │ └── homeContainer.js
 │ │ ├── index.js  // 页面路由配置文件 注意thunk属性
 │ │ └── reducer
 │ │  └── index.js // 页面的reducer 这里暴露出来给store统一处理 注意写法
 │ └── user
 │  ├── components
 │  │ └── userPage.js
 │  ├── containers
 │  │ └── userContainer.js
 │  └── index.js
 └── store
  ├── actions   // 各action存放地
  │ ├── home.js
  │ └── thunk.js
  ├── constants.js  // 各action名称汇集处 防止重名
  └── reducers
   └── index.js  // 引用各页面的所有reducer 在此处统一combine处理

Die Konstruktionsidee des Projekts

    Lokale Entwicklung verwendet Webpack-Dev-Server Um Hot-Updates zu erreichen, ähnelt der grundlegende Prozess der vorherigen Reaktionsentwicklung und ist immer noch browserseitiges Rendering. Beim Schreiben müssen Sie also eine Reihe von Logiken und zwei Rendering-Umgebungen berücksichtigen.
  1. Nachdem das Rendern der Front-End-Seite abgeschlossen ist, stellt der Router-Sprung keine Anforderungen an den Server, wodurch der Druck auf den Server verringert wird. Daher gibt es zwei Möglichkeiten Geben Sie die Seite ein, die ebenfalls berücksichtigt werden muss Das Problem des Routing-Isomorphismus in zwei Rendering-Umgebungen.
  2. Die Produktionsumgebung muss KOA als Back-End-Server verwenden, um das Laden bei Bedarf zu implementieren, Daten auf der Serverseite abzurufen und den gesamten HTML-Code zu rendern, wobei die neuesten Funktionen von verwendet werden React16, um den gesamten Zustandsbaum zusammenzuführen.
Einführung in die lokale Entwicklung

Die wichtigsten Dateien, die an der Überprüfung der lokalen Entwicklung beteiligt sind, sind die index.js-Dateien im src-Verzeichnis, um den aktuellen Stand zu ermitteln Die API von module.hot wird nur in der Entwicklungsumgebung verwendet, um die Aktualisierungsbenachrichtigung für das Seitenrendering zu implementieren. Beachten Sie die Hydrate-Methode. Dies ist eine neue API-Methode, die speziell für das serverseitige Rendering hinzugefügt wurde Version 16. Basierend auf der Render-Methode wird die maximale Wiederverwendung serverseitiger Rendering-Inhalte erreicht und der Prozess vom statischen DOM zu dynamischen Knoten realisiert. Der Kern besteht darin, den Prozess der Beurteilung der Prüfsummenmarkierung unter der Version v15 zu ersetzen und den Wiederverwendungsprozess effizienter und eleganter zu gestalten.

const renderApp=()=>{
 let application=createApp({store,history});
 hydrate(application,document.getElementById('root'));
}
window.main = () => {
 Loadable.preloadReady().then(() => {
 renderApp()
 });
};
if(process.env.NODE_ENV==='development'){
 if(module.hot){
 module.hot.accept('./store/reducers/index.js',()=>{
  let newReducer=require('./store/reducers/index.js');
  store.replaceReducer(newReducer)
 })
 module.hot.accept('./app/index.js',()=>{
  let {createApp}=require('./app/index.js');
  let newReducer=require('./store/reducers/index.js');
  store.replaceReducer(newReducer)
  let application=createApp({store,history});
  hydrate(application,document.getElementById('root'));
 })
 }
}

Achten Sie auf die Definition der Funktion window.main. In Kombination mit index.ejs können Sie erkennen, dass diese Funktion ausgelöst wird, nachdem alle Skripte geladen wurden. Die reaktionsfähige Schreibmethode wird zum verzögerten Laden verwendet Die Schreibmethode zum separaten Verpacken von Seiten sollte in Kombination mit den Routing-Einstellungen erläutert werden. Es ist zu beachten, dass die drei unter der App-Datei bereitgestellten Methoden sowohl auf der Browserseite als auch auf der Serverseite gleich sind. Im Folgenden geht es hauptsächlich um diesen Teil der Idee.

Routenverarbeitung

Schauen Sie sich als Nächstes die folgenden Dateien im Verzeichnis src/app an. Die drei beteiligten Methoden sind im Dienst enthalten wird in der clientseitigen und browserseitigen Entwicklung verwendet. In diesem Teil geht es hauptsächlich um die Codeideen in der Router-Datei und die Verarbeitung des Routings durch die Datei createApp.js beide Enden.

Die Routes.js im Router-Ordner ist eine Routing-Konfigurationsdatei. Sie importiert die Routing-Konfigurationen unter jeder Seite und synthetisiert ein Konfigurationsarray. Diese Konfiguration kann zur flexiblen Steuerung der Online- und Offline-Seiten verwendet werden. Die Datei index.js im selben Verzeichnis ist die Standardmethode zum Schreiben von RouterV4. Die Routing-Konfiguration wird durch das Durchlaufen des Konfigurationsarrays übergeben. Beachten Sie, dass der Verlauf als Parameter übergeben werden muss in der Datei „createApp.js“ enthalten sein. Führen Sie eine separate Verarbeitung durch. Werfen wir einen kurzen Blick auf mehrere Konfigurationselemente in der Route-Komponente. Bemerkenswert ist das Thunk-Attribut. Dies ist ein wichtiger Schritt beim Rendern, nachdem das Backend Daten erhalten hat Advance-Lebenszyklus-Hooks und andere Attribute finden Sie in der entsprechenden React-Router-Dokumentation und werden hier nicht beschrieben.

import routesConfig from './routes';
const Routers=({history})=>(
 <ConnectedRouter history={history}>
 <p>
  {
  routesConfig.map(route=>(
   <Route key={route.path} exact={route.exact} path={route.path} component={route.component} thunk={route.thunk} />
  ))
  }
 </p>
 </ConnectedRouter>
)
export default Routers;

查看app目录下的createApp.js里面的代码可以发现,本框架是针对不同的工作环境做了不同的处理,只有在生产环境下才利用Loadable.Capture方法实现了懒加载,动态引入不同页面对应的打包之后的js文件。到这里还要看一下组件里面的路由配置文件的写法,以home页面下的index.js为例。注意/* webpackChunkName: 'Home' */这串字符,实质是指定了打包后此页面对应的js文件名,所以针对不同的页面,这个注释也需要修改,避免打包到一起。loading这个配置项只会在开发环境生效,当页面加载未完成前显示,这个实际项目开发如果不需要可以删除此组件。

import {homeThunk} from '../../store/actions/thunk';
const LoadableHome = Loadable({
 loader: () =>import(/* webpackChunkName: 'Home' */'./containers/homeContainer.js'),
 loading: Loading,
});
const HomeRouter = {
 path: '/',
 exact: true,
 component: LoadableHome,
 thunk: homeThunk // 服务端渲染会开启并执行这个action,用于获取页面渲染所需数据
}
export default HomeRouter

这里多说一句,有时我们要改造的项目的页面文件里有从window.location里面获取参数的代码,改造成服务端渲染时要全部去掉,或者是要在render之后的生命周期中使用。并且页面级别组件都已经注入了相关路由信息,可以通过this.props.location来获取URL里面的参数。本项目用的是BrowserRouter,如果用HashRouter则包含参数可能略有不同,根据实际情况取用。

根据React16的服务端渲染的API介绍:

  1. 浏览器端使用的注入ConnectedRouter中的history为:import createHistory from 'history/createBrowserHistory'

  2. 服务器端使用的history为import createHistory from 'history/createMemoryHistory'

服务端渲染

这里就不会涉及到koa2的一些基础知识,如果对koa2框架不熟悉可以参考我的另外一篇博文。这里是看server文件夹下都是服务端的代码。首先是简洁的app.js用于保证每次连接都返回的是一个新的服务器端实例,这对于单线程的js语言是很关键的思路。需要重点介绍的就是clientRouter.js这个文件,结合/src/app/configureStore.js这个文件共同理解服务端渲染的数据获取流程和React的渲染机制。

/*configureStore.js*/
import {createStore, applyMiddleware,compose} from "redux";
import thunkMiddleware from "redux-thunk";
import createHistory from 'history/createMemoryHistory';
import { routerReducer, routerMiddleware } from 'react-router-redux'
import rootReducer from '../store/reducers/index.js';
const routerReducers=routerMiddleware(createHistory());//路由
const composeEnhancers = process.env.NODE_ENV=='development'?window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ : compose;
const middleware=[thunkMiddleware,routerReducers]; //把路由注入到reducer,可以从reducer中直接获取路由信息
let configureStore=(initialState)=>createStore(rootReducer,initialState,composeEnhancers(applyMiddleware(...middleware)));
export default configureStore;

window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__这个变量是浏览器里面的Redux的开发者工具,开发React-redux应用时建议安装,否则会有报错提示。这里面大部分都是redux-thunk的示例代码,关于这部分如果看不懂建议看一下redux-thunk的官方文档,这里要注意的是configureStore这个方法要传入的initialState参数,这个渲染的具体思路是:在服务端判断路由的thunk方法,如果存在则需要执行这个获取数据逻辑,这是个阻塞过程,可以当作同步,获取后放到全局State中,在前端输出的HTML中注入window.__INITIAL_STATE__这个全局变量,当html载入完毕后,这个变量赋值已有数据的全局State作为initState提供给react应用,然后浏览器端的js加载完毕后会通过复用页面上已有的dom和初始的initState作为开始,合并到render后的生命周期中,从而在componentDidMount中已经可以从this.props中获取渲染所需数据。

但还要考虑到页面切换也有可能在前端执行跳转,此时作为React的应用不会触发对后端的请求,因此在componentDidMount这个生命周期里并没有获取数据,为了解决这个问题,我建议在这个生命周期中都调用props中传来的action触发函数,但在action内部进行一层逻辑判断,避免重复的请求,实际项目中请求数据往往会有个标识性ID,就可以将这个ID存入store中,然后就可以进行一次对比校验来提前返回,避免重复发送ajax请求,具体可看store/actions/home.js`中的逻辑处理。

import {ADD,GET_HOME_INFO} from '../constants'
export const add=(count)=>({type: ADD, count,})
export const getHomeInfo=(sendId=1)=>async(dispatch,getState)=>{
 let {name,age,id}=getState().HomeReducer.homeInfo;
 if (id === sendId) {
 return //是通过对请求id和已有数据的标识性id进行对比校验,避免重复获取数据。
 }
 console.log('footer'.includes('foo'))
 await new Promise(resolve=>{
 let homeInfo={name:'wd2010',age:'25',id:sendId}
 console.log('-----------请求getHomeInfo')
 setTimeout(()=>resolve(homeInfo),1000)
 }).then(homeInfo=>{
 dispatch({type:GET_HOME_INFO,data:{homeInfo}})
 })
}

注意这里的async/await写法,这里涉及到服务端koa2使用这个来做数据请求,因此需要统一返回async函数,这块不熟的同学建议看下ES7的知识,主要是async如何配合Promise实现异步流程改造,并且如果涉及koa2的服务端工作,对async函数用的更多,这也是本项目要求Node版本为8.x以上的原因,从8开始就可以直接用这两个关键字。

不过到具体项目中,往往会涉及到一些服务端参数的注入问题,但这块根据不同项目需求差异很大,并且不属于这个React服务端改造的一部分,没法统一分享,如果真是公司项目要用到对这块有需求咨询可以打赏后加我微信讨论。

以Home页面为例的渲染流程

为了方便大家理解,我以一个页面为例整理了一下数据流的整体过程,看一下思路:

  1. Der Server empfängt die Anfrage und findet die entsprechende Routing-Konfiguration über /home

  2. Es wird beurteilt, dass die Route eine Thunk-Methode hat, und führt dann den Speicher aus /actions/thunk. Die in js asynchron bereitgestellten Daten

  3. werden in den globalen Status eingefügt, und die Versandverteilung wird zu diesem Zeitpunkt nicht tatsächlich wirksam

  4. Der auszugebende HTML-Code setzt den globalen Status nach Erhalt der Daten in die globale Variable window.__INITIAL_STATE__, da sich initState

  5. window.__INITIAL_STATE__ in der Reaktion befindet Lebenszyklus Vor dem Inkrafttreten in den globalen Status verschmelzen Zu diesem Zeitpunkt stellt React fest, dass der Dom generiert wurde, das Rendern nicht erneut ausgelöst wird und der Datenstatus synchronisiert ist

Server direkt aus HTML

Der grundlegende Prozess wurde eingeführt. Was das funktionale Schreiben einiger Reduzierer und die Positionen von Aktionen betrifft, werden sie mit Bezug auf einige Analysen auf dem organisiert Internet. Es hängt vom Einzelnen ab, solange es Ihrem eigenen Verständnis entspricht und gut für die Teamentwicklung ist. Wenn Sie den Leserhintergrund kennen, den ich am Anfang des Artikels festgelegt habe, glaube ich, dass die Beschreibung in diesem Artikel ausreicht, um Ihre eigene serverseitige Rendering-Technologie zu beleuchten. Es spielt keine Rolle, ob Sie nicht viel über React wissen. Sie können hier nachschlagen, um einige Grundkenntnisse über React zu ergänzen.

Ich glaube, Sie haben die Methode gemeistert, nachdem Sie den Fall in diesem Artikel gelesen haben Informationen, bitte beachten Sie andere verwandte Artikel auf der chinesischen PHP-Website!

Empfohlene Lektüre:

So verwenden Sie das Webpack+Vue-Umgebungs-LAN

Vue zugrunde liegende Prinzipien und Komponentenkommunikation

Das obige ist der detaillierte Inhalt vonReact führt die Rendering-Transformation auf der Serverseite durch. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn