需求分析
之前做react项目的时候,最早的首屏需要加载的大小在5M左右,也就导致了特别慢的访问速度,后来更改了webpack的配置,启动压缩,分离依赖等各种搞法弄完之后,还有有1M的大小需要加载。如果第一次进入页面,还需要加载依赖,大小大概在1.5M左右。
极慢的首屏访问速度严重影响了用户体验,抵消了那次改版的所有优点,后由于我因为某些愿意离职,也就没有再关注过这方面了,之后入职一直在从事react-native,也一直没有搞过这方面了,赶上最近比较闲,打算用服务端渲染的方法重写一下我个人的小网站,练练手的同时也丰富一下网站的内容,当然丰富的内容就不在这个笔记里写了,这里主要写服务端渲染。
项目搭建
首先我们来搭建一个最简单的项目,大概需要的babel,webpack,express,react,另外还需要一个零零碎碎的依赖,具体可以再github里看,会在最后面放上我的仓库地址。
首先建立一个项目
npm init -Y
然后新建一个webpack文件
var path = require('path')
var webpack = require('webpack')
module.exports = {
entry: {
main: './app/index.js'
},
output: {
filename: '[name].js',
path: path.join(__dirname, 'dist'),
publicPath: '/'
},
resolve: {
extensions: ['.js', '.jsx']
},
module: {
rules: [
{
test: /\.jsx?$/,
loaders: ['babel-loader'],
}
]
}
}
这个就是一个最最基本的webpack设置了,然后我们在package文件里添加
"scripts": {
"start": "nodemon index.js",
"build": "webpack",
},
这样写之后运行
npm run start
是启动express服务,而执行
npm run build
是执行react的打包命令。
为什么这个东西需要分开呢?因为react的服务端渲染是在服务端渲染首屏,但是由webpack打包出来的js文件和spa模式下的react项目并没有区别,准确说是在运行的模式上没什么区别,但是具体写法上还有些许的不同。
react的ssr在服务端渲染出来的html是没有任何的事件的,还需要webpack打包出来的js再次绑定事件,这方面的东西一定要注意,很容易出现bug,并且出现了不好分析。
在webpack里写了入口和出口,所以下面我们就要建立入口文件和出口文件夹。
我们建立一个名字叫app的文件夹,里面存放react的内容。在app的文件夹内建立一个index.js的文件作为webpack打包的入口文件
import Hello from './hello'
import ReactDOM from 'react-dom'
import React from 'react'
ReactDOM.hydrate(<Hello />, document.getElementById('root'))
建立一个名为hello.js的文件
import React from 'react'
export default class Home extends React.Component {
render () {
return <div onClick={this.click}>hello world</div>
}
click(){
alert(123)
}
}
为什么只有一个组件也要分开呢?因为等会服务端也要读取组件信息,因为服务端没有index.js文件中的document对象,这个对象只在浏览器中有,所以index.js文件服务器是读不懂的,只能读取hello.js文件。
这个问题后面我们可以用其他方案避免,但是现在只是为了最简单的做出来一个react的服务端渲染,所以做最简单的处理。毕竟最简单的才做容易理解。
好了,目前我们的react方面就做好了,我们只需要建立一下出口目录之后就可以执行
npm run build
这样打包过后的文件就出现在./dist文件夹内了。
下面我们开始写express的文件,因为express不能用import,所以我们要先用babel转一遍,所以在项目下建立index.js文件
require('babel-core/register')()
require('babel-polyfill')
require('./server')
上面两个引用用来翻译,后面的才是我们真正的文件,所以我们要继续建立server.js文件
import express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server';
import Hello from './app/hello'
import template from './template';
const server = express();
server.use('/assets', express.static(__dirname + '/dist'));
server.get('/', (req, res) => {
const appString = renderToString(<Hello/>);
res.send(template({
body: appString,
title: 'Hello World from the server',
}));
});
server.listen(8080);
console.log('listening');
这里我们看到引入了刚刚写的组件,Hello组件。这里的关键是renderToString函数,这个函数可以把组件变成html的字符串,这也是react可以进行服务端加载的核心。
而template是一个自己写的简易的模板,内容如下
export default ({ body, title}) => {
return `
<!DOCTYPE html>
<html>
<head>
<title>${title}</title>
</head>
<body>
<div id="root">${body}</div>
</body>
<script src="/assets/main.js"></script>
</html>
`;
};
下面执行
npm run start
这样就可以启动express的服务了,然后去我们本地的8080端口就可以看到效果了。
这就是一个最简单的react服务端渲染。