小哥哥, React SSR 要不要了解下-實用篇

上一篇文章中咱們實現了一個簡單的 hello world, 這一節咱們繼續完善咱們的項目. 做爲實用篇本篇將會添加 react-router reduxhtml

並經過 react-helmet 實現自定義的 meta 標籤, 完善 SEO. 這個實在是不想寫了. 下篇吧...前端

加入路由暢遊皇冠賭場

客戶端渲染配置 react-router

來到皇冠賭場的你們那確定是丈二的和尚, 摸不着頭腦呀. 那麼路由就應運而生了, 關於路由的原理建議你們看看這篇文章.node

9150e4e5gy1fznxtqxc8sg206y06yab7

若是你看了還回來了, 那說明仍是咱們澳門 XXXX 更加的有意思 😹.react

談到賭場無非就是這老四樣, 抓牌, 看牌, 洗牌, 碼牌~ios

2019-07-16-17-28-14

那麼咱們就開始, 建立幾個頁面. 頁面的代碼結構以下圖所示.git

2019-07-16-17-30-43

爲了便於各個 level 的小夥伴理解, 這裏無恥的運用了拼音命名法. 代碼改動在這裏github

其次, 安裝 react-router-dom 依賴, 並修改 App.jsx 和 client.js 文件, diff 在這裏web

修改後的 App.jsx 文件面試

import React from 'react';
// 從 react-router-dom 引入基礎組件
import { NavLink, Switch, Route } from 'react-router-dom';

// 引入皇冠賭場的頁面
import Home from './Home.jsx';
import Zhuapai from './Zhuapai.jsx';
import Kanpai from './Kanpai.jsx';
import Xipai from './Xipai.jsx';
import Mapai from './Mapai.jsx';

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  render() {
    return (
      <div className="ssr-show">
        <h1>歡迎來到澳門皇冠賭場</h1>

        <NavLink to="/">首頁</NavLink>
        <NavLink to="/Zhuapai">抓牌</NavLink>
        <NavLink to="/Kanpai">看牌</NavLink>
        <NavLink to="/Xipai">洗牌</NavLink>
        <NavLink to="/Mapai">碼牌</NavLink>

        <Switch>
          <Route path="/" component={Home} />
          <Route path="/Zhuapai" component={Zhuapai} />
          <Route path="/Kanpai" component={Kanpai} />
          <Route path="/Xipai" component={Xipai} />
          <Route path="/Mapai" component={Mapai} />
        </Switch>
      </div>
    );
  }
}
複製代碼

修改後的 client.js 爲ajax

import React from 'react';
import ReactDOM from 'react-dom';
// 從 react-router-dom 裏邊導入 BrowserRouter 組件
import { BrowserRouter } from 'react-router-dom';
import App from './components/App.jsx';

// 包裝一下 App 組件
ReactDOM.render(
  <BrowserRouter> <App /> </BrowserRouter>,
  document.getElementById('app'),
);
複製代碼

目前爲止, 路由就算是配完了. 執行 npm run build:client 後, 用瀏覽器打開 index.html 文件.

2019-07-15-16-03-01

成功就在眼前, 可是不免有一點小小的障礙~

這個報錯的大體意思就是, 本地的文件不能用 react-router, 那麼咱們只能把它放到一個服務器上了. 仍是咱們的老夥伴 --- live-server 直接執行 live-server ./dist

瀏覽器打開 localhost:8080

react-router-err

咱們不難發現, 點擊連接的時候瀏覽器地址欄有變化, 可是咱們並不能體驗到從發牌到碼牌的一條龍"服務"...

看了下 react-router 的 官方文檔 原來咱們沒有指定路徑匹配必須得精準匹配. 那麼咱們加上 exact 屬性試試咧~

react-router

此時的代碼 diff

到目前爲止, 我咱們已經能夠暢遊澳門皇冠賭場了, 抓牌看牌洗牌碼牌樣樣精通~

配置服務端渲染的 react-router

輕鬆搞定了客戶端渲染的 react-router, 服務端渲染的話那就更加的簡單了.

  • 首先確定是要引入 react-router-dom 依賴的(ps: ssr 須要導入的是 StaticRouter)
    2019-07-27-09-21-30
  • 建立路由上線文對象, 並獲取到當前用戶訪問的路徑 path
    2019-07-27-09-22-59
  • 最後, 利用第一步導入的 StaticRouter 組件包裹一下以前生成的服務端渲染組件
    2019-07-27-09-24-17

代碼diff

最後的最後, 咱們執行一下 node index.js, 瀏覽器打開 localhost:9999

react-router-ssr

經過 gif 咱們能發現, 咱們的服務端渲染是貨真價實的服務端渲染了. 查看源代碼的 html 字符串沒有任何的問題了.(若是有樣式該咋辦呢 🤔)

細心的同窗可能發現了, 咱們每次點擊連接的時候頁面都會總體刷新. 這裏就又到了那個經典的面試題前端:你要懂的單頁面應用和多頁面應用, 咱們的目的很簡單, 只是須要 ssr 實現首屏的渲染, 以後就由客戶端接管, 這樣就結合了二者的優勢. 需求是有了, 怎麼實現咧~

====================

2019-07-27-09-38-00

====================

聰明的同窗已經猜到, 只要咱們咱們在服務端渲染的頁面中也引入客戶端渲染生成的 bundle.js 文件是否是就 OK 了呢.那咱們就試試咯

2019-07-27-09-41-08

修改 server.js 引入 koa-static 用於託管靜態文件, 文件總體 diff 以下:

2019-07-27-09-43-36

廢話少扯, 繼續 node index.js, 瀏覽器打開 localhost:9999 搞起~

react-router-dont-reload

服務端渲染的路由配置, 這就完成了~

9150e4e5gy1g53l5pua7og208c08c0tr

配置 redux, 帶上身份暢玩澳門皇冠

客戶端渲染引入 redux

因爲配置 redux 不是咱們的重點, 因此這裏就很少說了, 簡單配置一個 redux 開發環境. 代碼 diff

有疑問的問題, 評論區見~

2019-07-27-12-26-06

ssr 引入 redux

修改 server.js 文件以下:

import path from 'path';
import Koa from 'koa';
import Router from 'koa-router';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import serve from 'koa-static';

// 像客戶端渲染同樣導入 Provider 組件
import { Provider } from 'react-redux';
import App from './components/App.jsx';
import createStore, { init } from './store';

const app = new Koa();
const router = new Router();

const conf = {
  PORT: 9999,
};

const generateHtmlStr = reactDom => `
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>

    <div id="app">${reactDom}</div>
    <script src="/dist/bundle.js"></script>
</body>
</html>
`;

router.get('*', (ctx) => {
  const context = {};
  const { url } = ctx.req;

  // 初始化一個 store
  const store = createStore();

  // 手動觸發一下 init
  store.dispatch(init());
  // 首先把 React 組件變成一個字符串
  // eslint-disable-next-line
  const rNode = renderToString(
    // 把剛剛建立的 store 做爲屬性傳給 Provider 組件
    <Provider store={store}>
      <StaticRouter location={url} context={context}>
        <App />
      </StaticRouter>
    </Provider>,
  );
  // 而後替換 template 裏邊的內容
  const domString = generateHtmlStr(rNode);

  // 最後返回 html 字符串
  ctx.body = domString;
});

app.use(serve(path.resolve(__dirname, '../')));
app.use(router.routes(), router.allowedMethods());

app.listen(conf.PORT, () => {
  console.log(`The Server is listening on ${conf.PORT} now, enjoy`);
});
複製代碼

代碼 diff 以下:

2019-07-27-17-32-51

仍是那一套, 先 node index.js 再瀏覽器打開 localhost:9999~

2019-07-27-17-37-54

瀏覽器執行結果以下圖:

redux-ssr

細心的同窗不難發現, 這個圖片中抓牌的入口老是會閃動一下, 理論上講咱們執行了 store.dispatch(init()); 證實了用戶是已經登陸的用戶, 因此應該是能夠抓牌的纔對. 這是問啥呢???

2019-07-27-17-46-45

其實緣由很簡單, 咱們的項目在首屏渲染完成之後就有客戶端渲染接管了, 因此咱們應該在客戶端接管的時候把以前服務端渲染的數據保留下來. 怎麼搞呢?

  • 首先, 獲取首屏狀況下的狀態
  • 其次, 升級 server.js 文件, generateHtmlStr 中添加一個參數, 表示當前的狀態
  • 再次, 更新 html 模板, 在 window 下建立一個 REDUX_DATA 屬性, 用於放置服務端渲染時首屏的全局狀態
  • 最後, 升級客戶端相關的代碼, 在 createStore 的時候把 REDUX_DATA 做爲參數傳入. 也正所以, 定義 REDUX_DATA 要放在引入 bundle.js 的上方.

這就完了, 經過下邊的 gif 能夠看出, 狀態很好的保存下來了~

redux-ssr-goog

到了這裏, 可能有同窗會問, 咱們來到皇冠賭場, 全局狀態確定會灰常的多, 不該該只有一個登錄狀態. 鑑於此, 咱們擴充一下全局狀態. 添加一個音樂列表. 一遍歡歌一遍看牌~

下來咱們在看牌頁面添加一個音樂列表, 咱們聽着音樂看着牌, 要是再吃着火鍋那簡直就是人生巔峯了.

2019-08-16-10-16-02

在 store 中獲取異步數據

  • 首先, 咱們建立一個 mock.js, 主要就是訪問異步接口獲取歌曲列表
import axios from 'axios';

export default () => axios('https://music.niubishanshan.top/api/v2/music/toplist')
  .then(({ data }) => data);

// 在 mock.js 中咱們引用了高端的 ajax 請求庫 axios. 那麼就不得不 npm i axios -S 啦
複製代碼
  • 其次, 升級 store.js 添加歌曲相關的 actions 和 reducer.
    2019-08-16-11-31-25
  • 因爲咱們有以前的單 reducer 升級成了兩個 reducer, 此時要同步升級以前鏈接過 redux 的組件 Header Home Zhuapai
  • 最後, 在看牌頁面, 咱們引入音樂. party Time
    2019-08-16-11-40-48

到目前爲止, 代碼是 這樣 的.

廢話少說...

error-kanpai

1565938746

臥槽, 竟然很差使...

仔細看一下, 原來是 action 裏邊不能包含異步. 這個簡單. 咱們升級一下.

而後...

ok-kanpai

服務端渲染支持首屏展現初始異步數據

有的時候, 咱們服務端渲染的首屏網頁也須要從其餘異步接口來獲取初始化數據. 此時就須要吧 html 返回給前端前先去訪問異步接口. 那麼怎麼辦咧...

最後...

11a88f499af3f3b5d80b756fd

polyfill-error

這裏建議你們複習一下 polyfill 和 preset 的區別

反正我就這麼幹了一把

而後就...

polyfill-ok

是否是能夠痛快的玩耍啦 😺~

42cdf9611d1efb032b2b5b396
相關文章
相關標籤/搜索