- 组件的划分要满足高内聚(High Cohesion)和低藕合(LowCoupling)的原则
- prop 是组件的对外接口, state 是组件的内部状态,对外用prop ,内部用 state
- 组件不应该改变 prop 的值,而 state 存在的目的就是让组件来改变的
- 每个 React 组件的 props 中都可以一个特殊属性 children ,代表的是子组件。
React 严格定义了组件的生命周期,生命周期可能会经历如下三个过程:
- 装载过程(Mount ),也就是把组件第一次在 DOM 树中渲染的过程;
- 更新过程(Update ),当组件被重新渲染的过程;
- 卸载过程(Unmount ),组件从 DOM 中删除的过程 三种不同的过程, React 库会依次调用组件的一些成员函数,这些函数称为生命周期函数
依次被调用的函数为:
- constructor
- getlnitialState
- getDefaultProps
- componentWillMount
- render
- componentDidMount
- constructor,初始化 state ,绑定成员函数的 this 环境。并不是每个组件都需要定义自己的构造函数。
- getlnitialState 这个函数的返回值会用来初始化组件的 this.state ;getDefaultProps 函数的返回值可以作为 props 的初始值,这两个函数都只在React.createClass 方法创造的组件类才会用到;实际上 getlnitialState和getDefaultProps 两个方法在 ES6 的方法定义的 React 组件中根本不会用到。
- render,一定要实现 render 函数,所有 React 组件的父类 React.Component 类对除 render 之外的生命周期函数都有默认实现;render 函数应该是一个纯函数,完全根据this.state 和 this.props 来决定返回的结果,而且不要产生任何副作用。在 render 函数中不能调用 this.setState ,一个纯函数不应该引起状态的改变。
- componentWillMount 会在调用 render 函数之前被调用, component�DidMount 会在调用 render 函数之后被调用。component�WillMount 可以在服务器端被调用,也可以在浏览器端被调用;而 componentDidMount只能在浏览器端被调用。
- componentWillReceiveProps
- shouldComponentUpdate
- componentWillUpdate
- render
- componentDidUpdate
-
this.setState 方法触发的更新过程不会调用 componentWillReceiveProps ,这是因为这个函数适合根据新的 props 值来计算出是不是要更新内部状态 state 更新组件内部状态的方法就是 this.setState ,如果 this.setState 的调用导致component�WillReceiveProps 再一次被调用,那就是一个死循环了
-
shouldComponentUpdate的参数就是接下来的 props 和 state 的值,我们可以根据这2个参数的前后对比,判断出是返回 true 还是返回 false,从而决定是否要重新渲染。
shouldComponentUpdate(nextProps, nextState) { return (nextProps.caption !== this.props.caption)|| (nextState.count !==this.state.count); }
-
如果组件的 shouldComponentUpdate 函数返回 true, React 接下来就会依次调用对应 组件的 componentWillUpdate 、render 、componentDidUpdate 函数。这对函数,在服务端渲染时同样会执行。
- React 组件的卸载过程只涉及一个函数 omponentWillUnmount ,React 组件要从 DOM 树上删除掉之前,对应的 componentWillUnmount 函数会被调用,所以这个函数适 合做一些清理性的工作。
- componentWillUnmount 中的工作往往和 componentDidMount 有关,比如,在 componentDidMount 中用非 React 的方法创造了一些 DOM 元素,如果撒手不管可能会造 成内存泄露,那就需要在 componentWillUnmount 中把这些创造的 DOM 元素清理掉。
基本原则:
- 唯一数据源( Single Source of Truth)
- 保持状态只读( State is read-only)
- 数据改变只能通过纯函数完成(Changes are made with pure functions)
- Context ,就是"上下文环境",让一个树状组件上所有组件都能访问一个共同的对象,为了完成这个任务,需要上级组件和下级组件配合
- 上级组件要宣称自己支持 context ,并且提供一个函数来返回代表 Context对象
- 这个上级组件之下的所有子孙组件,只要宣称自己需要这个 context ,就可以通过 this.context 访问到这个共同的环境对象
要点:
- 代码文件的组织结构(提倡按功能组织);
- 确定模块的边界(高内聚,低耦合);
- Store 的状态树设计(一个模块控制一个状态节点、避免冗余数据、树形结构扁平)
- 利用 shouldComponentUpdate 函数
- 列表渲染,用稳定唯一的key值,不要用不稳定的key值,如index
- 用 reselect 提高数据获取性能
- 范式化状态树
高阶组件,有两种实现方式:一种是代理方式,另一种是继承方式。通过比较不难发现,代理方式更加容易实现和控制,继承方式的唯一优势是可以操纵特定组件的生命周期函数。和高阶组件相比,"以函数为子组件"的模式更加灵活,因为有函数的介入,连接两个组件的方式可以非常自由。
意义:
- 重用代码,有时候很多 React 组件都需要公用同样一个逻辑,比如说 react�-redux 中容器组件的部分,没有必要让每个组件都实现一遍 shouldComponentUpdate 这些生命周期函数,把这部分逻辑提取出来,利用高阶组件的方式应用出去,就可以减少很多组件的重复代码
- 修改现有 React 组件的行为,我们不想去触碰有些组件的内部逻辑,这时候高阶组件有了用武之地,通过一个独立于原有组件的函数,可以产生新的组件,对原有组件没有任何侵害
分类:
- 代理方式的高阶组件
- 继承方式的高阶组件
代理方式的高阶组件,可以应用在下列场景中:
- 操纵 prop(增加、减少或者编辑后的props重新传递给某个组件);
- 访问 ref;
- 抽取状态(参考react-redux中的connect返回的函数);
- 包装组件(例如添加div包裹,增加样式展示行为)
继承方式的高阶组件可以应用于下列场景:
- 操纵 prop (如高阶组件需要根据参数组件渲染结果来决定如何修改 props);
- 操纵生命周期函数(因为继承方式的高阶函数返回的新组件继承了参数组件,所以可以重新定义任何一个 React 组件的生命周期函数)
结论:优先考虑代理,然后才考虑继承
异步action请求终止的常用方法:
- 从交互入手,当前请求没完成时,锁操作
- action 构造函数文件中定义一个文件模块级的 nextSeqld 变量,这是一个递增的整数数字,给每一个访问 API 的请求做序列编号,请求回后匹配 nextSeqld 变量是否一致
常用的辅助操作库:
- redux-thunk
- redux-saga
- redux-effects
- redux-side-effects
- redux-loop
- redux-observable
尽量不让 React 存储状态,把状态存储到 Redux 的 Store 上,也就是让 React 组件只是一个根据数据负责渲染的纯函数就好,这样的 React 组件是非常方便测试的,测试一个根据输入返回输出的纯函数,要比测试一个包含很多状态的对象容易得多 因为纯函数的结果根据输入完全可以预测
- 单元测试框架;
- 单元测试代码组织;
- 辅助工具
常用框架:
- Mocha+Chai 的组合
- Jest
单元测试代码组织:
- 文件名以.test.js 为 后缀的代码文件
- 存于 test 目录下的代码文件
辅助工具:
- Enzyme
- sinon.js
- redux-mock-store
- 中间件
- Store Enhancer
路由:
React-Router 库提供了两个组件来完成路由功能,一个是 Router ,另 一个是 Route。前者 Router 在整个应用中只需要一个实例,代表整个路由器 后者 Route 则代表每一个路径对应页面的路由规则,一个应用中应该会有多个 Route 实例。