8 React业务开发 - 准备工作

 

0x0 React16新特性

官网:https://reactjs.org/

新特性官方说明

  1. react+react-dom包大小:40k → 30k
  2. 整个代码都用Fiber重新了
  3. error boundary
    1. 捕获react渲染过程中的错误
      1. 可以在正式环境中,弹个提醒告诉用户
      2. 错误日志,辅助排错
    2. 开发中排错
  4. new render return types
    1. render function中支持直接返回数组、字符串
  5. Protals:可以在组件中,把一个标签强制插入到页面任意其他标签下
  6. 服务端渲染的升级 - 可以使用「流」的方式做渲染了
    1. 在有服务端渲染的情况下,使用hydrate方法渲染客户端内容

0x1 Material-UI的安装和使用

官网:https://www.material-ui.com/

说明:React components that implement Google's Material Design

注意:需要考虑到服务端渲染相关配置

特点:直接在js里面写css文件,可以有非常好的动态性

目标:展示出来Material-UI的一个Button

基础使用 & 配置服务端渲染

安装

https://material-ui.com/getting-started/installation/

npm i @material-ui/core @material-ui/icons -S

基本使用

app.js

...
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core'
import { lightBlue, pink } from '@material-ui/core/colors'
...
const theme = createMuiTheme({ # 1
  palette: {
    primary: lightBlue,
    accent: pink,
    type: 'light',
  },
})

...

const render = (Component) => {
  ReactDOM.hydrate(
...
          <MuiThemeProvider theme={theme}> # 2
            <Component />
          </MuiThemeProvider>
...
  )
};

说明:

  1. 创建调色盘
    1. 定义主色
    2. 定义辅助色
    3. 定义通过「亮度」区分差异色
  2. 传入theme & 包裹Component


topic-list/index.jsx

业务组件

...
import Button from '@material-ui/core/Button'
...


  render() {
    return (
      ...
        <Button variant="contained" color="primary">
          不...不要点我...
        </Button>
      ...
    )
  }
}

说明:

  1. Button组件相关API:https://material-ui.com/api/button/


效果

说明:

  1. 由于未做服务端渲染,故访问2333端口无法看到此按钮效果
  2. 检查元素可以看到,相关css的style已经插入到该页面中

服务端渲染

https://material-ui.com/guides/server-rendering/#server-rendering

安装插件:`npm i react-jss jss jss-preset-default -S`


server-entry.js

...
import { JssProvider } from 'react-jss'
import { MuiThemeProvider } from '@material-ui/core/styles'
...


export default (store, routerContext, sheetsRegistry, jss, theme, url) => (
  ...
      <JssProvider registry={sheetsRegistry} jss={jss}>
        <MuiThemeProvider theme={theme}>
          <App />
        </MuiThemeProvider>
      </JssProvider>
  ...
)

说明:


server-render.js

...
const SheetsRegistry = require('react-jss').SheetsRegistry
const create = require('jss').create
const preset = require('jss-preset-default').default
const createMuiTheme = require('@material-ui/core/styles').createMuiTheme
const createGenerateClassName = require('@material-ui/core/styles/createGenerateClassName').default
const colors = require('@material-ui/core/colors')
...
module.exports = (bundle, template, req, res) => {
  return new Promise((resolve, reject) => {
    ...
    const sheetRegistry = new SheetsRegistry()
    const jss = create(preset())
    jss.options.createGenerateClassName = createGenerateClassName
    const theme = createMuiTheme({
      palette: {
        primary: colors.lightBlue,
        accent: colors.pink,
        type: 'light',
      },
    })

    const app = createApp(stores, routerContext, sheetRegistry, jss, theme, req.url)

    asyncBootstrapper(app).then(() => {
      ...
      const html = ejs.render(template, {
        ...
        materialCss: sheetRegistry.toString(), // #1
      })
      ...
    }).catch(reject)
  })
}

说明:

  1. 把jss样式渲染成css样式,传入到html模板中


server.template.ejs

...
<head>
  ...
  <style>
    <%%- materialCss %>
  </style>
</head>
...

说明:

  1. 把渲染好的css样式插入到模板文件中
  2. 效果
    1. 可以看到,生成的css 已经被插入进来了


删除重复的CSS定义

需要在客户端侧,渲染完成之后,把这些css删掉,防止冲突

思路:给服务端渲染生成的style添加id,之后通过id拿到标签做删除

server.template.ejs

...
<head>
  ...
  <style id="jss-server-side">
    <%%- materialCss %>
  </style>
</head>
...


app.js

...
const createApp = (TheApp) => {
  class Main extends React.Component {
    componentDidMount() {
      const jssStyles = document.getElementById('jss-server-side')
      if (jssStyles && jssStyles.parentNode) {
        jssStyles.parentNode.removeChild(jssStyles)
      }
    }

    render() {
      return <TheApp />
    }
  }

  return Main
}


const render = (Component) => {
  const ComponentR = createApp(Component)

  ReactDOM.hydrate(
          ...
            <ComponentR />
          ...
  )
}
...

说明: