深入React知识点整理(一)
使用React也满一年了,从刚刚会使用到逐渐探究其底层实现,以便学习几招奇技淫巧从而在自己的代码中使用,写出高效的代码。下面整理一些知识点,算是React看书,使用,感悟的一些总结:
1. 函数式编程函数式编程是一种如何编写程序的方法论,与之对应的就是命令式编程。 以我自己的理解,函数式编程就是以函数为中心,将大段过程拆成一个个函数,组合嵌套使用。这个思想在JavaScript中很常见。举个阮一峰老师的例子: 我们有一个数学表达式: (1 + 2) * 3 - 4 将上述表达式不假思索的转换成代码: const a = 1 + 2; const b = a * 3; const c = b - 4; 以函数式编程思想:将运算过程定义成不同的函数,如下: const result = substract(multiply(add(1,2),3),4); 是不是感觉很高端但又一脸懵逼。没错,函数式编程在处理大段过程中就显得很容易理解,但是简单逻辑中就显得复杂,因为封装起来的函数需要时间去阅读。 对上述表达式进行变形: add(1,2).multiply(3).subtract(4); 是不是也很熟悉。函数式编程在JavaScript中应用确实很普遍。 目前最当红的Python、Ruby、Javascript,对函数式编程的支持都很强,就连老牌的面向对象的Java、面向过程的PHP,都忙不迭地加入对匿名函数的支持。越来越多的迹象表明,函数式编程已经不再是学术界的最爱,开始大踏步地在业界投入实用。 这里不做多介绍,有兴趣可以看看:
2.React事件系统React事件与DOM事件React 基于 Virtual DOM 实现了一个 SyntheticEvent (合成事件)层,我们所定义的事件处理器会接收到一个 SyntheticEvent 对象的实例,它完全符合 W3C 标准,不会存在任何 IE 标准的兼容性问题。并且与原生的浏览器事件一样拥有同样的接口,同样支持事件的冒泡机制,我们可以使用 stopPropagation() 和 preventDefault() 来中断它。 使用React的时候都知道,React有一套自己的事件系统,典型的特征就是元素绑定事件都要使用React提供的事件接口: // in html <button onclick="activateLasers()"> Activate Lasers </button> // in React <button onClick={activateLasers}> Activate Lasers </button> React的合成事件实际上是做了一层事件委托(事件代理): 它并不会把事件处理函数直接绑定到真实的节点上,而是把所有事件绑定到结构的最外层,使用一个统一的事件监听器,这个事件监听器上维持了一个映射来保存所有组件内部的事件监听和处理函数。当组件挂载或卸载时,只是在这个统一的事件监听器上插入或删除一些对象;当事件发生时,首先被这个统一的事件监听器处理,然后在映射里找到真正的事件处理函数并调用。这样做简化了事件处理和回收机制,效率 也就是说React使用了一个事件代理,所有事件绑定都只是事件代理保存了一个映射,事件发生的时候,调用处理函数,并没有真正的使用原生事件。我们来看一个例子: componentDidMount () { document.querySelector('#testEvent').addEventListener('click',(e)=>{ console.log('dom event'); console.log(e); }) } componentDidUnMount () { document.querySelector('#testEvent').removeEventListener('click'); } handleClick (e) { console.log('react event'); console.log(e); } render () { return ( <div> <div onClick={::this.handleClick}>Test React Event</div> <div id='testEvent'>Test dom Event</div> </div> ); } 这里有两个div,使用React绑定事件和原生DOM事件,两种事件绑定方法不同导致相同的效果,完全不同的原理。 使用原生DOM绑定打印的事件就是原生的,React事件打印出来的事件: 可以看到是个Proxy对象,里面有触发事件的target和处理事件的handler,这就是React的合成事件。 另外如果在react中绑定原生事件,组件卸载的时候记得解除绑定,避免内存泄漏。 React的合成事件还有一个优点在于不需要处理浏览器事件兼容性,方便操作。 原生事件分成三个部分:事件捕获,目标事件处理,事件冒泡。IE9以下不支持事件捕获,所以React没有实现它,仅支持事件冒泡。 所以,请尽量避免在 React 中混用合成事件和原生 DOM 事件。因为两者是不同的事件系统,阻止 React 事件冒泡的行为只能用于 React 合成事件系统中,且没办法阻止原生事件的冒泡。反之,在原生事件中的阻止冒泡行为,却可以阻止 React 合成事件的传播。 3.高阶组件高阶组件是React中比较有特点的一类问题,高阶组件(High Order Component)文章里单独进行了详细介绍。 这里只是补一张图:组合式组件开发实践 4.组件性能优化从过往的经验与实践中,我们都知道影响网页性能最大的因素是浏览器的重绘(reflow)和重排版(repaint)。React 背后的 Virtual DOM 就是尽可能地减少浏览器的重绘与重排版。 关于浏览器重绘和重排版问题,请看我之前的文章:浏览器渲染页面过程与页面优化 这里要介绍的就是:
对这块有兴趣的,推荐几篇文章:
5.React源码初探React项目目录构成如下图:
renderers包包含内容:
这里简单介绍React目录构成以及每块的功能,大致了解,需要的时候找到对应位置深入研究。 React 也能够实现 Virtual DOM 的批处理更新,当操作 Virtual DOM 时,不会马上生成真实的DOM,而是会将一个事件循环(event loop)内的两次数据更新进行合并,这样就使得 React 能够在事件循环的结束之前完全不用操作真实的 DOM。 6.VirtualDOM 模型VirtualDOM是React的一个核心,也是React一个著名的特点,之前我有篇文章对此有过简单的介绍,以及如何简单实现根据VirtualDOM渲染页面:React学习报告,可以做基本入门查看。 VirtualDOM与真实DOM的关系很简单:
Virtual DOM中的节点成为ReactNode,分成ReactELement,ReactFragment,ReactText。ReactElement又分成ReactComponentElemnt和ReactDOMElement。 下面是 ReactNode 中不同类型节点所需要的基础元素: type ReactNode = ReactElement | ReactFragment | ReactText; type ReactElement = ReactComponentElement | ReactDOMElement; type ReactDOMElement = { type : string,props : { children : ReactNodeList,className : string,etc. },key : string | boolean | number | null,ref : string | null }; type ReactComponentElement<TProps> = { type : ReactClass<TProps>,props : TProps,ref : string | null }; type ReactFragment = Array<ReactNode | ReactEmpty>; type ReactNodeList = ReactNode | ReactEmpty; type ReactText = string | number; type ReactEmpty = null | undefined | boolean; 这里以DOM标签(ReactDOMComponent)为例,介绍VirtualDOM模型如何创建节点: 属性更新当执行 mountComponent 方法时,ReactDOMComponent 首先会生成标记和标签,通过 this.createOpenTagMarkupAndPutListeners(transaction) 来处理 DOM 节点的属性和事件。
其实,早有开发者向 React 官方提过问题,建议去掉这个鸡肋的属性标识(data-reactid)这终于在 React 15.0版本上实现了。据官方宣称,去除 data-reactid 使得 React 性能有了 10% 的提升。 更新子节点当执行 mountComponent 方法时,ReactDOMComponent 会通过 this._createContentMarkup(transaction,props,context) 来处理 DOM 节点的内容。 先是删除不需要的子节点和内容。如果旧节点存在,而新节点不存在,说明当前节点在更新后被删除,此时执行方法 this.updateChildren(null,transaction,context);如果旧的内容存在,而新的内容不存在,说明当前内容在更新后被删除,此时执行方法 this.updateTextContent('')。 再是更新子节点和内容。如果新子节点存在,则更新其子节点,此时执行方法 当卸载组件时,ReactDOMComponent 会进行一系列的操作,如卸载子节点、清除事件监听、清空标识等。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |