├── .gitignore ├── BrowerNetwork ├── Event Loop.md ├── assets │ ├── activation.jpg │ ├── authorStlyeSheets.png │ ├── beforeunload.png │ ├── browerprocess.jpg │ ├── browertabs.gif │ ├── browserArch.png │ ├── browserui.png │ ├── busyExample.gif │ ├── callstack.gif │ ├── cclayerlist.jpg │ ├── computedStyle.png │ ├── computedStyleChart.png │ ├── computerArchitecture.png │ ├── cpu.png │ ├── cssFuncToken.png │ ├── cssLink.png │ ├── cssParse.png │ ├── cssTokenType.png │ ├── dataStructure.png │ ├── demoStylesheet.jpg │ ├── differentSite.png │ ├── drawquads.jpg │ ├── eventloop.png │ ├── excutepainoperations.jpg │ ├── execute.png │ ├── fontsizeCompute.png │ ├── gpu.png │ ├── gpurasterization.jpg │ ├── htmlParseDom.png │ ├── idleExample.gif │ ├── incorrectRendering.png │ ├── input.png │ ├── interFrameIdlePeriod.jpg │ ├── isolation.png │ ├── layerTree.png │ ├── layerexample.png │ ├── layerstotilesforraster.jpg │ ├── layoutObject.jpg │ ├── layoutTree.png │ ├── lookprocess.jpg │ ├── mainthreadcommit.jpg │ ├── memory.svg │ ├── multiProcessArch.png │ ├── navigation.png │ ├── navigationPreloads.png │ ├── navstart.png │ ├── noScreenUpdatesIdlePeriod.jpg │ ├── pageLoaded.png │ ├── paintOrder.png │ ├── paintRecords.png │ ├── paintRecordsExample.jpg │ ├── paragraphLayout.gif │ ├── preloadScanner.png │ ├── prepaint.jpg │ ├── prepareRenderProcess.png │ ├── processthread.png │ ├── queen.png │ ├── rafAnimation.gif │ ├── rafsettimeout.jpg │ ├── rastertile.png │ ├── reciveData.png │ ├── redirect.png │ ├── redraw.png │ ├── relayout.png │ ├── renderingPipeline.png │ ├── reraster.png │ ├── response.png │ ├── serverWokerExcute.png │ ├── serviceWorker.png │ ├── servicfication.svg │ ├── singleProcess.png │ ├── stack.png │ ├── telLayout.png │ ├── thread.png │ ├── timeoutAnimation.gif │ ├── updateStatus.png │ ├── vizdisplay.jpg │ └── workerprocess.svg ├── 浏览器如何计算CSS.md └── 窥探浏览器基本原理.md ├── Essay ├── assets │ ├── audioContextPlaybackRate.jpg │ ├── pitchStretch.jpg │ ├── playbackRateMDN.jpg │ ├── setCurrentTime.jpg │ ├── startError.png │ └── wavesurferPlaybackRate.jpg └── 音频可视化小记.md ├── JavaScript ├── 3D 词云.md ├── JS异步编程史.md ├── assets │ ├── breakoff.jpg │ ├── chainbreak.jpg │ ├── constructor.png │ ├── generator.jpg │ ├── promise.jpg │ ├── proto.png │ ├── protoConstructor.png │ ├── prototype.png │ ├── prototypeChain.png │ └── wordcloud.gif ├── prototype、__proto__与constructor.md └── utils.md ├── README.md ├── React ├── Class组件绑定this方法区别.md ├── React Hooks初次挂载.md ├── React18批量更新.md ├── ReactElement对象.md ├── React事件系统.md ├── React源码调试环境搭建.md ├── React调度Scheduler.md ├── Zustand源码浅析.md └── assets │ ├── appCompFirstHook.svg │ ├── appCompSecondUseState.svg │ ├── appCompUseCallback.svg │ ├── appCompUseEffect.svg │ ├── appCompUseLayoutEffect.svg │ ├── appCompUseMemo.svg │ ├── appCompUseRef.svg │ ├── appCompWorkInProgressOk.svg │ ├── beforeCommitBeforeMutationEffects.svg │ ├── beginWorkUpdateHostRoot.svg │ ├── browserdebug.jpg │ ├── commitRootBeforeFlushPassiveEffects.svg │ ├── disableEslint.jpg │ ├── enqueueUpdate.png │ ├── exportWebpack.jpg │ ├── hostRootFiberEnqueue.svg │ ├── hostRootFiberWorkInProgress.svg │ ├── ideDebugReactPerfect.jpg │ ├── initialMountRoot.svg │ ├── moduleScopeError.jpg │ ├── mountSyncExternalStore.svg │ ├── performSyncWorkOnRoot.svg │ ├── performUnitOfWork.drawio │ ├── performUnitOfWork.svg │ ├── react16JsxTransform.jpg │ ├── react17EventBindDom.jpg │ ├── react17JsxTransform.jpg │ ├── react18ensureRootIsScheduled.svg │ ├── reactBuildSourcemapError.jpg │ ├── reactBuildWithSourcemap.jpg │ ├── reactDebugAlias.jpg │ ├── reactDivRootEvents.jpg │ ├── reactEventDispatch.drawio │ ├── reactEventDispatch.svg │ ├── reactEventFiberTree.svg │ ├── reactEventScroll.jpg │ ├── reactEventVarsInit.svg │ ├── reactHooksEnsureRootIsScheduled.svg │ ├── reactHooksFirstDispatchAction.svg │ ├── reactHooksFirstDispatchSetState.svg │ ├── reactHooksMount.drawio │ ├── reactHooksMount.svg │ ├── reactHooksMountSimple.svg │ ├── reactHooksSecondDispatchAction.svg │ ├── reactHooksSecondDispatchActionOk.svg │ ├── reactHooksThirdDispatchActionAfter.svg │ ├── reactHooksThirdDispatchActionOk.svg │ ├── reactHooksUpdateLanes.svg │ ├── reactNoSourcemap.jpg │ ├── reactScheduler.drawio │ ├── reactScheduler.svg │ ├── reactSourcemapTrue.jpg │ ├── rootFinishedWork.svg │ ├── scheduleUpdateOnFiber17Vs18.jpg │ ├── taskPriority.jpg │ ├── thisbindvs.jpeg │ ├── vscodeAddDebug.jpg │ ├── vscodeDebugConfig.jpg │ ├── vscodeDebugReactPerfect.png │ ├── vscodeDebugSuccess.png │ ├── vscodeRunDebug.jpg │ ├── vscodeSourcemapTrue.jpg │ ├── webstormAddDebug.jpg │ ├── webstormDebugConfig.jpg │ ├── webstormDebugReactPerfect.jpg │ ├── webstormDebugSuccess.jpg │ ├── webstormRunDebug.jpg │ └── workInProressHostRoot.svg └── Shell ├── Git.md └── pkg manager.md /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | *.log 3 | -------------------------------------------------------------------------------- /BrowerNetwork/assets/activation.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/activation.jpg -------------------------------------------------------------------------------- /BrowerNetwork/assets/authorStlyeSheets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/authorStlyeSheets.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/beforeunload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/beforeunload.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/browerprocess.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/browerprocess.jpg -------------------------------------------------------------------------------- /BrowerNetwork/assets/browertabs.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/browertabs.gif -------------------------------------------------------------------------------- /BrowerNetwork/assets/browserArch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/browserArch.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/browserui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/browserui.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/busyExample.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/busyExample.gif -------------------------------------------------------------------------------- /BrowerNetwork/assets/callstack.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/callstack.gif -------------------------------------------------------------------------------- /BrowerNetwork/assets/cclayerlist.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/cclayerlist.jpg -------------------------------------------------------------------------------- /BrowerNetwork/assets/computedStyle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/computedStyle.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/computedStyleChart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/computedStyleChart.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/computerArchitecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/computerArchitecture.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/cpu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/cpu.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/cssFuncToken.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/cssFuncToken.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/cssLink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/cssLink.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/cssParse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/cssParse.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/cssTokenType.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/cssTokenType.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/dataStructure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/dataStructure.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/demoStylesheet.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/demoStylesheet.jpg -------------------------------------------------------------------------------- /BrowerNetwork/assets/differentSite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/differentSite.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/drawquads.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/drawquads.jpg -------------------------------------------------------------------------------- /BrowerNetwork/assets/eventloop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/eventloop.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/excutepainoperations.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/excutepainoperations.jpg -------------------------------------------------------------------------------- /BrowerNetwork/assets/execute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/execute.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/fontsizeCompute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/fontsizeCompute.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/gpu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/gpu.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/gpurasterization.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/gpurasterization.jpg -------------------------------------------------------------------------------- /BrowerNetwork/assets/htmlParseDom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/htmlParseDom.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/idleExample.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/idleExample.gif -------------------------------------------------------------------------------- /BrowerNetwork/assets/incorrectRendering.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/incorrectRendering.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/input.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/interFrameIdlePeriod.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/interFrameIdlePeriod.jpg -------------------------------------------------------------------------------- /BrowerNetwork/assets/isolation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/isolation.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/layerTree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/layerTree.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/layerexample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/layerexample.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/layerstotilesforraster.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/layerstotilesforraster.jpg -------------------------------------------------------------------------------- /BrowerNetwork/assets/layoutObject.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/layoutObject.jpg -------------------------------------------------------------------------------- /BrowerNetwork/assets/layoutTree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/layoutTree.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/lookprocess.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/lookprocess.jpg -------------------------------------------------------------------------------- /BrowerNetwork/assets/mainthreadcommit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/mainthreadcommit.jpg -------------------------------------------------------------------------------- /BrowerNetwork/assets/multiProcessArch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/multiProcessArch.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/navigation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/navigation.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/navigationPreloads.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/navigationPreloads.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/navstart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/navstart.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/noScreenUpdatesIdlePeriod.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/noScreenUpdatesIdlePeriod.jpg -------------------------------------------------------------------------------- /BrowerNetwork/assets/pageLoaded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/pageLoaded.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/paintOrder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/paintOrder.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/paintRecords.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/paintRecords.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/paintRecordsExample.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/paintRecordsExample.jpg -------------------------------------------------------------------------------- /BrowerNetwork/assets/paragraphLayout.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/paragraphLayout.gif -------------------------------------------------------------------------------- /BrowerNetwork/assets/preloadScanner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/preloadScanner.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/prepaint.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/prepaint.jpg -------------------------------------------------------------------------------- /BrowerNetwork/assets/prepareRenderProcess.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/prepareRenderProcess.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/processthread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/processthread.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/queen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/queen.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/rafAnimation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/rafAnimation.gif -------------------------------------------------------------------------------- /BrowerNetwork/assets/rafsettimeout.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/rafsettimeout.jpg -------------------------------------------------------------------------------- /BrowerNetwork/assets/rastertile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/rastertile.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/reciveData.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/reciveData.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/redirect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/redirect.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/redraw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/redraw.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/relayout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/relayout.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/renderingPipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/renderingPipeline.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/reraster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/reraster.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/response.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/serverWokerExcute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/serverWokerExcute.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/serviceWorker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/serviceWorker.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/singleProcess.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/singleProcess.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/stack.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/telLayout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/telLayout.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/thread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/thread.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/timeoutAnimation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/timeoutAnimation.gif -------------------------------------------------------------------------------- /BrowerNetwork/assets/updateStatus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/updateStatus.png -------------------------------------------------------------------------------- /BrowerNetwork/assets/vizdisplay.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/BrowerNetwork/assets/vizdisplay.jpg -------------------------------------------------------------------------------- /BrowerNetwork/浏览器如何计算CSS.md: -------------------------------------------------------------------------------- 1 | # 浏览器如何计算CSS 2 | 3 | > 文章主要摘录于 [从Chrome源码看浏览器如何计算CSS](https://zhuanlan.zhihu.com/p/25380611) 4 | 5 | 首先我们知道页面可以通过三种方式设置自定义样式:1、通过 link 引用的外部 CSS 文件;2、 183 | 191 | 192 | 193 |
194 |
child
195 |
196 | 197 | 198 | ``` 199 | 200 | 在控制台查看 styleSheet 结构 201 | 202 | ![demoStylesheet.jpg](./assets/demoStylesheet.jpg) 203 | 204 | 从输出结果来看,确实是没有 `border-radius: 4px` 的样式内容,那内联的样式是如何加到 DOM 节点的计算样式上去的呢?别急,后文会讲到。 205 | 206 | # 4、设置DOM节点的computedStyle 207 | 208 | 在解析好 CSS 之后,会触发 Layout Tree,而在进行 Layout 之前需要确定每个 DOM 节点的样式,比如`dispaly:none`不会出现在 Layout Tree 中,而在 Layout 之前需要计算得到每个节点的 computedStyle。为什么需要 computedStyle,因为可能会有多个选择器的样式命中了该标签,所以需要把几个选择器的样式属性综合在一起,以及继承父元素的属性以及 UA 的提供的属性。这个过程主要包括两步:根据当前节点的选择器匹配到对应的样式和设置样式。 209 | 210 | 1. 首先是根据当前节点的选择器匹配到对应的样式**,**我们一个DOM树上有那么多的节点,我们如何快速高效的找到每一个节点对应的 CSS 样式呢?首先我们需要遍历每一个 DOM 节点,将按照id、class、伪元素、标签的顺序取出所有的 selector,之前说过我们会把每一条 rule 放入到一个 hasMap 中,我们可以根据遍历到的这个节点的选择器快速的和 hasMap 的键进行配对,找到保存的 cssRule。我们还是以这个demo为例 211 | 212 | ```html 213 | 221 |
222 |

hello, world

