全部源代碼、文檔和圖片都在 github 的倉庫裏,點擊進入倉庫javascript
// 一個很是簡單的組件 import React, { Component } from 'react'; class Home extends Component { render() { return ( <div> <h1>HELLO, HOME PAGE</h1> </div> ); } } export default Home;
http://localhost:3000
,就能夠看到 Home 組件裏的內容已經展現在頁面上,查看網頁源代碼,就發現網頁的源代碼就是 renderToString(<Home />) 生成的 HTML 字符串在 react-dom/server 中,還有一個方法 renderToStaticMarkup,這個方法與 renderToString 的主要做用都是將 React Component 轉化成 HTML 字符串。區別在於 renderToString 生成的 HTML 中的 DOM 會帶有額外的屬性,好比 data-reactroot="",在 renderToStaticMarkup 生成的 HTML 中的 DOM 沒有額外的屬性,能夠節省 HTML 字符串的大小。renderToString 生成的 HTML 裏邊的 DOM 屬性,在客戶端渲染 React 組件的時候,會根據 DOM 的屬性,判斷屬性值是否相等,若是相等,那麼不須要渲染組件,若是不相等,那麼就要從新渲染組件,能夠提升頁面性能。而 renderToStaticMarkup 生成的 HTML 裏的 DOM 沒有屬性,因此頁面數據變動的時候,會從新渲染組件,覆蓋掉服務器端的組件。因此,若是頁面是一個純粹的靜態頁面,最好使用 renderToStaticMarkup,不然,最好使用 renderToString。html
import express from 'express'; import React from 'react'; // react-dom 提供的一個方法,用來把 React 組件轉爲普通的 html 字符串 // 使用方法就是直接把組件放入這個方法裏便可 import { renderToString } from 'react-dom/server'; import Home from '../containers/Home'; const app = express(); const PORT = 3000; app.get('/', (req, res) => { let html = renderToString(<Home />); console.log(html); // 在控制檯輸入 html,獲得的就是一個很是簡單的 HTML 字符串 // <div data-reactroot=""><h1>HELLO, HOME PAGE</h1></div> res.send(html); }); app.listen(PORT, err => { if (err) { console.log(err); } else { console.log(`Server is running at http://localhost:${PORT}`); } });
import React, { Component } from 'react'; class Home extends Component { state = { number: 0 }; handleClick = () => { this.setState({ number: this.state.number + 1 }); console.log(this.state.number); }; render() { return ( <div> <h1>HELLO, HOME PAGE</h1> <h2>number: {this.state.number}</h2> <button onClick={this.handleClick}>click</button> </div> ); } } export default Home;
什麼是同構?java
爲何要同構?node
缺點react
dev:build:client
的啓動命令,命令內容是 webpack --config webpack.client.js --watch
const path = require('path'); module.exports = { mode: 'development', target: 'web', entry: './src/client/index.js', output: { path: path.resolve(__dirname, 'public'), filename: 'client.js' }, module: { rules: [ { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ } ] } };
// webpack.base.js module.exports = { mode: 'development', target: 'web', module: { rules: [ { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ } ] } };
// webpack.server.js const path = require('path'); const merge = require('webpack-merge'); const WebpackNodeExternals = require('webpack-node-externals'); const baseConfig = require('./webpack.base'); module.exports = merge(baseConfig, { target: 'node', entry: './src/server/index.js', output: { path: path.resolve(__dirname, 'build'), filename: 'server.js' }, externals: [WebpackNodeExternals()], });
const path = require('path'); const merge = require('webpack-merge'); const baseConfig = require('./webpack.base'); module.exports = merge(baseConfig, { entry: './src/client/index.js', output: { path: path.resolve(__dirname, 'public'), filename: 'client.js' } });
// client/index.js import React from 'react' import { render } from 'react-dom'; import Home from '../containers/Home'; render(<Home/>, window.root);
app.use(express.static('public'));
,這個目錄就是咱們在 webpack.client.js 裏配置生成的目錄,裏邊的 client.js 文件就是客戶端 webpack 打包後生成的代碼app.use(express.static('public')); app.get('/', (req, res) => { let domContent = renderToString(<Home />); let html = ` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> <title>react-ssr</title> </head> <body> <div id="root">${domContent}</div> <script src="/client.js"></script> </body> </html> `; res.send(html); });
hydrate
替換掉 render
,因此,咱們把客戶端裏的 render 渲染方法替換成 hydrate 渲染方法就能夠了// client/index.js import React from 'react' import { hydrate } from 'react-dom'; import Home from '../containers/Home'; hydrate(<Home/>, window.root);
原理webpack
id = "root"
的容器