服务端实现 首屏渲染;
首先服务端要先支持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>`;
至此 ,大功告成。