二、koa+React服務端渲染:添加路由

目標

上一次做了一個簡單的hello world,這個我給單頁面應用加入路由,這里我用的是react-router v4。這里為了簡單就只設置兩個頁面,一個home頁面,一個about頁面

項目設置

安裝路由包

npm install react-router react-router-dom --save

編寫應用

  1. 創(chuàng)建home和about組件
    <root>/app/web/component/home/home.js
import React from 'react';

const Home = () => {
  return (
    <div>Home</div>
  );
};

export default Home;

<root>/app/web/component/about/about.js

import React from 'react';

const About = () => {
  return (
    <div>About</div>
  );
};

export default About;
  1. 設置瀏覽器端路由
    <root>/app/web/component/app/admin.js
import React from 'react';
import { Route, Link } from 'react-router-dom';
import Home from '../home/home';
import About from '../about/about';

const App = ({ msg }) => {
  return (
    <div>
      <div>Hello { msg }</div>
      <ul>
        <li>
          <Link to="/admin">Home</Link>
        </li>
        <li>
          <Link to="/admin/about">About</Link>
        </li>
      </ul>
      <hr />
      <Route exact path="/admin" component={Home} />
      <Route path="/admin/about" component={About} />
    </div>
  )
};

export default App;

這樣當訪問/admin的時候就會渲染home組件,訪問/admin/about就會渲染about組件
然后我們期望瀏覽器端是使用H5的history API實現路由,所以我們使用BrowserRouter,修改
<root>/app/web/page/browser/admin.js

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';

import AdminApp from '../../component/app/admin';

ReactDOM.hydrate((
  <BrowserRouter {...window.__STATE__}>
    <AdminApp {...window.__STATE__}/>
  </BrowserRouter>
), document.getElementById('root'));
  1. 設置服務端路由
    首先,我們要確保當瀏覽器訪問/admin 和 /admin/about這樣的以/admin開頭的路由的時候,服務器能夠渲染出頁面。也就是說,當訪問/admin/about的時候,服務器不能返回404。我們修改
    <root>/app/router.js
const admin = require('./controller/admin');

module.exports = app => {
  const { router } = app;
  // 匹配所有/admin開頭的url
  router.get('/admin(.*)', admin.admin);
};

/admin(.*) 代表所有訪問以/admin開頭的url時,都會被admin這個controller處理。這樣就不會返回404了。
當然,光是不反回404還是不夠的,我們需要react根據url渲染出對應的html文本,我們還需要修改
<root>/app/web/page/server/admin.js

import React from 'react';
import { StaticRouter } from 'react-router-dom';

import AdminLayout from '../../component/layout/admin_layout';
import AdminApp from '../../component/app/admin';

const server = context => {
  return (
    <AdminLayout state={context}>
      <StaticRouter {...context} location={ context.url } >
        <AdminApp {...context} />
      </StaticRouter>
    </AdminLayout>
  )
};
export default server;

上面代碼關鍵點在于給StaticRouter傳入了一個context.url,但是目前為止,我們還沒有再服務端傳入這個屬性,所有需要修改
<root>/app/middleware/react_view.js

const assert = require('assert');
const path = require('path');
const fs = require('fs');
const ReactDOMServer = require('react-dom/server');

const defaults = {
  view: path.resolve(process.cwd(), 'view'),
  extname: 'js'
};

module.exports = (options, app) => {
  options = options || {};
  options = Object.assign({}, defaults, options);
  assert(typeof options.view === 'string', 'options.view required, and must be a string');
  assert(fs.existsSync(options.view), `Directory ${options.view} not exists`);
  options.extname = options.extname.trim().replace(/^\.?/, '.');
  app.context.render = function (filename, _context) {
    if (!path.extname(filename)) {
      filename += options.extname;
    }
    let filepath = path.isAbsolute(filename) ? filename : path.resolve(options.view, filename);
    const context = Object.assign({}, this.state, _context);
    // 添加 url 屬性
    if (!context.url) {
      context.url = this.url;
    }
    try {
      let view = require(filepath);
      view = view.default || component;
      this.body = ReactDOMServer.renderToString(view(context));
      this.type = 'html';
    } catch (err) {
      err.code = 'REACT';
      throw err;
    }
  }
};

目前為止,路由就加上了,執(zhí)行命令

npm run build && npm run start

在瀏覽器輸入http://localhost:3000/admin應該能看到

輸入http://localhost:3000/admin/about應該能看到

項目地址:https://github.com/leitc/isomorphic-react/tree/0.2

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現,斷路器,智...
    卡卡羅2017閱讀 136,711評論 19 139
  • 環(huán)境介紹 很久之前有個java項目,后來由于調整將這個項目的前端給調沒了,就剩下數據庫,所以之后添加賬號都是用my...
    Lee_M閱讀 3,626評論 0 6
  • 難得亞錦賽在無錫,剛好師姐也要去看,我也就去了。本來有一張昨天的票,一張今天的票,打算放棄的是今天。結果剛好醬油來...
    好夢不遙遠閱讀 421評論 6 2

友情鏈接更多精彩內容