有一段解释很透彻: 用脚本进行 DOM 操作的代价很昂贵。有个贴切的比喻,把 DOM 和 JavaScript 各自想象为一个岛屿,它们之间用收费桥梁连接,js 每次访问 DOM,都要途径这座桥,并交纳 "过桥费", 访问 DOM 的次数越多,费用也就越高。 因此,推荐的做法是尽量减少过桥的次数,努力待在 ECMAScript 岛上。 因为这个原因 react 的虚拟 dom 就显得难能可贵了,它创造了虚拟 dom 并且将它们储存起来,每当状态发生变化的时候就会创造新的虚拟节点和以前的进行对比(Diff 算法),让变化的部分进行渲染。整个过程没有对 dom 进行获取和操作,只有一个渲染的过程,所以 react 说是一个 ui 框架。
react 的一个组件很明显的由 dom 视图和 state 数据组成,两个部分泾渭分明。 state 是数据中心,它的状态决定着视图的状态。这时候发现似乎和我们一直推崇的 MVC 开发模式有点区别,没了 Controller 控制器,那用户交互怎么处理,数据变化谁来管理?然而这并不是 react 所要关心的事情,它只负责 ui 的渲染。与其他框架监听数据动态改变 dom 不同,react 采用 setState 来控制视图的更新。setState 会自动调用 render 函数,触发视图的重新渲染,如果仅仅只是 state 数据的变化而没有调用 setState,并不会触发更新。 组件就是拥有独立功能的视图模块,许多小的组件组成一个大的组件,整个页面就是由一个个组件组合而成。它的好处是利于重复利用和维护。
eact 的 diff 算法用在什么地方呢?当组件更新的时候,react 会创建一个新的虚拟 dom 树并且会和之前储存的 dom 树进行比较,这个比较多过程就用到了 diff 算法,所以组件初始化的时候是用不到的。react 提出了一种假设,相同的节点具有类似的结构,而不同的节点具有不同的结构。在这种假设之上进行逐层的比较,如果发现对应的节点是不同的,那就直接删除旧的节点以及它所包含的所有子节点然后替换成新的节点。如果是相同的节点,则只进行属性的更改。
对于列表的 diff 算法稍有不同,因为列表通常具有相同的结构,在对列表节点进行删除,插入,排序的时候,单个节点的整体操作远比一个个对比一个个替换要好得多,所以在创建列表的时候需要设置 key 值,这样 react 才能分清谁是谁。当然不写 key 值也可以,但这样通常会报出警告,通知我们加上 key 值以提高 react 的性能。
组件的创造方法为 React.createClass() ——创造一个类,react 系统内部设计了一套类系统,利用它来创造 react 组件。但这并不是必须的,我们还可以用 es6 的 class 类来创造组件, 这也是 Facebook 官方推荐的写法。
- class
- MyComponent
- extends
- React
- .
- Component
- {
- constructor(props){
- super(props);
- this.state ={
- params:'',
- MyList:[]
- }
- }
这两种写法实现的功能一样但是原理却是不同,es6 的 class 类可以看作是构造函数的一个语法糖,可以把它当成构造函数来看,extends 实现了类之间的继承 —— 定义一个类 MyComponent 继承 React.Component 所有的属性和方法,组件的生命周期函数就是从这来的。 constructor 是构造器,在实例化对象时调用,super 调用了父类的 constructor 创造了父类的实例对象 this,然后用子类的构造函数进行修改。这和 es5 的原型继承是不同的,原型继承是先创造一个实例化对象 this,然后再继承父级的原型方法。 当我们使用组件时,其实是对 MyComponent 类的实例化,只不过 react 对这个过程进行了封装,使其看起来像一个标签。 props 访问父组件的属性 this.props.children 访问组件的孩子节点 可以用 React.Children.map() 遍历 this.props.children
来源: https://juejin.im/post/5a3a12cff265da43062ae964