-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
react相关 #34
Comments
begin work & complete work |
commit |
event 委托在document,所以大概流程是,点击click,原生事件由顶到目标元素捕获阶段,目标元素阶段,目标元素往顶冒泡阶段,冒泡到document,document拿到触发事件的dom,从dom上获取到react实例,找到实例的所有祖先元素,模拟一遍捕获阶段,目标元素,模拟一遍冒泡阶段; 问题,两个父子reactDom.render(parent,) render(child),child取消冒泡也没用,还是会冒泡到parent react 17事件系统 当根容器接收到捕获事件时,先触发一次 React 事件的捕获阶段,然后再执行原生事件的捕获传播。所以 React 事件的捕获阶段调用e.stopPropagation()能阻止原生事件的传播。 https://musicfe.com/react-event/ |
fiberreact15在render阶段的reconcile是不可打断的,这会在进行大量节点的reconcile时可能产生卡顿,因为浏览器所有的时间都交给了js执行,并且js的执行时单线程。为此react16之后就有了scheduler进行时间片的调度,给每个task(工作单元)一定的时间,如果在这个时间内没执行完,也要交出执行权给浏览器进行绘制和重排,所以异步可中断的更新需要一定的数据结构在内存中来保存工作单元的信息,这个数据结构就是Fiber。 function FiberNode(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
) {
//作为静态的数据结构 保存节点的信息
this.tag = tag;//对应组件的类型
this.key = key;//key属性
this.elementType = null;//元素类型
this.type = null;//func或者class
this.stateNode = null;//真实dom节点
//作为fiber数架构 连接成fiber树
this.return = null;//指向父节点
this.child = null;//指向child
this.sibling = null;//指向兄弟节点
this.index = 0;
this.ref = null;
//用作为工作单元 来计算state
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.dependencies = null;
this.mode = mode;
//effect相关
this.effectTag = NoEffect;
this.nextEffect = null;
this.firstEffect = null;
this.lastEffect = null;
//优先级相关的属性
this.lanes = NoLanes;
this.childLanes = NoLanes;
//current和workInProgress的指针
this.alternate = null; |
virtual domvirtual dom是js对象来描述dom的结构和信息,由react.createElement生成 大量的dom操作慢,很小的更新都有可能引起页面的重新排列,js对象优于在内存中,处理起来更快,可以通过diff算法比较新老virtual Dom的差异,并且批量、异步、最小化的执行dom的变更,以提高性能 另外就是可以跨平台,jsx --> ReactElement对象 --> 真实节点,有中间层的存在,就可以在操作真实节点之前进行对应的处理,处理的结果反映到真实节点上,这个真实节点可以是浏览器环境,也可以是Native环境 virtual Dom真的快吗?其实virtual Dom只是在更新的时候快,在应用初始的时候不一定快 React.createElement的源码中做了如下几件事 处理config,把除了保留属性外的其他config赋值给props 结构: {
$$typeof: REACT_ELEMENT_TYPE,//表示是ReactElement类型
type: type,//class或function
key: key,//key
ref: ref,//ref属性
props: props,//props
_owner: owner,
}; |
react为什么要升级为什么因为老的react架构方式,一旦reconciler开始就只能执行到结束,又因为js单线程,所以此过程中如果有其他任务进来就得一直等待,造成卡顿 新的架构,希望reconciler可以中断 react的几个重大更新v0.14 => v15 v15.4 v16 v16.2 v16.3 v16.6 v16.8 v16.9 v17 v18 |
react工作流程概览触发更新(初次/更新)ReactDOM.render
this.setState this.forceUpdate render阶段产生的更新包装成task,(task长啥样)
push进taskQueue 或者 timerQueue 等待被schuler消费(调度逻辑)
reconciler,(做了些什么)生成wip树(一个task执行完成,或者单个task执行时间过长,都会判断超时)
commit阶段渲染界面,wip 替换 current tree
|
状态管理解决方案 |
调度:
1、大致流程
我们可以将react的运行流程大致分为输入与输出,输入包括首次渲染或者其他更新setState等等,输出就是指将生成的dom插入到页面中;cocurrent模式下,当输入发生时,输入会被包裹成一个task,放进taskqueue 或者 timer queue种被schuler进行消费;
为什么cocurrent?为了不让react长时间阻塞浏览器线程,造成页面卡顿或者页面假死。
怎么做,输入包裹成task,让schuler异步消费,schuler设定最大执行时间,每次执行完一个task,执行下一个task前判断是否超过最大时间,超过则取消task循环调用,让出线程。当然每个task也有可能执行时间过长,那么task内部也要进行判断;让出后,立刻注册下一回event loop的调用;
如何实现下一回的自动调用呢?使用了message channel,会在宏任务时机让浏览器调用,浏览器也会对调用时机做一些优化。
为什么不用setTimeout呢?
为什么不用RAF呢?RAF的执行时机是微任务,在一个event loop中,微任务会一直执行,不能起到让出线程的效果
为什么不用requestIdleCallback呢?因为这个方法受限于浏览器刷新频率的限制,调用频率不能保证。
既然维护了队列循环执行,那么就得有优先级,保证高优先级任务先执行。
schuler消费时机,是监听message channel的回调,
task queue 和 timer queue是小顶堆,保证每次
1、为什么用小顶堆,不用数组sort
因为task queue 是动态的,时刻在插入和推出,每次这样操作后都要重新调整数组内item的排序,而小顶堆的时间复杂度是logn,
sort的复杂度是nlogn
https://cloud.tencent.com/developer/article/2109572
https://www.jianshu.com/p/15a29c0ace73
https://www.qinguanghui.com/articles/react-concurrency
https://juejin.cn/post/6914089940649246734
https://github.com/xitu/gold-miner/blob/master/TODO1/scheduling-in-react.md
https://juejin.cn/post/6889314677528985614
http://www.paradeto.com/2020/12/30/react-concurrent-1/
The text was updated successfully, but these errors were encountered: