npm init -y
写入依赖和脚本命令
"scripts": {
"start": "node ./dist/server",
"build": "webpack",
"test": "echo \"Error: no test specified\" && exit 1"
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.4",
"babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.24.1",
"webpack": "^3.8.1",
"webpack-node-externals": "^1.2.0"
"dependencies": {
"express": "^4.16.3",
"react": "^16.3.1",
"react-dom": "^16.3.1"
当然,这里可以使用 npm install xxx
命令来安装,但是由于此时使用的 npm v5.8.0 在安装 webpack 时出现 write after end
错误,但通过手动写 package.json 的方法安装可以安装成功
搭建如下目录 tree -I 'node_modules|dist'
├── package-lock.json
├── package.json
├── src
│ ├── client
│ │ ├── App.js
│ │ ├── Html.js
│ │ └── index.js
│ └── server.js
└── webpack.config.js
编写源代码
📃 App.js
import React from "react";
const styles = {
textAlign: 'center',
backgroundColor: 'red',
const App = () => (
<div style=>🤨😒</div>
export default App;
App.js
目前只是简单地显示两个 emoji, 同时加了点红色底色
📃 index.js
import React from 'react';
import { render } from 'react-dom';
import App from './App';
render(<App />, document.getElementById('app'));
index.js
中,使用原生的 JavaScript 方法,获取到页面 HTML 中的 app
修饰的标签,并使用 <App />
这个 React 组件来作为 app
元素的内容
📃 Html.js
const Html = ({ body, title }) => (`
<!DOCTYPE html>
<title>${title}</title>
</head>
<body style="margin:0">
<div id="app">${body}</div>
</body>
</html>
export default Html;
Html.js
中,目标作用是提供一个函数,这个函数中包含了一个 HTML 页面的基础源代码,同时,这个函数接受两个参数,body
和 title
, 分别是 React 转换后的 HTML 字符串与页面标题
而函数最后的输入,就是页面的 HTML 源码字符串,到时直接服务端直接返回这些字符串,不需要再有客户端进行渲染
📃 server.js
import express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server';
import App from './client/App';
import Html from './client/Html';
const port = 3000;
const server = express();
server.get('/', (req, res) => {
const body = renderToString(<App />);
const title = 'Server side rendering';
res.send(Html({
body,
title,
server.listen(port, () => {
console.log('listening...')
server.js
中,主要就是搭建一个服务器,用于接受用户的请求,并对 React 页面进行渲染后,得出字符串并返回
const body = renderToString(<App />);
首先,这里将 React 页面,即 <App />
这个组件转换为了 HTML 字符串,并赋值到 body
res.send(Html({ body, title }));
然后,通过之前定义的方法 Html
, 将刚才 React 组件的 HTML 字符串拼接到 HTML 基础模版,形成一个完整的 HTML 字符串,并返回到客户端
配置 Webpack
const webpack = require('webpack');
const nodeExternals = require('webpack-node-externals');
const path = require('path');
module.exports = {
entry: './src/server.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'server.js',
publicPath: '/',
target: 'node',
externals: nodeExternals(),
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: `'production'`
module: {
loaders: [
test: /\.js$/,
loader: 'babel-loader',
entry
Webpack 打包入口
output
Webpack 打包后文件的输出目录
target
由于 JavaScript 可以运行在浏览器端和服务端,因此,需要将设定好编译环境,由于是服务端渲染,也就是运行在服务端了
externals
告诉 Webpack 不要打包的内容
loaders
用于文件的转换,例如我们的 React 控件,使用的是 JSX 语法编写,但是 JavaScript 解释器并不理解 JSX, 于是需要 loaders 中提供的工具,将 JSX 语法转换为 JS 语法
plugins
与 loaders 的作用相似,但可以比 loaders 做的更多,可以对文件进行加工,例如代码混淆,热加载等
配置 /babelrc
由于 Webpack 中使用了 Babel 来进行代码转换,而 Babel 会在项目中寻找 .babelrc
文件,因此,我们需要配置一下
"presets": ["env", "react"]
我们只需要转换服务端(env)和React(react)部分的代码
配置完成后,我们可以通过
npm run build
来进行代码的编译打包
npm start
使用编译打包后的代码,开启服务端
最后通过访问 localhost:3000
可以加载出服务端渲染的页面
References
The simple guide to server-side rendering React with styled-components
ReactDOMServer