React
· 阅读需 13 分钟
开发注意
- 单一职责(每个组件只做一件事,复杂了就需要拆分)
- DRY 原则(可计算的到不要单独存储,尽量无状态,所需数据通过 props 获得)
- 约定:大写字母开头为自定义组件,小写 tag 是原生 DOM 节点
- key 应该唯一,不使用随机值,使用 index 为 key 值,对性能没有优化(index 随机)
- 执行初始化函数的时候,使用 useEffect,依赖值为该函数名,确保在函数改变的时候才会重新执行 useEffect 中的方法
历史背景及特性
历史背景
- 传统 UI 操作关注过多细节
- 应用状态分散,难以追踪、维护
特性
- 一个新概念:组件
- 4 个必须的 API
- 单向数据流: flux 架构
- 完善的错误提示
flux 架构(单向数据流)
组件方式考虑 UI 的构建方式
React 组件(props + state = view)
- 一般不提供方法,而是某种状态机
- 可理解为纯函数(固定输入一定的到固定输出)
- 单向数据绑定
React 组件:受控组件 VS 非受控组件
受控:表单状态由使用者维护(外部 props ---- 受外部控制)
非受控:表单状态 DOM 自身维护(内部 state ----- 受内部控制)
组件开发原则
- 单一职责(每个组件只做一件事,复杂了就需要拆分)
- DRY 原则(可计算的到不要单独存储,尽量无状态,所需数据通过 props 获得)
- 约定:大写字母开头为自定义组件,小写 tag 是原生 DOM 节点
JSX 本质是动态创建组件的语法糖
- 声明式创建界面的直观
- 代码动态创建界面的灵活
- 无需学习新的模板语言
生命周期及使用场景
constructor (唯一可直接修改 state 位置)
初始化内部状态,很少使用
getDerivedStateFromProps(表单控件获取默认值)
当 state 需要从 props 中初始化的时候用到(不建议使用,维护 props-state 二者状态会增加复杂度)
每次 render 都会调用
componentDidMount(获取外部资源)
UI 渲染完成后调用,只执行一次
componentWillUnmount(资源释放)
组件移除的时候被调用
getSnapshotBeforeUpdate(获取 render 之前的 DOM 状态)
render 之前调用,state 已经更新了
componentDidUpdate(页面根据 props 变化重获数据)
每次 UI 更新都会调用
shouldComponentUpdate(性能优化)
Virtual DOM 是否会重新绘制
一般用 PureComponent 自动实现
Virtual DOM 与 key 属性
key 的作用是 为每一个元素赋予一个确定的标识,判断元素是新创建的还是被移动的,减少不必要的元素渲染
- key 值唯一
- 不用随机值
- 使用 index 为 key 值对性能没有优化
组件设计模式:高阶组件和函数组件
高阶组件:对业务组件的封装(一般没有自己的 UI 展现)
const EnhancedComponent = higherOrderComponent(WrappedComponent)
// 接收组件参数,返回新的组件
import React from 'react'
export default function withTimer(WrappedComponent){
return class extends React.Component{
state = {
time: new Date()
componentDidMount(){
this.timeID = setInterval(()=> this.tick(), 1000)
}
componentWillUnmount(){
clearInterval(this.timeID)
}
tick() {
this.setState({
time: new Date()
})
}
render(){
return <WrappedComponent time={this.state.time} {...this.props}>
}
}
}
}
函数作为子组件(组件如何渲染,由使用者决定)----- 一种设计模式
class MyComponent extends React.Component {
render() {
return (
<div>
{this.props.children('Nate Wang')} // 默认传递进来的是一个函数,传递值直接执行
</div>
)
}
}
<MyComponent>
{(name) => ( <div>{name}</div> )}
</MyComponent>
新的 ContextAPI 及其使用场景(共享状态,comsumer 必须包裹在 provider 中)
const ThemeContext = React.createContext('light')
class App extends React.Component {
render(){
return (
<ThemeContext.Provider value="dark">
// 提供上下文给子组件使用
<ThemedButton />
</ThemeContext.Provider>
)
}
}
function ThemedButton(props) {
return (
// 消费上下文
<ThemeContext.Consumer>
{theme => <Button {...props} theme={theme}/>}
</ThemeContext.Consumer>
)
}
脚手架构建工具
creat-react-app 脚手架:babel + webpack config + testing + eslint
适合新手学习
rekit 脚手架:create-react-app + redux + react-router + less/scss + feature Oriented Architecture + Dedicated IDE
快速开发大型项目
codesandbox.io 在线编程环境
打包与部署
打包目的
- 编译 ES6 语法特性,编译 JSX
- 整合资源,如图片、less、sass 等
- 优化代码体积
打包注意
- 设置 nodejs 环境为 production
- 禁止开发时专用代码,如 logger
- 设置应用根路径 package 中的 homepage
redux
react:state ---> dom
Redux: store ---> view
使组件通信更加方便
特性
- Single Source of Truth 唯一状态来源
- state + action = new state 可预测性
- 纯函数更新 store
理解 Store
const store = createStore(reducer)
## reducer 处理函数
store 有三个方法:
- getState() 获取数据
- dispatch(action) 传递
- subscribe(listener) 监听
理解 Action(描述行为)
{
type: ADD_TODO,
text: 'Build my first Redux app'
}