223 |
224 | ``` 225 | 226 | 其会生成两个 cssRule,第一个 cssRule 会放到上面提到的四个 hasMap 中的 classRules 里面,而第二个 cssRule 会放到 tagRules 里面,我们在遇到`
`的时候会对应到hasMap 里的 classRule,首先与**`.text`**进行匹配,如果成功就判断其父选择器是否匹配,这个样式没有父选择器所以就成功返回了,如果失败就直接退出。 227 | 228 | 第二个我们遇见了**.`text p`**,还是一样的我们会由 tagRuled 先匹配 p 标签,因为选择器的类型是后代选择器**,**所以我们会对当前节点的所有父节点进行遍历,查看是否可以匹配到`.text`如果命中就再查看其左边再有没有其他的选择器了,如果没有就可以成功返回了。 229 | 230 | 这里需要注意的是我们在查找完右边第一个选择器后如果左边还有其他的选择器我们需要使用之前的方法递归判断当前节点的父节点或者其他情况,我们知道使用递归往往是比较消耗性能的,所以我们不应将复合选择器写的过长,最好不要超过三层。 231 | 232 | 2. 然后就是设置 DOM 节点的样式了,设置 style 的顺序是先继承父结点,然后使用 UA 的 style,最后再使用用户的 style。 233 | 234 | ```cpp 235 | style->inheritFrom(*state.parentStyle()) 236 | matchUARules(collector); 237 | matchAuthorRules(*state.element(), collector); 238 | ``` 239 | 240 | 每一步如果有 cssRule 匹配成功的话会把它放到当前元素的 m_matchedRules 的向量里面,并会去计算它的优先级,记录到 m_specificity 变量。这个优先级是怎么算的呢? 241 | 242 | ```cpp 243 | for (const CSSSelector* selector = this; selector; 244 | selector = selector->tagHistory()) { 245 | temp = total + selector->specificityForOneSelector(); 246 | } 247 | return total; 248 | ``` 249 | 250 | 如上代码所示,它会从右到左取每个selector的优先级之和。不同类型的selector的优级级定义如下 251 | 252 | ```cpp 253 | switch (m_match) { 254 | case Id: 255 | return 0x010000; 256 | case PseudoClass: 257 | return 0x000100; 258 | case Class: 259 | case PseudoElement: 260 | case AttributeExact: 261 | case AttributeSet: 262 | case AttributeList: 263 | case AttributeHyphen: 264 | case AttributeContain: 265 | case AttributeBegin: 266 | case AttributeEnd: 267 | return 0x000100; 268 | case Tag: 269 | return 0x000001; 270 | case Unknown: 271 | return 0; 272 | } 273 | return 0; 274 | } 275 | ``` 276 | 277 | 从中我们可以看到 ID 选择器的优先级最高是 16 进制的 0x010000=65536,类、属性、伪类的优先级是 0x100 = 256,标签选择器的优先级是 1,其他如通配符就是 0了,如下面计算所示: 278 | 279 | ```css 280 | /*优先级为257 = 265 + 1*/ 281 | .text h1{ 282 | font-size: 8em; 283 | } 284 | 285 | /*优先级为65537 = 65536 + 1*/ 286 | #my-text h1{ 287 | font-size: 16em; 288 | } 289 | ``` 290 | 291 | 内联 style 的优先级又是怎么处理的呢?当匹配完了当前元素的所有 cssRule 规则,全部放到了 collector 的 m_matchedRules 里面,再把这个向量根据优先级从小到大排序: 292 | 293 | ```cpp 294 | collector.sortAndTransferMatchedRules(); 295 | ``` 296 | 297 | 排序的规则是这样的: 298 | 299 | ```cpp 300 | static inline bool compareRules(const MatchedRule& matchedRule1,const MatchedRule& matchedRule2) { 301 | unsigned specificity1 = matchedRule1.specificity(); 302 | unsigned specificity2 = matchedRule2.specificity(); 303 | if (specificity1 != specificity2) 304 | return specificity1 < specificity2; 305 | return matchedRule1.position() < matchedRule2.position(); 306 | } 307 | ``` 308 | 309 | 先按优先级,如果两者的优先级一样,则比较它们先后位置。在把 css 样式表(CSSStyleSheet)的样式处理完了之后,Blink 再去取 style 的内联样式(这个在是在构建 DOM 的时候存放好了的),把内联样式 push_back 到上面排好序的容器里,由于它是由小到大排序的,所以放最后面的优先级肯定是最大的。 310 | 311 | ```cpp 312 | collector.addElementStyleProperties(state.element()->inlineStyle(),isInlineStyleCacheable); 313 | ``` 314 | 315 | 所有的样式规则都处理完毕,最后就是按照它们的优先级计算 CSS 了,将在下面这个函数执行: 316 | 317 | ```cpp 318 | applyMatchedPropertiesAndCustomPropertyAnimations(state, collector.matchedResult(), element); 319 | ``` 320 | 321 | 这个函数会按照下面的顺序依次设置元素的 style: 322 | 323 | ```cpp 324 | applyMatchedProperties(state, matchResult.allRules(), false, applyInheritedOnly, needsApplyPass); 325 | for (auto range : ImportantAuthorRanges(matchResult)) { 326 | applyMatchedProperties(state, range, true, applyInheritedOnly, needsApplyPass); 327 | } 328 | ``` 329 | 330 | 先设置正常的规则,最后再设置important的规则。所以越往后的设置的规则就会覆盖前面设置的规则。 331 | 332 | 接下来我们可以大概看一下计算出来的 style 是什么样的,按优先级计算出来的 style 会被放在一个ComputedStyle的对象里面,这个 style 里面的规则分成了几类,通过检查style对象可以一窥: 333 | 334 | ![computedStyle.png](./assets/computedStyle.png) 335 | 336 | 把它画成一张图表的话: 337 | 338 | ![computedStyleChart.png](./assets/computedStyleChart.png) 339 | 340 | 主要有几类,box 是长宽,surround 是 margin/padding,还有不可继承的 nonInheritedData 和可继承的 styleIneritedData 一些属性。Blink 还把很多比较少用的属性放到 rareData 的结构里面,为避免实例化这些不常用的属性占了太多的空间。 341 | 342 | 具体来说,上面示例中设置的 font-size 为:22em * 16px = 352px: 343 | 344 | ![fontsizeCompute.png](./assets/fontsizeCompute.png) 345 | 346 | 而所有的色值会变成 16 进制的整数,如 Blink 定义的两种颜色的色值: 347 | 348 | ```cpp 349 | static const RGBA32 lightenedBlack = 0xFF545454; 350 | static const RGBA32 darkenedWhite = 0xFFABABAB; 351 | ``` 352 | 353 | 同时 Blink 对 rgba 色值的转化算法: 354 | 355 | ```cpp 356 | RGBA32 makeRGBA32FromFloats(float r, float g, float b, float a) { 357 | return colorFloatToRGBAByte(a) << 24 | colorFloatToRGBAByte(r) << 16 | colorFloatToRGBAByte(g) << 8 | colorFloatToRGBAByte(b); 358 | } 359 | ``` 360 | 361 | 从这里可以看到,有些CSS优化建议说要按照下面的顺序书写CSS规则: 362 | 363 | > 1.位置属性(position, top, right, z-index, display, float等); 364 | > 365 | > 366 | > 2.大小(width, height, padding, margin); 367 | > 368 | > 3.文字系列(font, line-height, letter-spacing, color- text-align等); 369 | > 370 | > 4.背景(background, border等); 371 | > 372 | > 5.其他(animation, transition等); 373 | > 374 | 375 | 这些顺序对浏览器来说其实是一样的,因为最后都会放到 computedStyle 里面,而这个 style 里面的数据是不区分先后顺序的,所以这种建议与其说是优化,倒不如说是规范,大家都按照这个规范写的话,看 CSS 就可以一目了然,可以很快地看到想要了解的关键信息。 376 | 377 | 最后我们需要对我们计算出的 CSS 样式进行一些调整,如将 absoluet、fixed 定位,float 的元素转化为 block: 378 | 379 | ```cpp 380 | // Absolute/fixed positioned elements, floating elements and the document 381 | // element need block-like outside display. 382 | if (style.hasOutOfFlowPosition() || style.isFloating() || (element && element->document().documentElement() == element)) 383 | style.setDisplay(equivalentBlockDisplay(style.display())); 384 | ``` 385 | 386 | 如果有`:first-letter`选择器时,也会把元素 display 和 position 做调整: 387 | 388 | ```cpp 389 | static void adjustStyleForFirstLetter(ComputedStyle& style) { 390 | // Force inline display (except for floating first-letters). 391 | style.setDisplay(style.isFloating() ? EDisplay::Block : EDisplay::Inline); 392 | // CSS2 says first-letter can't be positioned. 393 | style.setPosition(StaticPosition); 394 | } 395 | ``` 396 | 397 | 另外还会对表格元素做一些调整。 398 | 399 | 400 | 最后当所有的样式都计算完之后我们会将其挂载到 window.getComputedStyle 上来供之后的 JavaScript 访问。 -------------------------------------------------------------------------------- /Essay/assets/audioContextPlaybackRate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/Essay/assets/audioContextPlaybackRate.jpg -------------------------------------------------------------------------------- /Essay/assets/pitchStretch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/Essay/assets/pitchStretch.jpg -------------------------------------------------------------------------------- /Essay/assets/playbackRateMDN.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/Essay/assets/playbackRateMDN.jpg -------------------------------------------------------------------------------- /Essay/assets/setCurrentTime.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/Essay/assets/setCurrentTime.jpg -------------------------------------------------------------------------------- /Essay/assets/startError.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/Essay/assets/startError.png -------------------------------------------------------------------------------- /Essay/assets/wavesurferPlaybackRate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/Essay/assets/wavesurferPlaybackRate.jpg -------------------------------------------------------------------------------- /Essay/音频可视化小记.md: -------------------------------------------------------------------------------- 1 | # 音频可视化小记 2 | 3 | # 前言 4 | 5 | 在项目中因为有涉及到对客服对话音频的质检,为了质检所打标记能在音频时间点有一个更直观的展示以及丰富界面UI而做了对会话音频波形的一个可视化处理,项目之前是直接使用的 [wavesurfer-js](https://wavesurfer-js.org/),开始也是OK的,但在项目迭代过程中发现涉及到这一块儿音频的出现了问题不好跟踪处理,考虑到后期功能迭代以及稳定性干脆直接重写了一个音频波形的可视化组件(当然还是有参考 [wavesurfer-js](https://wavesurfer-js.org/ ),这个库还是是很优秀的),Github 完整代码地址:[react-audio-wave](https://github.com/llaurora/react-audio-wave)。 6 | 7 | # wavesurfer.js部分踩坑 8 | 9 | 1. 音频倍速播放,音调异常; 10 | 11 | [wavesurfer-js](https://wavesurfer-js.org/) 有提供 `setPlaybackRate(rate)`方法来改变音频播放速度,但是发现音频倍速变化后,音调也异常,夸张的时候原本没加速时是一个男声,但加速后感觉变成了一个女声,这个通过查找 [Issues](https://github.com/katspaugh/wavesurfer.js/issues/149) 解决了,将音频初始化的配置`backend`参数改成 `MediaElement`,但这也为后期遇到另一个问题其实埋了个雷。 12 | 13 | ![wavesurferPlaybackRate.jpg](./assets/wavesurferPlaybackRate.jpg) 14 | 15 | 2. 音频初始化的时候同一段音频 xhr 请求了两次; 16 | 17 | 在找了一圈 Issue 后没找到解决办法,只能去看下源码是怎么写的,最后通过打断点发现了问题,音频初始化的时候调了一次 fetch 请求,调了一次 `audio`标签的 load 方法,通过配置参数减少了一次请求。 18 | 19 | 3. 想跳转到中间某个时间点播放的时候,音频老是重头开始播放; 20 | 21 | 这个问题确实没找到解决方法,而且以前没出现过这个问题,有的音频正常有的才有这个问题,不好排查(当然后期在重写的时候也遇到了才发现需要后端去解决这个问题),这也是让我打算重新写一个的主要原因。 22 | 23 | 24 | # 音频可视化 25 | 26 | 关于音频可视化入门可以参考网易云音乐团队的 [Web Audio在音频可视化中的应用](https://juejin.cn/post/6844903953130323976),只是注意里面对音频波形的展示用的是 [Web Audio ApI](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) 的 [AnalyserNode](https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode),但是 [AnalyserNode](https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode) 的问题是它只能用来做实时波形展示与我的需求不符合,我想要的是 [wavesurfer-js](https://wavesurfer-js.org/) 那样的。 27 | 28 | 这儿梳理下大概步骤: 29 | 30 | 1. 通过 fetch 拿到音频的 `ArrayBuffer`数据,然后通过 [Web Audio ApI](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) 提供的 `decodeAudioData` 方法解码拿到 `AudioBuffer`数据; 31 | 2. 对 `AudioBuffer` 提取音频数据采样,获取峰值,便于展示及分析; 32 | 3. 使用 canvas 或者 svg 渲染采样数据,绘制波形; 33 | 34 | 至于详细的怎么一步一步写的就不做详细介绍了,完整的代码放在 github 上了,可移步参考 [react-audio-wave](https://github.com/llaurora/react-audio-wave) ,在这儿记录一些在重写过程中遇到的一些问题。 35 | 36 | ## AudioContext 播放 37 | 38 | 在使用 `AudioContext` 控制播放暂停时,在暂停后再次播放,会播放不了,会报`cannot call start more than once`的错误。 39 | 40 | ![startError.png](./assets/startError.png) 41 | 42 | ```jsx 43 | this.source = this.audioContext.createBufferSource(); 44 | this.source.buffer = this.buffer; 45 | ... 46 | this.source.start(); 47 | ``` 48 | 49 | 之所以会出现这个问题,是因为一旦播放了 source node 就不能再使用了,要想再次播放得重新创建 [AudioBufferSourceNode](https://developer.mozilla.org/en-US/docs/Web/API/AudioBufferSourceNode),问题可参考 [Play an AudioBufferSourceNode twice](https://stackoverflow.com/questions/9439585/play-an-audiobuffersourcenode-twice)。所以解决办法就是每次播放的时候得重新创建 [AudioBufferSourceNode](https://developer.mozilla.org/en-US/docs/Web/API/AudioBufferSourceNode),然后使用新创建的 [AudioBufferSourceNode](https://developer.mozilla.org/en-US/docs/Web/API/AudioBufferSourceNode) 来播放。 50 | 51 | ## **AudioBufferSourceNode.start()参数** 52 | 53 | AudioBufferSourceNode.start(when, offset, duration) 播放的参数可直接移步 [MDN](https://developer.mozilla.org/en-US/docs/Web/API/AudioBufferSourceNode/start),主要是第1个参数`when`,我起初以为这个参数是说从音频的什么时间点开始播放,但其实是点击播放后指定多少秒后开始播放,第2个参数才是说从音频的哪个时间点开始播放,更多参考可前往 [Web Audio初步介绍和实践](https://www.cnblogs.com/ericHTML5/p/4039530.html)。 54 | 55 | ## AudioContext.currentTime含义 56 | 57 | 这个 currentTime 我起初以为和 `audio` 标签返回的 currentTime 是一样的,即返回当前音频播放到什么时间点了,通过这个 currentTime 可以知道音频播放进度,但实际上不一样,audioContext.currentTime 是一个绝对的时间,首次播放时(音频加载下来还未播放过)可当做x坐标原点即为0,不管你后面做了多少操作比如暂停多少次、暂停多久,这个时间都是一直在走着的(”并不以他人意志为转移”),所以使用 audioContext 来控制播放的话,要算出 AudioBufferSourceNode.start(when, offset, duration) 的第2个参数的值到底是多少,就不能用 audioContext.current 来获取,得在播放开始时记录一个时间点,然后播放暂停时记录一个时间点,以此来记录这次播放了多长,再累加上前面的时间段才能知道这次播放到音频的哪个时间点了。 58 | 59 | ```jsx 60 | public pause(): void { 61 | this.playing = false; 62 | ...... 63 | // this.audioContext.currentTime - this.startTime 可以计算出此次播放了多长时间,再+=开始播放前停止的位置的话就可以知道停止时播放到整段音频的什么位置了,即下次播放时知道从哪儿开始播了 64 | this.pausedAtOffsetTime += this.audioContext.currentTime - this.startTime; 65 | this.source?.stop(); 66 | this.source.disconnect(); 67 | } 68 | 69 | public play(): void { 70 | this.playing = true; 71 | ...... 72 | this.startTime = this.audioContext.currentTime; 73 | // 什么时候开始播(点击之后立即开始播)、从哪儿开始播、播多少时间 74 | this.source?.start(this.startTime, this.pausedAtOffsetTime, this.duration); 75 | } 76 | ``` 77 | 78 | ## AudioContext播放结束 79 | 80 | AudioBufferSourceNode 有提供一个 `ended` 的事件监听,这个也是区别于`audio` 标签的 `ended` 事件监听, `audio` 标签的 `ended` 事件监听是真的在音频播放结束(完成)才会触发。但是 AudioBufferSourceNode 的`ended` 的事件监听在播放暂停的时候也会触发,所以在使用 audioContext 控制播放的话要监听音频是否播放结束得结合当前音频的播放状态来。 81 | 82 | ```jsx 83 | if (ended && webAudioRef.current.playing) { 84 | // 使用audioContext控制播放的话,得结合播放状态判断是否播放结束 85 | } 86 | ``` 87 | 88 | ## AudioContext跳转播放 89 | 90 | audioContext再调整播放时遇到的问题主要是当前音频在播放状态,然后现在跳转到某个时间点播放,得先暂停再播放,而暂停后还不能立即播放,而得结合前面说的 `ended` 的事件监听确认暂停Ok了,才能开始播放。 91 | 92 | ```jsx 93 | // 使用audiobuffer播放控制的话,并且正在播放得先暂停 94 | pauseAudio(); 95 | webAudioRef.current.updateCurrentOffsetPositon(offsetTime); 96 | audioSourcePromiseRef.current.then(() => { 97 | playAudio(); 98 | }); 99 | ``` 100 | 101 | ## AudioContext倍速播放 102 | 103 | 这个是最坑的一个地方了,AudioBufferSourceNode 确实有提供一个改变播放速度的方法 [AudioBufferSourceNode.playbackRate](https://developer.mozilla.org/en-US/docs/Web/API/AudioBufferSourceNode/playbackRate),但这个倍速和我们平常理解的倍速有点儿不一样,确实音频倍速播放了,但同时音调也会跟着变,关于这个问题可以看下 ****[Web Audio API Issues](https://github.com/WebAudio/web-audio-api/issues/2487) 对该问题的讨论,而这其实也是之前在使用 [wavesurfer-js](https://wavesurfer-js.org/) 时遇到的那个倍速播放问题产生的原因,因为 [wavesurfer-js](https://wavesurfer-js.org/) 默认就是使用 AudioContext 来控制播放。 104 | 105 | ![audioContextPlaybackRate.jpg](./assets/audioContextPlaybackRate.jpg) 106 | 107 | 对于此,不是说这个 [AudioBufferSourceNode.playbackRate](https://developer.mozilla.org/en-US/docs/Web/API/AudioBufferSourceNode/playbackRate) 方法有 BUG,是因为其本身定义的含义就和我们平常理解的倍速含义不一样,直接看下[官方文档的解释](https://developer.mozilla.org/en-US/docs/Web/API/AudioBufferSourceNode)吧。 108 | 109 | ![playbackRateMDN.jpg](./assets/playbackRateMDN.jpg) 110 | 111 | 通俗点儿理解的话,可以认为当我们把声音加快的时候,其实是把波长(Wavelength)压短了,于是产生了更高的音高(Pitch),可移步 [JavaScript 音频变速保持音调](https://blog.crimx.com/2019/06/06/javascript-%E9%9F%B3%E9%A2%91%E5%8F%98%E9%80%9F%E4%BF%9D%E6%8C%81%E9%9F%B3%E8%B0%83/)。 112 | 113 | ![pitchStretch.jpg](./assets/pitchStretch.jpg) 114 | 115 | 虽然现在也有建议说给 AudioBufferSourceNode 倍速调整的时候,添加一个[preserve pitch](https://bugs.chromium.org/p/chromium/issues/detail?id=627111) 的参数来控制倍速播放时音调不发生变化,但目前为止还未被采纳,要想使用 audioContext 来倍速播放的话目前为止确实没得办法,要想倍速播放的话只能去调用 `audio` 封装好了的 playbackRate。而这也正是在使用 [wavesurfer-js](https://wavesurfer-js.org/) 时 `backend`默认为 `WebAudio` 倍速而产生音调异常的解决办法,就是将 `backend`从默认的 `WebAudio` 调整为 `MediaElement` 得以解决原因,就是因为这时的倍速以及播放控制啥的都是通过控制 `audio` 标签来实现的。 116 | 117 | ## audio标签 118 | 119 | 那既然要想音频可以倍速的话,得使用 `audio` 标签来控制播放暂停操作啥的,那能不能直接使用 `audio` 标签来实现音频的可视化,但实现音频可视化的前提是得拿到音频的 `audioBuffer` 数据,起初我以为能通过 `audio`标签来拿到 `audioBuffer`,但是抱歉不好意思,通过 `audio` 标签还真拿不到,还必须得通过 fetch 请求拿到音频的 `ArrayBuffer`数据然后通过解码得到 `AudioBuffer`数据,这应该是使用`audio`标签最大的一个遗憾了,`audio` 标签的播放、暂停以及播放结束事件监听都很棒,但唯独这个很尴尬(还有个跳转播放的问题,下面会说)。 120 | 121 | 所以最后要实现音频可视化,又得要倍速,只能通过 fetch 然后转码得到 `AudioBuffer`数据然后做可视化,通过原生`audio`标签来控制播放等操作,[wavesurfer-js](https://wavesurfer-js.org/) 源码也是这么做的。 122 | 123 | 然后 `audio` 可以通过`preload` 指定预加载策略,只要浏览器支持资源预加载(在 [窥探浏览器基本原理](https://github.com/llaurora/KnowledgeNote/blob/master/BrowerNetwork/%E7%AA%A5%E6%8E%A2%E6%B5%8F%E8%A7%88%E5%99%A8%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86.md) 里面也有写过,preload scanner 是浏览器优化资源加载的一种手段,但并不是所有浏览器都有 preload scanner,更多信息可前往 [资源预加载](https://mp.weixin.qq.com/s/8_4XGgrXqwuUKNwgp8KhTA) 查看),这个一般建议开启(开启之后的效果就是在页面进入的时候,即会加载资源,而如果不开启得点击播放时才会触发资源加载)。 124 | 125 | ## audio标签跳转播放 126 | 127 | 这个问题就是上面使用 [wavesurfer-js](https://wavesurfer-js.org/) 时列举的第3个问题,在想跳转到某个指定的时间点时老是从头开始播放的问题,而且这个问题在项目开发过程中只在谷歌浏览器有遇到这个问题,而其根本原因就是因为在使用`audio`标签控制播放时([wavesurfer-js](https://wavesurfer-js.org/) 在 backend 设置为 MediaElement 后,源码中也是使用 audio 标签控制播放),设置不上`currentTime`。而之所以设置不上`currentTime` 和服务器响应的 `response header` 有关,不同的浏览器对音频的 `response header` 的容错不一样,而谷歌浏览器就至少得需要`Content-Length` 和`Accept-Ranges`字段才可以更改音频的 `currentTime`,可参考前往 [HTML5 audio ,在chrome中设置currentTime无效](https://segmentfault.com/q/1010000002908474)。 128 | 129 | ![setCurrentTime.jpg](./assets/setCurrentTime.jpg) 130 | 131 | # 结语 132 | 133 | 一番折腾下来,收获还是不少,虽然最终采用了一个折中的方案,即使用 fetch 拿 `AudioBuffer`做可视化,使用 `audio`标签做倍速播放,但这毕竟是两种途径拿同一个资源,在拿到后又对两个资源做同步(一个播放,一个又精准的显示音频进度)可能会出现参差,但目前为止也没想到其它更好的方案了,只能尽可能的降低出现参差的概率。 134 | 135 | 放在 github上的音频可视化的组件 [react-audio-wave](https://github.com/llaurora/react-audio-wave) 代码是做了降级方案,当要倍速的话,使用的是上面的折中方案,但如果不要倍速的话,就全部使用`audioContext`来实现。 -------------------------------------------------------------------------------- /JavaScript/3D 词云.md: -------------------------------------------------------------------------------- 1 | # 3D 词云 2 | 3 | ![wordcloud](./assets/wordcloud.gif) 4 | 5 | ```typescript 6 | interface TagInfo { 7 | offsetWidth: number; 8 | offsetHeight: number; 9 | cx?: number; 10 | cy?: number; 11 | cz?: number; 12 | x?: number; 13 | y?: number; 14 | scale?: number; 15 | alpha?: number; 16 | } 17 | interface WordCloudConfig { 18 | baseFontSize?: number; 19 | } 20 | 21 | export class WordCould { 22 | private radius: number; 23 | private dtr: number; 24 | private d: number; 25 | private container: HTMLElement; 26 | private containerChildTags: HTMLElement[]; 27 | private tagsInfo: TagInfo[]; 28 | private active: boolean; 29 | private tspeed: number; 30 | private howElliptical: number; 31 | private baseFontSize: number; 32 | private sina: number; 33 | private cosa: number; 34 | private cosb: number; 35 | private sinb: number; 36 | private sinc: number; 37 | private cosc: number; 38 | constructor(container: HTMLElement, config?: WordCloudConfig) { 39 | const { baseFontSize } = config || {}; 40 | this.container = container; 41 | this.containerChildTags = null; 42 | this.tagsInfo = []; 43 | this.radius = 250; 44 | this.dtr = Math.PI / 180; 45 | this.d = 500; 46 | this.active = true; 47 | this.tspeed = 0.5; 48 | this.howElliptical = 1; 49 | this.baseFontSize = baseFontSize || 6; 50 | this.sina = 0; 51 | this.cosa = 0; 52 | this.sinb = 0; 53 | this.cosb = 0; 54 | this.sinc = 0; 55 | this.cosc = 0; 56 | } 57 | 58 | public onStart() { 59 | this.containerChildTags = Array.from(this.container.getElementsByTagName("i")); 60 | this.tagsInfo = this.containerChildTags.reduce((prev: TagInfo[], cur: HTMLElement) => { 61 | prev.push({ 62 | offsetWidth: cur.offsetWidth, 63 | offsetHeight: cur.offsetHeight, 64 | }); 65 | return prev; 66 | }, []); 67 | this.calcSineCosine(0, 0, 0); 68 | this.positionAll(); 69 | setInterval(() => { 70 | this.refresh(); 71 | }, 30); 72 | this.container.addEventListener( 73 | "mouseover", 74 | () => { 75 | this.active = false; 76 | }, 77 | false, 78 | ); 79 | this.container.addEventListener( 80 | "mouseout", 81 | () => { 82 | this.active = true; 83 | }, 84 | false, 85 | ); 86 | } 87 | 88 | private refresh() { 89 | this.calcSineCosine(this.active ? -this.tspeed : 0, this.active ? this.tspeed : 0, 0); 90 | for (const item of this.tagsInfo) { 91 | const rx1 = item.cx; 92 | const ry1 = item.cy * this.cosa + item.cz * -this.sina; 93 | const rz1 = item.cy * this.sina + item.cz * this.cosa; 94 | const rx2 = rx1 * this.cosb + rz1 * this.sinb; 95 | const ry2 = ry1; 96 | const rz2 = rx1 * -this.sinb + rz1 * this.cosb; 97 | const rx3 = rx2 * this.cosc + ry2 * -this.sinc; 98 | const ry3 = rx2 * this.sinc + ry2 * this.cosc; 99 | const rz3 = rz2; 100 | item.cx = rx3; 101 | item.cy = ry3; 102 | item.cz = rz3; 103 | const per = this.d / (this.d + rz3); 104 | item.x = this.howElliptical * rx3 * per - this.howElliptical * 2; 105 | item.y = ry3 * per; 106 | item.scale = per; 107 | item.alpha = (per - 0.6) * (10 / 6); 108 | } 109 | this.assginPosition(); 110 | this.depthSort(); 111 | } 112 | 113 | private depthSort() { 114 | const cloneContainerChildTags = []; 115 | for (const item of this.containerChildTags) { 116 | cloneContainerChildTags.push(item); 117 | } 118 | cloneContainerChildTags.sort((item1, item2) => { 119 | if (item1.cz > item2.cz) { 120 | return -1; 121 | } 122 | if (item1.cz < item2.cz) { 123 | return 1; 124 | } 125 | return 0; 126 | }); 127 | const len = cloneContainerChildTags.length; 128 | for (let i = 0; i < len; i += 1) { 129 | cloneContainerChildTags[i].style.zIndex = i; 130 | } 131 | } 132 | 133 | private assginPosition() { 134 | const halfWidth = this.container.offsetWidth / 2; 135 | const halfHeight = this.container.offsetHeight / 2; 136 | const len = this.tagsInfo.length; 137 | for (let i = 0; i < len; i += 1) { 138 | this.containerChildTags[i].style.cssText = `left:${ 139 | this.tagsInfo[i].cx + halfWidth - this.tagsInfo[i].offsetWidth / 2 140 | }px;top:${this.tagsInfo[i].cy + halfHeight - this.tagsInfo[i].offsetHeight / 2}px;font-size: ${ 141 | Math.ceil((12 * this.tagsInfo[i].scale) / 2) + this.baseFontSize 142 | }px;filter: alpha(opacity=${100 * this.tagsInfo[i].alpha});opacity: ${this.tagsInfo[i].alpha}`; 143 | } 144 | } 145 | 146 | private positionAll() { 147 | const max = this.tagsInfo.length; 148 | const fragment = document.createDocumentFragment(); 149 | const cloneContainerChildTags = []; 150 | for (const item of this.containerChildTags) { 151 | cloneContainerChildTags.push(item); 152 | } 153 | cloneContainerChildTags.sort(() => (Math.random() < 0.5 ? 1 : -1)); 154 | for (const item of cloneContainerChildTags) { 155 | fragment.append(item); 156 | } 157 | this.container.appendChild(fragment); 158 | for (let i = 1; i < max + 1; i += 1) { 159 | const phi = Math.acos(-1 + (2 * i - 1) / max); 160 | const theta = Math.sqrt(max * Math.PI) * phi; 161 | this.tagsInfo[i - 1].cx = this.radius * Math.cos(theta) * Math.sin(phi); 162 | this.tagsInfo[i - 1].cy = this.radius * Math.sin(theta) * Math.sin(phi); 163 | this.tagsInfo[i - 1].cz = this.radius * Math.cos(phi); 164 | this.containerChildTags[i - 1].style.cssText = `left: ${ 165 | this.tagsInfo[i - 1].cx + this.container.offsetWidth / 2 - this.tagsInfo[i - 1].offsetWidth / 2 166 | }px;top: ${ 167 | this.tagsInfo[i - 1].cy + this.container.offsetHeight / 2 - this.tagsInfo[i - 1].offsetHeight / 2 168 | }px`; 169 | } 170 | } 171 | 172 | private calcSineCosine(a, b, c) { 173 | this.sina = Math.sin(a * this.dtr); 174 | this.cosa = Math.cos(a * this.dtr); 175 | this.sinb = Math.sin(b * this.dtr); 176 | this.cosb = Math.cos(b * this.dtr); 177 | this.sinc = Math.sin(c * this.dtr); 178 | this.cosc = Math.cos(c * this.dtr); 179 | } 180 | } 181 | ``` 182 | --- 183 | 184 | ```typescript jsx 185 | // 在React 中使用示例 186 | export interface CloundWord { 187 | id: number | string; 188 | name: string; 189 | } 190 | interface WordCloudProps { 191 | cloudWords: CloundWord[]; 192 | className?: string; 193 | wordClassName?: string; 194 | onWordClick?: (word: CloundWord) => void; 195 | } 196 | 197 | const WordCloud = ({ className, cloudWords, wordClassName, onWordClick }: WordCloudProps) => { 198 | const containerRef = useRef(null); 199 | 200 | const hasCloudWords = useMemo(() => Array.isArray(cloudWords) && cloudWords.length, [cloudWords]); 201 | 202 | useEffect(() => { 203 | if (hasCloudWords) { 204 | const tagCloud = new WordCloud(containerRef.current, { 205 | baseFontSize: 16, 206 | }); 207 | tagCloud.onStart(); 208 | } 209 | }, [hasCloudWords]); 210 | 211 | if (!hasCloudWords) { 212 | return null; 213 | } 214 | 215 | return ( 216 |
217 | {cloudWords.map((item: CloundWord, index: number) => ( 218 | { 224 | onWordClick?.(item); 225 | }} 226 | > 227 | {item.name} 228 | 229 | ))} 230 |
231 | ); 232 | }; 233 | ``` -------------------------------------------------------------------------------- /JavaScript/JS异步编程史.md: -------------------------------------------------------------------------------- 1 | # JS异步编程史 2 | 3 | JavaScript 语言是一门单线程语言,同一时刻只能干一件事,就算是 HTML5 提出的 Web Worker 标准,允许 JavaScript 创建多个线程,但是子线程不得操作 DOM,这也没有改变 JavaScript 单线程的本质。 4 | 5 | 关于同步和异步,同步的意思是对于一段代码 JavaScript 引擎会严格按照单线程执行代码的规则(从上到下、从左到右),进行代码的编译和执行,前面的没执行完,后面的就得等着,换句话说同步会阻塞;而异步,比如在某一步需要发起一个 AJAX 请求,如果这时后面的代码还是得等着,多多少少是有点儿浪费的,这时就需要异步了,异步是非阻塞的,可以先执行后面的代码,待某个时刻条件达成了,比如拿到服务端响应的数据了,再回过来执行先前的异步代码,当然这里面还涉及到事件循环的一系列知识,事件循环相关的知识点可移步 [事件循环Event Loop](https://github.com/llaurora/KnowledgeNote/blob/master/%E6%B5%8F%E8%A7%88%E5%99%A8%E7%BD%91%E7%BB%9C/Event%20Loop.md) 。 6 | 7 | 从时间线来看,JavaScript 异步编程先后经历了 Callback ⇒ Promise ⇒ Generator ⇒ Async/Await 阶段。 8 | 9 | # Callback 10 | 11 | Callback 即回调函数,以形参的形式传入另一函数,并在该函数中被调用,用以完成某些任务 12 | 13 | > A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action. — [MDN Callback function](https://developer.mozilla.org/en-US/docs/Glossary/Callback_function) 14 | > 15 | 16 | 回调函数使用简单倒是简单,但在保证有 N 个步骤的异步编程中有序进行等情况时,就容易出现“回调地狱”,比如下面例子要求先读取A文本内容,再根据A文本内容读取B再根据B的内容读取C... 17 | 18 | ```jsx 19 | fs.readFile(A, 'utf-8', function(err, data) { 20 | fs.readFile(B, 'utf-8', function(err, data) { 21 | fs.readFile(C, 'utf-8', function(err, data) { 22 | fs.readFile(D, 'utf-8', function(err, data) { 23 | //.... 24 | }); 25 | }); 26 | }); 27 | }); 28 | ``` 29 | 30 | 在比如下面这个例子,每隔 1s 运行一个任务,这三个任务必须按时间顺序执⾏,并且每个下⼀秒触发前都能先拿到上⼀秒运⾏的结果 31 | 32 | ```jsx 33 | setTimeout(function () { 34 | console.log("第⼀秒之后执行的逻辑"); 35 | setTimeout(function () { 36 | console.log("第二秒之后执行的逻辑"); 37 | setTimeout(function () { 38 | console.log("第三秒之后执行的逻辑"); 39 | }, 1000); 40 | }, 1000); 41 | }, 1000); 42 | ``` 43 | 44 | 使用回调函数的方式异步编程,确实是能保证异步流程的有序进行,但这无法避免⼤量的逻辑在回调函数中不停的进⾏嵌套,导致出现“回调地狱”,难以阅读和维护。 45 | 46 | # Promise 47 | 48 | Promise 最早由社区提出和实现,在 ES2015 的时候被标准化,提供了原生的 Promise 对象,相较于之前异步流程控制使用的回调嵌套结构,Promise 将其转换成了 “Promise对象.then().then()...”的链式调用结构,虽然还是离不开回调函数,但更容易阅读理解,也更符合编写代码时从上到下从左右的线性思维。 49 | 50 | 使用 Promise 来改造上面 readFile 文件读取那个例子 51 | 52 | ```jsx 53 | function read(url) { 54 | return new Promise((resolve, reject) => { 55 | fs.readFile(url, "utf8", (err, data) => { 56 | if (err) { 57 | reject(err); 58 | } 59 | resolve(data); 60 | }); 61 | }); 62 | } 63 | read(A).then(data => { 64 | return read(B); 65 | }).then(data => { 66 | return read(C); 67 | }).then(data => { 68 | return read(D); 69 | }).catch(reason => { 70 | console.log(reason); 71 | }); 72 | ``` 73 | 74 | 同样的使用 Promise 来改造上面 setTimeout 的例子 75 | 76 | ```jsx 77 | const p = new Promise(function (resolve) { 78 | setTimeout(function () { 79 | resolve(); 80 | }, 1000); 81 | }); 82 | p.then(function () { 83 | console.log("第⼀秒之后执行的逻辑"); 84 | return new Promise(function (resolve) { 85 | setTimeout(function () { 86 | resolve(); 87 | }, 1000); 88 | }); 89 | }) 90 | .then(function () { 91 | console.log("第二秒之后执行的逻辑"); 92 | return new Promise(function (resolve) { 93 | setTimeout(function () {}, 1000); 94 | }); 95 | }) 96 | .then(function () { 97 | console.log("第三秒之后执行的逻辑"); 98 | }); 99 | ``` 100 | 101 | Pomise 的定义就是声明⼀个等待未来结果的对象,在结果发⽣之前它⼀直是初始状态,在结果发⽣之后它会变成其中⼀种⽬标状态。 102 | 103 | Promise 有且仅有三种状态: 104 | 105 | - **pending**:初始状态,也叫就绪状态,这是在 Promise 对象定义初期的状态,这时 Promise 仅仅做了初始化并注册了它对象上所有的任务; 106 | - **fulfilled**:已完成,通常代表成功执⾏了某⼀个任务,当初始化函数中的 resolve 执⾏时,Promise 的状态就变更为 fulfilled,并且 then 函数注册的回调函数会触发,resolve中传递的参数会进⼊回调函数作为形参; 107 | - **rejected**:已拒绝,通常代表执⾏了⼀次失败任务,或者流程中断,当调⽤ reject 函数时,catch 注册的回调函数就会触发,并且reject中传递的内容会变成回调函数的形参。 108 | 109 | Promise 中约定,当对象创建之后同⼀个 Promise 对象只能从 pending 状态变更为 fulfilled 或 rejected 中的其中⼀种,并且状态⼀旦变更就不会再改变。 110 | 111 | ## 链式调用 112 | 113 | 链式调⽤这个方式最经典的体现是在 JQuery 框架上,它本质就是在调⽤这些⽀持链式调⽤的函数的结尾时,它⼜返回了⼀个包含他⾃⼰的对象或者是⼀个新的自己 114 | 115 | ```jsx 116 | function MyPromise() { 117 | return this; 118 | } 119 | MyPromise.prototype.then = function () { 120 | console.log("触发了then"); 121 | return this; 122 | }; 123 | new MyPromise().then().then().then(); 124 | ``` 125 | 126 | ## Promise 链式调用基本规则 127 | 128 | 1. 只要有 then() 并且触发了 resolve,整个链条就会执⾏到结尾,这个过程中的第⼀个回调函数的参数是 resolve 传⼊的值; 129 | 2. 后续每个函数都可以使⽤ return 返回⼀个结果,如果没有返回结果的话下⼀个 then 中回调函数的参数就是 undefined; 130 | 3. 返回结果如果是普通变量,那么这个值就是下⼀个then中回调函数的参数; 131 | 4. 如果返回的是⼀个 Promise 对象,那么这个 Promise 对象 resolve 的结果会变成下⼀次 then 中回调的函数的参数; 132 | 5. 如果 then 中传⼊的不是函数或者未传值,Promise 链条并不会中断 then 的链式调⽤,并且在这之前最后⼀次的返回结果,会直接进⼊离它最近的正确的 then 中的回调函数作为参数; 133 | 134 | ```jsx 135 | const p = new Promise(function (resolve, reject) { 136 | resolve("我是Promise的值"); 137 | }); 138 | console.log(p); 139 | p.then(function (res) { 140 | // 该res的结果是resolve传递的参数 141 | console.log(res); 142 | }) 143 | .then(function (res) { 144 | // 该res的结果是undefined 145 | console.log(res); 146 | return "123"; 147 | }) 148 | .then(function (res) { 149 | // 该res的结果是123 150 | console.log(res); 151 | return new Promise(function (resolve) { 152 | resolve(456); 153 | }); 154 | }) 155 | .then(function (res) { 156 | // 该res的结果是456 157 | console.log(res); 158 | return "我是直接返回的结果"; 159 | }) 160 | .then() 161 | .then("我是字符串") 162 | .then(function (res) { 163 | // 该res的结果是“我是直接返回的结果” 164 | console.log(res); 165 | }); 166 | ``` 167 | 168 | 控制台会输出结果如下 169 | 170 | ![promise.jpg](./assets/promise.jpg) 171 | 172 | ## Promise 的链式调用中断 173 | 174 | Promise 的链式调用是可以中断的,可以使用 Promise.reject() 或者 throw() 的方式 175 | 176 | ```jsx 177 | const p = new Promise(function (resolve, reject) { 178 | resolve("我是Promise的值"); 179 | }); 180 | console.log(p); 181 | p.then(function (res) { 182 | console.log(res); 183 | }) 184 | .then(function (res) { 185 | // 有两种⽅式中断Promise 186 | // throw ("我是中断的原因"); 187 | return Promise.reject("我是中断的原因"); 188 | }) 189 | .then(function (res) { 190 | console.log(res); 191 | }) 192 | .then(function (res) { 193 | console.log(res); 194 | }) 195 | .catch(function (error) { 196 | console.log(error); 197 | }); 198 | ``` 199 | 200 | 控制台输出结果如下 201 | 202 | ![breakoff.jpg](./assets/breakoff.jpg) 203 | 204 | 在介绍 Promise 的时候强调了 Promise 对象的状态要么从 pending 状态变更为 fullfilled 状态,要么从 pending 状态变更为 rejected 状态,并且状态⼀旦变更就不会再发⽣变化。当我们使⽤链式调⽤的时候正常都是 then 函数链式调⽤,但是当我们触发中断的时候 catch 却执⾏了,这是为啥,这不是违背了 Promise 的约定吗?不慌,看一个例子先 205 | 206 | ```jsx 207 | const p = new Promise(function (resolve, reject) { 208 | resolve("我是Promise的值"); 209 | }); 210 | const p1 = p.then(function (res) {}); 211 | console.log(p); 212 | console.log(p1); 213 | console.log(p1 === p); 214 | ``` 215 | 216 | 控制台输出结果结果如下 217 | 218 | ![chainbreak.jpg](./assets/chainbreak.jpg) 219 | 220 | 从输出的结果可以发现返回的 p 和 p1 的状态本身就不⼀样,并且他们的对⽐结果是 false,这就代表他们在堆内存中开辟了两个空间,p 和 p1 对象分别保存了两个 Promise 对象的引⽤地址,所以 then 函数虽然每次都返回 Promise对象,来实现链式调⽤,但是 then 函数每次返回的都是⼀个新的 Promise 对象,这样便解释的通了!也就是说每⼀次 then 函数在执⾏时,我们都可以让本次的结果在下⼀个异步步骤执⾏时,变成不同的状态,⽽且这也不违背Promise对象最初的约定。 221 | 222 | Promise 虽然在一定程度上是解决了传统异步编程使用 Callback 导致的”回调地狱”问题,将回调嵌套调用转换成了 then() 链式调用,要更线性一些,但还是会充斥着大量的 then 函数,代码阅读性还是不高,还有没有其他更好的解决方案? 223 | 224 | # Generator 225 | 226 | Generator 即生成器,生成器函数是一个带星号函数,可以暂停执行和恢复执行,在 ES2015 的时候被标准化。 227 | 228 | > 虽然 Promise 和 Generator 都是在 ES2015 版本的时候被标准化,但是在社区,Promise 和 Generator 都早有自己的雏形,不过 Promise 的概念出现的时间相对 Generator 而言还是要更早一些的。 229 | > 230 | 231 | ```jsx 232 | function* genDemo() { 233 | console.log("开始执行第一段"); 234 | yield "generator 1"; 235 | console.log("开始执行第二段"); 236 | yield "generator 2"; 237 | console.log("执行结束"); 238 | return "generator 3"; 239 | } 240 | console.log("main 0"); 241 | const gen = genDemo(); 242 | console.log(gen.next().value); 243 | console.log("main 1"); 244 | console.log(gen.next().value); 245 | console.log("main 2"); 246 | console.log(gen.next().value); 247 | console.log("main 3"); 248 | ``` 249 | 250 | 观察上面这段代码执行后的输出结果,会发现函数 genDemo 并不是一次执行完的,全局代码和 genDemo 函数是交替执行的,这就是生成器函数可以暂停执行和恢复执行的特性。Generator 函数是怎么做到暂停执行和恢复执行的?这里面涉及到协程对线程控制权的一个转让。 251 | 252 | 协程是一种比线程更加轻量级的存在,可以将协程看做是跑在线程上的任务,正如一个进程可以拥有多个线程一样,一个线程上可以存在多个协程,只是在一个进程中可以有多个线程并行执行,一个线程上同时却只能执行一个协程,比如当前在主线程上执行的是 A 协程,要启动 B 协程,那么 A 协程就需要将主线程的控制权交给 B 协程,这就体现在 A 协程暂停执行,B 协程恢复执行;同样,也可以从 B 协程中启动 A 协程。通常,如果从 A 协程启动 B 协程,我们就把 A 协程称为 B 协程的父协程。 253 | 254 | 调用生成器函数会返回一个遍历器对象,这个遍历器对象同时也是一个协程。 255 | 256 | > 遍历器对象本质上是一个指针对象,指针对象有一个 next 方法用来移动指针,调用指针对象的 next 方法会返回给定数据结构的当前成员的信息,具体来说返回的是一个包含 value 和 done 两个属性的对象,value 属性是当前成员的值,done 属性是一个布尔值,表示遍历是否结束。 257 | > 258 | > 259 | > ```tsx 260 | > // 如果使用TypeScript来定义遍历器对象Iterator和next方法的返回值 261 | > interface Iterator { 262 | > next(value?: any): IterationResult; 263 | > } 264 | > 265 | > interface IterationResult { 266 | > value: any; 267 | > done: boolean; 268 | > } 269 | > ``` 270 | > 271 | 272 | ![generator.jpg](./assets/generator.jpg) 273 | 274 | 上图是结合前面那段代码执行过程的一个协程执行流程图,从图中可以看出协程的几点规则: 275 | 276 | 1. 通过调用生成器函数 genDemo 来创建一个协程 gen,创建之后,gen 协程并没有立即执行; 277 | 2. 要让 gen 协程执行,需要通过调用 gen.next(除了调用 gen.next,还可以调用 gen.throw 以及调用 gen.return 来让 gen 协程执行); 278 | 3. 当协程正在执行的时候,可以通过 yield 关键字来暂停 gen 协程的执行,并将紧邻 yield 关键字后面表达式的值返回给父协程; 279 | 280 | > 注意:紧邻 yield 关键字后面表达式,不会立即求值,只有当调用 next 方法,指针指向该语句时才会执行 281 | > 282 | > 283 | > ```tsx 284 | > function* gen() { 285 | > yield 123 + 456; 286 | > } 287 | > // yield 后面的表达式 123 + 456,不会立即求值,只会在 next 方法将指针移到这一句时,才会求值 288 | > ``` 289 | > 290 | 4. 如果协程在执行期间,遇到了 return 关键字,那么 JavaScript 引擎会结束当前协程,并将 return 后面的内容返回给父协程; 291 | 292 | 其实调用 Generator 函数生成的遍历器对象除了有 next 方法外,其实还有 return 方法和 throw 方法,这三者的共同点都是让 Generator 函数恢复执行(yield 暂停执行),并且使用不同的语句替换 yield 后面的表达式。 293 | 294 | - next() 是将 yield 后面的表达式替换成一个值 295 | 296 | ```jsx 297 | const g = function* (x, y) { 298 | const result = yield x + y; 299 | return result; 300 | }; 301 | 302 | const gen = g(1, 2); 303 | gen.next(); // Object {value: 3, done: false},在首次执行next()时,传入任何值都是无效的,比如这儿传100或者undefined,始终会输出Object {value: 3, done: false} 304 | 305 | gen.next(1); // Object {value: 1, done: true} 306 | // 相当于将 let result = yield x + y 307 | // 替换成 let result = 1; 308 | ``` 309 | 310 | 上面代码中,第二个 next 方法就相当于将 x + y 表达式替换成一个值 1,如果 next 方法没有参数,就相当于替换成 undefined。 311 | 312 | - throw() 是将 yield 后面的表达式替换成一个 throw 语句 313 | 314 | ```jsx 315 | gen.throw(new Error("出错了")); // Uncaught Error: 出错了 316 | // 相当于将 let result = yield x + y 317 | // 替换成 let result = throw(new Error('出错了')); 318 | ``` 319 | 320 | - return() 是将 yield 表达式替换成一个 return 语句 321 | 322 | ```jsx 323 | gen.return(2); // Object {value: 2, done: true} 324 | // 相当于将 let result = yield x + y 325 | // 替换成 let result = return 2; 326 | ``` 327 | 328 | 329 | 关于 Generator 函数更多语法,可移步 [Generator 函数的语法](https://es6.ruanyifeng.com/#docs/generator),利用 Generator 函数可以暂停执行和恢复执行的特性,换句话说,可以控制函数的分布执行,这个特性正好可以用于异步编程,同样的对于前面依次读取文本内容那个例子,先读取A文本内容,再根据A文本内容读取B再根据B的内容读取C...,如果是使用 Generator + co 库来实现的话 330 | 331 | ```jsx 332 | const fs = require("fs"); 333 | const co = require("co"); 334 | 335 | function readFile(url) { 336 | return new Promise((resolve, reject) => { 337 | fs.readFile(url, "utf8", (err, data) => { 338 | if (err) { 339 | reject(err); 340 | } 341 | resolve(data); 342 | }); 343 | }); 344 | } 345 | function* read() { 346 | yield readFile(A); 347 | yield readFile(B); 348 | yield readFile(C); 349 | // .... 350 | } 351 | // Generator函数只要传入co函数,就会自动执行 352 | co(read); 353 | // co函数返回一个Promise对象 354 | // co(read).then(data => { 355 | // //code 356 | // }).catch(err => { 357 | // //code 358 | // }); 359 | ``` 360 | 361 | 从上面代码可以看出,异步流程更加同步了,没有了回调地狱,也没了 Promise 的 then().then()... 链式调用,其中用的 [co 库](https://github.com/tj/co) 用于 Generator 函数的自动执行,其思路正式利用 generator 函数体可以停在 yield 语句处,直到下一次执行 next() 的这个特性,将异步操作跟在 yield 后面,当异步操作完成并返回结果后,再触发下一次 next(),下面实现一个简易版的 co 362 | 363 | ```jsx 364 | function myCo(gen) { 365 | const generator = gen(); 366 | return new Promise((resolve, reject) => { 367 | function next(data) { 368 | try { 369 | const result = generator.next(data); 370 | const { value, done } = result; 371 | if (done === true) { 372 | resolve(value); 373 | } else if (done === false && value instanceof Promise) { 374 | value.then(function (val) { 375 | next(val); 376 | }); 377 | } 378 | } catch (error) { 379 | return reject(error); 380 | } 381 | } 382 | next(); 383 | }); 384 | } 385 | ``` 386 | 387 | 将上面例子中的 co 替换成 myCo,效果也是一样的,同样的对于前面在1秒后、2秒后、3秒后依次执行的那个例子,使用 Generator 函数结合上面简易版的 co 来写的话 388 | 389 | ```jsx 390 | function* test() { 391 | const res1 = yield new Promise(function (resolve) { 392 | setTimeout(function () { 393 | resolve("第⼀秒之后执行的逻辑"); 394 | }, 1000); 395 | }); 396 | console.log(res1); 397 | const res2 = yield new Promise(function (resolve) { 398 | setTimeout(function () { 399 | resolve("第二秒之后执行的逻辑"); 400 | }, 1000); 401 | }); 402 | console.log(res2); 403 | const res3 = yield new Promise(function (resolve) { 404 | setTimeout(function () { 405 | resolve("第三秒之后执行的逻辑"); 406 | }, 1000); 407 | }); 408 | console.log(res3); 409 | } 410 | 411 | myCo(test); 412 | /* 可以看到在控制台输出结果如下 */ 413 | // 第⼀秒之后执行的逻辑 414 | // 第二秒之后执行的逻辑 415 | // 第三秒之后执行的逻辑 416 | ``` 417 | 418 | 从上面的例子可以看出,利用 Generator 函数可以使异步流程,看起来更加同步了。但其函数语义比如 yield 啥的还是比较晦涩,另外用 Generator 来异步编程,并不是开箱即用,基本需要进行较为完善的二次封装(增加执行器),才能成为一种异步编程模式,例如 co 库,那还没有既能用同步代码的思维来异步编程又是开箱即用的方案? 419 | 420 | # Async/Await 421 | 422 | async/await 在 ES2016 中得到了提案,并在 ES2017 中被标准化,有了 async/await 我们现在就可以用同步代码的思维来异步编程同时又可开箱即用,写法还特简单,这是到目前为止最完美的异步编程解决方案了,其语法定义可移步 [async函数](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/async_function)。 423 | 424 | 其实 async/await 只是一个 Generator 的语法糖,可以近似的认为是 Generator + 自执行器(比如 co) + Promise 的封装,可参考上面 Generator 结合 co 来进行异步流程控制的实现,另外就是 async/await 为向后兼容在使用 babel 时编译后的代码也不难发现这一点 425 | 426 | ```jsx 427 | async function test () { 428 | const result = await func(); 429 | console.log(result); 430 | } 431 | 432 | // 在使用babel编译(有经过格式整理) 433 | function asyncGeneratorStep() {...} 434 | 435 | function _asyncToGenerator(fn) { 436 | return function () { 437 | var self = this, 438 | args = arguments; 439 | return new Promise(function (resolve, reject) { 440 | var gen = fn.apply(self, args); 441 | function _next(value) { 442 | asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); 443 | } 444 | function _throw(err) { 445 | asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); 446 | } 447 | _next(undefined); 448 | }); 449 | }; 450 | } 451 | 452 | function test() { 453 | return _test.apply(this, arguments); 454 | } 455 | 456 | function _test() { 457 | _test = _asyncToGenerator( 458 | /*#__PURE__*/ regeneratorRuntime.mark(...), 459 | ); 460 | return _test.apply(this, arguments); 461 | } 462 | ``` 463 | 464 | 也正因为 async/await 是 Generator + 自执行器(比如 co) + Promise 的一个封装,await 关键字就类似于 yield 暂停标示,底层也是基于协程对主线程控制权的一个转让,可简单讲 async 函数中 await 上面的代码和紧邻 await 后面的代码视作 new Promise() 时传入的代码,而 await 左侧的代码以及 await 后面的代码可视作 Promise.then() 中的代码 465 | 466 | ```jsx 467 | async function test() { 468 | console.log(3); 469 | const a = await 4; 470 | console.log(a); 471 | return 1; 472 | } 473 | console.log(1); 474 | test(); 475 | console.log(2); 476 | // 控制台输出顺序 1、3、4、2 477 | ``` 478 | 479 | # 参考资料 480 | 481 | [浏览器工作原理与实践](https://time.geekbang.org/column/intro/100033601?tab=catalog) 482 | 483 | [JavaScript异步发展历史](https://segmentfault.com/a/1190000040156178) 484 | 485 | [细说JS异步发展历程](https://segmentfault.com/a/1190000019254694) -------------------------------------------------------------------------------- /JavaScript/assets/breakoff.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/JavaScript/assets/breakoff.jpg -------------------------------------------------------------------------------- /JavaScript/assets/chainbreak.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/JavaScript/assets/chainbreak.jpg -------------------------------------------------------------------------------- /JavaScript/assets/constructor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/JavaScript/assets/constructor.png -------------------------------------------------------------------------------- /JavaScript/assets/generator.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/JavaScript/assets/generator.jpg -------------------------------------------------------------------------------- /JavaScript/assets/promise.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/JavaScript/assets/promise.jpg -------------------------------------------------------------------------------- /JavaScript/assets/proto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/JavaScript/assets/proto.png -------------------------------------------------------------------------------- /JavaScript/assets/protoConstructor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/JavaScript/assets/protoConstructor.png -------------------------------------------------------------------------------- /JavaScript/assets/prototype.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/JavaScript/assets/prototype.png -------------------------------------------------------------------------------- /JavaScript/assets/prototypeChain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/JavaScript/assets/prototypeChain.png -------------------------------------------------------------------------------- /JavaScript/assets/wordcloud.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/JavaScript/assets/wordcloud.gif -------------------------------------------------------------------------------- /JavaScript/prototype、__proto__与constructor.md: -------------------------------------------------------------------------------- 1 | # prototype、__proto__与constructor 2 | 3 | # 前言 4 | 5 | 从一个简单例子先记住几个概念(叫法) 6 | 7 | ```jsx 8 | function Foo() {...}; 9 | let f1 = new Foo(); 10 | ``` 11 | 12 | **构造函数**:用来初始化新创建的对象的函数就是构造函数。在上面例子中,`Foo()`即是构造函数。 13 | 14 | **实例化对象**:通过构造函数的`new`操作创建的对象即被称为该构造函数的实例化对象(实例、实例对象、实例化对象指的都是它,叫法不一样而已)。在上面例子中,`f1`即为`Foo()`构造函数的实例化对象,并且一个构造函数可以构造多个实例化对象。 15 | 16 | 虽然是简简单单两行代码,但背后涉及到的 `prototype`、`__proto__`、`constructor`关系却是错综复杂的,如下图所示: 17 | 18 | ![prototypeChain.png](./assets/prototypeChain.png) 19 | 20 | 左图右下角为图例: 21 | 22 | - 红色箭头表示`__proto__`属性指向; 23 | - 绿色箭头表示`prototype`属性的指向; 24 | - 棕色实线箭头表示本身具有的`constructor`属性的指向; 25 | - 棕色虚线箭头表示继承而来的`constructor`r属性的指向; 26 | - 蓝色方块表示对象; 27 | - 浅绿色方块表示函数(这里为了更好看清,`Foo()`仅代表是函数,并不是指执行函数`Foo`后得到的结果,图中的其他函数同理); 28 | - 图的中间部分即为它们之间的联系,图的最左边即为例子代码。 29 | 30 | 我们还需要牢记两点: 31 | 32 | - `__proto__`和`constructor`属性是对象所独有的; 33 | - `prototype`属性是函数所独有的。但是由于JS 中函数也是一种对象,所以函数也拥有`__proto__`和`constructor`属性,这点是致使我们产生困惑的很大原因之一。 34 | 35 | 上图有点复杂,我们把它按照属性分别拆开,然后进行分析: 36 | 37 | # prototype 属性 38 | 39 | 构造函数上有一个键名为 `prototype`的属性(原型属性,普通函数同样也有),其键值为一个对象,该对象即是该构造函数实例化得到的实例化对象的原型对象(prototype object),通过同一个构造函数实例化的多个实例化对象具有相同的原型对象。 40 | 41 | 如上面所说`prototype`属性是**函数所独有的**,所以在图上它总是从**一个函数指向一个对象**。 42 | 43 | ![prototype.png](./assets/prototype.png) 44 | 45 | 那 `prototype` 属性的作用又是什么呢?先看一个场景,假设要给通过`Foo()` 构造函数实例化后的实例对象`f1` 和`f2` (let f2 = new Foo())都加上一个效果相同的 `say` 方法,可能会有如下代码: 46 | 47 | ```jsx 48 | // 下面是给实例f1和f2添加了同一个效果的方法say 49 | f1.say = function () { 50 | console.log("Hello!"); 51 | }; 52 | f2.say = function () { 53 | console.log("Hello!"); 54 | }; 55 | console.log(f1.say === f2.say); // false,它们并不指向堆内存同一个地方,而是各自都占有内存 56 | ``` 57 | 58 | 当你去对比这两个方法的时候,你会发现它们只是效果相同、名字相同,本质上却是**各自都占用了部分内存**的不同方法。这时候就出问题了,如果这时候有千千万万个实例(夸张)要这样效果同样的方法,那内存岂不是要炸。 59 | 60 | 而这正是`prototype` 属性解决的问题,当需要为大量实例添加相同的属性和方法时,可以将它们存放在这些实例化对象的原型对象中,即对应的构造函数的`prototype` 属性(该属性值为一个对象)中而达到共享、公用的效果以节省内存。 61 | 62 | 还是上面那个场景,通过`prototype` 属性的话,代码如下: 63 | 64 | ```jsx 65 | Foo.prototype.say = function () { 66 | console.log("Hello!"); 67 | }; 68 | console.log(f1.say === f2.say); // true,指向堆内存同一个地方 69 | ``` 70 | 71 | 通过`prototype` 属性确实解决了给多个实例添加相同属性和方法而又不浪费内存的问题,可是这些实例对象又是怎么找到这个共用方法或者属性的呢?那就涉及到`__proto__` 属性了。 72 | 73 | # ****__ proto__ 属性**** 74 | 75 | 如上面所说`__proto__` 属性是**对象独有的**,从图上可以看到`__proto__` 属性总是由**一个对象指向一个对象**,即指向它们的原型对象(可以理解为父对象)。 76 | 77 | 81 | 82 | ![proto.png](./assets/proto.png) 83 | 84 | 那`__proto__` 属性的作用是什么呢?它的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的`__proto__` 属性所指向的那个对象(即该实例对象的原型对象,可以理解为该实例对象的父对象)里找,如果父对象也不存在这个属性,则继续往父对象的`__proto__` 属性所指向的那个对象(可以理解为爷爷对象)里找,如果还没找到,则继续往上找…直到原型链顶端`null` (可以理解为原始人。。。),再往上找就相当于在`null`上取值,会报错(可以理解为,再往上就已经不是“人”的范畴了,找不到了,到此结束,`null`为原型链的终点),由以上这种通过`__proto__` 属性来连接对象直到`null` 的一条链即为我们所谓的原型链。 85 | 86 | 这也就可以解释上面实例化对象`f1` 和`f2`其是怎么找到公用方法`say()`了。其实我们平时调用的字符串方法、数组方法、对象方法、函数方法等都是靠`__proto__`继承而来的。 87 | 88 | 关于 `__proto__` 和 `prototype` 的关系:实例化对象的 `__proto__` 属性 和构造函数的`prototype` 属性指向的是堆内存里面同一个地方,所以图上的示例有 f1.**proto** === Foo.prototype。 89 | 90 | # ****constructor 属性**** 91 | 92 | 同样如上面所说`constructor`属性也是**对象才拥有的**,它是从**一个对象指向一个函数**,含义就是指向该对象的构造函数,任何一个对象都可以找到其构造函数(本身拥有或继承而来,继承而来的要结合__proto__属性查看会更清楚点,如下图所示)。 93 | 94 | > 在 JS 中函数是拥有特定功能的对象,而`Function` 这个对象比较特殊,其构造函数就是`Function` 自身。另外任何一个构造函数都是构造函数 `Function()` 的实例化对象,内置的 `Object()` 、`Number()` 、`String()` 等构造函数都是 `Function()` 的实例化对象。 95 | > 96 | 97 | ![constructor.png](./assets/constructor.png) 98 | 99 | 之所以说“任何一个对象都可以找到其构造函数”而不是“任何一个对象都有构造函数”,是因为创建对象的前提是需要有`constructor`,而这个`constructor`可能是对象自己本身显式定义的或者通过`__proto__`在原型链中找到的。而纯粹的从`constructor`属性来讲的话,是只有构造函数.prototype(实例对象的原型对象)才显示定义有`constructor`属性,指向该原型对象对应的构造函数即构造函数.prototype.constructor === 构造函数本身。而实例化对象本身即使自己没有`constructor`属性的,也可以通过`__proto__` 找到其原型对象上的`constructor` 属性,所以任何一个对象最终都可以找到其构造函数(null如果当成对象的话,将null除外)。 100 | 101 | > Foo.prototype(实例对象的原型对象)因其自身显示的定义有 constructor 属性,覆盖了其继承自原型对象 Object.prototype 的 constructor 属性,所以 Foo.prototype.constructor === Foo 输出的是 true,而 Foo.prototype.constructor === Foo 输出的是 false。 102 | > 103 | 104 | ![protoConstructor.png](./assets/protoConstructor.png) 105 | 106 | # 总结 107 | 108 | - 我们需要牢记两点:①`__proto__`和`constructor`属性是**对象**所独有的;② `prototype`属性是**函数**所独有的,因为函数也是一种对象,所以函数也拥有`__proto__`和`constructor` 属性; 109 | - `__proto__`属性的**作用**就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的`__proto__`属性所指向的那个对象(父对象)里找,一直找,直到`__proto__`属性的终点`nul`,再往上找就相当于在`nul`上取值,会报错。通过`__proto__`_属性将对象连接起来的这条链路即**我们所谓的原型链**; 110 | - `prototype` 属性的作用就是让通过该函数所实例化的对象们都可以找到公用的属性和方法,即 f1.**proto** === Foo.prototype; 111 | - `constructor`属性的含义就是指向该对象的构造函数,所有函数(此时看成对象了)最终的构造函数都指向`Function()`; 112 | - 函数(`Function`也是函数)是`new Function()`的结果,所以函数可以作为实例对象,其构造函数是`Function()`,原型对象是`Function.prototype`; 113 | - 对象(函数也是对象)是`new Object()`的结果,所以对象可以作为实例对象,其构造函数是`Object()`,原型对象是`Object.prototype`。 114 | 115 | # 理一下 116 | 117 | --- 118 | 119 | ![prototypeChain.png](./assets/prototypeChain.png) 120 | 121 | - 实例化对象`f1`是通过构造函数`Foo()`的new操作创建的。构造函数`Foo()`显示定义有原型(prototype)属性即`Foo.prototype`;实例化对象`f1`通过`__proto__`属性也指向其原型对象`Foo.prototype`。 122 | 123 | ```jsx 124 | console.log(f1.__proto === Foo.prototype);//true 125 | ``` 126 | 127 | - 实例对象`f1`本身并没有`constructor`属性,但它可以继承其原型对象`Foo.prototype`的`constructor`属性。 128 | 129 | ```jsx 130 | console.log(Foo.prototype.constructor === Foo);//true 131 | console.log(f1.constructor === Foo); //true 132 | console.log(f1.hasOwnProperty('constructor'));//false 133 | ``` 134 | 135 | - `Foo.prototype`是`f1`的原型对象,同时它也是实例对象。实际上,任何对象都可以看做是通过`Object()`构造函数的new操作实例化的对象。所以`Foo.prototype`作为实例对象,它的构造函数是`Object()`,原型对象是`Object.prototype`。 136 | 137 | ```jsx 138 | console.log(Foo.prototype.__proto__ === Object.prototype);//true 139 | ``` 140 | 141 | - `Foo.prototype` 作为实例化对象,因其本身具有`constructor`属性,所以它会覆盖继承自原型对象`Object.prototype`的`constructor`属性。 142 | 143 | ```jsx 144 | console.log(Foo.prototype.constructor === Object);//false 145 | console.log(Foo.prototype.constructor === Foo);//true 146 | console.log(Object.prototype.constructor === Object);//true 147 | console.log(Foo.prototype.hasOwnProperty('constructor'));//true 148 | ``` 149 | 150 | - 如果`Object.prototype`作为实例对象的话,其原型对象是什么,结果是null。这大概也是 typeof null 的结果是 'object' 的原因之一吧。 151 | 152 | ```jsx 153 | console.log(Object.prototype.__proto__ === null);//true 154 | ``` 155 | 156 | - 函数也是对象,只不过是具有特殊功能的对象而已。任何函数都可以看做是通过`Function()`构造函数的new操作实例化的结果,如果把函数`Foo()`当成实例对象的话,其构造函数是`Function()`,其原型对象是`Function.prototype`;类似地,函数`Object()`的构造函数也是`Function()`,其原型对象是`Function.prototype` 。 157 | 158 | ```jsx 159 | console.log(Foo.__proto__ === Function.prototype);//true 160 | console.log(Object.__proto__ === Function.prototype);//true 161 | ``` 162 | 163 | - 原型对象`Function.prototype`的`constructor`属性指向构造函数`Function()`;实例对象`Object`和`Foo`本身没有`constructor`属性,需要继承原型对象`Function.prototype`的`constructor`属性。 164 | 165 | ```jsx 166 | console.log(Function.prototype.constructor === Function);//true 167 | console.log(Foo.constructor === Function);//true 168 | console.log(Foo.hasOwnProperty('constructor'));//false 169 | console.log(Object.constructor === Function);//true 170 | console.log(Object.hasOwnProperty('constructor'));//false 171 | ``` 172 | 173 | - 所有的函数都可以看成是构造函数`Function()`的new操作的实例化对象,只是`Function`作为实例对象时,其比较特殊,它的构造函数就是`Function` 自身,它的原型对象是`Function.prototype`。 174 | 175 | ```jsx 176 | console.log(Function.__proto__ === Function.prototype);//true 177 | console.log(Function.prototype.constructor === Function);//true 178 | console.log(Function.prototype === Function.prototype);//true 179 | ``` 180 | 181 | - 如果`Function.prototype`作为实例对象的话,其原型对象是什么呢?和前面一样,所有的对象都可以看成是`Object()`构造函数的new操作的实例化结果。所以,`Function.prototype`的原型对象是`Object.prototype`,其构造函数是`Object()`。 182 | 183 | ```jsx 184 | console.log(Function.prototype.__proto__ === Object.prototype);//true 185 | ``` 186 | 187 | - 前面介绍过`Object.prototype`的原型对象是`null` 。 188 | 189 | ```jsx 190 | console.log(Object.prototype.__proto__ === null);//true 191 | ``` 192 | 193 | 194 | # 参考资料 195 | 196 | [帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)](https://blog.csdn.net/cc18868876837/article/details/81211729) 197 | 198 | [一张图理解prototype、proto和constructor的三角关系](https://www.cnblogs.com/xiaohuochai/p/5721552.html) -------------------------------------------------------------------------------- /JavaScript/utils.md: -------------------------------------------------------------------------------- 1 | # utils 2 | 3 | ### **位数不够,补0** 4 | 5 | ```tsx 6 | /** 7 | * 位数不够,补0 8 | * @returns string 9 | * @param num 10 | * @param n 11 | */ 12 | export function prefixZero(num: number, n: number): string | number { 13 | if (String(num)?.length < 2) { 14 | return (Array.from({ length: n }).join("0") + num).slice(-n); 15 | } 16 | return num; 17 | } 18 | ``` 19 | 20 | ### **将秒数转换成 ‘时分秒’ 的格式** 21 | 22 | ```tsx 23 | /** 24 | * 将秒数转换成 '时分秒' 的格式 25 | * @returns string 26 | * @param value 27 | * @param format 28 | * @param precision 29 | */ 30 | export function formatSeconds(value: number, format = "zh", precision = "second"): string { 31 | if (Number.isNaN(Number(value))) { 32 | return "--"; 33 | } 34 | const formatValue = Number.parseInt(String(value), 10); // 对传过来的秒数做10进制处理 35 | const hour = Number.parseInt(String(formatValue / 3600), 10); // 小时 36 | const minute = Number.parseInt(String((formatValue % 3600) / 60), 10); // 分钟数 37 | const second = Number.parseInt(String(formatValue - hour * 3600 - minute * 60), 10); // 秒数 38 | if (format === "zh") { 39 | if (formatValue === 0) { 40 | return "0"; 41 | } 42 | let result = ""; 43 | if (second > 0 && precision === "second") { 44 | result = `${second}秒`; 45 | } 46 | if (minute > 0 && ["second", "minute"].includes(precision)) { 47 | result = `${minute}分${result}`; 48 | } 49 | if (hour > 0 && ["second", "minute", "hour"].includes(precision)) { 50 | result = `${hour}小时${result}`; 51 | } 52 | return result; 53 | } 54 | 55 | return `${prefixZero(hour, 2)}:${prefixZero(minute, 2)}:${prefixZero(second, 2)}`; 56 | } 57 | ``` 58 | 59 | ### **对数字的整数部分每隔三位加逗号处理** 60 | 61 | ```tsx 62 | /** 63 | * 对数字的整数部分每隔三位加逗号处理 64 | * @returns string 65 | * @param num 66 | * @param unit 单位 67 | */ 68 | export function formatCommaNum(num: number, unit?: string): string { 69 | if (Number.isNaN(num) || isNil(num)) { 70 | return "--"; 71 | } 72 | const [integer, decimal] = `${num}`.split("."); 73 | const commasInteger = (integer || 0).toString().replace(/(\d)(?=(?:\d{3})+$)/g, "$1,"); 74 | if (isNil(decimal)) { 75 | return isNil(unit) ? commasInteger : `${commasInteger}${unit}`; 76 | } 77 | return isNil(unit) ? `${commasInteger}.${decimal}` : `${commasInteger}.${decimal}${unit}`; 78 | } 79 | ``` 80 | 81 | ### **对小数转百分比处理** 82 | 83 | ```tsx 84 | /** 85 | * 对小数转百分比处理,默认保留2位小数 86 | * @returns string 87 | * @param rate 88 | * @param config 89 | * @param fixed 90 | */ 91 | interface FormatPercentConfig { 92 | multiply?: boolean; 93 | fixed?: number; 94 | percentMark?: boolean; 95 | } 96 | export function formatPercent(rate: number, config?: FormatPercentConfig): string { 97 | const { multiply = true, fixed = 2, percentMark = true } = config || {}; 98 | if (Number.isNaN(rate) || isNil(rate)) { 99 | return "--"; 100 | } 101 | if ([0, "0"].includes(rate)) { 102 | return "0"; 103 | } 104 | const str = multiply ? `${Number.parseFloat(String((Math.abs(rate) * 100).toFixed(fixed)))}` : `${Math.abs(rate)}`; 105 | if (percentMark) { 106 | return `${str}%`; 107 | } 108 | return str; 109 | } 110 | ``` 111 | 112 | ### **设置Storage** 113 | 114 | ```tsx 115 | /** 116 | * 设置Storage,默认localStorage 117 | * @returns void 118 | * @param key 119 | * @param value 120 | * @param storeType 121 | */ 122 | export function setStorage(key: string, value: any, storeType = "local"): void { 123 | let transSource: string; 124 | try { 125 | transSource = JSON.stringify(value); 126 | } catch { 127 | transSource = value; 128 | } 129 | if (storeType === "local") { 130 | localStorage.setItem(key, transSource); 131 | return; 132 | } 133 | sessionStorage.setItem(key, transSource); 134 | } 135 | ``` 136 | 137 | ### **获取Storage** 138 | 139 | ```tsx 140 | /** 141 | * 获取Storage,默认localStorage 142 | * @returns void 143 | * @param key 144 | * @param storeType 145 | */ 146 | export function getStorage(key: string, storeType = "local"): any { 147 | const getVal = storeType === "local" ? localStorage.getItem(key) : sessionStorage.getItem(key); 148 | return (() => { 149 | let transTarget: string; 150 | try { 151 | transTarget = JSON.parse(getVal); 152 | } catch { 153 | transTarget = getVal; 154 | } 155 | return transTarget; 156 | })(); 157 | } 158 | ``` 159 | 160 | ### **责任链钩子** 161 | 162 | ```tsx 163 | export class SyncBailHook { 164 | #tasks: any[]; 165 | 166 | constructor(array) { 167 | this.#tasks = array; 168 | } 169 | 170 | call(...args) { 171 | let ret; 172 | let index = 0; 173 | do { 174 | ret = this.#tasks[index](...args); 175 | index += 1; 176 | } while (ret === undefined && index < this.#tasks.length); 177 | 178 | return ret; 179 | } 180 | } 181 | ``` 182 | 183 | ### **发布订阅** 184 | 185 | ```tsx 186 | type EventHandler = (...args: any[]) => any; 187 | export class EventEmitter { 188 | #emiter = new Map(); 189 | 190 | public subscribe(topic: string, ...handlers: EventHandler[]) { 191 | let topics = this.#emiter.get(topic); 192 | if (!topics) { 193 | this.#emiter.set(topic, (topics = [])); 194 | } 195 | topics.push(...handlers); 196 | } 197 | 198 | public unsubscribe(topic: string, handler?: EventHandler): boolean { 199 | if (!handler) { 200 | return this.#emiter.delete(topic); 201 | } 202 | const topics = this.#emiter.get(topic); 203 | if (!topics) { 204 | return false; 205 | } 206 | const index = topics.indexOf(handler); 207 | if (index < 0) { 208 | return false; 209 | } 210 | topics.splice(index, 1); 211 | if (topics.length === 0) { 212 | this.#emiter.delete(topic); 213 | } 214 | return true; 215 | } 216 | 217 | public publish(topic: string, ...args: any[]): any[] | null { 218 | const topics = this.#emiter.get(topic); 219 | if (!topics) { 220 | return null; 221 | } 222 | return topics.map((handler: EventHandler) => { 223 | try { 224 | return handler(...args); 225 | } catch { 226 | return null; 227 | } 228 | }); 229 | } 230 | } 231 | ``` 232 | 233 | ### **深克隆** 234 | 235 | ```jsx 236 | const judeType = (obj) => { 237 | return Object.prototype.toString.call(obj).slice(8, -1); 238 | } 239 | const deepClone = (obj) => { 240 | let result; 241 | const type = judeType(obj); 242 | switch (type) { 243 | case 'Object': 244 | result = {}; 245 | break; 246 | case 'Array': 247 | result = []; 248 | break; 249 | default: 250 | return obj; 251 | } 252 | for (key in obj) { 253 | const copy = obj[key]; 254 | if (['Object', 'Array'].includes(judeType(copy))) { 255 | result[key] = arguments.callee(copy); 256 | } else { 257 | result[key] = obj[key]; 258 | } 259 | } 260 | return result; 261 | } 262 | ``` 263 | 264 | ### **Input 框只允许输入正数和小数** 265 | 266 | ```html 267 | 268 | ``` 269 | 270 | ### **谷歌浏览器去除记住密码 Input 框黄色背景** 271 | 272 | ```css 273 | input:-webkit-autofill{ 274 | -webkit-animation-name: autofill; 275 | -webkit-animation-fill-mode: both; 276 | } 277 | @-webkit-keyframes autofill { 278 | to{ 279 | color: #fff; 280 | background: transparent; 281 | } 282 | } 283 | ``` 284 | 285 | ### **火狐浏览器按钮点击后虚线去除** 286 | 287 | ```css 288 | ::-moz-focus-inner{border:none;} 289 | ``` 290 | 291 | ### **页面顶部阴影** 292 | 293 | ```css 294 | body:before { 295 | content: ""; 296 | position: fixed; 297 | top: -10px; 298 | left: 0; 299 | width: 100%; 300 | height: 10px; 301 | box-shadow: 0 0 10px rgba(0,0,0,.8); 302 | z-index: 100; 303 | } 304 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 洋洋晒晒 当个写手 2 | ### JavaScript 3 | * [JavaScript异步编程史](https://github.com/llaurora/KnowledgeNote/blob/master/JavaScript/JS%E5%BC%82%E6%AD%A5%E7%BC%96%E7%A8%8B%E5%8F%B2.md) 4 | * [prototype、__proto__与constructor](https://github.com/llaurora/KnowledgeNote/blob/master/JavaScript/prototype%E3%80%81__proto__%E4%B8%8Econstructor.md) 5 | * [utils常用工具函数](https://github.com/llaurora/KnowledgeNote/blob/master/JavaScript/utils.md) 6 | * [3D词云](https://github.com/llaurora/KnowledgeNote/blob/master/JavaScript/3D%20%E8%AF%8D%E4%BA%91.md) 7 | 8 | ### BrowerNetwork 9 | * [事件循环Event Loop](https://github.com/llaurora/KnowledgeNote/blob/master/BrowerNetwork/Event%20Loop.md) 10 | * [窥探浏览器基本原理](https://github.com/llaurora/KnowledgeNote/blob/master/BrowerNetwork/%E7%AA%A5%E6%8E%A2%E6%B5%8F%E8%A7%88%E5%99%A8%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86.md) 11 | * [浏览器如何计算CSS](https://github.com/llaurora/KnowledgeNote/blob/master/BrowerNetwork/%E6%B5%8F%E8%A7%88%E5%99%A8%E5%A6%82%E4%BD%95%E8%AE%A1%E7%AE%97CSS.md) 12 | 13 | ### React 14 | * [Class组件绑定this方法区别](https://github.com/llaurora/KnowledgeNote/blob/master/React/Class%E7%BB%84%E4%BB%B6%E7%BB%91%E5%AE%9Athis%E6%96%B9%E6%B3%95%E5%8C%BA%E5%88%AB.md) 15 | * [React源码调试环境搭建](https://github.com/llaurora/KnowledgeNote/blob/master/React/React%E6%BA%90%E7%A0%81%E8%B0%83%E8%AF%95%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA.md) 16 | * [ReactElement对象](https://github.com/llaurora/KnowledgeNote/blob/master/React/ReactElement%E5%AF%B9%E8%B1%A1.md) 17 | * [React调度Scheduler](https://github.com/llaurora/KnowledgeNote/blob/master/React/React%E8%B0%83%E5%BA%A6Scheduler.md) 18 | * [React事件系统](https://github.com/llaurora/KnowledgeNote/blob/master/React/React%E4%BA%8B%E4%BB%B6%E7%B3%BB%E7%BB%9F.md) 19 | * [React Hooks初次挂载](https://github.com/llaurora/KnowledgeNote/blob/master/React/React%20Hooks%E5%88%9D%E6%AC%A1%E6%8C%82%E8%BD%BD.md) 20 | * [React18 批量更新](https://github.com/llaurora/KnowledgeNote/blob/master/React/React18%E6%89%B9%E9%87%8F%E6%9B%B4%E6%96%B0.md) 21 | * [Zustand源码浅析](https://github.com/llaurora/KnowledgeNote/blob/master/React/Zustand%E6%BA%90%E7%A0%81%E6%B5%85%E6%9E%90.md) 22 | 23 | ### Essay 24 | * [音频可视化小记](https://github.com/llaurora/KnowledgeNote/blob/master/Essay/%E9%9F%B3%E9%A2%91%E5%8F%AF%E8%A7%86%E5%8C%96%E5%B0%8F%E8%AE%B0.md) 25 | 26 | ### Shell 27 | * [Git](https://github.com/llaurora/KnowledgeNote/blob/master/Shell/Git.md) 28 | * [Package Manager](https://github.com/llaurora/KnowledgeNote/blob/master/Shell/pkg%20manager.md) -------------------------------------------------------------------------------- /React/Class组件绑定this方法区别.md: -------------------------------------------------------------------------------- 1 | # Class组件this绑定 2 | 3 | > 在 Hooks 组件中不存在使用 this 的困扰,所以这儿只针对 Class 类组件 4 | 5 | # 为什么Class 类组件的事件处理函数需要绑定 this ? 6 | 7 | 8 | 这一点应该都知道,如果不对事件处理函数绑定 this,在处理函数中访问不到 this。 9 | 10 | ```jsx 11 | class Foo extends React.Component{ 12 | constructor(props){ 13 | super(props); 14 | } 15 | handleClick(){ 16 | console.log(this); // 'this' is undefined 17 | } 18 | render(){ 19 | return ( 20 | 23 | ); 24 | } 25 | } 26 | ``` 27 | 28 | 这和 React 其实并没有关系,是 JavaScript 的 this 绑定机制导致了上面的问题。 29 | 30 | ```jsx 31 | class Foo { 32 | constructor(name){ 33 | this.name = name 34 | } 35 | display(){ 36 | console.log(this.name); 37 | } 38 | } 39 | const foo = new Foo('Saurabh'); 40 | foo.display(); // Saurabh 41 | 42 | //下面的赋值操作模拟了上下文的丢失。 43 | //与实际在 React Component 中将处理程序作为 callback 参数传递 相似。 44 | 45 | const display = foo.display; 46 | display(); // TypeError: this is undefined 47 | ``` 48 | 49 | 更深层次原因在这儿不再赘述,今儿主要讨论 **Class类组件绑定 this 方法的区别。** 50 | 51 | # Class类组件绑定 this 52 | 53 | ## 直接在 render 上绑定 54 | 55 | ```jsx 56 | 57 | ``` 58 | 59 | 对于这种做法,会在每次 render 的时候,重新生成绑定函数,所以这种方式是不建议在在实际生产中使用的。 60 | 61 | ## Constructor bind 62 | 63 | Constructor bind 就是在 Class 的 constructor 中为方法 bind this。 64 | 65 | ```jsx 66 | class Foo extends React.Component{ 67 | constructor(props){ 68 | super(props); 69 | this.handleClick = this.handleClick.bind(this); 70 | } 71 | handleClick(){ 72 | console.log(this); 73 | } 74 | render(){ 75 | return ( 76 | 79 | ); 80 | } 81 | } 82 | ``` 83 | 84 | ## Class properties 85 | 86 | Class properties 就是在 Class 类方法中使用箭头函数语法。 87 | 88 | > Class properties 现已经在 ES2022 规范中发布 89 | > 90 | 91 | ```jsx 92 | class Foo extends React.Component{ 93 | constructor(props){ 94 | super(props); 95 | } 96 | handleClick = () => { 97 | console.log(this); 98 | } 99 | render(){ 100 | return ( 101 | 104 | ); 105 | } 106 | } 107 | ``` 108 | 109 | # Constructor bind Vs Class properties 110 | 111 | 直接在 render 上绑定 this 的方法不建议在项目生成中使用,故不考虑在此处比较。讲清楚 Constructor bind 和 Class properties 两者之间的差异,说白了其实就是**构造原型上的方法和原型链上方法的优劣。**暂且不论 ES6 的 Class,我们先来看ES5 的使用构造函数的方法。 112 | 113 | ```jsx 114 | function Student (name, subject) { 115 | this.name = name; 116 | this.subject = subject; 117 | this.study = function() { 118 | console.log('我在学习' + this.subject); 119 | } 120 | } 121 | ``` 122 | 123 | 现在我们来实例化 `Student` 构造函数, 生成 `student1`和 `student2`, 并分别调用其 `study`方法。 124 | 125 | ```jsx 126 | const student1 = new Student('阿辉', '前端开发'); 127 | const student2 = new Student('阿傻', '大数据开发'); 128 | 129 | student1.study(); //我在学习前端开发 130 | student2.study(); //我在学习大数据开发 131 | ``` 132 | 133 | 这样生成的实例对象表面上看没有任何问题,但大家应该有知道这样其实是有性能问题的。 134 | 135 | ```jsx 136 | console.log(student1.study === student2.study); //false 137 | ``` 138 | 139 | 其实对于每一个实例对象 `studentx`,其 `study`方法的函数体是一模一样的,但是生成的每个实例都需要生成一个 `study`方法去占用一份内存,这样是非常不经济的做法。 140 | 141 | 所以不难知晓解决方法是将 `study`方法挂载到 `Student.prototype`原型对象上,所有的 `studentx`实例都能继承该方法。 142 | 143 | ```jsx 144 | function Student (name, subject) { 145 | this.name = name; 146 | this.subject = subject; 147 | } 148 | Student.prototype.study = function() { 149 | console.log('我在学习' + this.subject); 150 | } 151 | ``` 152 | 153 | 现在再实例化 `student1`和 `student2`。 154 | 155 | ```jsx 156 | const student1 = new Student('阿辉', '前端开发'); 157 | const student2 = new Student('阿傻', '大数据开发'); 158 | 159 | student1.study(); //我在学习前端开发 160 | student2.study(); //我在学习大数据开发 161 | console.log(student1.study === student2.study); //true 162 | ``` 163 | 164 | 从上面的代码我们可以看出,`student1`和 `student2`的 `study`方法执行结果没有发生变化,但是 `study`本身指向了一个内存地址。 165 | 166 | 而 **ES6 的 Class 其实只是ES5 构造函数的一个语法糖而已,在其中声明的方法是挂载到原型链上的**,再回到话题上 Constructor bind Vs Class properties。 167 | 168 | ![thisbindvs.jpeg](./assets/thisbindvs.jpeg) 169 | 170 | 其实就不难看出,**若在 constructor 里面 bind 一次,不止在原型链上声明有一个 fn 方法,还在实例上有多一个 fn 方法。** 171 | 172 | # 总结 173 | 174 | 在 React 中,如果我们的事件处理函数不需要访问 this,那么直接在 Class 组件中声明方法是要优于用Class properties(减少内存消耗),而如果我们的事件处理函数需要访问 this,那还是建议采用Class properties,毕竟你如果是在 constructor 里面 进行bind的话,相较采用 Class properties 的方式而言, 不但等同于用 Class properties 在实例上声明了方法,还多了一个原型链上面的方法。 175 | -------------------------------------------------------------------------------- /React/ReactElement对象.md: -------------------------------------------------------------------------------- 1 | # ReactElement对象 2 | 3 | # JSX 装换 4 | 5 | JSX是一种语法糖,全称 JavaScript XML,是 React 定义的一种类似于 XML 的 JS 扩展语法。JSX 代码是无法在浏览器中直接使用的,需要依靠 Babel 或者 TypeScript **将 JSX 代码转换为 JavaScript**。 6 | 7 | ```jsx 8 |
title
9 | ``` 10 | 11 | 在 [Babel REPL](https://babeljs.io/repl) 可以去看下上面这段 JSX 代码进行编译后输出的是啥,选定预设 [@babel/preset-react](https://babeljs.io/docs/en/babel-preset-react/) 并将配置项 runtime 设置为 classic。 12 | 13 | ![react16JsxTransform.jpg](./assets/react16JsxTransform.jpg) 14 | 15 | 可以看到经 Babel 编译后,上面的 JSX 代码变成了下面这段 JavaScript 代码: 16 | 17 | ```jsx 18 | React.createElement("div", {key: "title"}, "title") 19 | ``` 20 | 21 | 那如果将预设 [@babel/preset-react](https://babeljs.io/docs/en/babel-preset-react/) 的配置项 runtime 设置为 automatic 呢? 22 | 23 | ![react17JsxTransform.jpg](./assets/react17JsxTransform.jpg) 24 | 25 | 可以看到经 Babel 编译后,上面的 JSX 代码变成了下面这段 JavaScript 代码: 26 | 27 | ```jsx 28 | import { jsx as _jsx } from "react/jsx-runtime"; 29 | _jsx("div", {children: "title"}, "title"); 30 | ``` 31 | 32 | 而之所以会有这两种输出,是因为在 React 17 之后,React 官方与 Babel 进行了合作,对 JSX 进行了新的转换, 在 React 17 之后如果只是使用 JSX 而不使用 React 提供的其他 api,在源代码中则可以不引入 React,更多有关于 JSX 转换的内容可移步 React 官网:[介绍全新的 JSX 转换](https://zh-hans.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html) 。 33 | 34 | # React.createElement 35 | 36 | > 后面代码分析针对 React17.0.2 版本。 37 | > 38 | 39 | 虽然在 React 17 之后,只是使用 JSX 语法是可以不用在源代码引入 React,但在实际场景中以及很多开源包中使用`React.createElement`手动创建元素的场景并不少见,学习下 [React.createElemt 源码](https://github.com/facebook/react/blob/17.0.2/packages/react/src/ReactElement.js) 还是有必要的。 40 | 41 | `React.createElement`定义在`ReactElement.js`文件中([见源码](https://github.com/facebook/react/blob/12adaffef7105e2714f82651ea51936c563fe15c/packages/react/src/ReactElement.js#L348))。 42 | 43 | ```jsx 44 | export function createElement(type, config, children) { 45 | let propName; 46 | 47 | // Reserved names are extracted 48 | const props = {}; 49 | 50 | let key = null; 51 | let ref = null; 52 | let self = null; 53 | let source = null; 54 | 55 | if (config != null) { 56 | if (hasValidRef(config)) { 57 | ref = config.ref; 58 | 59 | if (__DEV__) { 60 | warnIfStringRefCannotBeAutoConverted(config); 61 | } 62 | } 63 | if (hasValidKey(config)) { 64 | key = '' + config.key; 65 | } 66 | 67 | self = config.__self === undefined ? null : config.__self; 68 | source = config.__source === undefined ? null : config.__source; 69 | // Remaining properties are added to a new props object 70 | for (propName in config) { 71 | if ( 72 | hasOwnProperty.call(config, propName) && 73 | !RESERVED_PROPS.hasOwnProperty(propName) 74 | ) { 75 | props[propName] = config[propName]; 76 | } 77 | } 78 | } 79 | 80 | // Children can be more than one argument, and those are transferred onto 81 | // the newly allocated props object. 82 | const childrenLength = arguments.length - 2; 83 | if (childrenLength === 1) { 84 | props.children = children; 85 | } else if (childrenLength > 1) { 86 | const childArray = Array(childrenLength); 87 | for (let i = 0; i < childrenLength; i++) { 88 | childArray[i] = arguments[i + 2]; 89 | } 90 | if (__DEV__) { 91 | if (Object.freeze) { 92 | Object.freeze(childArray); 93 | } 94 | } 95 | props.children = childArray; 96 | } 97 | 98 | // Resolve default props 99 | if (type && type.defaultProps) { 100 | const defaultProps = type.defaultProps; 101 | for (propName in defaultProps) { 102 | if (props[propName] === undefined) { 103 | props[propName] = defaultProps[propName]; 104 | } 105 | } 106 | } 107 | if (__DEV__) { 108 | if (key || ref) { 109 | const displayName = 110 | typeof type === 'function' 111 | ? type.displayName || type.name || 'Unknown' 112 | : type; 113 | if (key) { 114 | defineKeyPropWarningGetter(props, displayName); 115 | } 116 | if (ref) { 117 | defineRefPropWarningGetter(props, displayName); 118 | } 119 | } 120 | } 121 | return ReactElement( 122 | type, 123 | key, 124 | ref, 125 | self, 126 | source, 127 | ReactCurrentOwner.current, 128 | props, 129 | ); 130 | } 131 | ``` 132 | 133 | 忽略掉开发环境的配置,大致可以分成四个部分: 134 | 135 | 1. 处理元素的 props 136 | 137 | ```jsx 138 | let propName; 139 | 140 | // Reserved names are extracted 141 | const props = {}; 142 | 143 | let key = null; 144 | let ref = null; 145 | let self = null; 146 | let source = null; 147 | 148 | // 如果元素上有配置属性,则对配置属性进行提取处理,内置的保留属性会从props中被剔除 149 | if (config != null) { 150 | //如果存在有效的ref值(config.ref !== undefined),赋值给上面的 ref 151 | if (hasValidRef(config)) { 152 | ref = config.ref; 153 | // 开发环境相关,忽略 154 | if (__DEV__) { 155 | warnIfStringRefCannotBeAutoConverted(config); 156 | } 157 | } 158 | // 如果存在有效的key值(config.key !== undefined),对key转字符串处理 159 | if (hasValidKey(config)) { 160 | key = '' + config.key; 161 | } 162 | 163 | // self 和 source 是开发环境下对代码在编译器中位置等信息进行记录,用于开发环境下调试 164 | // 更多关于这俩的作用,可参考该 [issue](https://github.com/alangpierce/sucrase/issues/232) 165 | self = config.__self === undefined ? null : config.__self; 166 | source = config.__source === undefined ? null : config.__source; 167 | // Remaining properties are added to a new props object 168 | // 除去内置的属性将会被添加到props,内置的属性RESERVED_PROPS包含key、ref、__self和__source 169 | for (propName in config) { 170 | if ( 171 | hasOwnProperty.call(config, propName) && 172 | !RESERVED_PROPS.hasOwnProperty(propName) 173 | ) { 174 | props[propName] = config[propName]; 175 | } 176 | } 177 | } 178 | ``` 179 | 180 | 从上面这段代码也不难看出,如果一个父组件给子组件传递了 key 值,在子组件中是没办法通过 props.key 来取到值的,因为在处理 props 的时候,key 是内置保留属性,在 props 中被剔除掉了 181 | 182 | ```jsx 183 | const Child = (props) => { 184 | console.log(props.key); // undefined 185 | return
child
; 186 | }; 187 | 188 | const Parent = () => { 189 | return ( 190 |
191 | 192 |
193 | ); 194 | }; 195 | ``` 196 | 197 | 2. 将子节点添加到 props 的 children 属性上 198 | 199 | ```jsx 200 | // Children can be more than one argument, and those are transferred onto 201 | // the newly allocated props object. 202 | // arguments 前两个分别为 type 和 config,后面的均为 children 203 | const childrenLength = arguments.length - 2; 204 | // 如果子节点长度为1,则表明只有一个子节点,直接将其赋值给 props.children 205 | if (childrenLength === 1) { 206 | props.children = children; 207 | // 如果子节点的长度大于1,则将子节点 push 到一个数组中,最后将这个数组赋值给 props.children 208 | } else if (childrenLength > 1) { 209 | const childArray = Array(childrenLength); 210 | for (let i = 0; i < childrenLength; i++) { 211 | childArray[i] = arguments[i + 2]; 212 | } 213 | // 开发环境相关,忽略 214 | if (__DEV__) { 215 | if (Object.freeze) { 216 | Object.freeze(childArray); 217 | } 218 | } 219 | props.children = childArray; 220 | } 221 | ``` 222 | 223 | 3. 处理 defaultProps,如果存在 defaultProps,则给 props 对应的属性赋一个默认值 224 | 225 | ```jsx 226 | // Resolve default props 227 | if (type && type.defaultProps) { 228 | const defaultProps = type.defaultProps; 229 | for (propName in defaultProps) { 230 | if (props[propName] === undefined) { 231 | props[propName] = defaultProps[propName]; 232 | } 233 | } 234 | } 235 | ``` 236 | 237 | 无论是函数组件还是 class 组件都是支持 defaultProps,只是在函数组件中使用 defaultProps 给组件添加默认 props 不常用 238 | 239 | ```jsx 240 | // 函数组件 241 | const Child = (props) => { 242 | console.log(props.id); // 100 243 | return
child
; 244 | }; 245 | 246 | Child.defaultProps = { 247 | id: "100", 248 | }; 249 | 250 | // 类组件 251 | class Child1 extends Component { 252 | static defaultProps = { 253 | id: "100", 254 | }; 255 | 256 | render() { 257 | const { id } = this.props; 258 | console.log(id); // 100 259 | return
child
; 260 | } 261 | } 262 | ``` 263 | 264 | 4. 最后一段好说,就是将相应参数传递给 ReactElement 方法,并返回 265 | 266 | ```jsx 267 | // 开发环境相关,可忽略 268 | if (__DEV__) { 269 | if (key || ref) { 270 | const displayName = 271 | typeof type === 'function' 272 | ? type.displayName || type.name || 'Unknown' 273 | : type; 274 | if (key) { 275 | defineKeyPropWarningGetter(props, displayName); 276 | } 277 | if (ref) { 278 | defineRefPropWarningGetter(props, displayName); 279 | } 280 | } 281 | } 282 | return ReactElement( 283 | type, 284 | key, 285 | ref, 286 | self, 287 | source, 288 | ReactCurrentOwner.current, 289 | props, 290 | ); 291 | ``` 292 | 293 | 294 | # ReactElement() 295 | 296 | 从上面 React.createElement() 代码可以看到最后收尾在 ReactElement() 上,这里调用的 ReactElement() 也是定义在`ReactElement.js`文件中([见源码](https://github.com/facebook/react/blob/12adaffef7105e2714f82651ea51936c563fe15c/packages/react/src/ReactElement.js#L146)),忽略开发环境相关代码的话,ReactElement() 的代码很简单,其实就是返回了一个 JS 对象,这个 JS 对象也就是我们经常听到的虚拟 DOM,也可以将该对象称为 ReactElement 对象,其标志特征就是有一个 $$typeof 属性,其值为 REACT_ELEMENT_TYPE。 297 | 298 | ```jsx 299 | const ReactElement = function(type, key, ref, self, source, owner, props) { 300 | const element = { 301 | // This tag allows us to uniquely identify this as a React Element 302 | $$typeof: REACT_ELEMENT_TYPE, 303 | 304 | // Built-in properties that belong on the element 305 | type: type, 306 | key: key, 307 | ref: ref, 308 | props: props, 309 | 310 | // Record the component responsible for creating this element. 311 | _owner: owner, 312 | }; 313 | 314 | if (__DEV__) { 315 | // The validation flag is currently mutative. We put it on 316 | // an external backing store so that we can freeze the whole object. 317 | // This can be replaced with a WeakMap once they are implemented in 318 | // commonly used development environments. 319 | element._store = {}; 320 | 321 | // To make comparing ReactElements easier for testing purposes, we make 322 | // the validation flag non-enumerable (where possible, which should 323 | // include every environment we run tests in), so the test framework 324 | // ignores it. 325 | Object.defineProperty(element._store, 'validated', { 326 | configurable: false, 327 | enumerable: false, 328 | writable: true, 329 | value: false, 330 | }); 331 | // self and source are DEV only properties. 332 | Object.defineProperty(element, '_self', { 333 | configurable: false, 334 | enumerable: false, 335 | writable: false, 336 | value: self, 337 | }); 338 | // Two elements created in two different places should be considered 339 | // equal for testing purposes and therefore we hide it from enumeration. 340 | Object.defineProperty(element, '_source', { 341 | configurable: false, 342 | enumerable: false, 343 | writable: false, 344 | value: source, 345 | }); 346 | if (Object.freeze) { 347 | Object.freeze(element.props); 348 | Object.freeze(element); 349 | } 350 | } 351 | 352 | return element; 353 | }; 354 | ``` 355 | 356 | 这个 REACT_ELEMENT_TYPE 定义位置在 `ReactSymbols.js`文件中([见源码](https://github.com/facebook/react/blob/17.0.2/packages/shared/ReactSymbols.js)),就是一个常量值,如果浏览器支持 Symbol 这种数据类型,则优先用 Symbol 表示。 357 | 358 | ``` 359 | export let REACT_ELEMENT_TYPE = 0xeac7; 360 | export let REACT_PORTAL_TYPE = 0xeaca; 361 | // 省略... 362 | 363 | if (typeof Symbol === 'function' && Symbol.for) { 364 | const symbolFor = Symbol.for; 365 | REACT_ELEMENT_TYPE = symbolFor('react.element'); 366 | REACT_PORTAL_TYPE = symbolFor('react.portal'); 367 | // 省略... 368 | } 369 | ``` 370 | 371 | 上面代码除了定义 REACT_ELEMENT_TYPE 常量外,还定义有其他常量,比如 REACT_PORTAL_TYPE,调用 createPortal() 返回的 JS 对象的 $$typeof 值就是 REACT_PORTAL_TYPE。 372 | 373 | ```jsx 374 | // 文件路径 [react/packages/react-reconciler/src/ReactPortal.js](https://github.com/facebook/react/blob/17.0.2/packages/react-reconciler/src/ReactPortal.js) 375 | export function createPortal( 376 | children: ReactNodeList, 377 | containerInfo: any, 378 | // TODO: figure out the API for cross-renderer implementation. 379 | implementation: any, 380 | key: ?string = null, 381 | ): ReactPortal { 382 | return { 383 | // This tag allow us to uniquely identify this as a React Portal 384 | $$typeof: REACT_PORTAL_TYPE, 385 | key: key == null ? null : '' + key, 386 | children, 387 | containerInfo, 388 | implementation, 389 | }; 390 | } 391 | ``` 392 | 393 | # _jsx() 394 | 395 | 再看下 React 17 之后这个新的 JSX 转换,引用自 [react/jsx-runtime.js](https://github.com/facebook/react/blob/17.0.2/packages/react/jsx-runtime.js) 文件,只是该方法仅由编译器引入,禁止在开发项目代码时自己手动引入,这个 _jsx() 方法最后是定义在 `ReactJSXElement.js`文件中([见源码](https://github.com/facebook/react/blob/12adaffef7105e2714f82651ea51936c563fe15c/packages/react/src/jsx/ReactJSXElement.js#L209))。 396 | 397 | ```jsx 398 | // 同文件中还定义有 jsxDEV() 方法,供开发环境使用,和下面的jsx方法差不多 399 | export function jsx(type, config, maybeKey) { 400 | let propName; 401 | 402 | // Reserved names are extracted 403 | const props = {}; 404 | 405 | let key = null; 406 | let ref = null; 407 | 408 | // Currently, key can be spread in as a prop. This causes a potential 409 | // issue if key is also explicitly declared (ie.
410 | // or
). We want to deprecate key spread, 411 | // but as an intermediary step, we will use jsxDEV for everything except 412 | //
, because we aren't currently able to tell if 413 | // key is explicitly declared to be undefined or not. 414 | if (maybeKey !== undefined) { 415 | key = '' + maybeKey; 416 | } 417 | 418 | if (hasValidKey(config)) { 419 | key = '' + config.key; 420 | } 421 | 422 | if (hasValidRef(config)) { 423 | ref = config.ref; 424 | } 425 | 426 | // Remaining properties are added to a new props object 427 | for (propName in config) { 428 | if ( 429 | hasOwnProperty.call(config, propName) && 430 | !RESERVED_PROPS.hasOwnProperty(propName) 431 | ) { 432 | props[propName] = config[propName]; 433 | } 434 | } 435 | 436 | // Resolve default props 437 | if (type && type.defaultProps) { 438 | const defaultProps = type.defaultProps; 439 | for (propName in defaultProps) { 440 | if (props[propName] === undefined) { 441 | props[propName] = defaultProps[propName]; 442 | } 443 | } 444 | } 445 | 446 | return ReactElement( 447 | type, 448 | key, 449 | ref, 450 | undefined, 451 | undefined, 452 | ReactCurrentOwner.current, 453 | props, 454 | ); 455 | } 456 | ``` 457 | 458 | 其实仔细看下上面这个 jsx() 方法和 React.createElement() 的作用差不多,都是对调用 ReactElement() 方法的入参做一些预处理,都是调用 ReactElement() 方法然后返回一个 JS 对象,即 ReactElement 对象。 459 | 460 | 而且这里 jsx() 里面调用的 ReactElement() 方法虽然是定义在文件`ReactJSXElement.js`文件([见源码](https://github.com/facebook/react/blob/12adaffef7105e2714f82651ea51936c563fe15c/packages/react/src/jsx/ReactJSXElement.js#L147))中的,但是和 React.createElement() 里面调用的 ReactElement() 方法(定义在`ReactElement.js`文件([见源码](https://github.com/facebook/react/blob/12adaffef7105e2714f82651ea51936c563fe15c/packages/react/src/ReactElement.js#L146)))内容是一模一样的,也就不再分析了。 -------------------------------------------------------------------------------- /React/React源码调试环境搭建.md: -------------------------------------------------------------------------------- 1 | # React源码调试环境搭建 2 | 3 | ![ideDebugReactPerfect.jpg](./assets/ideDebugReactPerfect.jpg) 4 | 5 | # 创建项目 6 | 7 | 通过官方脚手架 [create-react-app](https://create-react-app.dev/) 创建一个 react项目,在终端执行以下命令(在这儿的创建的项目的 react 版本是 18.2.0): 8 | 9 | ```bash 10 | npx create-react-app react-debug 11 | ``` 12 | 13 | # 暴露webpack配置 14 | 15 | > 之所以要暴露 webpack 配置,是因为后面在修改引用 react 路径的时候,需要用到 webpack 的 alias 别名功能。并且最好在创建好项目后就去暴露,因为后面步骤因为或多或少有目录中的文件或代码有变更,再去暴露的话,会提示文件有变化,让回退或提交之后才能暴露。 16 | > 17 | 18 | 创建好项目后,通过`npm run eject`(通过 create-react-app 创建的项目默认是使用的 npm 作为包管理器)。 19 | 20 | 在执行`npm run eject`的时候会提示 `Are you sure you want to eject? This action is permanent.`直接选择 y 就可以,命令执行完毕后在新增的 config 文件夹下可以看到 webpack 的配置文件了。 21 | 22 | ![exportWebpack.jpg](./assets/exportWebpack.jpg) 23 | 24 | 另外通过 create-react-app 创建的项目,默认有 eslint,这个感觉有点儿烦人,可以禁用掉,对源码调试没有影响。 25 | 26 | ![disableEslint.jpg](./assets/disableEslint.jpg) 27 | 28 | # 用编辑器调试项目 29 | 30 | 项目创建好了之后,进入项目并运行 `npm run start` 即可跑起来,这时候是是可以通过浏览器控制台调试的。 31 | 32 | ![browserdebug.jpg](./assets/browserdebug.jpg) 33 | 34 | 那有没有办法通过编辑器来断点调试,当然是可以的,在这儿记录通过 Webstorm 和 VSCode 进行断点调试。 35 | 36 | ## Webstorm 断点调试 37 | 38 | 1. 如果是用的 Webstorm,在插件 Plugins 里面搜索 `JavaScript Debugger` 并安装; 39 | 2. 点击编辑器右上角 `Add Configurations` => 点击`+`号并找到`JavaScript Debugger` => 点击进入配置界面; 40 | 41 | ![webstormAddDebug.jpg](./assets/webstormAddDebug.jpg) 42 | 43 | 3. 在配置界面命名 debugger 名称,输入 URL,选择浏览器后,点击 OK 后在编辑器右上角(上图图 1 的位置)就可以看到刚刚的配置了; 44 | 45 | ![webstormDebugConfig.jpg](./assets/webstormDebugConfig.jpg) 46 | 47 | 4. 选择刚配置的 `react-debug`,点击旁边的甲壳虫按钮,这时会自动打开浏览器,不过这时打开的界面是空白的,因为项目没有启起来嘛; 48 | 49 | ![webstormRunDebug.jpg](./assets/webstormRunDebug.jpg) 50 | 51 | 5. 在代码某个位置打上断点,在终端运行`npm run start`,项目在 3000 端口跑起来,这个时候 Webstorm 即可在代码打断点的地方断住了(如果没断住,项目跑起来之后重复上面第4步); 52 | 53 | ![webstormDebugSuccess.jpg](./assets/webstormDebugSuccess.jpg) 54 | 55 | 56 | ## VSCode 断点调试 57 | 58 | 1. 在根目录下建一个 `.vscode/launch.json`的文件,添加一个 Chrome 类型的调试配置; 59 | 60 | ![vscodeAddDebug.jpg](./assets/vscodeAddDebug.jpg) 61 | 62 | 2. 输入调试的 URL(项目跑起来的调试地址); 63 | 64 | ![vscodeDebugConfig.jpg](./assets/vscodeDebugConfig.jpg) 65 | 66 | 3. 点击运行 Debug 按钮,这时同样会自动打开浏览器,不过界面也是空白的,同样因为项目没有启动嘛; 67 | 68 | ![vscodeRunDebug.jpg](./assets/vscodeRunDebug.jpg) 69 | 70 | 4. 在代码某个位置打上断点,在终端运行`npm run start`,项目在 3000 端口跑起来,这个时候 VSCode 即可在代码打断点的地方断住了(如果没断住,项目跑起来之后重复上面第3步); 71 | 72 | ![vscodeDebugSuccess.png](./assets/vscodeDebugSuccess.png) 73 | 74 | 75 | # 构建有 sourcemap 的 react 包 76 | 77 | 到目前为止,不管是在浏览器调试,还是在编辑器里面调试,调试的都是 node_modules 中的打包好之后的 react 中的文件,许多代码都糅杂在一个文件中(比如 react-dom.development.js、scheduler.development.js等),而源码里这些逻辑是分散在不同的包里的,所以就算搞懂了逻辑,也不知道这些逻辑在哪些包里哪些位置,只能靠搜索来定位,那怎么能调试 react 最初的源码呢?这儿就涉及到需要构建一个带有 sourcemap 的 react 包了。 78 | 79 | 因此在项目根目录下引入一下 react 的源码(在这儿 clone 的是 react 当前最新的版本 18.2.0): 80 | 81 | ```bash 82 | git clone https://github.com/facebook/react 83 | # git clone https://github.com/facebook/react.git -b 17.0.2 84 | ``` 85 | 86 | 并进入到刚引入的`react`目录下执行下命令安装依赖: 87 | 88 | ```bash 89 | yarn install 90 | ``` 91 | 92 | 注:因为 react 源码本身是用 yarn 管理包依赖,这儿用 npm install 的话,会报错 `npm ERR! Unsupported URL Type "link:": link:./scripts/eslint-rules` ,虽然可将`link`关键字改为`file`,但用 npm 的话可能还会导致其他报错,这儿建议直接使用 yarn。 93 | 94 | 此时在`react`目录下运行构建命令`yarn run build`的话,构建出来的代码是不带有 sourcemap 的(和通过`npm install react`安装在项目`node_modules`目录下的东西是一样的),需要改造下 build 配置。 95 | 96 | ![reactNoSourcemap.jpg](./assets/reactNoSourcemap.jpg) 97 | 98 | build 命令执行的是`./scripts/rollup/build.js`,打开这个文件做一些修改,找到 rollup 的配置,将 sourcemap 改为 true。 99 | 100 | ![reactSourcemapTrue.jpg](./assets/reactSourcemapTrue.jpg) 101 | 102 | 再次运行构建命令 `yarn run build`,会报如下错误: 103 | 104 | ![reactBuildSourcemapError.jpg](./assets/reactBuildSourcemapError.jpg) 105 | 106 | sourcemap 被中断了,某个转换的插件没有生成 sourcemap。这是因为构建的过程中会进行多次转换,会生成多次 sourcemap,然后把 sourcemap 串联起来就是最终的 sourcemap。如果中间有一步转换没有生成 sourcemap,那就断掉了,也就没法把 sourcemap 串联起来了,解决这个问题的办法就是找出没有生成 sourcemap 的那几个插件注释掉就可以了。 107 | 108 | 在 `./scripts/rollup/build.js` 文件中找到 getPlugins 方法然后注释掉下面几个插件: 109 | 110 | ```jsx 111 | // Remove 'use strict' from individual source files. 112 | // { 113 | // transform(source) { 114 | // return source.replace(/['"]use strict["']/g, ''); 115 | // }, 116 | // }, 117 | ``` 118 | 119 | ```jsx 120 | // Apply dead code elimination and/or minification. 121 | // isProduction && 122 | // closure({ 123 | // compilation_level: 'SIMPLE', 124 | // language_in: 'ECMASCRIPT_2015', 125 | // language_out: 126 | // bundleType === BROWSER_SCRIPT ? 'ECMASCRIPT5' : 'ECMASCRIPT5_STRICT', 127 | // env: 'CUSTOM', 128 | // warning_level: 'QUIET', 129 | // apply_input_source_maps: false, 130 | // use_types_for_optimization: false, 131 | // process_common_js_modules: false, 132 | // rewrite_polyfills: false, 133 | // inject_libraries: false, 134 | // 135 | // // Don't let it create global variables in the browser. 136 | // // https://github.com/facebook/react/issues/10909 137 | // assume_function_wrapper: !isUMDBundle, 138 | // renaming: !shouldStayReadable, 139 | // }), 140 | ``` 141 | 142 | ```jsx 143 | // Note that this plugin must be called after closure applies DCE. 144 | // isProduction && stripUnusedImports(pureExternalModules), 145 | ``` 146 | 147 | ```jsx 148 | // Add the whitespace back if necessary. 149 | // shouldStayReadable && 150 | // prettier({ 151 | // parser: 'babel', 152 | // singleQuote: false, 153 | // trailingComma: 'none', 154 | // bracketSpacing: true, 155 | // }), 156 | ``` 157 | 158 | ```jsx 159 | // License and haste headers, top-level `if` blocks. 160 | // { 161 | // renderChunk(source) { 162 | // return Wrappers.wrapBundle( 163 | // source, 164 | // bundleType, 165 | // globalName, 166 | // filename, 167 | // moduleType, 168 | // bundle.wrapWithModuleBoundaries 169 | // ); 170 | // }, 171 | // }, 172 | ``` 173 | 174 | 注释掉上面这几个插件之后,再运行构建命令`yarn run build`,这时候就能正常进行构建了,然后构建出的代码就是带有 sourcemap 的。 175 | 176 | ![reactBuildWithSourcemap.jpg](./assets/reactBuildWithSourcemap.jpg) 177 | 178 | # 修改项目react引用路径 179 | 180 | 从之前的`react`目录回到项目`react-debug`根目录,在之前通过`npm run eject` 暴露出的 webpack 配置文件`config/webpack.config.js`中通过 alias 配置修改 react 相关的包的引用路径(将其指向改为刚引入的源码)。 181 | 182 | ```jsx 183 | alias: { 184 | // .... 185 | 'react-dom$': path.resolve(__dirname, '../react/build/node_modules/react-dom/umd/react-dom.development.js'), 186 | 'react$': path.resolve(__dirname, '../react/build/node_modules/react/umd/react.development.js'), 187 | // ... 188 | }, 189 | ``` 190 | 191 | ![reactDebugAlias.jpg](./assets/reactDebugAlias.jpg) 192 | 193 | 这时候通过`npm run start`跑起来会报错,如下图: 194 | 195 | ![moduleScopeError.jpg](./assets/moduleScopeError.jpg) 196 | 197 | 这个是因为`ModuleScopePlugin`的限制,去webpack 配置文件`config/webpack.config.js`里把这个插件注释掉,再重新跑就能跑起来了。 198 | 199 | ```jsx 200 | // new ModuleScopePlugin(paths.appSrc, [ 201 | // paths.appPackageJson, 202 | // reactRefreshRuntimeEntry, 203 | // reactRefreshWebpackPluginRuntimeEntry, 204 | // babelRuntimeEntry, 205 | // babelRuntimeEntryHelpers, 206 | // babelRuntimeRegenerator, 207 | // ]), 208 | ``` 209 | 210 | # 编辑器调试react最初的源码 211 | 212 | 到此,如果是用的 webstorm 调试的话,在代码里面想断点的地方打上断点,然后运行`npm run start`,再运行之前配置的断点,即可以在 webstorm 里面愉快的调试最初的源码(可以定位到代码在react源码中具体的包具体的行)了。 213 | 214 | ![webstormDebugReactPerfect.jpg](./assets/webstormDebugReactPerfect.jpg) 215 | 216 | 而如果是用 VSCode的话,需要 VSCode Debugger 也支持 sourcemap,有个 sourceMaps 的调试配置选项来开启和关闭 sourcemap 功能,将 sourceMaps 设置为 true。 217 | 218 | ![vscodeSourcemapTrue.jpg](./assets/vscodeSourcemapTrue.jpg) 219 | 220 | 在命令行运行`npm run start`,再运行 Debug 按钮就可以在 VSCode 里面愉快的调试最初的源码了。 221 | 222 | ![vscodeDebugReactPerfect.png](./assets/vscodeDebugReactPerfect.png) 223 | 224 | # 参考资料 225 | 226 | [全网最优雅的 React 源码调试方式](https://juejin.cn/post/7126501202866470949) 227 | 228 | [React源码学习进阶篇(一)新版React如何调试源码?](https://mp.weixin.qq.com/s/rjSrV6opaef1lqLM7S5F_Q) -------------------------------------------------------------------------------- /React/assets/browserdebug.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/browserdebug.jpg -------------------------------------------------------------------------------- /React/assets/disableEslint.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/disableEslint.jpg -------------------------------------------------------------------------------- /React/assets/enqueueUpdate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/enqueueUpdate.png -------------------------------------------------------------------------------- /React/assets/exportWebpack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/exportWebpack.jpg -------------------------------------------------------------------------------- /React/assets/ideDebugReactPerfect.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/ideDebugReactPerfect.jpg -------------------------------------------------------------------------------- /React/assets/moduleScopeError.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/moduleScopeError.jpg -------------------------------------------------------------------------------- /React/assets/performUnitOfWork.drawio: -------------------------------------------------------------------------------- 1 | 7Z1dc5s4F4B/jS+dQRKfl42btDtv2+20s9Nmb3aILTu8IeDFJE766xcMwiDJNh+SDNS5iREgG+noOUfnHIkJmj29fojc9cPncIH9CdQWrxP0fgLhFGoaTP6nRW9ZEXAcPStZRd4iL9sXfPd+4bxQy0ufvQXeVC6Mw9CPvXW1cB4GAZ7HlTI3isJt9bJl6Fe/de2uMFPwfe76bOkPbxE/ZKU2tPblH7G3eiDfDEwnO/PkkovzJ9k8uItwWypCNxM0i8Iwzj49vc6wnzYfaZfsvtsDZ4sfFuEgrnMD+vpjZoJPf7jWp5t/kWNvH5d4mnfGi+s/5w/8MdzEt949jr6lvyz75fEbaY4ofA4WOK0RTND19sGL8fe1O0/PbhMRSMoe4ic/P730fH8W+mG0uxctXGwv50n5Jo7CR1w6Y85tfL9MzrDPlD/mC45i/Foqyp/xAw6fcBy9JZfkZ3WNNHguclNgG1nBdt+BgFz0UOo8PS9zc5lZFZXvmzX5kLdsg1a2INPMP8Lo8Y/gaxSuIrzZiG1nA9sLndfONrxHpimonZHeu3YmP6DUzq4f4yhwY8w0MQ4W71JAJEdz391svHm1VatdgF+9+Gfp813yWbsy8qP3aaNo5OCNHATJQ/0sH5TuSg/3t+2OyH0He2cTPkdzfHo4x260wnENgcSLCv3Yzi51pcHpSVIWYd+NvZcqM3ndm3/D19BLHq2QJWQ59pVBSZOZFGmlP1StNWuLvKIy9qi6dWgAtm7nChqH687aj6l7J5NF43QQU5MRU0Y658/Ry174GonqMgzi0qDPnrAkwlobEQaqRJiI5kkZ1nslwm3ErK4ItxkeB0Q4ESP3rXTZOr1gc+yxaG1KmRnJh6xOsSPEagLyRAI+ufeJ6VkZFK7vrYJ0xCQiiJOxcJ3qNi+x7N7lJ568xSKt4zrRwN4v935XXyq8eaMklRvXE+P9kSHFivnxAU9r18Juzb99UjYNeVpXu9KQqVd6BOpCBJi+I1wuN1gK/iBg+fduve5mA/mpAFy788fV7jbSWUEY4PNZSAakhy0xfUpQcgw5FtKX2aP5LfhnbS7hZnr33v777n83U6g7TNsvvJfkyZMemO4+jb8XgKauF35uP25/zd2Xfz58A+8+/Yk//4SPU8D0wWbtBvtOmGaHo+sJZFPk0q/KKgwwvQKgym7R2dnw7MHzF+PrCVujxwQkD3+uMWHyjGLz3+fUPXI9uTEnya+51iY3ziQxDuzb0rnR9Q4yQKVvdIDYvnEU9o2DjvTNjqbj7QyDcnOcvTOApR1Q4TtYjVOJs8Di9oMkYMXftvH72RZ//Nu6DTRtfTd1vhAlTk1EvueHYRQ/hKswcP2bfWlpop7OHvbXfArDdd76/8dx/Jb7wd3nOKz2TTGHL+btd6Uz/Dn8wfYvz8WPjvvyXPzohScn47UnKZ26Rq/hYKnvU9F40l1MAbMzJDbA8b/c3mra7a3MvuvmRymmY33xpOgUcYFDDeHarj/doWqiYSDO0ceXQ9aNkahKP86FZDf9J3Ky16BEtkpF5ir9P8+M0ayC5AdldWTnuDBS6xUpBJ0V2uOjVIRXRLM6ukGE+j34z2ufplJLt29HRO1dxEXEYu/hLSB1xN3LR5smCW0FsYYW59ChXZ0BT822aKPn0lMA6rGtsQcYIcoD7Ih1APPHCuuminD8HLFekV55f4+P+86cm2pXyEaI6nhTiGxOKU+lVa1AHhYNdnI553pdzoBDpRYbF2vHvbh94RpjstEwam2y0YCUbLEZrAvwoqNPCPNxER3e/ANZFh0wAEZbNc3WpUhNGwq0tMHm2wxWSxcj/6Kluc3TJ5dKJwV9iJ/aCX4KZuGxwGBfSEirdcaZ2t4TQ/t0ZOt1dj5xwMjsP6hEuk2AVeUUFEMpqAxLPA/bxV5r7uonufWDm3ogm2aU3tZWo2oCdE2yTDWQr4mQa6ux3sfh2mpk1A/KVqPmEPKgSObMI/GoSDXY6sLwaM5KX1g4HnvNZFPkLnq9jSibHJ/iEKYeyDSurCpAkdNasyeVQSrfraZAN1fuVMI8UuGIMeF4lHsx9Put3JnwmzLtzsZLNt697wWrHun33iyT6wbF2nlNFwXfVIp/g0BLgzHQXpZr5+j1TMGfX5Qbq3b6JyMVmRDmiGIsxaAXodqBSUI2xdJEIEa1q5uos0GViypvhr+6U3Xiy7vgT7gQ/wYueCWanIjo8Hzw5xfmxrrcoKbpJNFIri4fkQ/eFOaDT2bpJnFgFKrc6SaZCnR37YjyxcleG3fHF7z1FXdIE4W74icq0t2cpYSj091qwuekJQdniJoOvbKyiIM3FmdLZ+pCkvS3ZVJjUIWb3WKjUoPV38XQF+JmR5Dyh0NBbnZlbnXOHn2SUTibcVAIa6Gw2exa2jRmZAtPi4qIENPwqh9w1KiaaKQK257L4n+RXAxyt4Oosax1Pq8uay2NLVKYVjDd7Ja/v0suAOb6dXcpvRaWfOF9p1pAaTXtPV11/1bYFiRIy/M9AgB/i4XjjBPBfGjqVO5oR/erAsYrDzqNlPEDXqpG52dMWy9VS4weem0PokIHsjCvxNhlA08DpTz8zSgvMsomnPJ51dR2pRKZL2+z3dEyf1yrmBBkOd2a+UxdBZplMx8oMe1P7lizIa9oKPZ/q7C+pk4QiXc0ULzXxbmw9V6jwLnyTXSGj/NRrXFgNpG1W2eFQ0TvKUi8mMJZbhi0Y1xXsObLYmOUvae5PnKai9wMSBbNqVEgj+bELdpHmudXio0/ytqpczQZ7Wbr4COzc5Cs0CP9RSSVSCrKbd6CuCrKWyPX6DNe82FYE68FUARkJQGdxHuLtLZuMl94HJXhFXKERiJeix1AL3jtKV5tYalKVs1Upc54NYEKvJ4MabaxlJdLTVsuL8HLdign8BKzDpR4tshUr+MAe+PeIBHlikOX9VG+39+85bvi1GbqDXW9nAGqWEQ0f2uT3KBT52idICzHnvJgIyUkPxy2ZCDZ2XKGB3HLI/PZEdx0h/YCOkLW61kk9ZWY07DbyFAA3R7HDhn7uR10m6zuUwDd3mVHUwjT6WhfberaZAvCQzUJy4ymqasibsh5YacwyJojsmkF7t0o3D1BUI2qgqrQG9zb2B5r4zZcJqXWXTHcpVU0vIzWmRo2leeny0rBdhDfMJfL2wFG9qxh+SsaG8tCX/MB9Kq/ovPmFvLxTfx2/XNRDMxarpua0Tt6m8J8FKYqH4XpnMFH4ZwO5vXO2zy4LLtmlnkBLxH01mytappPYccRVqxIU2WLO5Ajor2AOfM+zVYbFDX0UXeA+WBXhjv0Tqat38Pp0C6Jmu/hbG6L22ewxflvx5bkcdYPIncUHucCO0LS4ywEK+IgykMClVF4AFG/FtE79RQe7t5aBv1WCtjapqYcFdIc0NTid6DEpFYZ9jucMDcOCAsL+yWmsEl2AqHcyUNJvHAUxwDPkHgh3Kuh7I2QRCFXZ1t6zoIzZWa0fqcznZgh61WRRoNXRbLGD73Xo7Ldcp3DPm/xiD8cdBwH4gUGIqFFhbYJITsHIlVJFiAWywXyMjYGObpN1EAYT2/o1HrXELYmWYnUNOanx7ar7xHngXbYPS4e9Na4Qb9H24X0u+bgOevoXpWaVlLe0aV+0KJn2ch02GwKTOOKvJm2+b6h9MZ7vNokb4ULNJ6vLWPAwns5zJil++T5bxllPuPADydwln4MA3eef07ucJ/WyQ35fbOkYbwEDFD7grf0yeyWpzAIN2s3VbZpwI+mGcxppvlegKekh3en7NN7KWUF93jlBT/C6PFEZG/39AcAmCAllsc+9zkO80goYEZaEAZpLUvP9+mi46PsCEKJ4qFZeZCJHNEl/rrSSHQ4I5FObCoPuopsNxdknitMLOJO2K+dGr+/iIP2lWE4+z/KIuqAuxM1S0cfz8c1ZvTNw6e1j2P8V+DFfy4vDBTNQM7sDPDGagsIJodRmPbufgRE7vrhc7hIrfCb/wA= -------------------------------------------------------------------------------- /React/assets/react16JsxTransform.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/react16JsxTransform.jpg -------------------------------------------------------------------------------- /React/assets/react17EventBindDom.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/react17EventBindDom.jpg -------------------------------------------------------------------------------- /React/assets/react17JsxTransform.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/react17JsxTransform.jpg -------------------------------------------------------------------------------- /React/assets/react18ensureRootIsScheduled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
nextLanes
nextLanes
是否为NoLanes
是否为NoLanes
YES
YES
取消旧任务并重置根节点root的callbackNode和callbackPriority
取消旧任务并重置根节点root的callbackNode和callbac...
NO
NO
新旧优先级
callbackPriority
是否相同
新旧优先级callbackPriority...
旧任务root.callbackNode是否存在
旧任务root.callbackNode是否存在
取消旧任务
取消旧任务
root.callbackNode
是否为null
root.callbackNode...
NO
NO
结束
结束
结束
结束
YES
YES
结束
结束
No
No
YES
YES
注册调度任务并重新赋值根节点root的callbakcNode和callbackPriority
注册调度任务并重新赋值根节点root的callbakcNode和callbackPrior...
Text is not SVG - cannot display
-------------------------------------------------------------------------------- /React/assets/reactBuildSourcemapError.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/reactBuildSourcemapError.jpg -------------------------------------------------------------------------------- /React/assets/reactBuildWithSourcemap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/reactBuildWithSourcemap.jpg -------------------------------------------------------------------------------- /React/assets/reactDebugAlias.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/reactDebugAlias.jpg -------------------------------------------------------------------------------- /React/assets/reactDivRootEvents.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/reactDivRootEvents.jpg -------------------------------------------------------------------------------- /React/assets/reactEventDispatch.drawio: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /React/assets/reactEventScroll.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/reactEventScroll.jpg -------------------------------------------------------------------------------- /React/assets/reactHooksEnsureRootIsScheduled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
nextLanes
nextLanes
是否为NoLanes
是否为NoLanes
YES
YES
取消旧任务并重置根节点root的callbackNode和callbackPriority
取消旧任务并重置根节点root的callbackNode和callbac...
NO
NO
root.callbackNode
是否为null
root.callbackNode...
NO
NO
新旧优先级
callbackPriority
是否相同
新旧优先级callbackPriority...
NO
NO
取消旧任务
取消旧任务
root.callbackNode
是否为null
root.callbackNode...
NO
NO
结束
结束
YES
YES
结束
结束
结束
结束
YES
YES
YES
YES
注册调度任务并重新赋值根节点root的callbackNode和callbackPriority
注册调度任务并重新赋值根节点root的callbackNode和callbackPriority
结束
结束
Text is not SVG - cannot display
-------------------------------------------------------------------------------- /React/assets/reactHooksMount.drawio: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /React/assets/reactNoSourcemap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/reactNoSourcemap.jpg -------------------------------------------------------------------------------- /React/assets/reactScheduler.drawio: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /React/assets/reactSourcemapTrue.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/reactSourcemapTrue.jpg -------------------------------------------------------------------------------- /React/assets/scheduleUpdateOnFiber17Vs18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/scheduleUpdateOnFiber17Vs18.jpg -------------------------------------------------------------------------------- /React/assets/taskPriority.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/taskPriority.jpg -------------------------------------------------------------------------------- /React/assets/thisbindvs.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/thisbindvs.jpeg -------------------------------------------------------------------------------- /React/assets/vscodeAddDebug.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/vscodeAddDebug.jpg -------------------------------------------------------------------------------- /React/assets/vscodeDebugConfig.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/vscodeDebugConfig.jpg -------------------------------------------------------------------------------- /React/assets/vscodeDebugReactPerfect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/vscodeDebugReactPerfect.png -------------------------------------------------------------------------------- /React/assets/vscodeDebugSuccess.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/vscodeDebugSuccess.png -------------------------------------------------------------------------------- /React/assets/vscodeRunDebug.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/vscodeRunDebug.jpg -------------------------------------------------------------------------------- /React/assets/vscodeSourcemapTrue.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/vscodeSourcemapTrue.jpg -------------------------------------------------------------------------------- /React/assets/webstormAddDebug.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/webstormAddDebug.jpg -------------------------------------------------------------------------------- /React/assets/webstormDebugConfig.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/webstormDebugConfig.jpg -------------------------------------------------------------------------------- /React/assets/webstormDebugReactPerfect.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/webstormDebugReactPerfect.jpg -------------------------------------------------------------------------------- /React/assets/webstormDebugSuccess.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/webstormDebugSuccess.jpg -------------------------------------------------------------------------------- /React/assets/webstormRunDebug.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llaurora/KnowledgeNote/b9e9f61d1d177b6cb0aa80537eae30efbd56ea4f/React/assets/webstormRunDebug.jpg -------------------------------------------------------------------------------- /Shell/Git.md: -------------------------------------------------------------------------------- 1 | # Git 2 | 3 | ## Git 删除远程 Tag 4 | 5 | ```bash 6 | git tag -d tag-name 7 | git push origin :refs/tags/tag-name 8 | ``` 9 | 10 | ## Git 回退版本并提交到远程分支 11 | 12 | ```bash 13 | git reset --hard commitId 14 | git push -f origin branchName 15 | ``` 16 | 17 | ## 使用 Shell 批量修改 Git 提交作者和邮箱 18 | 19 | 新建 rewrite.sh 脚本并运行 20 | 21 | ```bash 22 | #!/bin/sh 23 | 24 | git filter-branch --env-filter ' 25 | 26 | OLD_EMAIL="your-old-email@example.com" 27 | CORRECT_NAME="Your Correct Name" 28 | CORRECT_EMAIL="your-correct-email@example.com" 29 | 30 | if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ] 31 | then 32 | export GIT_COMMITTER_NAME="$CORRECT_NAME" 33 | export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL" 34 | fi 35 | if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ] 36 | then 37 | export GIT_AUTHOR_NAME="$CORRECT_NAME" 38 | export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL" 39 | fi 40 | ' --tag-name-filter cat -- --branches --tags 41 | ``` 42 | 43 | 强制推送修改的 git 记录到远程 Github 44 | 45 | ```bash 46 | git push --force --tags origin 'refs/heads/*' 47 | ``` 48 | 49 | ## Git 修改账户信息 50 | 51 | ```bash 52 | // 查看 Git 配置 53 | git config --list 54 | 55 | // 修改全局仓库账户信息 56 | git config --global user.name "Author Name" 57 | git config --global user.email "Author Email" 58 | 59 | // 修改本地项目仓库账户信息 60 | git config user.name "Author Name" 61 | git config user.email "Author Email" 62 | ``` 63 | 64 | ## Git 根据 Tag 创建分支 65 | ```bash 66 | // 根据 Tag 创建分支 67 | git branch 68 | 69 | // 切换到新的分支 70 | git checkout newbranch 71 | ``` -------------------------------------------------------------------------------- /Shell/pkg manager.md: -------------------------------------------------------------------------------- 1 | # Package Manager 2 | 3 | | Name | Pnpm | Yarn | Npm | 4 | | ------------------------------ | --------------------------------- | --------------------------------- | ---------------------------------- | 5 | | 项目初始化 | pnpm init | yarn init | npm init | 6 | | 安装依赖 | pnpm install | yarn install 或者 yarn | npm install | 7 | | 添加生产环境依赖 | pnpm add [package] | yarn add [package] | npm install [package] —save | 8 | | 添加开发环境依赖 | pnpm add -D [package] | yarn add [package] -D | npm install [package] --save-dev | 9 | | 卸载依赖 | pnpm remove [package] | yarn remove [package] | npm uninstall [package] | 10 | | 清除缓存 | | yarn cache clean [package] | npm cache clean | 11 | | 添加全局依赖 | pnpm add [package] --global | yarn global add [package] | npm install [package] --global | 12 | | 删除全局安装的依赖 | pnpm remove [package] --global | yarn global remove [package] | npm uninstall -g [package] | 13 | | 更新全局安装的依赖 | pnpm update [package] —global | yarn global upgrade [package] | npm update [package] --global | 14 | | 更新全局所有依赖 | pnpm update —global | yarn global upgrade | npm update --global | 15 | | 更新当前目录下的所有依赖 | pnpm update | yarn upgrade | rm -rf node_modules && npm install | 16 | | 手动更新当前目录下的依赖 | pnpm update --interactive -latest | yarn upgrade-interactive --latest | | 17 | | 查看依赖的历史版本 | pnpm view [package] versions | | npm view [package] versions | 18 | | 运行 package.json 中预定义脚本 | pnpm | yarn run | npm run | 19 | | 查看配置信息 | pnpm config list | yarn config list | npm config list | 20 | | 更换仓库地址 | | yarn config set registry 仓库地址 | npm config set registry 仓库地址 | 21 | | 将包发布到 npm | | yarn publish | npm publish | 22 | | 删除 store 中未被引用的依赖 | pnpm store prune | | | 23 | 24 | --------------------------------------------------------------------------------