markdownpad2 html渲染组件出错_「万字长文」一文吃透React SSR服务端同构渲染
寫在前面
前段時(shí)間一直在研究 react ssr技術(shù),然后寫了一個(gè)完整的 ssr開(kāi)發(fā)骨架。今天寫文,主要是把我的研究成果的精華內(nèi)容整理落地,另外通過(guò)再次梳理希望發(fā)現(xiàn)更多優(yōu)化的地方,也希望可以讓更多的人少踩一些坑,讓更多的人理解和掌握這個(gè)技術(shù)。
相信看過(guò)本文(前提是能對(duì)你的胃口,也能較好的消化吸收)你一定會(huì)對(duì) react ssr服務(wù)端渲染技術(shù)有一個(gè)深入的理解,可以打造自己的腳手架,更可以用來(lái)改造自己的實(shí)際項(xiàng)目,當(dāng)然這不僅限于 react ,其他框架都一樣,畢竟原理都是相似的。
為什么要服務(wù)端渲染(ssr)
至于為什么要服務(wù)端渲染,我相信大家都有所聞,而且每個(gè)人都能說(shuō)出幾點(diǎn)來(lái)。
首屏等待
在 SPA 模式下,所有的數(shù)據(jù)請(qǐng)求和 Dom 渲染都在瀏覽器端完成,所以當(dāng)我們第一次訪問(wèn)頁(yè)面的時(shí)候很可能會(huì)存在“白屏”等待,而服務(wù)端渲染所有數(shù)據(jù)請(qǐng)求和 html內(nèi)容已在服務(wù)端處理完成,瀏覽器收到的是完整的 html 內(nèi)容,可以更快的看到渲染內(nèi)容,在服務(wù)端完成數(shù)據(jù)請(qǐng)求肯定是要比在瀏覽器端效率要高的多。
沒(méi)考慮SEO的感受
有些網(wǎng)站的流量來(lái)源主要還是靠搜索引擎,所以網(wǎng)站的 SEO 還是很重要的,而 SPA 模式對(duì)搜索引擎不夠友好,要想徹底解決這個(gè)問(wèn)題只能采用服務(wù)端直出。改變不了別人(搜索yinqing),只能改變自己。
SSR + SPA 體驗(yàn)升級(jí)
只實(shí)現(xiàn) SSR 其實(shí)沒(méi)啥意義,技術(shù)上沒(méi)有任何發(fā)展和進(jìn)步,否則 SPA 技術(shù)就不會(huì)出現(xiàn)。
但是單純的 SPA又不夠完美,所以最好的方案就是這兩種體驗(yàn)和技術(shù)的結(jié)合,第一次訪問(wèn)頁(yè)面是服務(wù)端渲染,基于第一次訪問(wèn)后續(xù)的交互就是 SPA 的效果和體驗(yàn),還不影響 SEO 效果,這就有點(diǎn)完美了。
單純實(shí)現(xiàn) ssr 很簡(jiǎn)單,畢竟這是傳統(tǒng)技術(shù),也不分語(yǔ)言,隨便用 php 、jsp、asp、node 等都可以實(shí)現(xiàn)。
但是要實(shí)現(xiàn)兩種技術(shù)的結(jié)合,同時(shí)可以最大限度的重用代碼(同構(gòu)),減少開(kāi)發(fā)維護(hù)成本,那就需要采用 react 或者 vue 等前端框架相結(jié)合 node(ssr) 來(lái)實(shí)現(xiàn)。
本文主要說(shuō) ReactSSR技術(shù) ,當(dāng)然 vue 也一樣,只是技術(shù)棧不同而已。
核心原理
整體來(lái)說(shuō) react 服務(wù)端渲染原理不復(fù)雜,其中最核心的內(nèi)容就是同構(gòu)。
node server 接收客戶端請(qǐng)求,得到當(dāng)前的 req url path,然后在已有的路由表內(nèi)查找到對(duì)應(yīng)的組件,拿到需要請(qǐng)求的數(shù)據(jù),將數(shù)據(jù)作為 props 、 context或者 store 形式傳入組件,然后基于 react 內(nèi)置的服務(wù)端渲染api renderToString()orrenderToNodeStream() 把組件渲染為 html字符串或者 stream流, 在把最終的 html 進(jìn)行輸出前需要將數(shù)據(jù)注入到瀏覽器端(注水),server 輸出(response)后瀏覽器端可以得到數(shù)據(jù)(脫水),瀏覽器開(kāi)始進(jìn)行渲染和節(jié)點(diǎn)對(duì)比,然后執(zhí)行組件的 componentDidMount 完成組件內(nèi)事件綁定和一些交互,瀏覽器重用了服務(wù)端輸出的 html節(jié)點(diǎn),整個(gè)流程結(jié)束。
技術(shù)點(diǎn)確實(shí)不少,但更多的是架構(gòu)和工程層面的,需要把各個(gè)知識(shí)點(diǎn)進(jìn)行鏈接和整合。
這里放一個(gè)架構(gòu)圖
react ssr
從 ejs 開(kāi)始
實(shí)現(xiàn) ssr 很簡(jiǎn)單,先看一個(gè) node ejs的栗子。
// index.html react ssr //node ssr const ejs = require('ejs'); const http = require('http');http.createServer((req, res) => { if (req.url === '/') { res.writeHead(200, { 'Content-Type': 'text/html' }); // 渲染文件 index.ejs ejs.renderFile('./views/index.ejs', { title: 'react ssr', data: '首頁(yè)'}, (err, data) => { if (err ) { console.log(err); } else { res.end(data); } }) }}).listen(8080);jsx 到字符串
上面我們結(jié)合 ejs模板引擎 ,實(shí)現(xiàn)了一個(gè)服務(wù)端渲染的輸出,html 和 數(shù)據(jù)直接輸出到客戶端。
參考以上,我們結(jié)合 react組件 來(lái)實(shí)現(xiàn)服務(wù)端渲染直出,使用 jsx 來(lái)代替 ejs,之前是在 html 里使用 ejs 來(lái)綁定數(shù)據(jù),現(xiàn)在改寫成使用 jsx 來(lái)綁定數(shù)據(jù),使用 react 內(nèi)置 api 來(lái)把組件渲染為 html 字符串,其他沒(méi)有差別。
為什么react 組件可以被轉(zhuǎn)換為 html字符串呢?
簡(jiǎn)單的說(shuō)我們寫的 jsx 看上去就像在寫 html(其實(shí)寫的是對(duì)象) 標(biāo)簽,其實(shí)經(jīng)過(guò)編譯后都會(huì)轉(zhuǎn)換成 React.createElement方法,最終會(huì)被轉(zhuǎn)換成一個(gè)對(duì)象(虛擬DOM),而且和平臺(tái)無(wú)關(guān),有了這個(gè)對(duì)象,想轉(zhuǎn)換成什么那就看心情了。
const React = require('react');const { renderToString} = require( 'react-dom/server');const http = require('http');//組件class Index extends React.Component{ constructor(props){ super(props); } render(){ return{this.props.data.title}
}}//模擬數(shù)據(jù)的獲取const fetch = function () { return { title:'react ssr', data:[] }}//服務(wù)http.createServer((req, res) => { if (req.url === '/') { res.writeHead(200, { 'Content-Type': 'text/html' }); const data = fetch(); const html = renderToString(); res.end(html); }}).listen(8080);ps:以上代碼不能直接運(yùn)行,需要結(jié)合babel 使用 @babel/preset-react 進(jìn)行轉(zhuǎn)換
npx babel script.js --out-file script-compiled.js --presets=@babel/preset-react引出問(wèn)題
在上面非常簡(jiǎn)單的就是實(shí)現(xiàn)了 react ssr ,把 jsx作為模板引擎,不要小看上面的一小段代碼,他可以幫我們引出一系列的問(wèn)題,這也是完整實(shí)現(xiàn) react ssr 的基石。
- 雙端路由如何維護(hù)?
首先我們會(huì)發(fā)現(xiàn)我在 server 端定義了路由 '/',但是在 react SPA 模式下我們需要使用 react-router來(lái)定義路由。那是不是就需要維護(hù)兩套路由呢?
- 獲取數(shù)據(jù)的方法和邏輯寫在哪里?
發(fā)現(xiàn)數(shù)據(jù)獲取的 fetch 寫的獨(dú)立的方法,和組件沒(méi)有任何關(guān)聯(lián),我們更希望的是每個(gè)路由都有自己的 fetch 方法。
- 服務(wù)端 html 節(jié)點(diǎn)無(wú)法重用
雖然組件在服務(wù)端得到了數(shù)據(jù),也能渲染到瀏覽器內(nèi),但是當(dāng)瀏覽器端進(jìn)行組件渲染的時(shí)候直出的內(nèi)容會(huì)一閃而過(guò)消失。
好了,問(wèn)題有了,接下來(lái)我們就一步一步的來(lái)解決這些問(wèn)題。
同構(gòu)才是核心
react ssr 的核心就是同構(gòu),沒(méi)有同構(gòu)的 ssr 是沒(méi)有意義的。
所謂同構(gòu)就是采用一套代碼,構(gòu)建雙端(server 和 client)邏輯,最大限度的重用代碼,不用維護(hù)兩套代碼。而傳統(tǒng)的服務(wù)端渲染是無(wú)法做到的,react 的出現(xiàn)打破了這個(gè)瓶頸,并且現(xiàn)在已經(jīng)得到了比較廣泛的應(yīng)用。
路由同構(gòu)
雙端使用同一套路由規(guī)則, node server 通過(guò) req url path 進(jìn)行組件的查找,得到需要渲染的組件。
//組件和路由配置 ,供雙端使用 routes-config.js
class Detail extends React.Component{ render(){ return detail }}class Index extends React.Component { render() { return index }}const routes = [ { path: "/總結(jié)
以上是生活随笔為你收集整理的markdownpad2 html渲染组件出错_「万字长文」一文吃透React SSR服务端同构渲染的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: C# 解决串口接收数据不完整
- 下一篇: flink 写kafka_网易云音乐基于