React 服务器渲染原理解析与实践

Webpack配置

关于Webpack配置文件的写法,看这篇篇足够!;值得注意的是,当Webpack分别在客户端和服务端打包时,行为不同:即部分打包和全部打包的区别!

  • webpack-node-externals,此模块用于SSR中,使webpack选择性打包。
  • webpack --watchwatch参数可以自动监听入口文件和依赖文件,实现变化后的自动打包
  • npm-run-all,文档在这里!
  • webpack-merge
  • isomorphic-style-loader, 用于SSR的style-loader

同构

SSR仅仅负责首屏展示,CSR负责交互,即同一套JS代码,需要在服务端和客户端分别运行一次。

  • ReactDom.hydrate,SSR时使用此方法

路由

  • StaticRouter,用于SSR的路由,代码摘自官网:
import { createServer } from 'http'
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import { StaticRouter } from 'react-router'
createServer((req, res) => {
  // This context object contains the results of the render
  const context = {}
  const html = ReactDOMServer.renderToString(
    
      
    
  )
  // context.url will contain the URL to redirect to if a  was used
  if (context.url) {
    res.writeHead(302, {
      Location: context.url
    })
    res.end()
  } else {
    res.write(html)
    res.end()
  }
}).listen(3000)
  • react-router-config,匹配多层路由,传送门
  • renderRoutes,用于渲染多级路由,是react-router-config的一个方法
  • 渲染时,路由数据会随属性向下传递,可用props.route获取

Redux

  • store,SSR创建store时,需要每次访问时新生成一个store,通过函数执行来生成

SSR获取异步数据

  • react-async-bootstrapper,传送门
  • 构建getSomeData方法,即在组件类下构建静态方法getSomeData
const routes = [
  { path: '/',
    component: someClass,
    loadData: () => someClass.getSomeData(),
  },
  // etc.
]
import { routes } from './routes'

const App = () => (
  
    {routes.map(route => (
      
    ))}
  
)
import { matchPath } from 'react-router-dom'

// inside a request
const promises = []
// use `some` to imitate `` behavior of selecting only
// the first to match
routes.some(route => {
  // use `matchPath` here
  const match = matchPath(req.path, route)
  if (match)
    promises.push(route.loadData(match))
  return match
})

Promise.all(promises).then(data => {
  // do something w/ the data so the client
  // can access it then render the app
})
  • 数据同步,即将SSR的数据同步到CSR,通过模板HTML为桥梁
//注水
''
//出水
const getClientStore = () => {
  const defaultState = window.context.state//默认store数据
  return creatStore(reducer, defaultState)
}

数据请求

  • express-http-proxy,用于中间层请求转发
  • 同一个请求,在SSR和CSR下的路径不同,需要作出区分
  • 数据请求失败时,用新建的Promise进行包裹,达到容错的目的
const outerPromise = new Promise((resolve, reject) => {
  innerPromise
    .then(resolve)
    .catch(resolve)
})

Axios高级用法

  • instance,传送门
const instance = axios.create({
  baseURL: 'https://some-domain.com/api/',
  timeout: 1000,
  headers: {'X-Custom-Header': 'foobar'},
  params: { /*some params*/}
});

redux-thunk

  • withExtraArgument(),此为redux-thunk的一个方法
const store = createStore(
  reducer,
  applyMiddleware(thunk.withExtraArgument(api))
)

// later
function fetchUser(id) {
  return (dispatch, getState, api) => {
    // you can use api here
  }
}

cookie

刷新页面时登陆状态异常,可能与node中间层转发请求时未携带cookie造成,可使用AxiosInstanceheaders搞定,见上方

404

  • 通过多极路由添加404组件,当路由匹配失败会渲染
  • 借助staticContext(此属性为SSR时专有)控制404组件的行为
  • 404时的状态码为404(默认为200)

CSS

  • 借助staticContext(此属性为SSR时专有)为SSR添加样式,即将css挂载到staticContext
//所有样式
this.props.staticContext.css.push(styles._getCss())

//格式化样式
const css = css.length ?css.join('/n') : ' '

HOC

  • 当不同组建存在相同行为时,请使用HOC进行抽象
  • HOC为函数,其参数为组件,返回为新的组件
const seniorComponentGenerator = (DecoratoredComponent) => {
  return class newComponent extends Component {
    componentWillMount() {
      /*组件的相同行为*/
    }
    render(){
      
    }
  }
}

SEO

  • react-helmet,定制titlediscription
  • 预渲染技术,Prerender

你可能感兴趣的:(React 服务器渲染原理解析与实践)