什么是同構(gòu)
一份代碼,先通過服務(wù)端渲染(server-side rendering,ssr),生成html字符串以及初始化數(shù)據(jù),客戶端拿到后,通過對(duì)html的dom進(jìn)行patch和事件綁定對(duì)dom進(jìn)行客戶端激活(client-side hydration,csh),這個(gè)整體的過程叫同構(gòu)渲染。
同構(gòu)產(chǎn)生的背景 / 同構(gòu)能夠解決單純客戶端渲染的哪些痛點(diǎn)?
1.客戶端渲染帶來的的首屏白屏?xí)r間過長(zhǎng):
? ? ? ? ?在 SPA 模式下,所有的數(shù)據(jù)請(qǐng)求和 Dom 渲染都在瀏覽器端完成,所以當(dāng)我們第一次訪問頁(yè)面的時(shí)候很可能會(huì)存在“白屏”等待,而服務(wù)端渲染所有數(shù)據(jù)請(qǐng)求和 html內(nèi)容已在服務(wù)端處理完成,瀏覽器收到的是完整的 html 內(nèi)容,可以更快的看到渲染內(nèi)容。
? ? ? ? ?2. 客戶端渲染不利于SEO:
? ? ? ? ?由于現(xiàn)階段大多搜索引擎采用的爬蟲算法是直接抓取頁(yè)面代碼分析,而SPA應(yīng)用只有一個(gè)入口文件而沒實(shí)質(zhì)內(nèi)容,SEO性能差。
適用的場(chǎng)景?
? ? ? ? ? 1.特別依賴搜索引擎流量的項(xiàng)目;
2.對(duì)首屏?xí)r間有特殊要求;
現(xiàn)有的的開源框架?
next.js、react server、Beidou(北斗)?、
核心原理及架構(gòu)流程圖?
? ? ? ? ? 借助虛擬DOM的特性,將一份代碼在服務(wù)端和客戶端各執(zhí)行一次:
1、服務(wù)端使用?react-dom包提供的 server端渲染api: renderToString (常用)或?renderToStaticMarkup直接渲染出包含頁(yè)面信息的靜態(tài) html字符串。
? ? ? ? ? 2、客戶端根據(jù)渲染出的靜態(tài) html 進(jìn)行二次渲染,做一些綁定事件等交互操作。
同構(gòu)的缺點(diǎn)?? ??
? ? ? ? ? 1.同構(gòu)會(huì)增加服務(wù)器的負(fù)載,對(duì)此一般推薦對(duì)于首屏和個(gè)別頁(yè)面進(jìn)行SSR服務(wù)端的渲染,此外仍保持應(yīng)用為客戶端SPA,充分利用瀏覽器特點(diǎn),達(dá)到最優(yōu)性能。
? ? ? ? ? 2.使原本簡(jiǎn)單的 React 項(xiàng)目變得非常復(fù)雜,項(xiàng)目的可維護(hù)性會(huì)降低,代碼問題的追溯也會(huì)變得困難。所以,使用 SSR 在解決問題的同時(shí),也會(huì)帶來非常多的副作用,有的時(shí)候,這些副作用的傷害比起 SSR 技術(shù)帶來的優(yōu)勢(shì)要大的多。
關(guān)鍵點(diǎn)?
?1.雙端框架選擇:
? ? ? ? ? ?客戶端:react(v16+)
? ? ? ? ? ?服務(wù)器端:koa/express
2.環(huán)境區(qū)分
? ? ? ? ? ?同一套代碼在客戶端和服務(wù)端執(zhí)行兩次,但因?yàn)榄h(huán)境不同,有些對(duì)象是不自帶的,要加以區(qū)分避免出現(xiàn)問題。像前端特有的 Window 對(duì)象,Ajax 請(qǐng)求 在后端是無法使用上的,后端需要去掉這些前端特有的對(duì)象? ? ? ? ? ? 邏輯或使用對(duì)應(yīng)的后端方案,如后端可以使用 http.request 替代 Ajax 請(qǐng)求,所以需要進(jìn)行平臺(tái)區(qū)分,主要有以下幾種方式:
? ? ? ? ? 1. 代碼使用前后端通用的模塊,如 isomorphic-fetch
? ? ? ? ? 2. 前后端通過 webpack 配置 resolve.alias 對(duì)應(yīng)不同的文件
? ? ? ? ? 3. 使用 webpack.DefinePlugin 在構(gòu)建時(shí)添加一個(gè)平臺(tái)區(qū)分的值
??3.react服務(wù)端提供的渲染API
??React 之所以可以做到服務(wù)端渲染 就是因?yàn)閞eact-dom/server提供了服務(wù)端渲染的API。
renderToString 同步地把一個(gè)react 元素轉(zhuǎn)換成帶html字符串,遇到復(fù)雜頁(yè)面時(shí)用戶等待時(shí)間也不短。
renderToNodeStream把渲染結(jié)果以‘流’的形式返回給瀏覽器端,用戶體驗(yàn)更友好。
后續(xù)為了客戶端在渲染組件時(shí),最大限度地保留在服務(wù)端使用 renderToString 生成的內(nèi)容結(jié)構(gòu),ReactDom 相應(yīng)的在客戶端提供了一個(gè)新的 API:hydrate。
4.路由同構(gòu)
首先需要抽離出一份路由組件文件routes.js:
客戶端使用 react-router-dom 下的?BrowserRouter?進(jìn)行前端路由控制。
服務(wù)端使用 react-router-dom 下的?StaticRouter?進(jìn)行靜態(tài)路由控制。
使用 react-router-config 下的 matchRoutes 匹配后端路由,使用 renderRoutes 渲染匹配到的路由。
使用 react-router-dom/server 下的 renderToString 方法,渲染出 html 字符串,并返回給前端。
使用 StaticRouter 中通過 context 可以和前端頁(yè)面通信,傳參。
數(shù)據(jù)交互與狀態(tài)同步?
可以選用redux實(shí)現(xiàn)數(shù)據(jù)狀態(tài)的同步:在服務(wù)端通過組件的靜態(tài)API方法獲取接口數(shù)據(jù)后創(chuàng)建store,再通過 window.store= store 傳遞給前端;此時(shí)應(yīng)用中的所有頁(yè)面都可以共享使用store中的數(shù)據(jù),可以使用dispatch方法進(jìn)行交互。
雙端打包差異?
客戶端打包:
? ? ? ? ? 服務(wù)端打包: