react服务端渲染(1)

使用react这么久 ,都是基于客户端渲染,闲来无事,想去玩玩基于react的服务器端渲染。

客户端渲染的问题:

  • 用户初次访问的体验不好
  • 对 SEO 不友好

客户端渲染的页面通常是很简洁:

<!doctype html>
<html>
  <head>
    <title>这是一个单页面应用</title>
  </head>
  <body>
    <div id='root'></div>
    <script src='app.js'></script>
  </body>
</html>

访问该页面,浏览器便开始解析 HTML 并加载 app.js 文件,加载完成浏览器执行 js,接着 <div id='root'></div> 下会生出许多节点,并且具备交互能力。

但是这里会有两个问题:

  • 在 js 文件加载完成并执行以前,我们只看到一片空白,
  • 一旦 js 文件中有错误,也可能导致页面空白

无论哪种情况,对用户来说,都是糟糕的体验。

而服务端渲染能改善用户的体验:

  • 用户能马上看到页面内容 - 而不是等待 js 文件下载、执行完成后才能看到,
  • 哪怕 js 中有错误,也只会导致页面交互问题,而不是一片空白。

在seo层面,我们希望搜索引擎能够抓取完整的HTML文件,而不是上述中的<div id='root'></div>,服务器端渲染则能够提供完整的 HTML 内容给搜索引擎,对 SEO 更友好。

服务器端渲染流程

传统的服务器端渲染大致是一个这样的流程:

  1. 从后台数据中读取信息
  2. 编译模板生成HTML代码
  3. 返回HTML给客户端
  4. 浏览器解析html并下载html页面中的脚本文件然后执行

React.js的服务器端渲染也是如此,只不过这里的模板换成了react组件,js事件的绑定由React.js 操作。

服务器端如何渲染React.js 

我们将使用 ReactDOMServer 提供的 renderToString 方法在服务器端渲染 React.js 组件。下面将简单的搭建一个基于React.js的服务器端渲染的小demo。

$ mkdir react-server-render

$ cd react-server-render

$ npm init -y

$ npm i react react-dom --save

在项目目录中新增index.html文件

<!doctype html>
<html>
  <head>
    <title>React 服务器端渲染</title>
  </head>
  <body>
    <div id='root'></div>
    <script src='dist/main.js'></script>
  </body>
</html>

然后在react-server-render 下新建一个 src 目录,在 src 下新建 index.js 以及 App.js,两个文件的内容分别如下:

index.js:

const React = require('react')
const ReactDOM = require('react-dom')
const App = require('./App')
ReactDOM.render(React.createElement(App), document.getElementById('root'))

App.js:

const React = require('react')
class App extends React.Component {
  constructor (props) {
    super(props)
    this.state = {
      color: false
    }
  }
  render () {
    const self = this
    return React.createElement('div', {
      onClick: function () {
        self.setState({
          color: !self.state.color
        })
      },
      style: {
        color: this.state.color ? 'red' : 'black'
      }
    }, 'hello react')
  }
}
module.exports = App

接下来我们需要webpack打包index.js。

$ npm install webpack webpack-cli --save-dev

接着运行 webpack:

$ npx webpack --mode development (webpack 4新做的改进)

现在访问index.html,我们就能看到客户端渲染出的内容了。

然后我们搭建一个简单的node.js的服务器,在 react-server-render 目录下新建一个 index.js 文件:

const http = require('http')
const server = http.createServer(function (request, response) {
  response.writeHead(200, { 'Content-Type': 'text/html' })
  response.write('hello react')
  response.end()
})
server.listen(3000)

执行 node index.js,我们就能在 http://localhost:4200 访问页面 - 看到写死的 hello react.js。

那要如何返回 html 文件?

也很简单,我们读取 html 文件并返回即可:

const http = require('http')
const fs = require('fs')
const path = require('path')
const server = http.createServer(function (request, response) {
  response.writeHead(200, { 'Content-Type': 'text/html' })
  fs.createReadStream(path.join(__dirname, 'index.html')).pipe(response)
})
server.listen(3000)

但上面的代码有一个问题,就是所有的请求都会返回 index.html 的内容,不管它是 / 还是 dist/main.js。我们需要优化上面的代码,区分不同的请求:

const http = require('http')
const fs = require('fs')
const path = require('path')
const server = http.createServer(function (request, response) {
  if (request.url === '/') {
    response.writeHead(200, { 'Content-Type': 'text/html' })
    fs.createReadStream(path.join(__dirname, 'index.html')).pipe(response)
  } else {
    if (/\.js/.test(request.url)) {
      response.writeHead(200, { 'Content-Type': 'application/javascript' })
      fs.createReadStream(path.join(__dirname, `${request.url}`)).pipe(response)
    } else {
      response.writeHead(404)
      response.end()
    }
  }
})
server.listen(3000)

好了,我们成功运行一个 Node.js 应用,它能够返回 html 文件,也能返回 JavaScript 文件。当然,大部分时候我们不需要写原生的 Node.js 代码,而是使用 Express.js 等成熟框架。同理,我们可以返回服务器端渲染的 React 代码,  我们要用到 ReactDOMServer 提供的 renderToString

const http = require('http')
const fs = require('fs')
const path = require('path')
const ReactDOMServer = require('react-dom/server')
const React = require('react')
const App = require('./src/App.js')
function renderHTML (body) {
    return `
  <!DOCTYPE html>
  <html lang="en">
  <head>
    <title>React 服务器端渲染</title>
  </head>
  <body>
    <div id="root">${body}</div>
    <script src="dist/main.js"></script>
  </body>
  </html>
  `
}
const server = http.createServer(function (request, response) {
    if (request.url === '/') {
        let body = ReactDOMServer.renderToString(React.createElement(App))
        response.writeHead(200, { 'Content-Type': 'text/html' })
        response.write(renderHTML(body))
        response.end()
    } else {
        if (/\.js/.test(request.url)) {
            response.writeHead(200, { 'Content-Type': 'application/javascript' })
            fs.createReadStream(path.join(__dirname, `${request.url}`)).pipe(response)
        } else {
            response.writeHead(404)
            response.end()
        }
    }
})
server.listen(3000)

如上,一个简单的基于react 的服务器端渲染已经完成。

通过控制台模拟Slow 3G的网络环境,可以明显发现,通过客户端渲染的时候,会产生白屏现象,而通过服务器端渲染,

无白屏现象。作为前端小白,在我理解的是,可以客户端渲染为主,服务器端渲染为辅,用服务器端渲染进行页面的首屏加载,这

样,不仅会加快渲染速度,也不会造成服务器端太大的压力。

参考:

https://reactjs.org/docs/react-dom-server.html

人真是懒惰的生物。好久没有认真的开始学习,生活好像没有了目标。

猜你喜欢

转载自blog.csdn.net/qq_34431912/article/details/81432109