react 服务端的改造

服务端实现 首屏渲染;

首先服务端要先支持es6;

cnpm i --save babel-cli

安装 babel包 先 ;

"scripts": {
    "start": "node scripts/start.js",
    "build": "node scripts/build.js",
    "test": "node scripts/test.js --env=jsdom",
    "server": "NODE_ENV=test  nodemon  --exec babel-node server/server.js"
  },

server命令中把 原来的node 的nodemon  用--exec 替换成  babel-node  就可以了;

NODE_ENV=test 是说 node环境是测试;

 

现在node 环境就支持es6了 

 

然后服务端要支持jsx;

在package.json文件里把babel的值粘贴出来

   {
    "presets": [
      "react-app"
    ],
    "plugins": [
      [
        "import",
        {
          "libraryName": "antd-mobile",
          "style": "css"
        }
      ],
      "transform-decorators-legacy"
    ]
  }

在最外层新建 .babelrc文件 ,把上面的部分粘贴到到该文件 ,node中就支持jsx了;

 

如果你通过 <script> 标签加载React,这些高阶API可用于 ReactDOMServer 全局。

如果你使用ES6,你可以写成 

import ReactDOMServer from 'react-dom/server'

。如果你使用ES5,你可以写成 

var ReactDOMServer = require('react-dom/server')

 

这里我们

import ReactDOMServer from 'react-dom/server'

这个ReactDOMServer 里有2个方法;

一般都是用renderToString的;

这个方法可以把 jsx生成的的dom 编译成 node可以执行的 dom ;

function App() {
    return <div>hello world!!!!</div>
}

renderToString(<App></App>);

就变成了<div data-reactroot="">hello world!!!!</div> 这样 node就可以在首屏展示了;

我们把原来的 在首屏用的代码

//利用中间件拦截路由 改变最终执行路径;
app.use(function (req,res,next) {
    //定义路由 白名单 ,把静态资源放置进去;
    //如果url路径是/user或者/static 说明是 用户的请求 或者静态的资源,这样就还是执行原来的下一个方法
    if(req.url.startsWith("/user/")||req.url.startsWith("/static/")){
    return next();
    }

    const html=ReactDOMServer.renderToString(<App></App>)
   
    return res.send(html);

     //否则就去加载 静态资源路径了;
  // return res.sendFile(path.resolve("build/index.html"));
});

把原来首屏加载文件的

return res.sendFile(path.resolve("build/index.html"));

改为 

res.send(html);

这样就完成了首屏显示;

下面来改写之前的项目;

把原来的index的公用部分重新抽离出来一个app.js

import  React from "react";
import {Route,Switch} from "react-router-dom"
import Login from "./container/login/login"
import Register from "./container/register/register"
import AuthRoute from "./container/authRoute/AuthRoute"
import Bossinfo from "./container/bossinfo/bossinfo"
import Niureninfo from "./container/niureninfo/niureninfo"
import DashBoard from "./container/dashboard/dashboard"
import Chat from "./container/chat/chat"
class App extends React.Component{
    render(){
        return(
            <div>
                <AuthRoute/>
                <Switch>
                    <Route path="/niureninfo" component={Niureninfo}/>
                    <Route path="/bossinfo" component={Bossinfo} />
                    <Route path="/login" component={Login}/>
                    <Route path="/register" component={Register} />
                    <Route path="/chat/:user" component={Chat} />
                    <Route component={DashBoard}/>
                </Switch>
            </div>
        )
    }
}

export  default  App;

然后 index 就成了一个 比较独立的存在 

import  React from "react"
import  ReactDom from "react-dom"
import  {createStore,applyMiddleware,compose} from "redux";
import  thunk from "redux-thunk"
import {Provider} from "react-redux"
import {BrowserRouter} from "react-router-dom"
import reducers from  "./reducers"
import App from "./app"
import "./config"
import "./app.css"

const store=createStore(reducers,compose(applyMiddleware(thunk),
    window.devToolsExtension?window.devToolsExtension():()=>{}
));

    ReactDom.render(
        <Provider store={store}>
            <BrowserRouter>
              <App/>
            </BrowserRouter>
           </Provider>
        ,document.getElementById("root"))


