8 React业务开发 - 准备工作
Sat, Oct 13, 2018
0x0 React16新特性
新特性官方说明
- react+react-dom包大小:40k → 30k
- 整个代码都用Fiber重新了
- error boundary
- 捕获react渲染过程中的错误
- 可以在正式环境中,弹个提醒告诉用户
- 错误日志,辅助排错
- 开发中排错
- 捕获react渲染过程中的错误
- new render return types
- render function中支持直接返回数组、字符串
- Protals:可以在组件中,把一个标签强制插入到页面任意其他标签下
- 服务端渲染的升级 - 可以使用「流」的方式做渲染了
- 在有服务端渲染的情况下,使用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> ... ) }; |
说明:
- 创建调色盘
- 定义主色
- 定义辅助色
- 定义通过「亮度」区分差异色
- 传入theme & 包裹Component
topic-list/index.jsx
业务组件
... import Button from '@material-ui/core/Button' ... render() { return ( ... <Button variant="contained" color="primary"> 不...不要点我... </Button> ... ) } } |
说明:
- Button组件相关API:https://material-ui.com/api/button/
效果
说明:
- 由于未做服务端渲染,故访问2333端口无法看到此按钮效果
- 检查元素可以看到,相关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> ... ) |
说明:
- 入口需要添加JssProvider和MuiThemeProvider的包裹
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) }) } |
说明:
- 把jss样式渲染成css样式,传入到html模板中
server.template.ejs
... <head> ... <style> <%%- materialCss %> </style> </head> ... |
说明:
- 把渲染好的css样式插入到模板文件中
- 效果
- 可以看到,生成的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 /> ... ) } ... |
说明:
- 这种「非侵入式」的配置方法,值的注意哟~