<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                [TOC] # Server-Side Rendering ![CSR](https://img.kancloud.cn/7e/f7/7ef701ff3069a35bafe3a8285f46379f_1271x989.png) ![SSR](https://img.kancloud.cn/3a/56/3a564b0bc4e6d7b2c483332ed0b1caca_948x619.png) # 什么時候適合SSR? React 在有 node 中間層的時候比較適合做 SSR,其實是否 SSR 應該是業務決定的,比如如果你需要做 SEO 那你就需要 SSR,比如新聞網站,內容類網站;對于不需要 SEO 的系統,比如后端系統,webapp,都是不需要 SSR 的。 同構的出發點不是 “為了做同構,所以做了”, 而是回歸業務,去解決業務場景中 SEO、首屏性能、用戶體驗 等問題,驅動我們去尋找可用的解決方案。在這樣的場景下,除了同構本身,我們還需要考慮的是: * 高性能的 Node Server * 可靠的 同構渲染服務 * 可控的 運維成本 * 可復用的 解決方案 * ... 簡單歸納就是,我們需要一個 企業級的同構渲染解決方案。 >網絡調到 3G,查看差異: ![ssr](https://img.kancloud.cn/fb/fb/fbfb439565c42ff6bb5928783853d695_700x650.gif) 注意??:對于 單頁面 SPA 首頁白屏時間長,不利于 SEO 優化的問題。 目前主流的解決方案:服務端渲染 SSR 和 **預渲染技術 prerender**(參見 Vue 手冊)。 # 服務端渲染 與 同構 > you have heard a lot of smart people talking about “Isomorphic” or “universal” applications. Some people call it server side rendering (SSR). > 所謂同構,通俗的講,就是一套 React 代碼在服務器上運行一遍,到達瀏覽器又運行一遍。服務端渲染完成**頁面結構**,瀏覽器端渲染完成**事件綁定**。 服務端渲染主要側重**架構層面的實現**,而同構更側重**代碼復用**。 # 理論上的性能優點 使用 CSR 渲染的話,頁面很容易白屏。相反,如果你使用 SSR 渲染的話,白屏就不(那么)容易出現啦。盡管大家都知道,使用 CSR(在很大程度上)就意味著頁面白屏,不過大多數人還是會使用下面的這種方式來規避(白屏)風險(在服務器返回所有數據之前,給頁面添加 loading 圖,然后在所有數據到達之后,把 loading 圖撤掉) 不過對于使用 SSR 方式渲染出的 HTML 頁面來說,用戶是可以在這些操作(指的是下載 React、構建虛擬 DOM、綁定事件)完成之前就能看到頁面。 反觀使用 CSR 方式渲染出的 HTML 頁面,你必須等到上面的這些操作(指的是下載 React、構建虛擬 DOM、綁定事件)都完成,virtual-dom 轉換成(瀏覽器)頁面上的真實 dom 之后,用戶才能看到頁面。 ## SSR、CSR 兩種渲染方式共同點: * 都需要下載 React 的 * 都需要經歷虛擬 DOM 構建過程 * 都需要(給頁面元素)綁定事件來增強頁面的可交互性 ## SSR 問題 1. [在使用 SSR 方式渲染 HTML 頁面的過程中,瀏覽器獲取第一個字節的時間(Time To First Byte)要長于用 CSR 渲染 HTML 頁面所獲取的時間](https://imququ.com/post/transfer-encoding-header-in-http.html),(為啥呢)? 這是因為在你使用 SSR 方式渲染頁面的過程中,你服務器需要花更多的時間來渲染出(瀏覽器所需要的)HTML 結構,(最后才將渲染好的 HTML 結構作為響應返回),而不像 CSR 那樣,服務器只需要返回字節相對較少的 Json 數據(relatively empty respons)。 2. SSR 方式渲染 HTML 頁面的過程中,服務器的吞吐量會明顯少于用 CSR 渲染 HTML 頁面時服務器的吞吐量。尤其是當你在服務端使用 react 的時候,(你會發現,是否使用 react 的服務端渲染特性,服務器吞吐量往往也是我們考慮的因素),這是因為 react 對服務器吞吐量的影響太大啦。 `ReactDOMServer.renderToString`具有以下特點: * 同步方法 * (屬于 CPU 獨享型),[在調用過程中,會綁定 CPU](http://www.tuicool.com/articles/fiuURnZ) * 會阻塞(hold)整個事件循環流程 (換句話說),在 `ReactDOMServer.renderToString` 沒有執行完之前,服務器是(絕)不可能處理其它請求的。(啥?你說我講的太抽象啦,完全聽不懂),(那好吧,不妨)讓我們做個假設(Let’s say ),在使用 SSR 渲染 HTML 頁面的過程中,執行`ReactDOMServer.renderToString`就花了 500ms,這意味你現在每秒最多只能處理兩個請求。(如果有同學對這方面感興趣的話,可以重點關注一下) # 原理 node server 接收客戶端請求, 得到當前的 req url path, 然后在已有的路由表內查找到對應的組件, 拿到需要請求的數據,將數據作為 props 、context 或者 store 形式傳入組件, 然后基于 react 內置的服務端渲染 api renderToString() or renderToNodeStream() 把組件渲染為 html字符串或者 stream 流 , 在把最終的 html 進行輸出前需要將數據注入到瀏覽器端 (注水), server 輸出 (response) 后瀏覽器端可以得到數據 (脫水),瀏覽器開始進行渲染和節點對比, 然后執行組件的 componentDidMount 完成組件內事件綁定和一些交互, 瀏覽器重用了服務端輸出的 html 節點,整個流程結束。 ![](https://img.kancloud.cn/a6/52/a652ca7923c03814c2471b9092c48d16_1280x662.png) ## 服務器端開發一個頁面 后端一般都會使用模版(ejs 模板引擎),先看一個 `node ejs` 的栗子: ``` // ejs index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>react ssr <%= title %></title> </head> <body> <%= data %> </body> </html> ``` ``` 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: '首頁'}, (err, data) => { if (err ) { console.log(err); } else { res.end(data); } }) } }).listen(8080); ``` 上面渲染出的頁面就是有頁面結構的,也是我們常說的服務器端頁面開發! # 實戰詳解 ## 前端搭建開發環境 推薦 自定義 webpack ? babel形式,CRA 沒有 SSR 的配置,所以不推薦,與其他后端可能融合起來費勁! > [cra-ssr 的 typescript 版本](https://github.com/leidenglai/cra-ssr-ts) ## 服務端實現組件的渲染 `react-dom` 這個庫中剛好實現了編譯虛擬 DOM 的方法: ``` import { renderToString } from 'react-dom/server'; ... ``` 但是你會發現問題: ### 事件綁定無效! 當然了,事件,樣式這些東西,是瀏覽器干的事,服務端當然沒辦法了!唯一的方式就是讓瀏覽器去拉取 JS 文件執行,讓 JS 代碼來控制。 用 webpack 將按照平常打包,將前端項目編譯打包成 js 文件引入到頁面中!好的現在瀏覽器已經可以接管頁面的事件等等。 ### 路由問題 需要將服務端的路由邏輯執行一遍。 這一需要注意下,在客戶端我們采用 `BrowserRouter` 來配置路由,在服務端采用 `StaticRouter` 來配置路由。 1. 客戶端配置 ``` copyimport React from 'react'; import ReactDOM from 'react-dom'; import { BrowserRouter } from "react-router-dom"; import Router from '../router'; function ClientRender() { return ( <BrowserRouter > <Router /> </BrowserRouter> ) } ``` 2. 服務端配置 ``` copyimport React from 'react'; import { StaticRouter } from 'react-router' import Router from '../router.js'; function ServerRender(req, initStore) { return (props, context) => { return ( <StaticRouter location={req.url} context={context} > <Router /> </StaticRouter> ) } } export default ServerRender; ``` 服務器端路由代碼相對要復雜一點,需要你把 location(當前請求路徑)傳遞給 StaticRouter 組件,這樣 StaticRouter 才能根據路徑分析出當前所需要的組件是誰。(PS:StaticRouter 是 React-Router 針對服務器端渲染專門提供的一個路由組件。) 通過 BrowserRouter 我們能夠匹配到瀏覽器即將顯示的路由組件,對瀏覽器來說,我們需要把組件轉化成 DOM,所以需要我們使用 `ReactDom.render` 方法來進行 DOM 的掛載。 而 StaticRouter 能夠在服務器端匹配到將要顯示的組件,對服務器端來說,我們要把組件轉化成字符串,這時我們只需要調用 ReactDom 提供的 `renderToString` 方法,就可以得到 App 組件對應的 HTML 字符串。 對于一個 React 應用來說,路由一般是整個程序的執行入口。在 SSR 中,服務器端的路由和客戶端的路由不一樣,也就意味著服務器端的入口代碼和客戶端的入口代碼是不同的。 ### 多級路由渲染 ``` // 增加renderRoutes方法 import { renderRoutes } from 'react-router-config'; ``` ## 服務端處理css,圖片,字體等靜態資源 ### webpack-manifest-plugin 該插件會生成 一個 `manifest` 文件 大概結構是這樣: ``` { "page1.css": "page1.css", "page1.js": "page1.js" } ``` ``` const manifest = require('./public/manifest.json'); /** * 處理鏈接 * @param {*要進行服務器渲染的文件名默認是build文件夾下的文件} fileName */ function handleLink(fileName, req, defineParams) { ... obj.script = `<script src="${manifest[`${fileName}.js`]}"></script>`; ... } ``` 所以 通過外聯的方式,鏈接樣式,可能會造成頁面渲染閃現。 ### asset-require-hook 使用 asset-require-hook 過濾掉一些類似 `import logo from "./logo.svg";` 這樣的資源代碼。因為我們服務端只需要純的 HTML 代碼,不過濾掉會報錯。這里的 name,我們是去掉了 hash 值的: ``` require("asset-require-hook")({ extensions: ["svg", "css", "less", "jpg", "png", "gif"], name: '/static/media/[name].[ext]' }); require("babel-core/register")(); require("babel-polyfill"); require("./app"); ``` ### webpack-isomorphic-tools 實現方法有多種,我這里使用`webpack-isomorphic-tools`插件來實現,之后會做介紹。 ### isomorphic-style-loader 針對第一種使用內聯樣式,直接把樣式嵌入到頁面中,需要用到 css–loader 和 style-loader, css-loader 可以繼續用,但是 style-loader 由于存在一些跟瀏覽器相關的邏輯,所以無法在服務器端繼續用了,但好在早就有了替代插件,isomorphic-style-loader,此插件用法跟 style-loader 差不多,但是同時支持在服務器端使用 **isomorphic-style-loader**會將導入 css 文件轉換成一個對象供組件使用,其中一部分屬性是類名,屬性的值是類對應的 css 樣式,所以可以直接根據這些屬性在組件內引入樣式,除此之外,還包括幾個方法,SSR 需要調用其中的 `_getCss` 方法以獲取樣式字符串,傳輸到客戶端 鑒于上述過程(即將 css 樣式匯總及轉化為字符串)是一個通用流程,所以此插件項目內主動提供了一個用于簡化此流程的 HOC 組件:withStyles.js 此組件所做的事情也很簡單,主要是為 isomorphic-style-loader 中的兩個方法:`_insertCss` 和 `_getCss` 提供了一個接口,以 Context 作為媒介,傳遞各個組件所引用的樣式,最后在服務端和客戶端進行匯總,這樣一來,就能夠在服務端和客戶端輸出樣式了 ## 數據管理 對于復雜的項目,加上全局狀態管理 `Redux` ? 服務器渲染中其順序是同步的,因此,要想在渲染時出現首屏數據渲染,必須得提前準備好數據。 在服務端通過預取數據交給狀態管理 redux 的 store,插入到 `window.__INIT_STORE__`作為初始 store,在客戶端拿 `window.__INIT_STORE__`作為初始 store (數據注水),connect 組件得到數據填充(數據脫水): * 提前獲取數據 * 初始化 store * 根據路由顯示組件 * 結合數據和組件生成 HTML,一次性返回 ``` window.__INIT_STORE__ = ${JSON.stringify(initStore)} ``` 1. 后端渲染出得數據都是同一份? ``` const getStore = (req) => { return createStore(reducer, defaultState); } export default getStore; ``` 把這個函數執行就會拿到一個新的 store, 這樣就能保證每個用戶訪問時都是用的一份新的 store。 還可以結合 [react-frontload](https://github.com/davnicwil/react-frontload) 做異步數據獲取時的展現; `react-router` 包里面提供了 [react-router-config](https://github.com/ReactTraining/react-router/tree/master/packages/react-router-config) 主要用于靜態路由配置。 提供的 `matchRoutes` API 可以根據傳入的 url 返回對應的路由數組。我們可以通過這個方法在服務端直接訪問到對應的 React 組件。 如果要從路由中直接獲取異步方法,我看了很多類似的同構方案, ~~~ // matchedRoutes 是當前路由對應的所有需要顯示的組件集合 matchedRoutes.forEach(item => { // 組件上的 loadData 幫助服務器端的 Store 獲取到這個組件所需的數據。 if (item.route.loadData) { const promise = new Promise((resolve, reject) => { item.route.loadData(store).then(resolve).catch(resolve); }) promises.push(promise); } }) Promise.all(promises).then(() => { // 生成 HTML 邏輯 }) ~~~ 這里,我們使用 Promise 來解決這個問題,我們構建一個 Promise 并發請求,等待所有的 Promise 都執行結束后,也就是所有 `store.dispatch` 都執行完畢后,再去生成 HTML。這樣的話,我們就實現了結合 Redux 的 SSR 流程。 在客戶端獲取數據,使用的是我們最習慣的方式,通過 `componentDidMount` 進行數據的獲取。這里要注意的是,`componentDidMount` 只在客戶端才會執行,在服務器端這個生命周期函數是不會執行的。所以我們不必擔心 `componentDidMount` 和 **loadData** 會有沖突,放心使用即可。 > 參考代碼 [sanyuan0704/react-ssr/server/index.js](https://github.com/sanyuan0704/react-ssr/blob/master/my_ssr/src/server/index.js) ### 當服務端獲取數據之后 其實當服務端獲取數據之后,客戶端并不需要再發送 Ajax 請求了,而客戶端的 React 代碼仍然存在這樣的浪費性能的代碼。怎么辦呢? ``` componentDidMount() { //判斷當前的數據是否已經從服務端獲取 //要知道,如果是首次渲染的時候就渲染了這個組件,則不會重復發請求 //若首次渲染頁面的時候未將這個組件渲染出來,則一定要執行異步請求的代碼 //這兩種情況對于同一組件是都是有可能發生的 if (!this.props.list.length) { this.props.getHomeList() } } ``` ### fetch 同構 可以使用 `isomorphic-fetch`、`axios` 或者 `whatwg-fetch + node-fetch` 等庫來實現支持雙端的 `fetch 數據請求`,這里推薦使用 `axios` 主要是比較方便。 ## Webpack 服務器端配置 ``` { "presets": ["@babel/preset-react", ["@babel/preset-env",{ "targets": { "browsers": [ "ie >= 9", "ff >= 30", "chrome >= 34", "safari >= 7", "opera >= 23", "bb >= 10" ] } }] ], "plugins": [ [ "import", { "libraryName": "antd", "style": true } ] ] } ``` 這份配置由**服務端和客戶端**共用,用來處理 `React` 和轉義為 `ES5` 和瀏覽器兼容問題。 ``` // 服務端配置 const serverConfig = { target: 'node', entry: { page1: './web/render/serverRouter.js', }, resolve, output: { filename: '[name].js', path: path.resolve(__dirname, './app/build'), libraryTarget: 'commonjs' }, mode: process.env.NODE_ENV, externals: [nodeExternals()], module: { rules: [ { test: /\.(jsx|js)?$/, exclude: /node_modules/, use: { loader: 'babel-loader' } }, { test: /\.(css|less)$/, use: [ { loader: 'css-loader', options: { importLoaders: 1 } }, { loader: 'less-loader', } ] } ] } }; ``` 1. `target: 'node'` 由于輸出代碼的運行環境是node,源碼中依賴的 node 原生模塊沒必要打包進去; 2. `externals: [nodeExternals()]` `webpack-node-externals` 的目的是為了防止 `node_modules`目錄下的第三方模塊被打包進去;服務端使用 `CommonJS` 規范,而且服務端代碼也并不需要構建,因此,對于 `node_modules` 中的依賴并不需要打包,所以借助 `webpack` 第三方模塊 `webpack-node-externals` 來進行處理,經過這樣的處理,兩份構建過的文件大小已經相差甚遠了。 3. `{ test: /\.css/, use:["ignore-loader"] }` 忽略掉依賴的 css 文件,css 會影響服務端渲染性能,又是做服務端渲染不重要的部分; 4. `libraryTarget: "commonjs2",` 以 commonjs2規范導出渲染函數,以供給采用nodejs編寫的http服務器代碼調用。 ## 優化項目 ## 實現按需加載 主要使用的是 `react-loadable` 包來實現按需加載,在 `SSR` 增加這個配置相對比較繁瑣,但是官網基本已經給出詳細的步驟[詳細配置流程](https://github.com/jamiebuilds/react-loadable)。 ### HTML 模板 這里以 nunjucks 模版引擎示例,在渲染 `HTML` 時,會將有 `< >` 進行安全處理,因此,我們還需對我們傳入的數據進行過濾處理。 ``` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>koa-React服務器渲染</title> {{ link | safe }} </head> <body> <div id='app'> {{ html | safe }} </div> </body> <script> window.__INIT_STORE__ = {{ store | safe }} </script> {{ script | safe }} </html> ``` # API ## renderToString 將 `React` 元素渲染到其初始 `HTML` 中。 該函數應該只在服務器上使用。 `React` 將返回一個 `HTML` 字符串。 您可以使用此方法在服務器上生成 `HTML` ,并在初始請求時發送標記,以加快網頁加載速度,并允許搜索引擎抓取你的網頁以實現 `SEO` 目的。 ## renderToStaticMarkup 類似于 `renderToString` ,除了這不會創建 `React` 在內部使用的額外 `DOM` 屬性,如 `data-reactroot`。 如果你想使用 `React` 作為一個簡單的靜態頁面生成器,這很有用,因為剝離額外的屬性可以節省一些字節。 但是如果這種方法是在瀏覽訪問之后,會全部替換掉服務端渲染的內容,因此會造成頁面閃爍,所以并不推薦使用該方法。 ***** `data-reactroot`簡單的說就是`react 組件`的一個唯一標示 id, 具體可以去 google 下 **對于服務端渲染而言** * 使用`renderToStaticMarkup`渲染出的是不帶`data-reactid`的純`html`在前端`react.js`加載完成后, 之前服務端渲染的頁面會抹掉之前服務端的重新渲染(可能頁面會閃一下). 換句話說 前端`react`就根本就不認識之前服務端渲染的內容,`render`方法會使用`innerHTML`的方法重寫`#react-target`里的內容 * 而使用`renderToString`方法渲染的節點會帶有`data-reactid`屬性, 在前端`react.js`加載完成后, 前端`react.js`會認識之前服務端渲染的內容, 不會重新渲染`DOM 節點`, 前端`react.js`會接管頁面, 執行`componentDidMout``綁定瀏覽器事件`等 這些在服務端沒完成也不可能執行任務。 > https://reactjs.org/docs/react-dom-server.html#rendertostaticmarkup ## ReactDOM.hydrate() `ReactDOM.hydrate()`與 `render()` 相同,但是它對 由 `ReactDOMServer` 渲染出 HTML 內容的容器 進行補充水分(附加事件偵聽器), React 將嘗試將事件偵聽器附加到現有標記。 使用`ReactDOM.render()` 對服務器渲染的容器進行水合是因為速度較慢,因此已被棄用,并將在 React 17 中被刪除,因此請使用`hydrate() `代替。 > [react 中出現的 "hydrate" 這個單詞到底是什么意思? ](https://www.zhihu.com/question/66068748) > [hydrate() and render() in React 16](https://stackoverflow.com/questions/46516395/whats-the-difference-between-hydrate-and-render-in-react-16) ## renderToNodeStream() React 16 最新發布的東西,它支持直接渲染到節點流。 渲染到流可以減少你的內容的第一個字節`(TTFB)`的時間,在文檔的下一部分生成之前,將文檔的開頭至結尾發送到瀏覽器。返回一個 可讀的流 (`stream`) ,即輸出 `HTML` 字符串。這個 流 (`stream`) 輸出的 `HTML` 完全等同于 `ReactDOMServer.renderToString` 將返回的內容。 當內容從服務器流式傳輸時,瀏覽器將開始解析 HTML 文檔。速度是 `renderToString` 的`三倍` 我們也可以使用上述 `renderToNodeSteam` 將其改造下: ``` let element = React.createElement(dom(req, defineParams)); ctx.res.write(' <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>koa-React服務器渲染</title> </head><body><div id="app">'); // 把組件渲染成流,并且給Response const stream = ReactDOMServer.renderToNodeStream(element); stream.pipe(ctx.res, { end: 'false' }); // 當React渲染結束后,發送剩余的HTML部分給瀏覽器 stream.on('end', () => { ctx.res.end('</div></body></html>'); }); ``` ## renderToStaticNodeStream() 類似于 `renderToNodeStream` ,除了這不會創建 `React` 在內部使用的額外 `DOM` 屬性,如 `data-reactroot` 。 如果你想使用 `React` 作為一個簡單的靜態頁面生成器,這很有用,因為剝離額外的屬性可以節省一些字節。 這個 流 (`stream`) 輸出的 `HTML` 完全等同于 `ReactDOMServer.renderToStaticMarkup` 將返回的內容。 # SEO 解決 ``` <title>測試</title> <meta name="Description" content="測試"/> <meta name="Keywords" content="測試"/> ``` 1. title 用于瀏覽器標簽欄的顯示標題、網頁爬蟲爬取后的標題。 最好控制在 30 字內。 2. description 用于描述網站信息,會顯示在搜索結果中。 簡要概述網站主要內容 3. keywords 用于匹配搜索引擎的搜索結果。 最好控制在 3 個左右。 三者的合理設置都有利于 SEO ## React-Helmet 的使用 ``` npm i -S react-helmet ``` ``` // 拿到 helmet 對象,然后在 html 字符串中引入 const helmet = Helmet.renderStatic(); // 模版中渲染 ${helmet.title.toString()} ${helmet.meta.toString()} ``` > [網站的 TDK 該怎么設置?它有什么作用?](https://github.com/haizlin/fe-interview/issues/1009) # 部署 [React 服務端渲染 + pm2 自動化部署](https://juejin.im/post/5b55e6a96fb9a04fcf59d754) # 總結 實際上SSR開發通常是在一個項目基礎上改,而不是重新搭建一個項目,比較很多人拿它當做優化,而不是重構。 通常來說我們一個項目按照 SPA 模式開發,針對特定頁面做 SSR 的修改,修改之后的項目既可以SPA 也可以 SSR,只不過SPA 模式時對應頁面獲取不到數據,因為獲取數據的方法均被修改。 所謂同構,其實就是服務端借助客戶端的JS去渲染頁面,沒有影響到客戶端的JS,還是正常打包,客戶端做代碼分割也不會受影響。 SSR 實現方式并不唯一,還有很多其他的方式, 比如 `next.js`, `umi.js`, 但是原理相似。 # 其他工具 ## 大量文本操作 [cheerio](https://github.com/cheeriojs/cheerio) ## 業界生態 [Egg + React + SSR 服務端渲染](http://ykfe.net/guide/#%E5%88%9D%E8%A1%B7) https://imajs.io/ https://catberry.org/ [Easy-team](https://www.yuque.com/easy-team) [next.js](https://github.com/zeit/next.js):輕量級的同構框架 [react-server](https://react-server.io/):React 服務端渲染框架 [razzle](https://github.com/jaredpalmer/razzle):通用服務端渲染框架 [beidou](https://github.com/alibaba/beidou):阿里自己的同構框架,基于 eggjs, 定位是企業級同構框架 除了開源框架,底層方面 React16 重構了 SSR, react-router 提供了更加友好的 SSR 支持等等,從某種程度上來說,同構也是一種趨勢,至少是方向之一。 # 參考 [DrReMain/egg-react-ssr/app/controller/home.js](https://github.com/DrReMain/egg-react-ssr/blob/master/app/controller/home.js) [ykfe/egg-react-ssr/ssr-with-ts/src/app/controller/page.ts](https://github.com/ykfe/egg-react-ssr/blob/dev/example/ssr-with-ts/src/app/controller/page.ts) [如何搭建一個高可用的服務端渲染工程](https://www.infoq.cn/article/Ugb49pzllUvbzeCdD6xw) [從頭開始,徹底理解服務端渲染原理 (8 千字匯總長文)](https://juejin.im/post/5d1fe6be51882579db031a6d#heading-21) [從零開始 React 服務器渲染(SSR)同構??(基于 Koa)](https://juejin.im/post/5c627d9b6fb9a049f23d3e38#heading-21) [打造高可靠與高性能的 React 同構解決方案](https://github.com/alibaba/beidou/blob/master/packages/beidou-docs/articles/high-performance-isomorphic-app.md) [Server-Side Rendering with React, Redux, and React-Router](https://itnext.io/server-side-rendering-with-react-redux-and-react-router-fa5b67d4965e) [【長文慎入】一文吃透 React SSR 服務端渲染和同構原理](https://juejin.im/post/5d7deef6e51d453bb13b66cd#heading-30)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看