前後端同構渲染:當客戶端請求一個包含React元件頁面的時候,服務端首先回應輸出這個頁面,客戶端和服務端有了第一次互動。然後,如果載入元件的過程需要向服務端發出Ajax請求等,客戶端和服務端又進行了一次交互,這樣,耗時相對較長。前後端同構渲染可以在頁面初次載入時把所有地方渲染好一次性回應給客戶端。本文主要跟大家介紹了淺談react前後端同構渲染,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟著小編過來看看吧,希望能幫助大家。
實作方式:保證套件管理工具和模組依賴方式一致
套件管理工具-npm管理,保證前後端都使用同一個相容套件
模組依賴方式-webpack,確保前後端都採用commonjs的依賴方式,確保程式碼可以互相依賴
服務端如何渲染:
react全家桶:react、react-router、redux
react 和reactDOM
reactDOM在這裡提供的支援就是reactDOM.render和reactDOM.renderToString函數,其中欠著會在瀏覽器產生DOM結構,後者會在服務端產生對應的HTML字串模板。 react會在產生的DOM結構上加入一個 data-react-checksum的屬性,這是一個 adler32 演算法的校驗和,以確保兩份範本的一致性。
同時 react 的生命週期在前後端渲染過程中也有所不同。前端渲染的元件擁有完整的生命週期,而後端渲染僅有 componentWillMount 的生命週期。這意味著,如果我們想進行前後端共同操作的邏輯,例如發送資料請求等,可以放在 componentWillMount 的生命週期中;如果想單獨處理客戶端的邏輯,可以放在其他生命週期,如 componentDidMount 中。
react-router
react-router是react的路由-視圖控制庫,可以書寫邊界的宣告式路由以控制不同頁面的渲染。 react-router 本身就是一個狀態機,根據配置好的路由規則,和輸入的 url 路徑,透過 match 方法找到對應的元件並進行渲染。
這套機制在前端和後端都是相通的,例如在後端,就是下面這樣一種實作形式來渲染:
app.use(async (ctx, next) => { match({ location: ctx.originalUrl, routes }, callback) // 渲染完成之后,调用 callback 回调 // 将 组件 renderToString 返回前端即可 })
對於前端來說,其實也是處理的上面這些邏輯,不過它被很好的封裝在元件中,我們只需要寫好聲明式的路由,這一切就可以隨著url 的變化自動發生。
redux
redux是react的資料流管理函式庫,他對服務端渲染的支援很簡單,就是單一的store和狀態可初始化。後端在進行渲染的時候會建立好單一的store,並且將建構好的初始狀態透過以json格式,透過全域變數寫到產生好的html字串模板上。
前端透過取得初始狀態,產生跟後端徐然完成後一模一樣的store,就可以保證前後端渲染資料的一致性,以確保前後端產生的dom結構一致。
最佳化結果:
#開發效率低的問題:同構應用程式只有一個專案和一套技術棧,只要擁有react 開發經驗,就可以快速投入前端和後端的開發當中;
可維護性差的問題:同構應用可以進行大量的程式碼公用,包括工具方法、常數、頁面元件和redux 的大部分邏輯等,可重複使用性大大提高;首屏效能、SEO 等
處理過程:
#用戶端發出請求-服務端渲染出元件-傳回給客戶端
1、在需要同構直出的頁面(例如index.html)放上佔位符
<p id="root">@@@</p> ###
以上,當客戶端發出首次請求,服務端渲染出元件的html內容放@@@這個位置,然後服務端再渲染出類似這樣的js程式碼段把元件最終渲染到DOM上。也就是說,renderApp方法其實就是在渲染元件。
2、而為了直接呼叫renderApp方法,必須讓renderApp方法成為window下的方法
window.renderApp = function(){ReactDOM.render(...)}
3、服務端取出index.html,渲染出佔位符的內容,取代佔位符,並一次回應給客戶端
案例
#檔案結構
browser.js(在这里把渲染组件的过程赋值给window.renderApp) bundle.js(把browser.js内容bundle到这里) Component.js(组件在这里定义) express.js(服务端) index.html(同构直出的页面) package.json
index.html,直出頁面放上佔位符
<!doctype html> <html> <head> <meta charset="UTF-8"> <title>Untitled Document</title> </head> <body> <p id="root">@@@</p> <script src="bundle.js"></script> ### </body> </html>
Component.js,在這裡定義元件
var React = require('react'); var ReactDOM = require('react-dom'); var Component = React.createClass({ clickHandler: function(){ alert(this.props.msg) }, render: function(){ return React.createElement('button', {onClick: this.clickHandler}, this.props.msg) } }) module.exports = Component;
browser.js,把元件渲染過程賦值給window物件
var React = require('react'); var ReactDOM = require('react-dom'); var Component = React.createFactory(require('./Component')); window.renderApp = function(msg){ ReactDOM.render(Component({msg: msg}), document.getElementById('root')); }
可以通过来触发组件的渲染。稍后,在服务端会把这段代码渲染出来。
express.js,服务端
以上,需要直出的页面有了占位符,定义了组件,并把渲染组件的过程赋值给了window对象,服务端现在要做的工作就是:生成组件的html和渲染组件的js,放到直出页面index.html的占位符位置。
var express = require('express'); var React = require('react'); var ReactDOMServer = require('react-dom/server'); var fs = require('fs'); var Component = React.createFactory(require('./Component')); //原先把文件读出来 var BUNDLE = fs.readFileSync('./bundle.js',{encoding:'utf8'}); var TEMPLATE = fs.readFileSync('./index.html',{encoding:'utf8'}); var app = express(); function home(req, res){ var msg = req.params.msg || 'Hello'; var comp = Component({msg: msg}); //@@@占位符的地方放组件 var page = TEMPLATE.replace('@@@', ReactDOMServer.renderToString(comp)); //###占位符的地方放js page = page.replace('###', '<script>renderApp("'+msg+'")</script>') res.send(page); } //路由 app.get('', home); app.get('/bundle.js', function(req, res){ res.send(BUNDLE); }) app.get('/:msg', home); app.listen(4000);
package.json中的配置
"scripts": { "start": "watchify ./browser.js -o ./bundle.js" },
启动方式
运行:npm start
运行:node express.js
浏览:localhost:4000
相关推荐:
以上是react前後端同構渲染範例程式碼的詳細內容。更多資訊請關注PHP中文網其他相關文章!