Server Side Rendering 服務器渲染是各 Spa 項目目前很熱衷於解決的一個問題,畢竟針對SEO和首次加載優化javascript
.Net Core SPA 服務器渲染 將分爲 兩篇:html
第一篇 主要分析 .net core 最新的服務器渲染中間件 和 基礎使用方式 和一些庫的原理分析java
第二篇 主要分析 一些特殊的服務器渲染需求 和 實現方式, 外加客戶端 對於渲而後的html的優化處理等node
注:主要針對 .Net Core 2.1 以上 MVC 和 SPA 徹底分離的設計結構 因此下面主要講述的是使用 UseSpaPrerendering 而非 PrerenderResult + MVCgit
相關 Github Library github
Microsoft.AspNetCore.SpaServicesnpm
Spa服務器渲染中間件promise
Microsoft.AspNetCore.NodeServices服務器
調用NodeJs的核心服務app
JS 服務端渲染入口
JS 異步請求Task管理庫
服務器渲染流程
頁面請求 -> SPA 中間件 -> SSR 服務 -> Node 服務 -> 服務端js -> 生成網頁String結果 -> 返回
定義.Net Core服務器渲染
Angular
上一篇有講到過新的Spa 中間件 UseSpa 在 UseSpa下有選項 UseSpaPrerendering, 這就是主要定義服務器渲染的接口
Angular的服務器渲染相對來講比較簡單, 案例也多, 由於AngularCliBuilder 是一個自帶的SSR BootModuleBuilder
spa.UseSpaPrerendering(options => { options.BootModulePath = $"{spa.Options.SourcePath}/dist-server/main.js"; options.BootModuleBuilder = env.IsDevelopment() ? new AngularCliBuilder(npmScript: "build:ssr") : null; options.ExcludeUrls = new[] { "/sockjs-node" }; });
這是一段Angular 經過 AngularCliBuilder 來定義 服務器渲染 從build 到 入口的定義。
隨便找了一個案例 https://github.com/joshberry/dotnetcore-angular-ssr
React
不過這不是我主要想探討的。。由於我對ng不算那麼熟,哈哈,我主要來講如何自定義吧。下面用React做爲案例 具體原理是相近的。
這個是個人案例 https://github.com/JiarongGu/ReactCoreTemplate
app.UseSpa(spa => { spa.Options.SourcePath = "ClientApp"; spa.UseSpaPrerendering(options => { options.BootModulePath = $"{spa.Options.SourcePath}/build/server/bundle.js"; options.SupplyData = SpaPrerenderingServiceLocator.GetProcessor(app); }); });
這是簡單的定義
BootModulePath:是咱們 服務端 js 的入口
SupplyData: 是咱們要給服務端js 的基本數據 例如當前的local的服務器地址和請求的url,具體有什麼用 我下面會一一講解
我這裏定義了一個單例的服務外加一個服務定位來執行處理 SupplyData 具體實現能夠看個人github
製做服務器渲染入口
根據上面這個處理流程 咱們首先須要定義一個 服務端的頁面 也就是 server/bundle.js 來執行客戶端的 javascript, 經過它來得到咱們須要的html結果。
由於在服務器上執行咱們將會使用NodeJS來執行這個bundle.js,並且必須是能返回一個html結果的函數。
這裏微軟給咱們提供了一個npm包 aspnet-prerendering, 他提供了包裝Promise 返回值, SSR PreRenderer會調用NodeJs 去執行。
主要的方法就是這個 createServerRenderer
export default createServerRenderer(params => {
return new Promise<RenderResult>((resolve, reject) => { ... } }
params 的數據類型
export interface BootFuncParams { location: any; // e.g., Location object containing information '/some/path' origin: string; // e.g., 'https://example.com:1234' url: string; // e.g., '/some/path' baseUrl: string; // e.g., '' or '/myVirtualDir' absoluteUrl: string; // e.g., 'https://example.com:1234/some/path' domainTasks: Promise<any>; data: any; // any custom object passed through from .NET }
其中 data 即是上面所說的 SupplyData 裏面能夠包括任何想傳遞的資料 最重要的是 裏面會包括 originalHtml, 這就是默認讀取到的index.html頁面string
在最後 咱們會渲染完Spa的全部組件 而後把生成的 string 替換在這個originalHtml 適合的位置上 這樣 服務器渲染就算是完成了
resolve promise 結果類型
在 JavaScriptService 下 RenderToStringResult 這即是咱們須要返回的 結果給SSR Service
public class RenderToStringResult { public JObject Globals { get; set; } // 服務端HTMl結果 public string Html { get; set; } // 重定向,跳過服務渲染 public string RedirectUrl { get; set; } // 自定義Response Code, 通常不須要設置 public int? StatusCode { get; set; } }
注:在使用UseSpaPrerendering的時候 Gloabls 必須爲空 這是給 MVC 實現使用的
Webpack/Cli Build服務端
由於服務端JS的性質不一樣 因此 Webpack也須要另外定義一個 關鍵是 target: 'node', 而後 entry 定義爲新的server.js 入口 這樣就會根據新的入口 來進行nodejs 模式編譯.
總結
這篇主要簡單講解 如何從SPA 經過服務器渲染生成靜態頁面,可是不包括異步數據獲取。
寫的仍是比較粗糙,若是有哪裏不清楚的 歡迎留言。
下一篇會主要講述關於數據管理,異步請求管理 包括如何使用 domian-task 或者 其餘方式的實現,外加最近收集到的多Spa項目的管道管理