在server.js 去  把provider相关的全部 写到那个 中间件 里输出出来;  上面该定义的定义,该引入的引入;

import  {createStore,applyMiddleware,compose} from "redux";
import  thunk from "redux-thunk"
import {Provider} from "react-redux"
import {StaticRouter} from "react-router-dom"
import reducers from  "../src/reducers"
import App from "../src/app"
import "../src/config"
import "../src/app.css"
//利用中间件拦截路由 改变最终执行路径;
app.use(function (req,res,next) {
    //定义路由 白名单 ,把静态资源放置进去;
    //如果url路径是/user或者/static 说明是 用户的请求 或者静态的资源,这样就还是执行原来的下一个方法
    if(req.url.startsWith("/user/")||req.url.startsWith("/static/")){
    return next();
    }

    //如果路由有跳转可以告诉我们路由有没有跳转;
    const context={};
    const store=createStore(reducers,compose(applyMiddleware(thunk) ));
    const markUp=ReactDOMServer.renderToString(
        (
            <Provider store={store}>
                <StaticRouter
                context={context}
                location={req.url}
                >
                    <App/>
                </StaticRouter>
            </Provider>

        )
    );
  //  const html=ReactDOMServer.renderToString(<App></App>);
    return res.send(markUp);
     //否则就去加载 静态资源路径了;
  // return res.sendFile(path.resolve("build/index.html"));
});

其中要注意 的是 BrowserRouter 要改为服务端的 StaticRouter;并且要加入两参数 location  ,context;

              <StaticRouter
                context={context}
                location={req.url}
                >
                    <App/>
                </StaticRouter>

context是 用来 告诉我们路由有没有跳转的;

这个时候会报错 ,因为css和图片 在后端没有办法加载, 需要挂载;

先安装 工具包

cnpm i --save css-modules-require-hook

去这地址http://npm.taobao.org/package/css-modules-require-hook

找到  Using with babel-node / ES6 Imports  这行

下面的代码就是我们要用的:

在server.js引入

// server.js
import csshook from 'css-modules-require-hook/preset' // import hook before routes

这句代码要放到 引入app之前 ,    

在最外层编写配置文件 cmrh.conf.js

// cmrh.conf.js
module.exports = {
  // Same scope name as in webpack build
  generateScopedName: '[name]__[local]___[hash:base64:5]',
}

依然会报引入图片的错误;继续安装包;

cnpm i --save asset-require-hook

然后装包成功以后;

打开地址https://github.com/aribouius/asset-require-hook

然后在server.js引入 

//asset-hook.js
import assethook from "asset-require-hook";

然后在下面执行一下(放到引入app之前);

assethook({
    extensions: ['jpg']
});

 

然后还需要真个页面的骨架 ,

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
  <meta name="theme-color" content="#000000">
 
  <title>React App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>

然后 把 原来写的markUp 放到 root的div里;

  const page = `<!DOCTYPE html>
    <html lang="en">
     <head>
        <meta charset="utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1, 
               maximum-scale=1, minimum-scale=1, user-scalable=no" />
             <meta name="theme-color" content="#000000">
                <link rel="stylesheet" href="">
                  
                <title>51job</title>
        </head>
       <body>
         <div id="root">${markUp}</div>
        </body>
    </html>`;

引入 原来生成的那个目录;build文件夹下asset-manifest.json文件;

import assetmanifest from "../build/asset-manifest"

引入该文件  css,js文件 ;

  <link rel="stylesheet" href=${assetmanifest["main.css"]}>
<script src=${assetmanifest["main.js"]}></script>

 

const page = `<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
            <meta name="theme-color" content="#000000">
                <link rel="stylesheet" href=${assetmanifest["main.css"]}>
                  
                        <title>51job</title>
    </head>
    <body>
    <div id="root">${markUp}</div>
    </body>
    <script src=${assetmanifest["main.js"]}></script>
    </html>`;

 

至此 ,大功告成。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值