# React高级

# PropTypes与DefaultProps

  • 每个组件都有自己的props参数,这个参数是从父组件接收的一些属性,如何对参数的类型做校验,如何定义参数的默认值,这就需要用到PropTypes和DefaultProps

# React中的ref

  • React中利用ref直接获取元素对应的DOM
  • 但是不推荐使用ref,React建议使用数据驱动的方式来编写代码,尽量不要直接操作DOM,除非在一些及其复杂的业务中,如操作动画
  • 使用ref可能会遇到各种各样的问题,如ref与setState合用时,可能会出现DOM更新不及时,导致获取的DOM不准确,原因时setState是异步的,如果希望页面更新后,在去获取DOM,一定要记得把获取DOM的语法,放在setState的第二个参数的函数中,它是一个回调函数

# Props,State与render函数

  • react是一个由数据驱动的框架,当数据发生变化,页面就会自动跟着变化,它背后的原理是什么呢?
  • 当组件的state或者props发生改变的时候,render函数就会重新执行
  • 当父组件的render函数被运行时,它的子组件的render函数都将被重新运行
  • 之所以子组件的render函数会被重新执行,可以从两方面理解:1)它的props本身发生了改变,2)它是父组件的一个子组件,父组件的render函数被重新执行时,子组件的render函数也会被重新执行一次

# React中的虚拟DOM

  • 当组件的props或者state数据发生变化的时候,组件的render函数就会重新被执行,组件就会被重新渲染,react中实现这种重新渲染,他的性能是非常高的,因为它引入了一个虚拟DOM的概念
  • 如何实现虚拟DOM?
    • 实现思路:
    思路1:
    1)定义一个state,先有数据
    2)再定义一份JSX模版(render函数中return返回的即为模版)
    3)将数据与模版结合,生成真实的DOM,来显示(render函数执行时,会把数据和模版相结合,
    来生成真实的DOM,然后把DOM挂载到页面上,这样就实现了页面的渲染) 
    4)一旦数据发生改变
    5)那就再做一次操作,用新的数据与模版结合,生成真实的DOM,替换原始的DOM
    缺陷:
    第一次生成了一个完整真实的DOM片段
    第二次又生成了一个完整真实的DOM片段
    第二次的DOM去替换第一次的DOM,三次操作都非常耗费性能
    
    思路2:
    1)state 数据
    2)JSX 模版
    3)将数据与模版结合,生成真实的DOM,来显示
    4)state 发生改变
    5)数据 + 模版 结合,生成真实的DOM,并不直接替换原始的DOM
    6)新的DOM(DocumentFragment,文档片段,存在与内存中)和原始的DOM做比对,找差异
    7)找出input框发生了变化
    8)只用新的DOM中的input元素,替换掉老的DOM中的input元素
    缺陷:
    性能提升不明显
    
    虚拟DOM底层原理:
    1)state 数据
    2)JSX 模版
    3)数据 + 模版 生成虚拟DOM(虚拟DOM就是一个js对象,用它来描述真实DOM)
    ['div', {id: 'abc'}, ['span', {}, 'hello']]
    // 用JS生成一个js对象,代价很小,但是用js生成一个DOM元素,代价极高,
    // 因为它在底层会调用一个WebApplication级别的一个api,这种级别的api性能损耗很大
    
    4)用虚拟DOM的结构,生成真实的DOM,来显示
    <div id="abc"><span>hello</span></div>
    
    5)state 发生变化
    
    6)数据 + 模版 生成新的虚拟DOM(极大的提升性能)
    ['div', {id: 'abc'}, ['span', {}, 'bye bye']]
    
    7)比较原始的虚拟dom和新的虚拟dom,找出区别是span中内容(极大的提升性能)
    // 比对两个JS对象,非常节约性能
    
    8)直接操作dom,改变span中的内容
    
    • react中引入虚拟DOM,为什么提升性能?因为它减少了对真实DOM的创建以及真实DOM的对比,取而代之,创建的都是JS对象,对比的也都是JS对象,通过这种方式,react底层实现了极大的性能飞跃
    • 虚拟DOM本质上就是一个JS对象,本质上它能提高性能,是因为js中去比较js对象不怎么耗费性能,去比较真实的DOM会很耗费性能

# 深入了解虚拟DOM

  • react底层操作: JSX模版 -> createElement ->(虚拟DOM)JS对象 -> 真实的DOM
return <div><span>hello</span></div>
return React.createElement('div', {}, React.createElement('span', {}, 'hello'))
  • JSX语法的存在,是为了方便代码书写,更方便,更简洁
  • 虚拟DOM带来的好处:
    • 性能提升(DOM的比对,变为js对象的比对)
    • 使得跨端应用得以实现(在网页中,可以使用虚拟DOM生成真实的DOM,进而在浏览器中渲染出真实的页面,在RN原生应用中,可以使用虚拟DOM生成原生的组件,进而展示原生的页面,所以React既可以开发网页应用,又可以开发原生应用)

# 虚拟DOM中的diff算法

  • 虚拟DOM(JS对象)比对的方式,就叫做Diff算法,(difference),实际上,React的Diff算法,大大的提升了两个虚拟DOM之间的比对性能
  • 什么时候数据会发生变化,其实都是当调用setState时,数据才会发生变化,然后虚拟DOM才重新比对
  • setState其实是异步的,它在react底层的一个性能优化实现是,将多次setState结合为一次setState,减少虚拟DOM比对的次数
  • Diff算法比对两个虚拟DOM差异时,它会逐层比对,如果一层不满足匹配要求,那下面的就不会再去比对了,直接废弃掉,用新的替换掉旧的
  • Diff算法中,有个很重要的概念,叫做同层比对,即只比对同层虚拟DOM,发现差异后,不会再继续比对,整个对原始DOM进行全部替换,好处:比对算法简单,比对速度很快,虽然可能造成DOM重新渲染的一些浪费,但大大减少了DOM比对算法上的性能损耗
  • 为什么React中做列表循环时,要引入key值?在做虚拟DOM的比对循环时,当增加key值后,虚拟DOM的比对,会根据key值做关联,有关联的元素就可以直接复用,只需要把没有做关联的元素增加到DOM树上即可,这样就提高了虚拟DOM比对的性能,但是要提高性能有一个前提,就是比对前后的key值不变,key值要保持稳定,所以不能用index做key值,因为增删dom后,index就移位了,元素对应的key值就不稳定了,这就失去了key值的意义,无法做关联复用了