主要是对阅读深入React技术栈的总结和实践。
React
事件系统
React基于Viral DOM实现了一个Synthetic(合成事件)层,使用stopPropagation()和preventDefault()来中断它。
所有事件都自动绑定到最外层,如果要访问原生事件对象,可以使用nativeEvent属性。
一、 合成事件的实现机制
- 事件委派:把所有事件绑定到结构最外层,使用同一的事件监听器–映射所有组件内部的事件监听和处理函数。
- 自动绑定:手动绑定this为当前组件的方法:
- bind方法:使用::this.handleClick等同于this.handleClick.bind(this)
- 构造器内绑定声明,在constructor中完成
- 箭头函数
二、 使用原生事件
在ComponentDidMount中
this.refs.button.addEventListener(‘click’,e=>{
hanldeCliick(e);
});
在ComponentWillUnMount中移除
this.refs.button.removeEventListener(‘click’);
三、 合成事件和原生事件混用
比如在web页面添加移动设备扫描二维码的功能。
- 不要把合成事件和原生事件混用
- 通过e.target判断来避免
四、 对比React合成事件和JS的原生事件
组件间通信
一、 父组件向子组件通信
通过props
二、 子组件向父组件通信
- 利用回调函数:this.props的function的回调
- 利用自定义事件机制,触发事件。
三、 跨级组件通信
子组件跨级访问信息,使用context实现跨级父子组件间的通信。
在父组件中定义了ChildContext,在子组件中使用this.context从而调用。
一般只有在全局比如:界面主题、用户信息才会使用。总体原则:使用它,可以写成高阶组件。
四、 没有嵌套关系的组件通信
通过一个单例的EventEmitter,然后把这个实例输出到各组件中使用。
import {EventEmitter} from ‘event’;
export default new EventEmitter();
import emitter from ‘./events’;
在APP的类中的ComponentDidMount中emmit
组件间抽象
mixin和高阶组件
mixin:创造一种类似多重继承的效果,组合。
Flux架构
不是MVC的架构,Flux三大部分组成:1. dispatcher、store、view。dispatcher负责分发事件,store负责保存数据、view负责订阅store中的数据,并使用这些数据渲染相应的页面。
与MVC的主要区别:
没有职责明确的Controller,存在一个Controller-view的角色,将view和store进行绑定,并没有Controller需要承担的复杂的逻辑。
- dispatcher与action
dispatcher的实现就是下面两个API:
.register(callback)方法用来注册一个监听器。
.dispatch(action)用来分发一个action。
action是一个普通的JavaScript对象,一般包含type、payload字段,用于描述一个事件以及需要改变的相关数据。
store
负责保存数据,并且修改数据的逻辑,调用dispatcher的register(callback)方法将自己注册为一个监听器。
当.dispatch(action)用来分发一个action时,store注册的监听器就会被调用,同时得到这个action作为参数。store之暴露getter不暴露setter,只能读取不能修改。Controller-view
最顶层view,主要进行store与React组件(view层)之间的数据的绑定,定义数据更新以及传递的方式。会调用store保留的getter获取存储其中的数据并设置为自己的state,在render时以props的形式传给自己的子组件。view
React组件扮演。结合Angular、Vue来发挥Flux的能力。
一条特殊的约定:Flux的view不能直接修改数据,如果页面操作需要修改数据,则必须使用dispatcher分发一个action。
- actionCreator
用来创造action的。为什么需要?因为在分发action的时候代码是冗余的。
Redux架构
Redux简介
Redux的核心代码是一个库,类似Flux的架构思想,
三大原则
单一数据源
一个应用永远只有唯一的一个数据源,整个应用状态都保存在一个对象中。状态是只读的
Flux中store没有setter只有getter,而Redux中没有store,只有reducer,根据当前触发的action对当前应用state进行迭代。
- 状态修改均由纯函数完成
Redux与Flux最大的不同。
在Flux中,我们在actionCreator里调用APPDispatcher.dispatch方法来触发action,直接修改了store中数据.
用户通过 View 发出 Action; store.dispatch(action);
然后 Store 自动调用 Reducer,并且传入两个参数:当前 State 和收到的 Action。 Reducer 会返回新的 State
State 一旦有变化,Store 就会调用监听函数。 store.subscribe(listener);
listener可以通过 store.getState() 得到当前状态。如果使用的是 React,这时可以触发重新渲染 View。
面试题:
React中key的作用?怎样设置key值?
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity:
用key值来标识哪些元素是改变的,新增的,或者移除的。
- 使用数据项中的ID
- 生成唯一标识字符串
- 使用index数组下标;
- SetState发生了什么?
将传入参数对象与当前状态合并,触发reconciliation调和,构建新的元素树。自动计算新的树和老树的差异,按需渲染。不是全部重新渲染。
setState 可能会导致 DOM 的重绘,如果调用一次就马上去进行重绘,那么调用多次就会造成不必要的性能损失。设计成异步的话,就可以将多次调用放入一个队列中,在恰当的时候统一进行更新过程。
多次调用会合并为一次,只有当更新结束后 state 才会改变。所以setState是异步的,
两种方法解决:
- 1.利用setState的第二个参数设置回调函数,setState调用后会触发执行这个callback函数;
- 2.利用setTimeout
setTimeout(() => {console.log(this.state.counter)}, 0)
- props和state的区别?
- props用于定义外部接口,使用state来存储控制当前页面逻辑的数据;
- props的赋值是在父级组件,state赋值在当前组件内部;
- props是不可变的,而state是可变的;
- 使用props比state会有更好的性能;
- react 生命周期函数
初始化阶段:
getDefaultProps:获取实例的默认属性
getInitialState:获取每个实例的初始化状态
componentWillMount:组件即将被装载、渲染到页面上
render:组件在这里生成虚拟的 DOM 节点
componentDidMount:组件真正在被装载之后
运行中状态:
componentWillReceiveProps:组件将要接收到属性的时候调用
shouldComponentUpdate:组件接受到新属性或者新状态的时候(可以返回 false,接收数据后不更新,阻止 render 调用,后面的函数不会被继续执行了)
componentWillUpdate:组件即将更新不能修改属性和状态
render:组件重新描绘
componentDidUpdate:组件已经更新
销毁阶段:
componentWillUnmount:组件即将销毁
- Virtual DOM算法
Virtual DOM 算法主要的实现就是三个步骤:
- 用JS对象模拟DOM树element,
- 比较两棵虚拟DOM树的差异diff,
- 把差异应用到真正的DOM树上patch
Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。可以类比 CPU 和硬盘,既然硬盘这么慢,我们就在它们之间加个缓存:既然 DOM 这么慢,我们就在它们 JS 和 DOM 之间加个缓存。CPU(JS)只操作内存(Virtual DOM),最后的时候再把变更写入硬盘(DOM)