<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
        當前位置: 首頁 - 科技 - 知識百科 - 正文

        利用React Router4實現的服務端直出渲染(SSR)

        來源:懂視網 責編:小采 時間:2020-11-27 22:02:19
        文檔

        利用React Router4實現的服務端直出渲染(SSR)

        利用React Router4實現的服務端直出渲染(SSR):我們已經熟悉React 服務端渲染(SSR)的基本步驟,現在讓我們更進一步利用 React RouterV4 實現客戶端和服務端的同構。畢竟大多數的應用都需要用到web前端路由器,所以要讓SSR能夠正常的運行,了解路由器的設置是十分有必要的 基本步驟 路由器配置 前
        推薦度:
        導讀利用React Router4實現的服務端直出渲染(SSR):我們已經熟悉React 服務端渲染(SSR)的基本步驟,現在讓我們更進一步利用 React RouterV4 實現客戶端和服務端的同構。畢竟大多數的應用都需要用到web前端路由器,所以要讓SSR能夠正常的運行,了解路由器的設置是十分有必要的 基本步驟 路由器配置 前

        我們已經熟悉React 服務端渲染(SSR)的基本步驟,現在讓我們更進一步利用 React RouterV4 實現客戶端和服務端的同構。畢竟大多數的應用都需要用到web前端路由器,所以要讓SSR能夠正常的運行,了解路由器的設置是十分有必要的

        基本步驟

        路由器配置

        前言已經簡單的介紹了React SSR,首先我們需要添加ReactRouter4到我們的項目中

        $ yarn add react-router-dom
        
        # or, using npm
        $ npm install react-router-dom
        

        接著我們會描述一個簡單的場景,其中組件是靜態的且不需要去獲取外部數據。我們會在這個基礎之上去了解如何完成取到數據的服務端渲染。

        在客戶端,我們只需像以前一樣將我們的的App組件通過ReactRouter的BrowserRouter來包起來。

        src/index.js

        import React from 'react';
        import ReactDOM from 'react-dom';
        import { BrowserRouter } from 'react-router-dom';
        
        import App from './App';
        
        ReactDOM.hydrate(
         <BrowserRouter>
         <App />
         </BrowserRouter>,
         document.getElementById('root')
        );
        
        

        在服務端我們將采取類似的方式,但是改為使用無狀態的 StaticRouter

        server/index.js

        app.get('/*', (req, res) => {
         const context = {};
         const app = ReactDOMServer.renderToString(
         <StaticRouter location={req.url} context={context}>
         <App />
         </StaticRouter>
         );
        
         const indexFile = path.resolve('./build/index.html');
         fs.readFile(indexFile, 'utf8', (err, data) => {
         if (err) {
         console.error('Something went wrong:', err);
         return res.status(500).send('Oops, better luck next time!');
         }
        
         return res.send(
         data.replace('<div id="root"></div>', `<div id="root">${app}</div>`)
         );
         });
        });
        
        app.listen(PORT, () => {
         console.log(`😎 Server is listening on port ${PORT}`);
        });

        StaticRouter組件需要 location和context屬性。我們傳遞當前的url(Express req.url)給location,設置一個空對象給context。context對象用于存儲特定的路由信息,這個信息將會以staticContext的形式傳遞給組件

        運行一下程序看看結果是否我們所預期的,我們給App組件添加一些路由信息

        src/App.js

        import React from 'react';
        import { Route, Switch, NavLink } from 'react-router-dom';
        import Home from './Home';
        import Posts from './Posts';
        import Todos from './Todos';
        import NotFound from './NotFound';
        
        export default props => {
         return (
         <div>
         <ul>
         <li>
         <NavLink to="/">Home</NavLink>
         </li>
         <li>
         <NavLink to="/todos">Todos</NavLink>
         </li>
         <li>
         <NavLink to="/posts">Posts</NavLink>
         </li>
         </ul>
        
         <Switch>
         <Route
         exact
         path="/"
         render={props => <Home name="Alligator.io" {...props} />}
         />
         <Route path="/todos" component={Todos} />
         <Route path="/posts" component={Posts} />
         <Route component={NotFound} />
         </Switch>
         </div>
         );
        };
        
        

        現在如果你運行一下程序($ yarn run dev),我們的路由在服務端被渲染,這是我們所預期的。

        利用404狀態來處理未找到資源的網絡請求

        我們做一些改進,當渲染NotFound組件時讓服務端使用404HTTP狀態碼來響應。首先我們將一些信息放到NotFound組件的staticContext

        import React from 'react';
        
        export default ({ staticContext = {} }) => {
         staticContext.status = 404;
         return <h1>Oops, nothing here!</h1>;
        };
        
        

        然后在服務端,我們可以檢查context對象的status屬性是否是404,如果是404,則以404狀態響應服務端請求。

        server/index.js

        // ...
        
        app.get('/*', (req, res) => {
         const context = {};
         const app = ReactDOMServer.renderToString(
         <StaticRouter location={req.url} context={context}>
         <App />
         </StaticRouter>
         );
        
         const indexFile = path.resolve('./build/index.html');
         fs.readFile(indexFile, 'utf8', (err, data) => {
         if (err) {
         console.error('Something went wrong:', err);
         return res.status(500).send('Oops, better luck next time!');
         }
        
         if (context.status === 404) {
         res.status(404);
         }
        
         return res.send(
         data.replace('<div id="root"></div>', `<div id="root">${app}</div>`)
         );
         });
        });
        
        // ...
        
        

        重定向

        補充一下,我們可以做一些類似重定向的工作。如果我們有使用Redirect組件,ReactRouter會自動添加重定向的url到context對象的屬性上。

        server/index.js (部分)

        if (context.url) {
         return res.redirect(301, context.url);
        }
        

        讀取數據

        有時候我們的服務端渲染應用需要數據呈現,我們需要用一種靜態的方式來定義我們的路由而不是只涉及到客戶端的動態的方式。失去定義動態路由的定義是服務端渲染最適合所需要的應用的原因(譯者注:這句話的意思應該是SSR不允許路由是動態定義的)。

        我們將使用fetch在客戶端和服務端,我們增加isomorphic-fetch到我們的項目。同時我們也增加serialize-javascript這個包,它可以方便的序列化服務器上獲取到的數據。

        $ yarn add isomorphic-fetch serialize-javascript
        
        # or, using npm:
        $ npm install isomorphic-fetch serialize-javascript
        
        

        我們定義我們的路由信息為一個靜態數組在routes.js文件里

        src/routes.js

        import App from './App';
        import Home from './Home';
        import Posts from './Posts';
        import Todos from './Todos';
        import NotFound from './NotFound';
        
        import loadData from './helpers/loadData';
        
        const Routes = [
         {
         path: '/',
         exact: true,
         component: Home
         },
         {
         path: '/posts',
         component: Posts,
         loadData: () => loadData('posts')
         },
         {
         path: '/todos',
         component: Todos,
         loadData: () => loadData('todos')
         },
         {
         component: NotFound
         }
        ];
        
        export default Routes;
        
        

        有一些路由配置現在有一個叫loadData的鍵,它是一個調用loadData函數的函數。這個是我們的loadData函數的實現

        helpers/loadData.js

        import 'isomorphic-fetch';
        
        export default resourceType => {
         return fetch(`https://jsonplaceholder.typicode.com/${resourceType}`)
         .then(res => {
         return res.json();
         })
         .then(data => {
         // only keep 10 first results
         return data.filter((_, idx) => idx < 10);
         });
        };
        
        

        我們簡單的使用fetch來從REST API 獲取數據

        在服務端我們將使用ReactRouter的matchPath去尋找當前url所匹配的路由配置并判斷它有沒有loadData屬性。如果是這樣,我們調用loadData去獲取數據并把數據放到全局window對象中在服務器的響應中

        server/index.js

        import React from 'react';
        import express from 'express';
        import ReactDOMServer from 'react-dom/server';
        import path from 'path';
        import fs from 'fs';
        import serialize from 'serialize-javascript';
        import { StaticRouter, matchPath } from 'react-router-dom';
        import Routes from '../src/routes';
        
        import App from '../src/App';
        
        const PORT = process.env.PORT || 3006;
        const app = express();
        
        app.use(express.static('./build'));
        
        app.get('/*', (req, res) => {
         const currentRoute =
         Routes.find(route => matchPath(req.url, route)) || {};
         let promise;
        
         if (currentRoute.loadData) {
         promise = currentRoute.loadData();
         } else {
         promise = Promise.resolve(null);
         }
        
         promise.then(data => {
         // Lets add the data to the context
         const context = { data };
        
         const app = ReactDOMServer.renderToString(
         <StaticRouter location={req.url} context={context}>
         <App />
         </StaticRouter>
         );
        
         const indexFile = path.resolve('./build/index.html');
         fs.readFile(indexFile, 'utf8', (err, indexData) => {
         if (err) {
         console.error('Something went wrong:', err);
         return res.status(500).send('Oops, better luck next time!');
         }
        
         if (context.status === 404) {
         res.status(404);
         }
         if (context.url) {
         return res.redirect(301, context.url);
         }
        
         return res.send(
         indexData
         .replace('<div id="root"></div>', `<div id="root">${app}</div>`)
         .replace(
         '</body>',
         `<script>window.__ROUTE_DATA__ = ${serialize(data)}</script></body>`
         )
         );
         });
         });
        });
        
        app.listen(PORT, () => {
         console.log(`😎 Server is listening on port ${PORT}`);
        });
        

        請注意,我們添加組件的數據到context對象。在服務端渲染中我們將通過staticContext來訪問它。

        現在我們可以在需要加載時獲取數據的組件的構造函數和componentDidMount方法里添加一些判斷

        src/Todos.js

        import React from 'react';
        import loadData from './helpers/loadData';
        
        class Todos extends React.Component {
         constructor(props) {
         super(props);
        
         if (props.staticContext && props.staticContext.data) {
         this.state = {
         data: props.staticContext.data
         };
         } else {
         this.state = {
         data: []
         };
         }
         }
        
         componentDidMount() {
         setTimeout(() => {
         if (window.__ROUTE_DATA__) {
         this.setState({
         data: window.__ROUTE_DATA__
         });
         delete window.__ROUTE_DATA__;
         } else {
         loadData('todos').then(data => {
         this.setState({
         data
         });
         });
         }
         }, 0);
         }
        
         render() {
         const { data } = this.state;
         return <ul>{data.map(todo => <li key={todo.id}>{todo.title}</li>)}</ul>;
         }
        }
        
        export default Todos;

        工具類

        ReactRouterConfig是由ReactRouter團隊提供和維護的包。它提供了兩個處理ReactRouter和SSR更便捷的工具matchRoutes和renderRoutes。

        matchRoutes

        前面的例子都非常簡單都,都沒有嵌套路由。有時在多路由的情況下,使用matchPath是行不通的,因為它只能匹配一條路由。matchRoutes是一個能幫助我們匹配多路由的工具。

        這意味著在匹配路由的過程中我們可以往一個數組里存放promise,然后調用promise.all去解決所有匹配到的路由的取數邏輯。

        import { matchRoutes } from 'react-router-config';
        
        // ...
        
        const matchingRoutes = matchRoutes(Routes, req.url);
        
        let promises = [];
        
        matchingRoutes.forEach(route => {
         if (route.loadData) {
         promises.push(route.loadData());
         }
        });
        
        Promise.all(promises).then(dataArr => {
         // render our app, do something with dataArr, send response
        });
        
        // ...

        renderRoutes

        renderRoutes接收我們的靜態路由配置對象并返回所需的Route組件。為了matchRoutes能適當的工作renderRoutes應該被使用。

        通過使用renderRoutes,我們的程序改成了一個更簡潔的形式。

        src/App.js

        import React from 'react';
        import { renderRoutes } from 'react-router-config';
        import { Switch, NavLink } from 'react-router-dom';
        
        import Routes from './routes';
        
        import Home from './Home';
        import Posts from './Posts';
        import Todos from './Todos';
        import NotFound from './NotFound';
        
        export default props => {
         return (
         <div>
         {/* ... */}
        
         <Switch>
         {renderRoutes(Routes)}
         </Switch>
         </div>
         );
        };

        譯者注

      1. SSR服務端React組件的生命周期不會運行到componentDidMount,componentDidMount只有在客戶端才會運行。
      2. React16不再推薦使用componentWillMount方法,應使用constructor來代替。
      3. staticContext的實現應該跟redux的高階組件connect類似,也是通過包裝一層react控件來實現子組件的屬性傳遞。
      4. 文章只是對SSR做了一個入門的介紹,如Loadable和樣式的處理在文章中沒有介紹,但這兩點對于SSR來說很重要,以后找機會寫一篇相關的博文
      5. 原文地址

        聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

        利用React Router4實現的服務端直出渲染(SSR)

        利用React Router4實現的服務端直出渲染(SSR):我們已經熟悉React 服務端渲染(SSR)的基本步驟,現在讓我們更進一步利用 React RouterV4 實現客戶端和服務端的同構。畢竟大多數的應用都需要用到web前端路由器,所以要讓SSR能夠正常的運行,了解路由器的設置是十分有必要的 基本步驟 路由器配置 前
        推薦度:
        標簽: ssr 服務端 React
        • 熱門焦點

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top
        主站蜘蛛池模板: 亚洲精品色在线网站| 亚洲同性男gay网站在线观看| 色偷偷噜噜噜亚洲男人| 免费无码又爽又刺激高潮的视频| 91亚洲精品自在在线观看| 在免费jizzjizz在线播| 亚洲另类视频在线观看| av大片在线无码免费| 亚洲人成77777在线播放网站不卡 亚洲人成77777在线观看网 | 亚洲午夜久久久久久尤物| 国产精品1024永久免费视频| 亚洲成a人片在线观看中文app | 成人免费午夜无码视频| 亚洲精品国产国语| 日韩免费视频播放| 一级毛片免费在线| 日韩亚洲人成在线综合日本| 亚洲精品视频免费在线观看| 亚洲一级毛片视频| 国产极品粉嫩泬免费观看| 免费人成网站永久| 亚洲成AV人片在| 麻豆最新国产剧情AV原创免费| 亚洲欧美日韩中文字幕在线一区| 永久黄网站色视频免费观看| 免费国产黄网站在线看| 自拍偷自拍亚洲精品情侣| 免费A级毛片av无码| 亚洲人成网站18禁止| 免费大黄网站在线观| a毛片免费全部在线播放**| 亚洲国产精品乱码在线观看97| 国产美女无遮挡免费视频网站 | 99re热免费精品视频观看| 亚洲av永久中文无码精品| 亚洲国产综合无码一区| 青青青免费国产在线视频小草| 国产亚洲视频在线观看| 日本久久久久亚洲中字幕| 国产免费人成视频在线观看| 日韩免费视频一区二区|