91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么實現ssr服務端渲染

發布時間:2021-06-02 17:16:48 來源:億速云 閱讀:220 作者:Leah 欄目:web開發

今天就跟大家聊聊有關怎么實現ssr服務端渲染,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。

什么是ssr

最初聽說有單頁面的服務端渲染的時候,就理解為類似傳統的服務端路由+模板渲染,只是需要用單頁面應用的框架寫。后面尋思這樣好像有點傻,再一了解,原來只是在首次加載的時候,后端進行當前路徑頁面的組件渲染和數據請求,組裝成html返回給前端,用戶就能很快看到看到頁面,當html中的js資源加載完成后,剩下執行和運行的就是一般的單頁面應用。  所以ssr是后端模板渲染和單頁面的組合。  ssr有兩種模式,單頁面和非單頁面模式,第一種是后端首次渲染的單頁面應用,第二種是完全使用后端路由的后端模版渲染模式。他們區別在于使用后端路由的程度。

優勢

ssr的兩個明顯的優勢:首次加載快和seo。  為什么說首次加載快呢。  一個普通的單頁面應用,首次加載的時候需要把所有相關的靜態資源加載完畢,然后核心js才會開始執行,這個過程就會消耗一定的時間,接著還會請求網絡接口,最終才能完全渲染完成。

ssr模式下,后端攔截到路由,找到對應組件,準備渲染組件,所有的js資源在本地,排除了js資源的網絡加載時間,接著只需要對當前路由的組件進行渲染,而頁面的ajax請求,可能在同一臺服務器上,如果是的話速度也會快很多。最后后端把渲染好的頁面反回給前端。  注意:頁面能很快的展示出來,但是由于當前返回的只是單純展示的dom、css,其中的js相關的事件等在客戶端其實并沒有綁定,所以最終還是需要js加載完以后,對當前的頁面再進行一次渲染,稱為同構。  所以ssr就是更快的先展示出頁面的內容,先讓用戶能夠看到。  為什么seo友好呢,因為搜索引擎爬蟲在爬取頁面信息的時候,會發送HTTP請求來獲取網頁內容,而我們服務端渲染首次的數據是后端返回的,返回的時候已經是渲染好了title,內容等信息,便于爬蟲抓取內容。

如何實現

大致對ssr有了一個了解,我們現在需要對實現整理一下大致實現思路和流程。

1.選擇一個單頁面框架(我目前選擇的是react)

2.選擇node服務端框架(我目前選擇的是koa2)

3.實現核心邏輯,讓node服務端能夠路由和渲染單頁面組件(這一點分為很多小實現點,后面說)

4.優化開發和發布環境自動化構建工具(webpack)

開始實現之前創建一個react-ssr項目,項目下創建client和server目錄用于寫客戶端和服務端代碼,webpack目錄用于weppack文件配置。

1.react應用

安裝react依賴,在client中創建好一個基礎的react文件夾結構,并寫好一個可以運行的有路由配置的應用,client文件目錄如下:

怎么實現ssr服務端渲染

2.server應用

安裝koa和相關依賴,在server中創建好一個基礎的服務端文件夾結構,并寫好一個簡單的可運行的后端應用服務。server文件夾如下:

怎么實現ssr服務端渲染

3.核心實現

因為有倉庫代碼就不對基礎代碼做解釋,現在我們有一個可以單獨運行的react單頁面應用和一個后端應用,他們都有各自的路由。接下來我們做改造,實現ssr的單頁面模式(非單頁面模式僅僅是做部分調整,因此這里只講實現單頁面模式)。

核心實現分為以下幾步:

1) 后端攔截路由,根據路徑找到需要渲染的react頁面組件X

2)調用組件X初始化時需要請求的接口,同步獲取到數據后,使用react的renderToString方法對組件進行渲染,使其渲染出節點字符串。

3)后端獲取基礎html文件,把渲染出的節點字符串插入到body之中,同時也可以操作其中的title,script等節點。返回完整的html給客戶端。

4)客戶端獲取后端返回的html,展示并加載其中的js,最后完成react同構。

1)我們在客戶端寫react的時候,router常規的會定義一個數組,存放組件和對應的path,然后注冊路由,如下:

怎么實現ssr服務端渲染

上面說過,實現ssr就是實現單頁面應用+首次服務端渲染,所以我們本身就是做的一個單頁面應用。  現在實現了單頁面應用,需要實現首次服務端渲染。  服務端的應用啟動以后,接受到url請求,比如訪問 http://localhost:9999/ ,后端服務獲取到當前的path為/,這個時候我們就希望后端找到配置path為‘/'的上圖的Index組件,對其進行渲染。  我們在client的router文件夾中建立兩個js文件index和pages:

怎么實現ssr服務端渲染

pages 里配置路由路徑和組件的映射,代碼大致如下,使其能被客戶端路由和服務端路由同時使用。

怎么實現ssr服務端渲染

在server路由中代碼大致是這樣的,在服務端獲取到get請求以后,匹配路徑,如果路徑path是有映射頁面組件的,獲取到此組件并渲染,這就是我們的第一步:后端攔截路由,根據路徑找到需要渲染的react頁面組件。 

怎么實現ssr服務端渲染

2)如上圖,匹配到組件以后,執行了組件的getInitialProps方法(和nextjs的命名保持一致),此方法是一個封裝的靜態方法,主要用于獲取初始化所需要的ajax數據,在服務端會同步獲取,而后通過ssrData參數傳入組件prorps并執行組件渲染。  此方法在客戶端依然是異步請求。  這一步比較重要,為什么我們需要一個靜態方法,而不是直接把請求寫在willmount中呢。  因為在服務端使用renderToString渲染組件時,生命周期只會執行到willmount之后的第一次render,在willmount內部,請求是異步的,第一次render完成的時候,異步的數據都沒有獲取到,這個時候renderToString就已經返回了。  那我們頁面的初始化數據就沒有了,返回的html不是我們所期望的。  因此定義了一個靜態方法,在組件實例化之前獲取到這個方法,同步執行,數據獲取完成后,通過props把數據傳入給組件進行渲染。  那么這個方法是如何實現的呢?  我們根據代碼截圖來看base.js:

怎么實現ssr服務端渲染 

首先在client的pages里新建一個base組件,base繼承React.Component,所有pages里的頁面組件都需要繼承這個base,base有一個靜態方法getInitialProps,此方法主要是返回組件初始化需要的異步數據。  如果有初始化的ajax請求,就應該重寫在此方法里,并且return數據對象。 constructor判斷了頁面組件是否有初始化定義的state靜態方法,有的話傳遞給組件實例化的state對象,如果props有傳入ssrData,把ssrData傳遞值給組件state對象。   base中的componentWillMount會判斷是否還需要去執行getInitialProps方法,如果在服務端渲染的時候,數據已經在組件實例化之前同步獲取并傳入了props,所以忽略。  如果在客戶端環境,分兩種情況,第一種:用戶第一次進到頁面,這時候是服務端去請求的數據,服務端獲取到數據后在服務端渲染組件,同時也會把數據存放在html的script代碼中,定義一個全局變量ssrData,如下圖,react在注冊單頁面應用并且同構的時候會把全局ssrData傳遞給頁面組件,這個時候頁面組件在客戶端同構渲染的時候,就可以延續使用服務端之前的數據,這樣也保持了同構的一致性,也避免了一次重復請求。  第二種情況:就是當前用戶在單頁面之中切換路由,這樣就沒有服務端渲染,那么就執行getInitialProps方法,把數據直接返回給state,幾乎等同于在willmount中執行請求。  這樣封裝我們就可以用一套代碼兼容服務端渲染和單頁面渲染。 

怎么實現ssr服務端渲染 

client/app.js

怎么實現ssr服務端渲染

再看看如何寫頁面組件,下面是頁面組件Index的截圖,Index繼承Base,定義了靜態state,組件constructor方法會把此對象傳遞給組件實例化的state對象中,之所以用靜態方法來寫默認數據,是想保證定義的默認state先傳遞給實例對象的state,接口請求傳遞的props數據后傳遞給實例對象的state。 為什么不直接寫state屬性而要加static,因為state屬性會執行在constructor之后,這樣會覆蓋constructor定義的state,也就是會覆蓋我們getInitialProps返回的數據。

怎么實現ssr服務端渲染 

注意:在服務端渲染環境下,執行renderToString的時候,組件會被實例化,并且返回字符串形式的dom,這個過程react組件的生命周期只會執行到willmount之后的render。

3)我們寫好一個html文件,大致如下。  當前已經渲染出了相應的節點字符串,后端需要返回html文本,內容應該包含標題,節點和最后需要加載的打包好的js,依次去替換html占位部分。 

index.html

怎么實現ssr服務端渲染

server/router.js

怎么實現ssr服務端渲染

4)最后客戶端js加載完成后,會運行react,并且執行同構方法ReactDOM.hydrate,而不是平時用的ReactDOM.render。

