express+react+SSR 同构和动态页面渲染

267 阅读1分钟

结合create-react-app和上一章静态页面babel进行配置。

目录结构

image.png

依赖配置

package.json

{
  "name": "react_ssr",
  "version": "0.1.0",
  "private": "true",
  "dependencies": {
    "babel-node": "0.0.1-security",
    "babel-plugin-transform-decorators-legacy": "^1.3.5",
    "babel-preset-env": "^1.7.0",
    "babel-preset-react": "^6.24.1",
    "express": "^4.16.4",
    "react": "^16.6.3",
    "react-dom": "^16.6.3",
    "react-scripts": "2.1.1"
  },
  "devDependencies": {
    "cross-env": "^5.2.0"
  },
  "scripts": {
    "server": "cross-env NODE_ENV=test nodemon --exec babel-node src/server.js",
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browsersList": [
    ">0.2%",
    "not dead",
    "not ie <=11",
    "not op_mini all"
  ],
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ]
}

.babelrc

{
    "presets": [
        "env",
        "react"
    ],
    "plugins": [
        "transform-decorators-legacy"
    ]
}

页面渲染

/src/index.js

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

/src/App.js

import React, { Component } from 'react'
export default class App extends Component {
    sayHello(){
        alert('Hello!')
    }
    render() {
        return (
            <div>
                <button onClick={this.sayHello}>hello</button>
            </div>
        )
    }
}

/src/server.js

import express from 'express'
import App from './App'
import React from 'react'
import {renderToString} from 'react-dom/server'
import fs from 'fs'
var app=express()
app.get('/',function(req,res){
    const html=fs.readFileSync('./build/index.html')
    const content=renderToString(<App/>)
    res.send(html.toString().replace(`<div id="root"></div>`,`<div id="root">${content}</div>`))
})
app.use('/',express.static('build'))
app.listen(3002,function(){
    console.log('listening on 3002!')
})

最后npm run server启动服务器

如果对APP组件里有修改,需要重新build,再npm run server启动服务器。会对比之前一个版本增加script标签。

对于不同写法的state和函数表示方式,配置会有不同,以上试用于state写在constructor里的情况。

如果state写在外面,安装@babel/plugin-proposal-class-properties,修改.babelrc

{
  "presets": ["@babel/preset-env", "@babel/preset-react"],
  "plugins": ["@babel/plugin-proposal-class-properties"]
}

下面state写法属于ES7语法,目前浏览器还不支持,需要安装@babel/plugin-proposal-class-properties,并在.babelrc中plugins属性中配置。

import React from 'react'
export default class App extends React.Component {
  state = {
    count: 0
  }

  render() {
    return <div>{this.state.count}</div>
  }
}