diff --git a/docs/Book/Javascript-dataStructure.md b/docs/Basics/Javascript-dataStructure.md similarity index 100% rename from docs/Book/Javascript-dataStructure.md rename to docs/Basics/Javascript-dataStructure.md diff --git a/docs/Basics/_category_.json b/docs/Basics/_category_.json index 4de6101..ca02d96 100644 --- a/docs/Basics/_category_.json +++ b/docs/Basics/_category_.json @@ -1,5 +1,5 @@ { - "label": "计算机基础", + "label": "前端基础", "position": 2, "link": { "type": "generated-index", diff --git a/docs/Basics/computer_network.md b/docs/Basics/computer_network.md new file mode 100644 index 0000000..e0ecf6f --- /dev/null +++ b/docs/Basics/computer_network.md @@ -0,0 +1,112 @@ +--- +sidebar_position: 5 +--- + +# 计算机网络 + +计算机网络由若干节点和链路组成。 + +因特网根据工作方式可以分成以下的两大块: +* 边缘部分:用户直接使用,用来通信和资源共享。 +* 核心部分:由大量网络和连接这些网络的路由器组成,为边缘部分提供服务。 + +在边缘部分:端可以是一个手机、平板、电脑,也可以是大型计算机,摄像头。 +在网络边缘的端系统之间的通信方式通常可划分为两大类:客户-服务器方式(c/s)和对等方式(p2p) + +在核心部分:起特殊作用的是路由器。路由器是**实现分组交换**的关键构件,其任务是转发收到的分组。 + +在建立连接->通话->释放连接。这三个步骤被称为电路交换。 +## 什么是报文 +分组交换采用存储转发技术。通过将一个报文拆解,每个数据端携带必要的首部及组合信息,通过(多个)交换机的连接最终找到需要通信的主机。(类似快递:收货->城市1仓库->城市2仓库...->送达) + +## 交换方式 +* 电路交换:整个报文的比特流连续地从源头到终点 +* 报文交换:整个报文到相邻节点,最后再到终点 +* 分组交换:单个分组到相邻节点,最后再到终点 + +计算机网络的最简单定义是:一些互相连接的、自洽的计算机的集合。这里自洽的概念即独立的计算机,有自己的硬件和软件,可以单独运行使用,而相互连接代表他们之间能够进行数据通信或交换信息。 + +* 按网络的作用范围可以分成:广域网WAN、城域网MAN、局域网LAN、个人局域网PAN。 +* 按网络的使用者可以分成:公用网、专用网、接入网 + +## 性能指标 + +1. 速率:传输数据的速率 单位bit +2. 带宽:允许信号的频带范围 +3. 吞吐量:单位时间通过的数据量 +4. 时延:数据从一端到另一段所需的时间 +5. 时延带宽积:传播时延✖️带宽 +6. 往返时间 +7. 利用率 + +非特性指标 +1. 费用 +2. 质量 +3. 标准化 +4. 可靠性 +5. 可扩展性和可升级行 + +## 五层协议的体系结构 + +### 应用层 + +通过应用进程间的交互来完成特定网路应用。应用层协议定义的是应用进程间通信和交互的规则。 + +### 运输层 + +运输层的任务就是负责向两个主机中进程之间的通信提供通用的数据传输服务 + +TCP 协议 —— 提供面向连接的、可靠的数据传输服务,其数据传输的单位事报文段。 +UDP 协议 —— 提供无连接的、尽最大努力的数据传输服务(不保证数据传输的可靠性)。数据传输的单位是用户数据报。 + +### 网络层(网际层) + +通过分组交换网上不同的主机提供通信服务。 + +IP协议 + +> 网络层网络二字,已不是我们通常谈到的具体的网络,而是在计算机网络体系结构模型中的专用名词。 + +### 数据链路层 + +数据链路层将网络层交下来的IP数据报组装成帧(framing),在两个相邻结点间的链路上传送帧(frame)。每一帧包括数据和必要的控制信息(同步信息、地址信息、差错控制等) + +### 物理层 + +传输的数据事比特。要考虑用多大的电压代表0或者1。 + +> 传递信息所利用的物理媒介不在物理层内。 + + +每一层加上必要的控制信息就变成了下一层的数据单元。而第一层(物理层由于是比特流的传送,所以不再加上控制信息。 + +* 实体:任何可发送或接受信息的硬件或者软件进程。 +* 协议:控制两个对等实体(或多个实体)进行通信的规则的集合。 +* 服务:要实现本层协议,还需要使用下面一层所提供的服务。 +* 服务访问点:在同一系统中相邻两层的实体进行交互的地方 + +其中,协议事水平的,服务是垂直的。 + +## TCP/IP的体系结构 + +应用层: HTTP SMTP DNS RTP +运输层: TCP UDP +网络层: IP +数据链路层: 网络接口1 网络接口2 + +## 物理层 + +### 物理层的任务 +物理层考虑的是怎样才能在连接各种计算机的传输媒体上传输数据比特流,而不是指具体的传输媒体。物理层的作用是要尽可能地屏蔽掉多种传输媒体和通信手段的差异,使物理层上面的数据链路层感觉不到这些差异。用于物理层的协议也常被称为物理层规程。 +可以将物理层的主要任务描述为确定与传输媒体的接口有关的一些特性: +1. 机械特性:接口所用接线器的形状和尺寸,引脚数目和排列等等。 +2. 电器特性:指明在接口电缆的各条线上出现的电压的范围。 +3. 功能特性:指明某条线上出现的某一电平的电压表示何种意义。 +4. 过程特性:指明对于不同功能的何种可能时间的出现顺序。 + +几种常用的信道复用技术 + +集中常用的宽带接入技术,主要是ADSL和FTTx + +### 数据通信系统的模型 + diff --git a/docs/Book/dont-down-js.md b/docs/Basics/dont-down-js.md similarity index 100% rename from docs/Book/dont-down-js.md rename to docs/Basics/dont-down-js.md diff --git a/docs/Book/dont-down-js2.md b/docs/Basics/dont-down-js2.md similarity index 100% rename from docs/Book/dont-down-js2.md rename to docs/Basics/dont-down-js2.md diff --git a/docs/Book/javascript-essence.md b/docs/Basics/javascript-essence.md similarity index 100% rename from docs/Book/javascript-essence.md rename to docs/Basics/javascript-essence.md diff --git a/docs/Book/_category_.json b/docs/Book/_category_.json deleted file mode 100644 index 6436b10..0000000 --- a/docs/Book/_category_.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "label": "书籍摘抄", - "position": 8, - "link": { - "type": "generated-index", - "description": "书籍摘抄" - } -} diff --git a/docs/React/2.react-source.md b/docs/React/2.react-source.md index d28799d..0b7c7be 100644 --- a/docs/React/2.react-source.md +++ b/docs/React/2.react-source.md @@ -2,11 +2,11 @@ sidebar_position: 2 --- -# React原理图解 +# React 原理 ## 简要的渲染原理 -`Web`开发中,我们需要将变化的数据**实时**显示在页面上,这个时候需要**`DOM`操作**,但是频繁的`DOM`操作通常是性能瓶颈产生的原因。 +`Web`开发中,我们需要将变化的数据**实时**显示在页面上,这个时候需要**`DOM`操作**,但是频繁的`DOM`操作通常是复杂繁琐的。因为我们需要精确地找到状态变化的`DOM`进行替换,而项目又多了许多与业务操作不相关的`DOM`操作。 因此`React`引入了**虚拟`DOM`**的机制——**每当数据变化时,`React`会重新构建起整个虚拟`DOM`树,然后将虚拟`DOM`树和上一次的虚拟`DOM`树进行对比,最终将需要变化的部分进行浏览器`DOM`的更新。** @@ -37,7 +37,7 @@ A[1.触发状态变化] --> B[2.创建VDOM] --> C[3.对比VDOM] --> D[4.更新VD 除此之外还有: * `ReactNative`:渲染App原生组件 * `ReactTest`:渲染App原生组件 -* `ReactArt`:渲染Canvas,SVG +* `ReactArt`:渲染Canvas,SVG ### 简要渲染原理总结 @@ -55,17 +55,17 @@ A[1.触发状态变化] --> B[2.创建VDOM] --> C[3.对比VDOM] --> D[4.更新VD `React`的性能瓶颈因此诞生了,这个问题也导致了后来两者架构上逐渐有了差异。 -可以说在`React:v17`之前,`React`的原理是比较简单明了的。但是目前`React`是一个很复杂的框架,特别`Filber`和`Hooks`的概念,这也是目前`React`生态不可或缺的原理。 +可以说在`React:v17`之前,`React`的原理是比较简单明了的。但是目前`React`是一个很复杂的框架,特别`Fiber`和`Hooks`的概念,这也是目前`React`生态不可或缺的组成部分。 ## Fiber架构 -在引入Fiber架构前,我们可以很明确的知道React的性能瓶颈在于递归执行计算VDOM。因此如何优化调和阶段(Reconciler)就成了首要问题。既然这个任务量太重无法在合理的时间内执行完毕,那么我们应该怎么做呢? +在引入Fiber架构前,我们可以很明确的知道React的性能瓶颈在于递归执行计算并渲染VDOM。因此如何优化调和阶段这个流程就成了首要问题。既然这个任务量太重无法在合理的时间内执行完毕,那么我们应该怎么做呢? 答案是时间切片,通过原本的长执行时间任务切成一帧范围内的任务,我们就可以做到在不影响页面性能的情况下计算。 因此为了做到时间切片这个概念,我们需要将原本任务变成可打断的。那么如何做成可打断的任务呢: 1. 中断后需要在下次的任务中找到原本的计算位置 -2. 将递归结构改成循环结构 +2. 将递归计算改成可随时中断的计算 ### 数据结构记录任务位置 @@ -115,9 +115,13 @@ flowchart TB; C --> |return| A; D --> |return| A; ``` + +举个例子,当我们在LI节点时有更高优先级别的任务,那么我们将会打断此次计算去执行高优先级的任务,执行之后在回到这个节点上。按照曾经的数据结构。我们无法知道接下来需要如何计算。而Fiber结构中还有额外信息(sibing)指导目前的任务进度,因此可以再中断后继续进行计算。 ### 循环流程 -所以在React17中是使用一个循环来进行调和的 +有了可以记忆中断前状态的数据结构,如何将递归计算改成可中断的呢? + +在新的React中是使用一个循环来进行做到的 ```typescript function workLoopConcurrent() { while (workInProgress !== null && !shouldYield()) { @@ -129,61 +133,59 @@ function workLoopConcurrent() { 因此新的`React`处理一次`setState()`时会有三个阶段: -* **调度阶段(Scheduler)**:调度任务的优先级,高优先级优先进入调和阶段。 -* **调和阶段(Reconciler)**: 负责找出变化的组件。 -* **渲染阶段(Renderer)**:负责将变化的组件渲染到页面上。 - -因为调和阶段`Reconciler`不仅仅是包括VDOM的创建也包含对比,因此在React中这又被称作两个阶段 -render阶段 -commit阶段 - -数据结构有了,那么如何进行打断式的计算呢?先前的的递归计算已经不适用了,新的计算方式变成可打断的循环计算. -```typescript -function workLoop() { - while (wip) { - performUnitOfWork(); - } +* 调度阶段(Scheduler):根据浏览器是否有剩余帧时间,是否有更高优先级任务调度任务调合任务。 +* 调和阶段(Reconciler):负责找出变化的组件。 +* 渲染阶段(Renderer):负责将变化的组件渲染到页面上。 - if (!wip && wipRoot) { - commitRoot(); - } -} -``` +在新的架构中: +* 调和阶段(Reconciler)又被称为 render 阶段,因为该阶段会调用组件的 render 方法。 +* 渲染阶段(Renderer)又被称为 commit 阶段,因此此阶段就像是git commit将代码提交一样,commit阶段会把render阶段提交的信息渲染在页面上。 +* render 和 commit 阶段被统称为work,即在React工作中。如果在Scheduler内调度,就不属于work。 -当循环完了,也就是 wip 为空了,那就执行 commit ,进入到渲染阶段。 +所以新的流程如下图所示: -那么循环执行中如何去执行更高优先级的任务,React 代码中使用 shouldYield 方法来解决这个问题。 +```mermaid +graph TD +A[1.触发状态变化] --> E[2.调度Scheduler阶段] --> |随时中断控制| B[3.调合阶段/render 阶段] --> C[4.渲染阶段/commit 阶段] +``` +#### render 阶段 +render 阶段主要是两个方法,这两个方法的主要功能是将Fiber节点组织成 Fiber 树: +* beginWork:该方法会根据传入的Fiber节点创建子Fiber节点,并将这两个Fiber节点连接起来。 +* completeWork:当某个Fiber节点执行完completeWork,如果其存在兄弟Fiber节点(即fiber.sibling !== null),会进入其兄弟Fiber的beginWork方法,如果不存在兄弟Fiber,会进入父级Fiber的completeWork方法。最终将rootFiber completeWork完毕,render阶段的工作就结束了。 +#### commit 阶段 +进入 commit 阶段,commit阶段主要分成三部分: +* before mutation 之前(执行DOM操作前),主要做一些变量赋值,状态重置的工作,在此阶段调度useEffect ```typescript -function workLoop() { - while (wip && shouldYield()) { - performUnitOfWork(); - } - - if (!wip && wipRoot) { - commitRoot(); +// 调度useEffect +if ((effectTag & Passive) !== NoEffect) { + if (!rootDoesHavePassiveEffects) { + rootDoesHavePassiveEffects = true; + scheduleCallback(NormalSchedulerPriority, () => { + // 触发useEffect + flushPassiveEffects(); + return null; + }); } } ``` -所以我们在每次处理 fiber 节点的 reconcile 之前,都先调用下 shouldYield 方法。如果通过 shouldYeld 中判断到有更高优先级的计算,那就先处理更高级别 fiber 计算,这边的先暂停一下。 - -通过这些循环,最后将所有的虚拟 dom 打上标签,并将需要更新的 dom 放入 effectList 队列(意思是等待渲染的 dom ),通过循环 effectList 最后将的更新渲染到页面上。 - -那么渲染阶段的具体步骤是什么呢? - -dom 创建前后就是 useEffect、useLayoutEffect 还有一些函数组件的生命周期函数执行的时候。 - -useEffect 被设计成了在 dom 操作前**异步调用**,useLayoutEffect 是在 dom 操作后**同步调用**。 - -为什么这样呢? - -因为都要操作 dom 了,这时候如果来了个 effect 同步执行,计算量很大,那不是把 fiber 架构带来的优势有毁了么? +在此处,被异步调度的回调函数就是触发useEffect的方法flushPassiveEffects。 +为什么需要异步?因为同步执行的话,将会阻塞浏览器渲染。 -所以 effect 是异步的,不会阻塞渲染。 +* mutation 阶段(执行DOM操作),遍历effectList,根据effectTag的不同,分别处理 + * Placement effect 插入节点 + * Update effect 更新节点,更新节点分成FunctionComponent以及HostComponent + * FunctionComponent mutation 将会执行所有useLayoutEffect 的销毁函数 + * HostComponent mutation + * Deletion effect 删除节点,将会执行如下操作 + * 递归调用Fiber节点及其子孙Fiber节点中fiber.tag为ClassComponent的componentWillUnmount (opens new window)生命周期钩子,从页面移除Fiber节点对应DOM节点 + * 解绑ref + * 调度useEffect的销毁函数 -而 useLayoutEffect,顾名思义是想在这个阶段拿到一些布局信息的,dom 操作完以后就可以了,而且都渲染完了,自然也就可以同步调用了。 +* layout 阶段(执行DOM操作后),该阶段的代码都是在DOM操作完成后执行,因此触发的生命周期和hook可以直接访问到改变后的DOM。和mutation 阶段一样,layout 阶段也是遍历 effectList。 +了解了整个 Fiber 的渲染流程,对Fiber的概念有一定的了解,那么 Hooks 又是如何作用于的呢 ## Hooks原理 hooks 们作为**一条链表**保存在组件内部,为什么需要使用链表结构?因为我们组件的 hooks 数目是不可预知的,因此使用链表结构可以组成一条可随时扩容的结构体。 diff --git a/docs/React/6.defaultProps.md b/docs/React/6.defaultProps.md new file mode 100644 index 0000000..71c8251 --- /dev/null +++ b/docs/React/6.defaultProps.md @@ -0,0 +1,53 @@ +# 为什么 React 团队在函数式组件中弃用了 defaultProps + +[源链接](https://github.com/reactjs/rfcs/blob/createlement-rfc/text/0000-create-element-changes.md#deprecate-defaultprops-on-function-components) + +简单来说,就是没有必要。 + +在函数式组件中,我们完全可以使用用JS默认参数的形式,来申明默认属性,代码如下: +```typescript +function Foo({foo = 1, bar = "hello"}) { + let props = {foo, bar}; + //... +} +``` + +对于大多数团队来说 `React` 的 `defaultProps` 是一个黑盒,如果我们把默认属性的行为放在函数组件定义中,从代码质量来说更加可控。 + +而`defaultProps`设计之出是为了什么呢? + +`React` 团队认为 `defaultProps`在类上非常有用,因为`props`对象会被传递给许多不同的方法。生命周期、回调等等。每一个都在它自己的范围内。这使得它很难使用`JS`默认参数,因为你必须在每个函数中复制相同的默认值。 + +简而言之,就是在每个生命周期和方法的作用域中可以使用新的默认值。 + +```typescript +class Foo { + static defaultProps = {foo: 1}; + componentDidMount() { + let foo = this.props.foo; + console.log(foo); + } + componentDidUpdate() { + let foo = this.props.foo; + console.log(foo); + } + componentWillUnmount() { + let foo = this.props.foo; + console.log(foo); + } + handleClick = () => { + let foo = this.props.foo; + console.log(foo); + } + render() { + let foo = this.props.foo; + console.log(foo); + return
; + } +} +``` + +因此,在 React 团队的后续更新中已经实现:在函数是组件中使用`defaultProps`,`React`会在`createElement`中添加一个警告。这包括其他特殊组件,如`forwardRef和memo`。 + + +## 总结:在函数式组件中应当使用JS函数默认参数定义组件默认属性,避免使用 defaultProps \ No newline at end of file