怎么實現ssr服務端渲染

以下是首次渲染過程大致流程圖,點擊查看大圖

怎么實現ssr服務端渲染

css處理

現在我們已經完成了最核心的邏輯,但是有一個問題。  我發現在后端渲染組件的時候,style-loader會報錯,style-loader會找到組件依賴的css,并在組件加載時,把style載入到html header中,但是我們在服務端渲染的時候,沒有window對象,因此style-loader內部代碼會報錯。    服務端webpack需要移除style-loader,用其他方法代替,后來我把樣式賦值給組件靜態變量,然后通過服務端渲染一并返回給前端,但是有個問題,我只能拿到當前組件的樣式,子組件的樣式沒辦法拿到,如果要給子組件再添加靜態方法,再想辦法去取,那就太麻煩了。  后來我找到了一個庫isomorphic-style-loader可以支持我們想要的功能,看了下它的源碼和使用方法,通過高階函數把樣式賦值給組件,然后利用react的Context,拿到當前需要渲染的所有組件的樣式,最后把style插入到html中,這樣解決了子組件樣式無法導入的問題。  但是我覺得有點麻煩,首先需要定義所有組件的高階函數和引入這個庫,然后在router之中需要寫相關代碼收集style,最后插入到html中。  后來我定義了一個ProcessSsrStyle方法,入參是style文件,邏輯是判斷環境,如果是服務端把style加載到當前組件的dom中,如果是客戶端就不處理(因為客戶端有style-loader)。  實現和使用非常簡單,如下:

ProcessSsrStyle.js

怎么實現ssr服務端渲染

使用:

怎么實現ssr服務端渲染

服務端返回html的內容如下,用戶馬上能夠看到完整的頁面樣式,而當客戶端react同構完成后,dom會被替換為純dom,因為ProcessSsrStyle方法在客戶端不會輸出style,最終style-loader執行后header中也會有樣式,,頁面不會出現不一致的變化,對于用戶來說這一切都是無感的。

怎么實現ssr服務端渲染 

至此,最核心的功能已經實現,但是在后來的開發中,我發現事情還并沒有那么簡單,因為開發環境似乎太不友好了,開發效率低,需要手動重啟。 

開發環境

先說說最初的開發環境如何工作:

  • npm run dev啟動開發環境

  • webpack.client-dev.js打包服務端代碼,代碼會被打包到dist/server中

  • webpack.server-dev.js打包客戶端代碼,代碼會被打包到dist/client中

  • 啟動服務端應用,端口9999

  • 啟動webpack-dev-server, 端口8888

webpack打包后,啟動了兩個服務,一個是服務端的app應用、端口為9999,一個是客戶端的dev-server、端口為8888,dev-server會監聽和打包client代碼,可以在客戶端代碼更新的時候,實時熱更新前端代碼。  當訪問localhost:9999時,server會返回html,我們的server返回的html中的js腳本路徑是指向的dev-serve端口的地址,如下圖。  也就是說,客戶端的程序和服務端的程序被分別打包,并且運行兩個不同的端口服務。  

怎么實現ssr服務端渲染

在生產環境下,因為不需要dev-server去監聽和熱更新,因此只一個服務就足夠, 如下圖,服務端注冊靜態資源文件夾:

server/app.js

怎么實現ssr服務端渲染

目前的構建系統,區分了生產環境和開發環境,現在的開發環境構建是沒有什么問題的。  但是開發環境問題就比較明顯,存在的最大問題是服務端沒有熱更新或者重新打包重啟。  這樣會導致很多問題,最嚴重的就是前端已經更新了組件,但是服務端并沒有更新,所以在同構的時候會出現不一致,就會導致報錯,有些報錯會影響運行,解決辦法只有重啟。  這樣的開發體驗是無法忍受的。  后來我開始考慮做服務端的熱更新。

監聽、打包、重啟

最初我的方法是監聽修改,打包然后重啟應用。  還記得我們的client/router/pages.js文件嗎,客戶端和服務端的路由都引入了這個文件,所以服務端和客戶端的打包依賴都有pages.js,因此所有pages的組件相關的依賴都可以被客戶端和服務端監聽,當一個組件更新了,dev-server已經幫助我們監聽和熱更新了客戶端代碼,現在我們要自己來處理以下如何更新和重啟服務端代碼。  其實方法很簡單,就是在服務端打包配置里開啟監聽,然后在插件配置中,寫一個重啟的插件,插件代碼如下:

怎么實現ssr服務端渲染 

當webpack首次運行之后,插件會啟動一個子進程,運行app.js,當文件發生變動后,再次編譯,判斷是否有子進程,如果有殺掉子進程,然后重啟子進程,這樣就實現了自動重啟。  因為客戶端和服務端是兩個不同的打包服務和配置,當文件被修改,他們同時會重新編譯,為了保證編譯后運行符合預期,要保證服務端先編譯完成,客戶端后編譯完成,所以在客戶端的watch配置里,增加一點延遲,如下圖,默認是300毫秒,所以服務端是300毫秒后執行編譯,而客戶端是1000毫秒后執行編譯。

怎么實現ssr服務端渲染

現在解決了重啟問題,但是我覺得還不夠,因為在開發的大部分時間里pages.js中組件,也就是展示端的代碼更新頻率會很高,如果老是去重啟編譯后端的代碼,我覺得效率太低。  因此我覺得再做一次優化。

抽離client/router/pages單獨打包

流程應該是這樣的,增加一個webpack.server-dev-pages.js配置文件,單獨監聽和打包出dist/pages,服務端代碼判斷如果是開發環境,在路由監聽方法中每次執行都重新獲取dist/pages包,服務端監聽配置忽略client文件夾。  看起來有點懵逼,其實最終的效果就是當pages中依賴的組件發生了更新,webpack.server-dev-pages.js重新編譯并打包到dist/pages中,服務端app不編譯和重啟,只需要在服務端app路由中重新獲取最新的dist/pages包,就保證了服務應用更新了所有客戶端組件,而服務端應用并不會編譯和重啟。  當服務端本身的代碼發生了修改,還是會自動編譯和重啟。  所以最終我們的開發環境需要啟動3個打包配置

  • webpack.server-dev-pages

  • webpack.server-dev

  • webpack.client-dev

server/router,如何清除和更新pages包

怎么實現ssr服務端渲染

至此,比較滿意的開發環境基本實現了。  后來又覺得每次更新css都需要去重新打包后端的pages也沒有必要,加上同構的時候css不一致,僅僅只有警告,沒有實質影響,因此我在server-dev-pages中忽略了less文件(因為我用的less)。  這樣會導致一個問題,因為沒有更新pages,所以頁面會刷新時會先展示舊的樣式,然后同構完成又立馬變成新樣式,在開發環境中這一瞬間是可以接受的,也不影響什么。  但是避免了無謂的編譯。

怎么實現ssr服務端渲染

沒有做的事情

  • 封裝成一個更有包裹性的三方腳手架

  • css作用域控制

  • 封裝性更強的webpack配置

  • 開發環境下,圖片路徑會出現不一致

最初做自己小站的目的是學習,加上自己使用,因此有太多個性的東西。  從自己的小站中抽離了出來,已經刪去了很多包和代碼,只為了讓他人更能快速理解其中的核心代碼。  代碼中有很多注釋都能幫助他人理解,如果大家想使用當前庫開發一個自己的小站,是完全可以的,也可以幫助大家更好的理解它。  如果是用于商業項目,推薦nextjs。  css沒有做作用域控制,因此如果想隔離作用域,手動添加上層css隔離,比如.index{ ..... }包裹一層,或者嘗試自己引入三方包。  webpack通用的配置可以封裝成一個文件,然后在每個文件里引入,再個性修改。  但是之前看其他代碼的時候發現,這種方法,會增加閱讀難度,加上本身配置內容不多,所以不做封裝,看起來更直觀。  開發環境下,圖片路徑會出現不一致,比如客戶端地址請求地址是localhost...assets/xx.jpg,而服務端是assets/xx.jpg,可能會有警告,但是不影響。  因為只是一個是絕對路徑,一個是相對路徑。 

怎么實現ssr服務端渲染

看完上述內容,你們對怎么實現ssr服務端渲染有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

ssr
AI

凤翔县| 皮山县| 文水县| 连平县| 麟游县| 临颍县| 上饶县| 沛县| 晋州市| 平谷区| 江达县| 庆云县| 汉沽区| 胶州市| 中阳县| 太和县| 江门市| 若尔盖县| 广灵县| 泰宁县| 自治县| 灌南县| 普格县| 神木县| 五家渠市| 大宁县| 霍州市| 民和| 江源县| 衢州市| 林西县| 城口县| 天台县| 裕民县| 从化市| 根河市| 安乡县| 盘山县| 小金县| 嵊州市| 茂名市|