1.开发调试秘籍--前端Debugger实战
2.放弃 console.log 吧!调试用 Debugger 你能读懂各种源码
3.react事件机制底层原理
4.搞懂React源码系列-React Diff原理
5.2022年末了,源源码react拖拽组件和最牛的深度代码调试技巧一起学!
6.react源码解析(二)时间管理大师fiber
开发调试秘籍--前端Debugger实战
前端开发过程中,解析遇到错误和Bug是调试常态,而有效的源源码易语言压枪源码调试技巧则是每个程序员不可或缺的技能。本课程针对初学者,深度假设您已具备基础CSS和JS知识,解析Vue或React经验会更有帮助。调试我们将主要关注定位问题的源源码策略,解决问题的深度实践将在课程之外被视为基本功。 首先,解析您将掌握Chrome DevTools的调试强大调试工具。课程内容包括:学会使用Chrome DevTool打开和调整调试工具面板,源源码如元素、深度控制台、源代码和网络面板。
理解断点功能,以及如何利用控制按钮进行代码执行控制。
通过HTTP协议理解浏览器与服务器交互过程,观察geek.itheima.net/的请求和响应信息。
识别和理解常见的JS错误类型,如ReferenceError、TypeError和RangeError。
课程的核心是定位bug并解决问题,具体步骤包括:查看控制台报错,区分程序员问题和类库兼容问题,以及识别HTTP状态码(2XX、4XX和5XX)以判断通信问题。 对于快速定位代码中的问题,学会使用Source面板分析报错。面对类库兼容问题,关注报错信息,确认是否为第三方库问题。当遇到前后端通信问题时,4XX状态码通常指示前端问题,而5XX则可能需要与后端合作解决。 总之,学习本课程后,您将能熟练运用前端Debugger,解决常见的开发调试问题,提升代码质量。视频教程:开发调试秘籍--前端Debugger实战放弃 console.log 吧!用 Debugger 你能读懂各种源码
很多同学不清楚为什么要使用debugger进行调试,难道console.log不行吗?
即使学会了使用debugger,还是b2b系统源码有很多代码看不懂,如何调试复杂的源码呢?
这篇文章将为你讲解为什么要使用这些调试工具:console.log vs Debugger。
相信绝大多数同学都会使用console.log进行调试,将想查看的变量值打印在控制台。
这种方法可以满足基本需求,但遇到对象打印时就无法胜任了。
比如,我想查看webpack源码中的compilation对象的值,我尝试打印了一下:
但你会发现,当对象的值也是对象时,它不会展开,而是打印一个[Object] [Array]这样的字符串。
更严重的是,打印的内容过长会超过缓冲区的大小,在terminal中显示不全:
而使用debugger来运行,在这里设置一个断点查看,就没有这些问题了:
有些同学可能会说,那打印一个简单的值时使用console.log还是很方便的。
比如这样:
真的吗?
那还不如使用logpoint:
代码执行到这里就会打印:
而且没有污染代码,使用console.log的话,调试完成后这个console也不得不删除掉。
而logpoint不需要,它就是一个断点的设置,不在代码中。
当然,最重要的是debugger调试可以看到调用栈和作用域!
首先是调用栈,它就是代码的执行路线。
比如这个App的函数组件,你可以看到渲染这个函数组件会经历workLoop、beginWork、renderWithHooks等流程:
你可以点击调用栈的每一帧,查看都执行了什么逻辑,用到了什么数据。比如可以看到这个函数组件的fiber节点:
再就是作用域,点击每一个栈帧就可以看到每个函数的作用域中的变量:
使用debugger可以看到代码的执行路径,每一步的作用域信息。而你使用console.log呢?
只能看到那个变量的值而已。
得到的信息量差距不是一点半点,调试时间长了,别人会对代码的运行流程越来越清晰,而你使用console.log呢?还是老样子,因为你看不到代码执行路径。
所以,不管是调试库的源码还是业务代码,不管是php b2b源码调试Node.js还是网页,都推荐使用debugger打断点,别再用console.log了,即使想打印日志,也可以使用LogPoint。
而且在排查问题的时候,使用debugger的话可以加一个异常断点,代码跑到抛异常的地方就会断住:
可以看到调用栈来理清出错前都走了哪些代码,可以通过作用域来看到每一个变量的值。
有了这些,排查错误就变得轻松多了!
而你使用console.log呢?
什么也没有,只能自己猜。
Performance
前面说debugger调试可以看到一条代码的执行路径,但是代码的执行路径往往比较曲折。
比如那个React会对每个fiber节点做处理,每个节点都会调用beginWork。处理完之后又会处理下一个节点,再次调用beginWork:
就像你走了一条小路,然后回到大路之后又走了另一条小路,使用debugger只能看到当前这条小路的执行路径,看不到其他小路的路径:
这时候就可以结合Performance工具了,使用Performance工具看到代码执行的全貌,然后用debugger来深入每一条代码执行路径的细节。
SourceMap
sourcemap非常重要,因为我们执行的都是编译打包后的代码,基本是不可读的,调试这种代码也没有什么意义,而sourcemap就可以让我们直接调试最初的源码。
比如vue,关联了sourcemap之后,我们能直接调试ts源码:
nest.js也是:
不使用sourcemap的话,想搞懂源码,但你调试的是编译后的代码,怎么读懂呢?
读懂一行
前面说的debugger、Performance、SourceMap只是调试代码的工具,那会了调试工具,依然读不懂代码怎么办呢?
我觉得这是不可能的。
为什么这么说呢?
就拿react源码来说:
switch case能读懂吧。三目运算符能读懂吧。函数调用能读懂吧。
每一行代码都能读懂,而全部的代码不就是由这一行行代码组成的么?
加上我们可以单步执行来知道代码执行路径。
为啥每行代码都能读懂,连起来就读不懂了呢?
那应该是android文件管理器源码代码太多了,而你花的时间不够而已。
先要读懂一行,一个函数,读懂一个小功能的实现流程,慢慢积累,之后了解的越来越多之后,你能读懂的代码就会越多。
总结
这篇文章讲了为什么要使用调试工具,如何读懂复杂代码。
console.log的弊端太多了,大对象打印不全,会超过terminal缓冲区,对象属性不能展开等等,不建议大家使用。即使要打印也可以使用LogPoint。
使用debugger可以看到调用栈,也就是代码的执行路径,每个栈帧的作用域,可以知道代码从开始运行到现在都经历了什么,而console.log只能知道某个变量的值。
此外,报错的时候也可以通过异常断点来梳理代码执行路径来排查报错原因。
但debugger只能看到一条执行路径,可以使用Performance录制代码执行的全流程,然后再结合debugger来深入其中一条路径的执行细节。
此外,只有调试最初的源码才有意义,不然调试编译后的代码会少很多信息。可以通过SourceMap来关联到源码,不管是Vue、React的源码还是Nest.js、Babel等的源码。
会了调试之后,就能调试各种代码了,不存在看不懂的源码,因为每一行代码都是基础的语法,都是能看懂的,如果看不懂,只可能是代码太多了,你需要更多的耐心去读一行行代码、一个个函数、理清一个个功能的实现,慢慢积累就好了。
掌握基于debugger、Performance、p2p搜索源码SourceMap等调试代码之后,各种网页和Node.js代码都能调试,各种源码都能读懂!
react事件机制底层原理
阅读须知1.采用react版本为v..2细节可能有所出入,但是原理都是一样的。2.请通过无痕模式调试否则插件容易干扰我们的调试。一、一个demo引发的问题
constApp=()=>{ return<inputonChange={ ()=>{ console.log('这是一个onChang额事件')}}/>}
这是一个平淡无奇的最简单demo,我们看到在input元素绑定onChange事件,当我们定位到这个dom元素的时候发现react为我们添加了input、keydown、blur、change等等事件,而且这些事件都是绑定到document上,并没有绑定到我们的input元素中。那么react究竟做了什么处理呢。
二、react事件是怎么被绑定的首先我们要明白,react中的事件不是原生事件是合成事件,如何合成的呢,我们debugger源码就知道了。当我们打断点调试的时候首先会出来这些东西,首先我们看到这个函数,在react-dom的第一次运行的时候会出现这个。其主要作用初始化一些事件的插件我们拿ChangeEventPlugin来简单的说一下里面主要是用来干什么的吧。
varChangeEventPlugin={ eventTypes:{ change:{ phasedRegistrationNames:{ bubbled:'onChange',captured:'onChangeCapture'},dependencies:[TOP_BLUR,TOP_CHANGE,TOP_CLICK,TOP_FOCUS,TOP_INPUT,TOP_KEY_DOWN,TOP_KEY_UP,TOP_SELECTION_CHANGE]}},_isInputEventSupported:isInputEventSupported,extractEvents:function(topLevelType,targetInst,nativeEvent,nativeEventTarget,eventSystemFlags){ vartargetNode=targetInst?getNodeFromInstance$1(targetInst):window;vargetTargetInstFunc,handleEventFunc;if(shouldUseChangeEvent(targetNode)){ getTargetInstFunc=getTargetInstForChangeEvent;}elseif(isTextInputElement(targetNode)){ if(isInputEventSupported){ getTargetInstFunc=getTargetInstForInputOrChangeEvent;}else{ getTargetInstFunc=getTargetInstForInputEventPolyfill;handleEventFunc=handleEventsForInputEventPolyfill;}}elseif(shouldUseClickEvent(targetNode)){ getTargetInstFunc=getTargetInstForClickEvent;}if(getTargetInstFunc){ varinst=getTargetInstFunc(topLevelType,targetInst);if(inst){ varevent=createAndAccumulateChangeEvent(inst,nativeEvent,nativeEventTarget);returnevent;}}if(handleEventFunc){ handleEventFunc(topLevelType,targetNode,targetInst);}//Whenblurring,setthevalueattributefornumberinputsif(topLevelType===TOP_BLUR){ handleControlledInputBlur(targetNode);}}}其中dependencies属性主要是告诉我们change事件是依赖于它下面的一些事件来合成的。而phasedRegistrationNames是在冒泡或者捕获两个阶段做的一些处理,react会在不同阶段调用不同的处理方法。所以初始化完成之后他们的结构是这样的会形成registrationNameModules与registrationNameDependencies这两个对象如下图所示
形成这两个对象是在diff过程更好的对事件进行处理。
registrationNameModules主要是在diff过程中如果检测到fiber上存在像onClick这些方法就会调用这些插件进行处理。
判断合成事件依赖于那些事件然后调用registrationNameDependencies对应的事件所依赖的原生事件进行绑定。绑定到了document上。
到现在为止我们的react事件绑定完成了并且绑定到了document对象上。
三、react事件如何触发的因为我们在document上绑定了事件,当我们点击的时候肯定知道是哪一个元素触发了这个事件,但是还有一个问题就是我们怎么找到我们在代码写的事件处理函数,因为我们的事件处理函数最后被react初始化到了对应的fiber对象中了所以如何通过dom元素找到其对应的fiber对象,这个就是我们需要探讨的。
这个就是react如何找到对应的fiber对象,是这样的,其中原生dom与其对应的fiber对象中存在一个指向问题,原生dom通过里面一个唯一key指向dom对应的fiber,而fiber中stateNode对应的也是该原生dom对象。所以我们就可以找到了我们写在react中的处理函数。即原生dom->fiber->memoizedProps这里面其实就是冒泡时候先寻找父组件如果父组件有这个事件就加到event._dispatchListeners上,然后遍历这个数组拿到回调然后触发事件为啥用e.preventDefault()?和?returnfalse阻止不了事件,就是这个原因。
好了,我们的react事件绑定机制讲完了,如果有什么意见或者建议请评论告诉我。。
原文:/post/搞懂React源码系列-React Diff原理
时隔2年,重新审视React源码,理解了许多过去未曾明晰的内容。本文聚焦于通过实际案例结合相关React源码,集中探讨React Diff原理。我们将使用当前最新React版本:..1。同时,今年计划推出“搞懂React源码系列”,旨在以通俗易懂的方式深入解析React的核心部分。年,该系列将涵盖以下内容:
React Diff原理
React 调度原理
搭建阅读React源码环境-支持所有版本断点调试
React Hooks原理
欢迎关注我的博客。
在深入Diff算法之前,有必要先理解React Fiber。虽然Fiber并不复杂,但全面理解需要时间。本文重点是Diff原理,因此Fiber介绍将简要进行。
Fiber是React中的抽象节点对象,它将所有节点连接成链表树。每个Fiber可能包含子Fiber、相邻Fiber以及父Fiber。React使用链表形式连接所有Fiber,形成树结构。Fiber还带有副作用标签(effectTag),如替换(Placement)和删除(Deletion),用于后续更新DOM。
值得注意的是,React Diff过程中,除了Fiber,还涉及基本的React元素对象,如将foo编译后的对象为:{ type: 'div', props: { children: 'foo' } }。
Diff过程始于reconcileChildren(...)
总流程如下:
reconcileChildren(...)
在Diff时,比较中的旧内容为Fiber,而新内容为React元素、文本或数组。新内容为对象时,流程分为两步:reconcileSingleElement(...)和placeSingleChild(...)。以React元素为例,流程如下:
reconcileSingleElement(...)
这里正式开始Diff,child为旧内容Fiber,element为新内容,它们的元素类型不同。React将“删除”旧内容Fiber及其所有相邻Fiber(添加Deletion标签),并基于新内容生成新的Fiber。然后将新Fiber设置为父Fiber的child。
如果新旧内容的元素类型相同,则通过fiber复用生成新的Fiber。同样设置为父Fiber的child。
总结新内容为React元素的Diff流程:
reconcileChildren(...)
新内容为文本时,逻辑与新内容为React元素类似。新内容为数组时,通过遍历数组元素,与React元素的处理方式类似,生成新的Fiber。
总结新内容为数组的Diff流程:
reconcileChildrenArray(...)
Diff的三种情况总结:比较结果相同:复用旧内容Fiber,结合新内容生成新Fiber
比较结果不同:仅通过新内容创建新Fiber
然后给旧内容Fiber添加替换标签,或给旧内容Fiber及其所有相邻元素添加删除标签。最后将新的(第一个)Fiber设为父Fiber的child。年末了,react拖拽组件和最牛的代码调试技巧一起学!
年末学习新技能,react拖拽组件与高效代码调试技巧不容错过!学习第三方库如react-sortable-hoc时,首先要掌握的就是调试技巧。首先,创建一个新项目,尝试简单的react-sortable-hoc示例,然后设置断点进行调试。但注意,直接在打包后的产物中打点是无用的,这时就需要启用sourcemap来映射源代码。在项目中开启sourcemap,通过克隆并配置项目的rollup打包设置,确保调试时能访问源码。遇到只读源码文件时,检查CALL STACK,可能需要调整映射路径。若需修改node_modules中的文件,可使用patch-package避免覆盖。接下来,分析组件的初始化过程。SortableContainer负责管理,创建Manager对象并传递给子组件SortableElement。SortableElement负责注册自身,SortableHandle则关注节点标识和事件绑定。理解事件触发顺序后,逐步探索handleStart、handlePress、handleSortMove等关键函数。handlePress利用克隆节点模拟拖拽,handleSortMove则调整节点位置并动画化排序,handleSortEnd则进行清理工作。react-sortable-hoc主要利用mousemove和touchmove事件,结合css3动画实现拖拽和排序。但要注意,它使用了不推荐的reactDom.findDomNodeapi,以及仅限于类组件使用。学习过程中,还可以记录一些实用的函数,如获取元素偏移、移动数组元素和过滤对象属性等。提升调试效率和理解库的工作原理,是学习react拖拽组件的重要一环。希望这些技巧能帮助你在年末的学习旅程中更顺利,别忘了分享给有需要的朋友哦!
react源码解析(二)时间管理大师fiber
React的渲染和对比流程在面对大规模节点时,会消耗大量资源,影响用户体验。为了改进这一情况,React引入了Fiber机制,成为时间管理大师,平衡了浏览器任务和用户交互的响应速度。 Fiber的中文翻译为纤程,是一种内部更新机制,支持不同优先级的任务管理,具备中断与恢复功能。每个任务对应于React Element的Fiber节点。Fiber允许在每一帧绘制时间(约.7ms)内,合理分配计算资源,优化性能。 相比于React,React引入了Scheduler调度器。当浏览器空闲时,Scheduler会决定是否执行任务。Fiber数据结构具备时间分片和暂停特性,更新流程从递归转变为可中断的循环,通过shouldYield判断剩余时间,灵活调整更新节奏。 Scheduler的关键实现是requestIdleCallback API,它用于高效地处理碎片化时间,提高用户体验。尽管部分浏览器已支持该API,React仍提供了requestIdleCallback polyfill,以确保跨浏览器兼容性。 在Fiber结构中,每个节点包含返回指针(而非直接的父级指针),这个设计使得子节点完成工作后能返回给父级节点。这种机制促进了任务的高效执行。 Fiber的遍历遵循深度优先原则,类似王朝继承制度,确保每一帧内合理分配资源。通过实现深度优先遍历算法,可以构建Fiber树结构,用于渲染和更新DOM元素。 为了深入了解Fiber,可以使用本地环境调试源码。通过创建React项目并配置调试环境,可以观察Fiber节点的结构和行为。了解Fiber的遍历流程和结构后,可以继续实现一个简单的Fiber实例,这有助于理解React渲染机制的核心。 Fiber架构是React的核心,通过时间管理机制优化了性能,使React能够在大规模渲染时保持流畅。了解Fiber的交互流程和遍历机制,有助于深入理解React渲染流程。未来,将详细分析优先级机制、断点续传和任务收集等关键功能,揭示React是如何高效地对比和更新DOM树的。 更多深入学习资源和讨论可参考以下链接: 《React技术揭秘》 《完全理解React Fiber》 《浅谈 React Fiber》 《React Fiber 源码解析》 《走进 React Fiber 的世界》react源码解析8.render阶段
本文深入解析React源码中的渲染阶段,带你掌握React高效学习的精髓。让我们一起探索React的源代码,从基础到进阶,实现深入理解。
1. 开篇介绍和面试题
从最基础开始,解读面试题背后的原理,为你的学习之旅铺垫。
2. React设计理念
了解React的核心理念,为何它在现代前端开发中独树一帜。
3. React源码架构
拆解React源码结构,理解其设计的精妙之处。
4. 源码目录结构与调试
掌握React源码的目录布局和调试技巧,提升代码阅读效率。
5. JSX与核心API
深入学习JSX语法与React核心API,构建高效、灵活的组件。
6. Legacy与Concurrent模式入口函数
比较Legacy和Concurrent模式,了解React性能优化之道。
7. Fiber架构
揭秘Fiber的运作机制,理解React渲染的高效实现。
8. Render阶段
重点解析Render阶段的核心工作,构建Fiber树与生成effectList。
9. Diff算法
深入了解React的Diff算法,高效计算组件更新。
. Commit阶段
探索Commit阶段的流程,将Fiber树转换为真实DOM。
. 生命周期
掌握React组件的生命周期,优化组件性能。
. 状态更新流程
分析状态更新的机制,实现组件响应式的开发。
. Hooks源码
深入Hooks源码,理解状态管理与函数组件的结合。
. 手写Hooks
实践动手编写Hooks,巩固理解。
. Scheduler与Lane
探讨React的调度机制与Lane概念,优化渲染性能。
. Concurrent模式
探索Concurrent模式下的React渲染流程,提高应用的交互流畅度。
. Context
学习Context的用法,简化组件间的数据传递。
. 事件系统
深入事件处理机制,实现组件间的交互。
. 手写迷你版React
实践构建一个简单的React框架,深化理解。
. 总结与面试题解答
回顾学习要点,解答面试常见问题,为面试做好充分准备。
. Demo
通过实际案例,直观展示React渲染流程与技巧。
本课程带你全面掌握React渲染阶段的关键知识与实战技能,从理论到实践,提升你的前端开发能力。