betasu / just-react Goto Github PK
View Code? Open in Web Editor NEW「React技术揭秘」 一本自顶向下的React源码分析书
Home Page: https://react.iamkasong.com/
License: MIT License
「React技术揭秘」 一本自顶向下的React源码分析书
Home Page: https://react.iamkasong.com/
License: MIT License
卡子哥,我知道你想表达的是旧架构栈调和 无法实现“可中断异步更新”,但是关于" Reconciler和Renderer是交替工作的,当第一个li在页面上已经变化后,第二个li再进入Reconciler " 这一句话的表述是否存在问题呢?
以上来自 陈屹的《深入 React 技术栈》,针对的是 React15。
以上是官方文档对旧架构栈调和的实现说明:https://zh-hans.reactjs.org/docs/implementation-notes.html。
综上所述,我的理解是:Reconciler和Renderer是交替工作的,但是在一次更新中 协调器Reconciler 工作结束后才是渲染器 Render 工作,当渲染器处理 第一个li在页面上的变化,紧接着就会处理第二个li在页面上的变化,如果这时候中途中断更新会看见不完整的变化,但实际上旧架构 Reconciler 和 Render 一起工作,并且各自是不可中断的,是一个长任务,导致帧的 Layout / Paint 缺失,也就是掉帧,给用户的体验就是卡顿,新架构启用 fiber,使得Reconciler工作异步可中断,长任务分割为短任务,保证页面渲染不掉帧,Render 同步工作,保证页面变化完整性。
不知道有什么错误的地方,希望卡子哥指点下~
update 树结构
这一块的描述看的我云里雾里,例子中就给出来 u1, u0 两个 update,不够生动,
建议描述大于2个 update 的情况。
当我自己画出来的时候,一切都清晰了。
react 为什么使用深度优先遍历构建fiber树 而不是广度优先呢
目前React关于Scheduler设计存在两个队列:保存未就绪任务timerQueue和保存已就绪任务taskQueue,每次调度时会判断未就绪任务是否已就绪,是加入到taskQueue。但是从过期时间的角度来看,貌似这种设计有点冗余,如果把延迟的任务也转换为过期时间,然后放在统一的任务队列(假设是taskQueue),统一走最小堆的维护和取值策略,那是不是也能满足需求,并且逻辑上也更简单点
if (hook.queue.pending) {
let firstUpdate = hook.queue.pending.next;
do {
const action = firstUpdate.action;
baseState = action(baseState);
firstUpdate = firstUpdate.next;
} while (firstUpdate !== hook.queue.pending)
hook.queue.pending = null;
}
hook.queue.pending = null;为空的话dispatchAction方法的else逻辑不会执行?
在看卡老师的电子书的 render阶段章节https://kasong.gitee.io/just-react/process/fiber.html,发现有一处地方好像和官方文档https://reactjs.org/docs/faq-internals.html有点点不一样:
1、卡老师的书中提到:“虚拟DOM在React中有个正式的称呼——Fiber。 ...... 我们会逐渐用 Fiber 来取代 React 16 虚拟DOM 这一称呼”:
2、但是官方文档提到:React 的“虚拟DOM”的实现其实包含了 React Element 和 fiber 两者:
如题,
按照多节点二次遍历的讲解思路,这里好像只介绍了第两次遍历后,old/new都没遍历完的情况中默认节点一一对应只是位置变化,但对于新节点的key完全变成新的(此种情况也会触发立即跳出第一次遍历,且在文档中介绍的第二次遍历中符合第四种情况【newChildren与oldFiber都没遍历完】),但此时,在oldMap中一个newFiber也找不存在,此种更新节点场景没有介绍
这样打印出来后,方便地铁上看,还能用笔做记录
用你在码云上打包,yarn link到创建项目里之后 跑起来就报各种依赖找不到为啥啊。。。
@BetaSu 看到React15架构这一节,有两个疑问
beforeMutation阶段
就讲过,before mutation阶段在scheduleCallback中调度flushPassiveEffects
而在layout阶段
说
对于FunctionComponent及相关类型,他会调用useLayoutEffect hook的回调函数,调度useEffect的销毁与回调函数
光从以上两段论述中,是不是可以说 useEffect的相关回调在beforeMutation和layout两个阶段都被调用了啊
17.0.2源码我大概看了下 对应位置的代码似乎变成了:
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent:
case Block:
{
// At this point layout effects have already been destroyed (during mutation phase).
// This is done to prevent sibling component effects from interfering with each other,
// e.g. a destroy function in one component should never override a ref set
// by a create function in another component during the same commit.
{
commitHookEffectListMount(Layout | HasEffect, finishedWork);
}
schedulePassiveEffects(finishedWork);
return;
}
https://react.iamkasong.com/process/reconciler.html#%E4%BE%8B%E5%AD%90
看图上所示的每个component都会执行到beginWork和completeWork,但实际上并不是这样的
completeWork只有在child没有的时候才会执行,然后找到fiber树上一个有slibing的组件执行beginWork
这样并不是所有组件都会有completeWork
尝试打印了下自己的代码大概如下
beginWork App
scheduler.development.js:468 beginWork RecoilRoot
scheduler.development.js:468 beginWork RecoilRoot_INTERNAL
2scheduler.development.js:468 beginWork undefined
scheduler.development.js:468 beginWork Batcher
scheduler.development.js:468 completeUnitOfWork Batcher
scheduler.development.js:468 beginWork GlobalThemeProvider
scheduler.development.js:468 beginWork undefined
scheduler.development.js:468 beginWork ThemeProvider
2scheduler.development.js:468 beginWork undefined
scheduler.development.js:468 beginWork CssBaseline
scheduler.development.js:468 beginWork LanguageProvider
scheduler.development.js:468 beginWork undefined
scheduler.development.js:468 beginWork ThemeProvider
scheduler.development.js:468 beginWork undefined
scheduler.development.js:468 beginWork Initer
scheduler.development.js:468 beginWork SnackbarProvider
scheduler.development.js:468 beginWork undefined
scheduler.development.js:468 beginWork AxiosIniter
scheduler.development.js:468 completeUnitOfWork AxiosIniter
scheduler.development.js:468 beginWork null
scheduler.development.js:468 completeUnitOfWork null
非常感谢repo主的文章,看过很多react分析的文章,能通过这样言简意赅的方式讲出来的真的不多,在此感谢,受教了!
https://react.iamkasong.com/process/fiber.html
https://react.iamkasong.com/process/reconciler.html
在 render阶段
中对于B&C流程(callstack分析和讲解)非常好,但是感觉缺少了我们在initial Mount和update进入B&C,到底是怎么建立起来的Fiber树 (child, silbing, return, DFS相关的workLoop) 的呢 。如果能在这方面有所讲解感觉会更容易我们理解B&C的意义。
个人感觉这方面比较好的文章:
自己会继续追您写的解析哈,感谢!
/Users/xxxx/resource/project/front-end/github/react/packages/react/index.js:11
export type StatelessFunctionalComponent<
^^^^^^
SyntaxError: Unexpected token export
at Module._compile (internal/modules/cjs/loader.js:721:23)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
at Module.require (internal/modules/cjs/loader.js:690:17)
at require (internal/modules/cjs/helpers.js:25:18)
at Object. (/Users/huanghao/resource/project/front-end/demo/a-react-demo/node_modules/react-scripts/scripts/start.js:52:15)
at Module._compile (internal/modules/cjs/loader.js:776:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
卡子哥,这节代数效应与Fiber中的描述是否有误?
所谓代数效应就是解耦程序逻辑和具体实现,目的是:
JS 中并没有代数效应的概念,Generator 污染外层函数也没有践行代数效应的理念,正因此 React 自己实现 Fiber,作为协程的一种实现,并且践行了代数效应,算是对 JS 的一种扩展。
结论:“所以,我们可以将纤程(Fiber)、协程(Generator)理解为代数效应**在JS中的体现” 应该修改为 “所以,我们可以将纤程(Fiber)理解为代数效应**在JS中的体现”。
不知描述是否有误,请多指教!
Can someone please point me to an English translation of this book
原因有以下几点:
因为是由 FiberRootNode
实例化而来
rootFiberNode 和 rootFiber 意思感觉有点相近,容易混淆
fiberRootNode 语义更符合,表示 fiber 树的根节点,而 rootFiberNode 则表示根 fiber 节点
源码中叫 fiberRoot
如果你觉得需要改的话,我可以贡献一个 PR 😆
在React15架构的最后一段,描述递归更新的缺点描述时,举的例子和我的理解有些出入,这里想提出来讨论一下。
我记得v15的stack的问题在于,节点一层套一层,必须同步递归处理要更新的节点和所有子节点,这样一来如果超过16ms,就会造成用户操作失去响应。更新过程中如果要支持其他异步操作,保存现场的开销会很大。而fiber用了链表和双缓存的结构来改进。
下面给出的例子,是出现在替换真实节点的时候,那就体现不了stack的上述缺点。
我认为真实节点的更新,在v16里还是“同步”操作,不会更新到一半,去处理其他异步任务。如果这里用户看到了未更新的节点,那在v16里应该也是一样的。
如题?请问如何付费?因为无法注册,没有电话。谢谢。
如题,commit的before mutation阶段,源码分为commitBeforeMutationEffects_begin,commitBeforeMutationEffects_complete,使用subtree的方式进行遍历。除此之外,flushPassiveEffects的遍历方式也有一定变化,大佬有时间可否更新下?
function commitBeforeMutationEffects_begin() {
while (nextEffect !== null) {
var fiber = nextEffect; // TODO: Should wrap this in flags check, too, as optimization
var deletions = fiber.deletions;
if (deletions !== null) {
for (var i = 0; i < deletions.length; i++) {
var deletion = deletions[i];
commitBeforeMutationEffectsDeletion(deletion);
}
}
var child = fiber.child;
if ((fiber.subtreeFlags & BeforeMutationMask) !== NoFlags && child !== null) {
ensureCorrectReturnPointer(child, fiber);
nextEffect = child;
} else {
commitBeforeMutationEffects_complete();
}
}
}
function commitBeforeMutationEffects_complete() {
while (nextEffect !== null) {
var fiber = nextEffect;
{
setCurrentFiber(fiber);
invokeGuardedCallback(null, commitBeforeMutationEffectsOnFiber, null, fiber);
if (hasCaughtError()) {
var error = clearCaughtError();
captureCommitPhaseError(fiber, fiber.return, error);
}
resetCurrentFiber();
}
var sibling = fiber.sibling;
if (sibling !== null) {
ensureCorrectReturnPointer(sibling, fiber.return);
nextEffect = sibling;
return;
}
nextEffect = fiber.return;
}
}
首先感谢大佬的教程,详细,对新手真的太友好了,如果出书了我马上下单。
这里我尝试了一下,在第一章中的拉取源码就卡住很久了,我花费了半天,也没能从 GitHub 上把 react 源码拉下来,后来我突然想到码云有很多镜像,尝试之后发现速度真的快,所以推荐码云镜像作为国内下载地址,这个镜像在地址是:
https://gitee.com/mirrors/react
我的环境:
作为小白最怕这样一开始卡住,这里感谢一下码云😝
永远的神,群0^0
作者这里https://react.iamkasong.com/process/beginWork.html#effecttag说RootFiber在mount阶段就会有alternate的原因在双缓存那一节,究竟是什么原因,为什么我没有找到🥶
拉取当前最新版本的react,执行响应的build命令之后没有打包出node_modules文件夹是怎么回事呀
新 acbd
旧 abcd
按照分析来说,
第一轮遍历 确定 a 可以复用,并在遍历 第二个元素 c 的时候 发现 key 不同,停止了遍历 进入了 第二轮遍历。
此时:
old bcd
new cbd
开始 第二轮遍历 -> c
lastPlacedIndex = 0
oldIndex = 2
oldIndex >= lastPlacedIndex
不移动且 lastPlacedIndex = oldIndex = 2
-> b
oldIndex = 1
oldIndex < lastPlacedIndex
标记移动 lastPlacedIndex = 2 不变
-> d
oldIndex = 3
oldIndex >= lastPlacedIndex
不移动且 lastPlacedIndex = oldIndex = 3
至此遍历完毕。
只有 b 元素被标记移动, 但是没有说要怎么移动, 移动到最右边吗。
但是实际中,也只有移动到 c 的后面
极简hooks实现一章中,存在多个update时,update数据结构的queue(环状单向链表)是只保留第一个和最后一个update吗?这样设计是为啥呢?
异步可中断的文档建议更新上去,不然有什么意义,一篇完整的教程应该全
作为一个从 Vue 技术栈转入的同学,我深感 React 社区文化的浓厚(潜台词:TMD 工程环境真难配)
大佬可否出一期文章,教教大家怎么样构建一个更系统、更工程化一点的 React 项目,让大家用起来学起来没那么懵 😭
实现篇-多节点diff-Demo2
Demo1中的新旧节点的对比,因为在遍历中第二次中断,所以lastPlacedIndex的值为a的索引0。
// 之前
abcd
// 之后
acdb
但是Demo2中遍历第一次就中断,lastPlacedIndex的值为何还是0呢?
小书原文描述位置:https://react.iamkasong.com/process/completeWork.html#effectlist
react官方commit更改信息:Remove remaining references to effect list #19673
We no longer use the effect list anywhere in our implementation. It's been replaced by a recursive traversal in the commit phase.
This removes all references to the effect list in the new fork.
希望能将该内容更新
每次看例子都要打开公众号输入好麻烦啊,至于每个例子都输入一遍口令吗,作者对自己的公众号这么没有信心吗
架构篇/completeWork/effectList章节, react 17.0.3版本 completeUnitOfWork里面并没有effectList的代码逻辑,。老卡,文档得更新了
// 环状单向链表操作
if (queue.pending === null) {
update.next = update;
} else {
update.next = queue.pending.next;
queue.pending.next = update;
}
queue.pending = update;
既然queue.pending保存着上个update,next指向自己,是否也可写为
...
} else {
update.next = queue.pending;
queue.pending.next = update;
}
...
第三章结尾说把 rootFiber
传给 commitRoot
方法,在第四章开头变成了 fiberRootNode
新创建的文件yarn link react react-dom时报警告
warning ../../../package.json: No license field
react-reconciler 0.26.1
react 17.0.3
在最新的
master
代码中,三个月之前有一次更新,移除了effectTag
const deletions = returnFiber.deletions;
if (deletions === null) {
returnFiber.deletions = [childToDelete];
returnFiber.flags |= Deletion;
} else {
deletions.push(childToDelete);
}
使用了
flags
进行判断
https://react.iamkasong.com/renderer/prepare.html#layout%E4%B9%8B%E5%90%8E
演示的 Demo 表现不出来两者的差异
执行yarn build react,react-dom,scheduler --type=NODE 时
报Cannot find module 'react-server-dom-webpack/node-loader'
node: '12.16.2',
v8: '7.8.279.23-node.34',
uv: '1.34.2',
zlib: '1.2.11',
brotli: '1.0.7',
ares: '1.15.0',
modules: '72',
nghttp2: '1.40.0',
napi: '5',
llhttp: '2.0.4',
http_parser: '2.9.3',
openssl: '1.1.1e',
cldr: '36.0',
icu: '65.1',
tz: '2019c',
unicode: '12.1'
The two component demos in page /preparation/idea.html
of vue and react are absolutely different. How should they be compared in that case?
What a react component similar to the vue component should look like the following:
// react jsx
<ul>
<li>0</li>
<li>{ name }</li>
<li>2</li>
<li>3</li>
</ul>
And. What a vue component similar to the react component should look like the following:
<!-- vue template -->
<template>
<ul>
<li v-for="(name, i) in data">{{ i !== 1 ? i : name }}</li>
</ul>
</template>
Looking forward to reply! thx :-)
你的文章里hook是单链表,文章《react hooks not magic,just arrays》里说是数组,怎么会有这种冲突的说法呢
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.