【react学习,知识点整理】

协调

确定该对宿主实例(真实dom节点的树)做什么来响应新的信息有时候叫做协调
简单讲就是将虚拟dom映射到真实dom的过程

闭包

如果外层函数定义了内层函数,则内层函数的作用域和作用域链都和外层做了绑定,如果返回了内层函数或异步使用了外层函数的变量(异步引用了原本的变量,只要引用了就不会被销毁),只要内存函数还在使用,这些作用域链上的变量就不会被销毁

setStatu(s=>s+1)函数式赋值之所以同步,因为里面的s未使用外层变量s,是useStatus自己的变量,所以不是闭包。

react元素

<dialog className="box">
  {message}
  <input />
</dialog>

这样的jsx格式在react眼里是这样的:

{
  type:"dialog",
  props:{
		className:"box",
    children:[
      message, // 此处的message的jsx内容已经有占位,所以改变也不会影响到input,不会导致
      {         // input重建,可以重用react实例
        type:"input",
        props:{}
      }
    ]
  }
}

react元素只有重建和删除

组件的纯净性和幂等性

只要调用组件多次是安全的,不会影响其他组件的渲染,react不会关心代码是否想严格模式下的函数式编程一样百分百纯净。在react中,幂等性比纯净性更重要。
简单来说,多次调用函数组件在屏幕上不能看到任何变化

纯函数(pure funtions)

只依赖输入参数的函数,并且返回值也依赖参数。不引用和影响函数外的变量,不产生console结果,写入文档,传递网络信息,任何异步操作等副作用。

优点:
1对于这种函数参数不变时,chrome v8 引擎会返回缓存的结果,而不是重新运行结果
2函数的结果更易预测,不会影响其他函数和组件

Pure Component
纯组件,是react component的一种,在props和state不改变时不会重新渲染,但注意只会进行浅比较,复杂数据类型只会比较引用从而阻断重新渲染

组件嵌套和递归

当元素类型是个组件函数时,react会调用这个函数组件,然后询问组件想要渲染什么函数。例如
● 你: ReactDOM.render(, domContainer)
● React: App ,你想要渲染什么?
○ App :我要渲染包含 的 。
● React: ,你要渲染什么?
○ Layout :我要在

中渲染我的子元素。我的子元素是 所以我猜它应该渲染到
中去。
● React: ,你要渲染什么?
○ :我要在
中渲染一些文本和

● React:
,你要渲染什么?
:我要渲染含有文本的

● React: 好的,让我们开始吧:

// 最终的 DOM 结构

<div>
  <article>
    Some text
    <footer>some more text</footer>
  </article>
</div>

为什么不直接调用函数组件而是<组件 />

因为直接调用 组件()react不知道这个组件,而<组件 />这种方式react就知道这是一个组件并且会自己掉用它,这就是控制反转。还有好几个好处。

  1. react能用在树中与组件本身紧密相连的局部状态来增强组件特性。
  2. react能更了解元素树的结构
  3. 推迟协调,让浏览器在组件调用之间做一些工作,这样重渲染大量组件时就不会阻塞主线程
  4. 惰性求值fn1( fn2() ) fn2先执行,fn1后执行,如
<Page>
  {Form()} // 函数组件Form直接调用
</Page>

react元素树变成

{
  type:Page,
  props:{
    children:[{
      type:Form()  // 此时不论Page是否想渲染它,它都会立即执行
    }]
  }
}

react将所有工作分为渲染阶段和提交阶段

渲染阶段是react调用你的组件然后进行协调的过程,在此阶段 进行干涉是安全的且在未来这个阶段会变成异步的
提交阶段就是react操作宿主树的时候,这个阶段永远是同步的

批量更新

react会在所有事件排队触发完后再进行批量更新,否则同时三次设置状态则会渲染三次组件。

为什么hook总在顶层调用

因为react的状态和在树中与其相关的组件紧密连接在一起,即状态都是定义为组件的属性的。
就像import都在顶层调用是一样的。如果在条件和子函数中调用则会改变状态的作用域。

effect状态过期

如果effect中直接或间接(比如函数包含)引用了props或status,并且没有添加进依赖,则因为闭包的缘故,这些引用是不会因为外界的更新而更新的。

react的视图更新 <div>{a}</div>

react的状态和props更新,使得视图发生更新,不是因为双向绑定啥的,状态和props也只是普通值,就像const a=1一样,只是它们会触发组件重新渲染(相当于组件被重新调用),就像const a=2被重新赋值,视图上的a自然也更新成2了。
ps:在组件每次被调用并重复渲染时,每次包含的状态或者props或者其他数据或者effect都独立于其他的渲染,并且每一次渲染的事件处理函数也是独立于其他渲染的,就好像多次调用普通函数一样

function sayHi(person) {
  const name = person.name;
  setTimeout(() => {
    alert('Hello, ' + name);
  }, 3000);
}

let someone = {name: 'Dan'};
sayHi(someone);

someone = {name: 'Yuzhi'};
sayHi(someone);

someone = {name: 'Dominic'};
sayHi(someone);

useEffect的清除函数执行的时机

是在重新渲染ui后,执行上次渲染的effect的清除函数,然后再执行这次的effect函数

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.id, handleStatusChange);
    };
  });

