(精華2020年5月31日更新) react基礎篇 手寫ssr服務端渲染

2b勿擾發表於2020-05-31

共用部分

import React ,{useState} from 'react'
import {connect} from 'react-redux'
import {getIndexList} from '../store/index'
const Index = (props) => {
    let [count,setCount] = useState(1)
    return (
        <div>
            <h1>服務端渲染</h1>
            <h1>{count}</h1>
            <button onClick={()=>{props.getIndexList()}}>載入</button>
            <button onClick={()=>{setCount(count+1)}}>增加</button>
            {
                props.list.map(item=><li key={item.id}>{item.name}</li>)
            }
        </div>
    )
}
Index.load = (store) => {
    return store.dispatch(getIndexList())
}
export default connect(
    state => ({list:state.index.list}),
    {getIndexList}
)(Index)

客戶端

import React from 'react'
import ReactDOM from 'react-dom'
import {BrowserRouter} from 'react-router-dom'
import {Provider} from 'react-redux'
import {getClientStore} from '../store/store'
import {Route,Link} from 'react-router-dom'
import routers from '../router'

const store = getClientStore()
const App = ()=>{
    return (
        <Provider store={store}>
            <BrowserRouter>
                <div>
                    <Link to="/">首頁</Link>
                    <Link to="/login">登入</Link>
                </div>
                {routers.map(route=><Route {...route}/>)}
            </BrowserRouter>
        </Provider>
    )
}
ReactDOM.hydrate(
    <App/>
    ,document.getElementById('root'))

服務端

import express from 'express'
import React from 'react'
import {renderToString} from 'react-dom/server' // 專門在server端使用的
import {StaticRouter} from 'react-router-dom'
import {Provider} from 'react-redux'
import {getStore} from '../store/store'
import {Route,Link} from 'react-router-dom'
import {matchRoutes} from 'react-router-config'
import routers from '../router'
let store = getStore()
let app = express()
app.use(express.static('public'))
app.get('*',function(req,res){
    // 遍歷所有的路由 尋找有load方法的
    const matchedRoutes = matchRoutes(routers,req.path)
    let promises = []
    matchedRoutes.forEach(item=>{
        let {load} = item.route.component
        if(load){
            const promise = new Promise((resolve,reject)=>{
                load(store).then(resolve).catch(reject)
            })  
            promises.push(promise)
        }
    })
    Promise.all(promises).then(()=>{
        let content = renderToString(
            <Provider store={store}>
                <StaticRouter>
                    <div>
                        <Link to="/">首頁</Link>
                        <Link to="/login">登入</Link>
                    </div>
                    {routers.map(route=><Route {...route}/>)}
                </StaticRouter>
            </Provider>
            
        )
        const html = `
            <html>
                <head>
                    <title>軟謀教育</title>
                </head>
                <body>
                    <div id="root">${content}</div>
                    <script>
                        window._context = ${JSON.stringify(store.getState())}
                    </script>
                    <script src="/main.js"></script>
                </body>
            </html>
        `
        res.send(html)
    })
    

    
})

app.listen(1000,()=>{console.log('監聽1000埠成功');
})

先編譯客戶端在啟動服務端

"scripts": {
    "dev:server": "webpack --config webpack.server.js --watch",
    "dev:start": "nodemon --watch build --exec node \"./build/bundle.js\"",
    "dev:client": "webpack --config webpack.client.js --watch"
  },

目錄store

import axios from 'axios'
const GET_LIST = 'INDEX/GET_LIST'

// action 

const changeList = list => ({
    type:GET_LIST,
    list
})

// 有一個請求 可以改變reducer

export const getIndexList = ()=>{
    return (dispatch)=>{
        return axios.get('http://xxxxxxxxxxx').then((res)=>{
            const {list} = res.data
            dispatch(changeList(list))
        })
    }
}
const initState = {
    list:[]
}
export default (state = initState,action) => {
    switch(action.type){
        case GET_LIST:
        const newState = {
            ...state,
            list:action.list
        }
        return newState
        default:
            return state
    }
}

相關文章