4. React
4.1 React和VUE的区别
- 共同点
- 不同点
- 核心思想不同。VUE追求降低前端开发的门槛,让更多的人能够更快地上手开发。React推崇函数式编程(纯组件),数据不可变以及单向数据流,当然需要双向的地方也可以手动实现, 比如借助onChange和setState来实现。
- 组件写法差异:React推荐的做法是JSX + inline style, 也就是把 HTML 和 CSS 全都写进 JavaScript 中,即 all in js; Vue 推荐的做法是 template 的单文件组件格式(简单易懂,从传统前端转过来易于理解),即 html,css,JS 写在同一个文件(vue也支持JSX写法)
- diff算法的不同
- 响应原理不同:Vue依赖收集,自动优化,数据可变。Vue递归监听data的所有属性,直接修改。当数据改变时,自动找到引用组件重新渲染。React基于状态机,手动优化,数据不可变,需要setState驱动新的state替换老的state。当数据改变时,以组件为根目录,默认全部重新渲染, 所以 React 中会需要 shouldComponentUpdate 这个生命周期函数方法来进行控制
4.2 Diff算法
4.2.1 TreeDiff
- (1)React通过updateDepth对Virtual DOM树进行层级控制。
- (2)对树分层比较,两棵树只对同一层次节点进行比较。如果该节点不存在时,则该节点及其子节点会被完全删除,不会再进一步比较。
- (3)只需遍历一次,就能完成整棵DOM树的比较。
4.2.2 component diff
- React对不同的组件间的比较,有三种策略
- (1)同一类型的两个组件,按原策略(层级比较)继续比较Virtual DOM树即可。
- (2)同一类型的两个组件,组件A变化为组件B时,可能Virtual DOM没有任何变化,如果知道这点(变换的过程中,Virtual DOM没有改变),可节省大量计算时间,所以用户可以通过 shouldComponentUpdate() 来判断是否需要判断计算。
- (3)不同类型的组件,将一个(将被改变的)组件判断为dirtycomponent(脏组件),从而替换整个组件的所有节点。
4.2.3 element diff
- 当节点处于同一层级时,diff提供三种节点操作:删除、插入、移动。
- 插入:组件 C 不在集合(A,B)中,需要插入
- 删除:
- (1)组件 D 在集合(A,B,D)中,但 D的节点已经更改,不能复用和更新,所以需要删除 旧的D ,再创建新的。
- (2)组件D之前在集合(A,B,D)中,但集合变成新的集合(A,B)了,D 就需要被删除。
- 移动:组件D已经在集合(A,B,C,D)里了,且集合更新时,D没有发生更新,只是位置改变,如新集合(A,D,B,C),D在第二个,无须像传统diff,让旧集合的第二个B和新集合的第二个D 比较,并且删除第二个位置的B,再在第二个位置插入D,而是 (对同一层级的同组子节点) 添加唯一key进行区分,移动即可。
4.3 虚拟DOM
- 虚拟DOM就是一个JS对象,通过对象的方式来表示DOM结构,通过事务处理机制,将多次DOM修改的结果一次性更新到页面上,从而有效的减少页面渲染次数,减少修改DOM重绘重排的时间,提高渲染性能。
- React在内存中维护一个跟真实DOM一样的虚拟DOM树,再改动完组件后,会再生成一个新的虚拟DOM,React会将新的虚拟DOM和原来的虚拟DOM进行对比,找出两个DOM树的不同的地方(diff),然后在真实DOM上更新diff,提高渲染速度。
4.4 setState函数
- 在 React 中,组件分为 有状态组件 和 无状态组件,有状态组件就是能够定义 state 的组件,比如类组件,无状态组件反之,比如函数组件。state 就是用来描述事物在某时刻的数据,可以被改变,改变后与视图相映射,用来保存数据和响应视图。
- 虽然状态可以改变,但不是响应式的,动态改变并没有与视图响应,想要改变并响应视图则需要 setState 修改并更新视图。
- 使用方法:this.setState({ 修改的数据 })
- setState并不会影响其他没有进行修改的数据,此方法是从Component中继承过来的
- 虽说 setState 很多人说是异步的,但是它本身的执行过程和代码是同步的,只是它在合并数据与钩子函数的调用顺序在更新后无法拿到值,形成了异步 ,我们可以通过第二个参数拿到更新后的结果。
4.5 React生命周期
- 初始渲染阶段:这是组件即将开始其生命之旅并进入 DOM 的阶段。
- getDefaultProps:获取实例的默认属性
- getInitialState:获取每个实例的初始化状态
- componentWillMount:组件即将被装载、渲染到页面上
- render:组件在这里生成虚拟的 DOM 节点
- componentDidMount:组件真正在被装载之后
- 更新阶段:一旦组件被添加到 DOM,它只有在 prop 或状态发生变化时才可能更新和重新渲染。这些只发生在这个阶段。
- componentWillReceiveProps:组件将要接收到属性的时候调用
- shouldComponentUpdate:组件接受到新属性或者新状态的时候(可以返回 false,接收数据后不更新,阻止 render 调用,后面的函数不会被继续执行了)
- 这个方法用来判断是否需要调用 render 方法重绘 dom。
- 因为 dom 的描绘非常消耗性能,如果我们能在这个方法中能够写出更优化的 dom diff 算法,可以极大的提高性能。
- componentWillUpdate:组件即将更新不能修改属性和状态
- render:组件重新描绘
- componentDidUpdate:组件已经更新
- 卸载阶段:这是组件生命周期的最后阶段,组件被销毁并从 DOM 中删除。
- componentWillUnmount:组件即将销毁
4.6 容器组件和展示组件
4.7 React的高阶组件
- 高阶组件的作用就是实现组件复用,节省内存
- 先定义一个函数式组价,传入一个参数,这个参数就是组件
- 组件内返回一个class类组件,类名可以写也可以不写
- 类组件内部可以写方法,数据,然后将参数当做组件返回出去,并将方法或者数据,传个这个组件
import React,{ Component } from 'react';
const Hoc = ( Comp ) =>{//参数首字母必须大写,必须要有返回值,在下面使用
return class banner extends Component{ //类名可以省略,省略的话标签名就是以temp或者其他的代替,必须要有返回值
banner = () => {//这里是实现某个功能的函数代码
return 'zhangyue'
}
render () {
return (
<Comp banner = { this.banner }></Comp>//将参数当做一个组件返回出去
)
}
}
}
class A extends Component{
render () {
return (
<div>
<p> A组件 </p>
{ this.props.banner() }//在下面使用了高阶组件后,这里就可以直接使用里面的方法了
</div>
)
}
}
class B extends Component{
render () {
return (
<div>
<p> B组件 </p>
{ this.props.banner() }
</div>
)
}
}
const HocA = Hoc(A)//组件名必须首字母大写,将组件名当参数传进去,这样这个组件就有高阶组件内的方法了
const HocB = Hoc(B)
class C extends Component{
render () {
return (
<div>
<p> C组件 </p>
<HocA></HocA>//这里使用的高阶组件
<HocB></HocB>
</div>
)
}
}
export default C
|