● React 渲染{id: 10}的UI。
● 浏览器绘制。我们在屏幕上看到{id: 10}的UI。
● React 运行{id: 10}的effect。
● React 渲染{id: 20}的UI。
● 浏览器绘制。我们在屏幕上看到{id: 20}的UI。
● React 清除{id: 10}的effect。
● React 运行{id: 20}的effect。
ps:执行上次effect的清理函数时,其中的参数和状态还是上一个函数保留的

useEffect设置状态时,此状态依赖另一个状态时

此时或者写类似setSomething(something => …)的代码时用useReducer去替换它们非常合适。
reducer可以让你把组件内发生了什么(actions)和状态如何响应并更新分开表述

const [state, dispatch] = useReducer(reducer, initialState);
const { count, step } = state;

useEffect(() => {
  const id = setInterval(() => {
    dispatch({ type: 'tick' }); // Instead of setCount(c => c + step);
  }, 1000);
  return () => clearInterval(id);
}, [dispatch]);

react会保证dispatch在组件声明周期内保持不变,所以上面的例子中不用再重新订阅定时器
1.useReducer里,每次重新渲染组件时react记住的是action,即{type:‘tick’},他会调用下一次渲染的reducer,这样就能保证每次reducer里的props和status是最新的,因为不是在effect里调用reducer
,所以可以去除不必要的依赖
2.如果某个函数不依赖组件里的任何值,那么可以将它提取到外部,减少依赖
3.class生命力周期组件发现不了函数改变,而useCallback和effect可以

jsx的原理

jsx每个html标签都是调用react.createElement方法创建,返回一个ReactElement方法,这个方法返回一个虚拟dom ,即

{
  type:Page,
  props:{
    children:[{
      type:Form()  // 此时不论Page是否想渲染它,它都会立即执行
    }]
  }
}

jsx代码>babel编译>React.createElement调用>ReactElement调用>虚拟dom>作为参数传入ReactDOM.render()>渲染处理>真实DOM

请求竞态

在生命周期update或者函数组件状态改变时会重新请求,如果此时发生多次更新,但是请求速度不一,可能前一次请求结果覆盖了最新的请求结果,此时effect中可以返回一个清楚函数清除上一个请求,或者不让上一个请求赋值。如下:

useEffect(() => {
  let didCancel = false;

  async function fetchData() {
    const article = await API.fetchArticle(id);
    if (!didCancel) {
      setArticle(article);
    }
  }

  fetchData();

  return () => {
    didCancel = true;
  };
}, [id]);

虚拟dom的价值

● 研发体验/研发效率的提升,数据驱动视图,声明式编程,不用过多关注dom操作而专注于数据处理,在数据更新量不大的时候,性能也不错
● 跨平台开发
● 批量更新,多次差量更新用户只能看到最后一次更新效果,但是每次更新都会重复渲染页面,这时batch函数缓冲每次生成的补丁集最后批量生成更新

diff算法

1.分层对比,尽量不要做跨层级的操作
2.相同类型的组件才有对比的必要性,不同类型组件直接替换
3.key属性帮助react记住同层级的节点,当重新渲染时相同key的节点就不会删除直接复用
● diff是深度优先遍历节点进行对比,调和器(协调)也是,重复父组件调用子组件的过程,直到最深一层节点更新完毕,再向上回朔,react15的 stack Reconciler 是同步进行渲染的,不能打断,所以可能会出现卡顿/卡死等问题

Fiber架构

FIber是比线程还要纤细的一个过程,所谓“纤程”,意在对渲染过程实现更精细的控制
● 架构角度:Fiber是对react核心算法的重写,实现增量渲染,目的是任务的可中断,可恢复,并赋予不同的优先级
○ Fiber把渲染更新过程拆分成多个子任务根据优先级执行。当时间不足时挂起当前任务
○ 增加了异步任务,调用requestIdleCallback api,浏览器空闲的时候执行之前未能完成的任务
render阶段:纯净没有副作用,可能暂停终止或重新启动,在内存中做计算,diff明确dom树的更新点,完成Fiber树的构建
commit:可以读取使用dom,运行副作用,安排更新

● 编码角度:FIber是React内部所定义的一种数据结构
○ dom diff树变成了链表,一个dom对应两个fiber(一个链表),对应两个队列,这都是为找到被中断的任务,重新执行
● 工作流:Fiber节点保存了组件需要更新的状态和副作用
FIber架构的重要特征就是可以被打断的异步渲染模式。
render阶段执行过程中允许暂停、终止、重启,是异步的,commit是同步的

对比React 15 的 Stack Reconciler:
当 React在渲染组件时,从开始到渲染完成整个过程是一气呵成的,无法中断
如果组件较大,那么js线程会一直执行,然后等到整棵VDOM树计算完成后,才会交给渲染的线程
这就会导致一些用户交互、动画等任务无法立即得到处理,导致卡顿的情况

react合成事件

js事件委托
利用事件的捕获和冒泡,将多个子元素的同一类型监听逻辑,合并到父元素上,通过一个监听函数来管理的行为(如ul下的每个li都输入其中的文字,则可以在ul上绑定监听)

react的合成事件,事件在具体dom上触发时,冒泡到document,在document上绑定统一的事件处理程序,将事件分发到具体的组件实例。
优点:底层抹平浏览器差异,上层暴露统一、稳定、与dom原生事件相同的事件接口
(若要通过react事件访问原生dom事件,e.nativeEvent)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值