客户端渲染(CSR)和服务端渲染(SSR)
CSR是什么流程?
1. 客户端(浏览器)请求服务器,服务器返回静态html文件,css,js文件等;
2. 客户端解析html静态文件,并下载css和js文件进行解析;
SSR是什么流程?
1. 服务器通过模板引擎解析html,返回给用户端客户端html字符串。
2. 客户端解析html字符串,读取静态js文件,然后进行解析;
CSR缺点
学过react的小伙伴都知道,react可以用来快速开发单页面和多页面应用。但是这样做出来的网站有些缺点
- seo问题
什么是seo问题呢?seo称为搜索引擎优化,react客户端渲染做出来的网站整个网站内部标签只有一个#root,还有一大堆的js文件,搜索引擎在爬取你的网站的时候会很难抓取到你网站的关键信息,虽然很多这样的网站多做了meta标识,降低了搜索引擎爬取的难度,但是这指标不治本。 - 首屏加载问题
我们都知道单页面应用就只有一个root标签,浏览器加载js的时候会会消耗比较长的时间,在这个期间用户会等待很长,这个就被称为首屏加载问题。
回到主题,我们来具体了解一下react服务端渲染是怎么实现的,这需要我们自己配置webpack,所以需要你掌握webpack相关的基础知识。我们的基本项目目前为止需要安装的包有:
- webpack配置
因为我们是服务端渲染,所以我们需要两套webpack.config.js文件,一套解析客户端的代码,一套解析服务端的代码(根据配置文件创建文件夹和文件)。当然我这里配置文件只会处理js,所以不必配置很多laoder。- webpack.base.js
这里需要安装几个包,建议安装最新版,不过向后兼容也可以.。
module.exports = { module: { rules: [ { test: /\.jsx?$/, exclude: '/node_modules/', loader: 'babel-loader', options: { presets: ["@babel/react", ['@babel/env', { targets: { browsers: ['last 2 versions'] } }]] } } ] } };
- webpack.client.js
这里需要安装webpack-merge包,这是一个合并多个webpack.config.js的包
const path = require('path'); const merge = require('webpack-merge'); const baseConfig = require('./webpack.base'); const clientConfig = { entry: './src/client/index.js', mode: 'development', output: { filename: 'index.js', path: path.resolve(__dirname, 'public'), } }; module.exports = merge(baseConfig, clientConfig);
- webpack.server.js
这里需要安装webpack-node-externals包,这是一个将引用到的node_modules下面的包也打包到文件里面,因为我们后台用的express框架,不然的话我们在打包之后express框架就被排除了。
const path = require('path'); const path = require('path'); const nodeExternals = require('webpack-node-externals'); const merge = require('webpack-merge'); const baseConfig = require('./webpack.base'); const serverConfig = { target: 'node', entry: './src/server/index.js', mode: 'development', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'build'), }, externals: [nodeExternals()], }; module.exports = merge(baseConfig, serverConfig);
- webpack.base.js
- .babelrc文件配置
babelrc文件是必须的,位于和webpack.base.js同级目录下,由于我们会写装饰器写法,所以我这里也配置了装饰器写法相关的babel配置
{
"presets": [
"@babel/preset-env"
],
"compact" : true,
"plugins": [
"@babel/plugin-transform-runtime",
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
],
[
"@babel/plugin-proposal-class-properties",
{
"loose": true
}
]
]
}
文件建好之后,我们看下我们的项目目录
需要安装的包在配置文件里面都可以查看到,若报错根据报错信息再安装相应的包。除此之外我们还需要安装express(后台node框架),react,react-dom
相当重要的一点,提高效率的方式
这个package.json文件的配置比较重要,你不这样配置的话,需要每次都要build,每次都要start。而用上了上面的配置,我们只需要每次启动的时候输入yarn dev 或npm run dev。这部分需要安装的包有:nodemon、npm-run-all
进入主题
前面我们在webpack.server.js中配置了服务器端的react代码的入口文件是
./src/server/index.js
我们需要新建server文件夹,新建index.js,具体的代码如下图。
import express from 'express'
import React from 'react'
const app = express();
// 这里的*是因为后面我们需要做路由,服务器端不可能自己新建多个路由,所以直接 * 完事儿
app.get('*', (req, res) => {
const content = 'hello world , this is the first SSR demo'
const html = `
<!doctype html>
<html lang="en">
<head>
<title>hello</title>
<body>
<!-- 这里的#root由于后面我们需要单页面应用的渲染,就在这里加上 -->
<div id="root">${content }</div>
<script src="/index.js"></script>
</body>
</head>
</html>`
res.send(html ) // 调用express框架的res.send方法,想客户端返回html字符串,
// 若出现跨域情况,可用res.jsonp()代替,
// 这个方法大家可以平时进行mock数据时可以用到
});
app.listen('3000');
这样一个小小的ssr demo就完成了,你可以尝试用yarn dev启动项目,然后在3000端口进行访问
怎么渲染组件呢?
通过上一个demo可以看到ssr服务器渲染好的html字符串已经可以在客户端运行,而且你会发现打开速度非常快,这是因为我们服务端解析的是字符串而不是一个庞大的js文件。
那么有些同学就会问了,那我们怎么渲染组件呢?
- 新建组件
在src目录下新建一个pages的文件夹,然后再新建一个home文件夹,再home文件夹里面新建一个index.js,具体代码如下,该组件可以写成无状态组件。
import React, {Component, Fragment} from 'react'
export default class Home extends Component {
render() {
return (
<Fragment>
this is SSR dell!
</Fragment>
)
}
}
- 引入组件
在我们之前写好的server下面的index.js添加如下代码
import Home from '../pages/home'
import {renderToString} from 'react-dom/server'
app.get('*', (req, res) => {
// 渲染成字符串,这个方法会讲组件渲染成一个字符串,
// 注意:这个组件上面的事件是不会被渲染上去的,因为事件渲染成字符串之后被省略掉了,
// 这个问题我们下一章解决
const home = renderToString(
<Home />
);
})
const html = `
....
<div id="root">${home}</div>
`
好了今天的ssr学习就学习完了