当前位置: 首页 > news >正文

[React源码解析] React的设计理念和源码架构 (一)

  1. 任务分割
  2. 异步执行
  3. 让出执法权

文章目录

      • 1.React的设计理念
        • 1.1 Fiber
        • 1.2 Scheduler
        • 1.3 Lane
        • 1.4 代数效应
      • 2.React的源码架构
        • 2.1 大概图示
        • 2.2 jsx
        • 2.3 Fiber双缓存
        • 2.4 scheduler
        • 2.5 Lane模型
        • 2.6 reconciler
        • 2.7 renderer
        • 2.8 concurrent
      • 3.React源码调试

1.React的设计理念

  1. Fiber: 即对应真实dom, 又作为分隔单元。
  2. Scheduler: 用js实现一套时间片运行的机制, 使得requestIdleCallback()的浏览器的兼容性和触发不稳定的问题解决。
  3. Lane: 异步调度有了, 需要细粒度的管理各个任务的优先级, 让高优先级的先执行, 各个Fiber工作单元还能比较优先级, 优先级相同的一起执行。

上面的机制能实现batchedUpdates批量更新和Suspense

1.1 Fiber

Fiber: react15的更新是同步的,因为它不能将任务分割,所以需要一套数据结构让它既能对应真实的dom又能作为分隔的单元,这就是Fiber。

  1. 对应真实dom。
  2. 作为分割单元。
let firstFiber
let nextFiber = firstFiber
let shouldYield = false
//firstFiber->firstChild->sibling
function performUnitOfWork(nextFiber){//...return nextFiber.next
}function workLoop(deadline){while(nextFiber && !shouldYield){nextFiber = performUnitOfWork(nextFiber)shouldYield = deadline.timeReaming < 1}requestIdleCallback(workLoop)
}requestIdleCallback(workLoop)
1.2 Scheduler

Scheduler: 有了Fiber, 需要用浏览器的时间片异步执行这些Fiber的工作单元, 有一个Api是requestIdleCallback(), 可以在浏览器空闲的时候执行一些任务, 用这个api执行react的更新。requestIdleCallback存在着浏览器的兼容性和触发不稳定的问题, 需要用js实现一套时间片运行的机制, react称为Scheduler。

1.3 Lane

Lane: 异步调度有了, 需要细粒度的管理各个任务的优先级, 让高优先级的先执行, 各个Fiber工作单元还能比较优先级, 优先级相同的一起执行。

1.4 代数效应

除了cpu的瓶颈问题, 还存在一些副作用, 比如获取数据、文件操作等。不同设备性能和网络状况都不一样, react如何处理这些问题, 需要react可以有分离副作用的能力, 解耦, 这就是代数效应。

function getPrice(id) {return fetch(`xxx.com?id=${productId}`).then((res)=>{return res.price})
}async function getTotalPirce(id1, id2) {const p1 = await getPrice(id1);const p2 = await getPrice(id2);return p1 + p2;
}async function run(){await getTotalPrice('001', '002');  
}

getPrice()是一个异步获取数据的方法, 可以用async+await的方式获取数据, 但是会导致调用getTotalPrice的run方法也会变成异步函数, 这就是async的传染性(副作用)。

function usePrice(id) {useEffect((id)=>{fetch(`xxx.com?id=${productId}`).then((res)=>{return res.price})}, [])
}function TotalPirce({id1, id2}) {const p1 = usePrice(id1);const p2 = usePrice(id2);return <TotalPirce props={...}>
}

getPrice换成usePrice, getTotalPirce换成TotalPirce组件, 这是hook的分离副作用能力。

generator: 也是有一定的传染性的, generator不能计算优先级, 排序优先级。

function getPrice(id) {return fetch(`xxx.com?id=${productId}`).then((res)=>{return res.price})
}function* getTotalPirce(id1, id2) {const p1 = yield getPrice(id1);const p2 = yield getPrice(id2);return p1 + p2;
}function* run(){yield getTotalPrice('001', '002');  
}

解耦副作用在函数式编程的实践中非常常见, 如react-saga, 将副作用从saga中分离, 自己不处理副作用, 发请求处理。

function* fetchUser(action) {try {const user = yield call(Api.fetchUser, action.payload.userId);yield put({type: "USER_FETCH_SUCCEEDED", user: user});} catch (e) {yield put({type: "USER_FETCH_FAILED", message: e.message});}
}

2.React的源码架构

  1. Scheduler(调度器): 排序优先级,让优先级高的任务先进行reconcile
  2. Reconciler(协调器): 找出哪些节点发生了改变,并打上不同的Flags(旧版本react叫Tag)
  3. Renderer(渲染器): 将Reconciler中打好标签的节点渲染到视图上

在这里插入图片描述

2.1 大概图示

在这里插入图片描述

jsx(mount/update) -> scheduler(render) -> reconciler(render) -> rerender(commit)

2.2 jsx

jsx: React通过Babel解析, 将jsx转换成React.createElement, React.createElement方法返回virtual-dom对象React.createElement方法返回virtual-dom对象, 所有jsx本质上就是React.createElement的语法糖。

createElement -> ReactElement

export function createElement(type, config, children) {let propName;// Reserved names are extractedconst props = {};let key = null;let ref = null;let self = null;let source = null;if (config != null) {if (hasValidRef(config)) {ref = config.ref;if (__DEV__) {warnIfStringRefCannotBeAutoConverted(config);}}if (hasValidKey(config)) {key = '' + config.key;}self = config.__self === undefined ? null : config.__self;source = config.__source === undefined ? null : config.__source;// Remaining properties are added to a new props objectfor (propName in config) {if (hasOwnProperty.call(config, propName) &&!RESERVED_PROPS.hasOwnProperty(propName)) {props[propName] = config[propName];}}}// Children can be more than one argument, and those are transferred onto// the newly allocated props object.const childrenLength = arguments.length - 2;if (childrenLength === 1) {props.children = children;} else if (childrenLength > 1) {const childArray = Array(childrenLength);for (let i = 0; i < childrenLength; i++) {childArray[i] = arguments[i + 2];}if (__DEV__) {if (Object.freeze) {Object.freeze(childArray);}}props.children = childArray;}// Resolve default propsif (type && type.defaultProps) {const defaultProps = type.defaultProps;for (propName in defaultProps) {if (props[propName] === undefined) {props[propName] = defaultProps[propName];}}}if (__DEV__) {if (key || ref) {const displayName =typeof type === 'function'? type.displayName || type.name || 'Unknown': type;if (key) {defineKeyPropWarningGetter(props, displayName);}if (ref) {defineRefPropWarningGetter(props, displayName);}}}return ReactElement(type,key,ref,self,source,ReactCurrentOwner.current,props,);
}const ReactElement = function(type, key, ref, self, source, owner, props) {const element = {// This tag allows us to uniquely identify this as a React Element$$typeof: REACT_ELEMENT_TYPE,// Built-in properties that belong on the elementtype: type,key: key,ref: ref,props: props,// Record the component responsible for creating this element._owner: owner,};if (__DEV__) {// The validation flag is currently mutative. We put it on// an external backing store so that we can freeze the whole object.// This can be replaced with a WeakMap once they are implemented in// commonly used development environments.element._store = {};// To make comparing ReactElements easier for testing purposes, we make// the validation flag non-enumerable (where possible, which should// include every environment we run tests in), so the test framework// ignores it.Object.defineProperty(element._store, 'validated', {configurable: false,enumerable: false,writable: true,value: false,});// self and source are DEV only properties.Object.defineProperty(element, '_self', {configurable: false,enumerable: false,writable: false,value: self,});// Two elements created in two different places should be considered// equal for testing purposes and therefore we hide it from enumeration.Object.defineProperty(element, '_source', {configurable: false,enumerable: false,writable: false,value: source,});if (Object.freeze) {Object.freeze(element.props);Object.freeze(element);}}return element;
};
2.3 Fiber双缓存

Fiber对象上面保存了包括这个节点的属性, dom, 类型, 通过child, sibling, reture 形成fiber树, 保存了更新状态时用于计算state的updateQueue, updateQueue为一个链表, 上面存在未计算的update, update也是一个数据结构, 上面存有更新的数据、优先级等, 还有副作用的信息。

双缓存是指存在两颗Fiber树, current Fiber树描述了当前呈现的dom树, workInProgress Fiber是正在更新的Fiber树, 这两树都是存在于内存, 在workInProgress Fiber构建完成之后会将它作为current Fiber应用到dom上。

function App() {const [count, setCount] = useState(0);return (<><h1 onClick={() => {setCount(() => count + 1);}}><p title={count}>{count}</p> </h1></>)
}ReactDOM.render(<App />, document.getElementById("root"));

在这里插入图片描述

2.4 scheduler

Scheduler的作用是调度任务。

在Scheduler中的每个任务的优先级使用过期时间表示的, 如果一个任务的过期时间离现在很近, 说明要过期了, 优先级很高。
没有过期的放在timerQueue中, 过期的放taskQueue, timerQueue和taskQueue都是小顶堆, 所以peek出的都是离现在时间最近也就是优先级最高的那个任务。

在这里插入图片描述

2.5 Lane模型

优先级表示方法Lane: Lane使用二进制的方式表示优先级, 1表示位置, 同一个二进制数可以有多个相同优先级的位, 这就可以表示‘批’的概念。低优先级的任务如果被高优先级的任务一直打断, 等到达它的时候, 优先级自动变为最高。

bit越多, 优先级越低。

//ReactFiberLane.js
export const NoLanes: Lanes = /*                        */ 0b0000000000000000000000000000000;
export const NoLane: Lane = /*                          */ 0b0000000000000000000000000000000;export const SyncLane: Lane = /*                        */ 0b0000000000000000000000000000001;
export const SyncBatchedLane: Lane = /*                 */ 0b0000000000000000000000000000010;export const InputDiscreteHydrationLane: Lane = /*      */ 0b0000000000000000000000000000100;
const InputDiscreteLanes: Lanes = /*                    */ 0b0000000000000000000000000011000;const InputContinuousHydrationLane: Lane = /*           */ 0b0000000000000000000000000100000;
const InputContinuousLanes: Lanes = /*                  */ 0b0000000000000000000000011000000;export const DefaultHydrationLane: Lane = /*            */ 0b0000000000000000000000100000000;
export const DefaultLanes: Lanes = /*                   */ 0b0000000000000000000111000000000;const TransitionHydrationLane: Lane = /*                */ 0b0000000000000000001000000000000;
const TransitionLanes: Lanes = /*                       */ 0b0000000001111111110000000000000;const RetryLanes: Lanes = /*                            */ 0b0000011110000000000000000000000;export const SomeRetryLane: Lanes = /*                  */ 0b0000010000000000000000000000000;export const SelectiveHydrationLane: Lane = /*          */ 0b0000100000000000000000000000000;const NonIdleLanes = /*                                 */ 0b0000111111111111111111111111111;export const IdleHydrationLane: Lane = /*               */ 0b0001000000000000000000000000000;
const IdleLanes: Lanes = /*                             */ 0b0110000000000000000000000000000;export const OffscreenLane: Lane = /*                   */ 0b1000000000000000000000000000000;
2.6 reconciler

Reconciler发生在render阶段, render分为节点执行beginWork和completeWork, 或是计算state, 对比节点的差异, 为节点赋值相应的effectFlags。

Reconciler会创建或者更新Fiber节点。在mount的时候会根据jsx生成Fiber对象,在update的时候会根据最新的state形成的jsx对象和current Fiber树对比构建workInProgress Fiber树, 对比就是diff算法。

diff算法发生在render阶段的reconcileChildFibers函数中, diff算法分为单节点的diff和多节点的diff, 单节点会根据节点的key和type, props判断节点是复用还是直接新创建节点, 多节点diff会涉及节点的增删和节点位置的变化。

reconcile时会在这些Fiber上打上Flags标签, 在commit阶段把这些标签应用到真实dom上, 这些标签代表了节点的增删改。

//ReactFiberFlags.js
export const Placement = /*             */ 0b0000000000010;
export const Update = /*                */ 0b0000000000100;
export const PlacementAndUpdate = /*    */ 0b0000000000110;
export const Deletion = /*              */ 0b0000000001000;

render阶段遍历Fiber树类似dfs的过程, ‘捕获’阶段发生在beginWork函数中, 该函数做的主要工作是创建Fiber节点, 计算state和diff算法, ‘冒泡’阶段发生在completeWork中, 该函数主要是做一些收尾工作, 例如处理节点的props、和形成一条effectList的链表, 该链表是被标记了更新的节点形成的链表。

function App() {return (<><h1><p>count</p> xiaochen</h1></>)
}

在这里插入图片描述

function App() {const [count, setCount] = useState(0);return (<><h1onClick={() => {setCount(() => count + 1);}}><p title={count}>{count}</p> xiaochen</h1></>)
}

如果p和h1节点更新了则effectList如下, rootFiber->h1->p, fiberRoot是整个项目的根节点, rootFiber为应用的根节点, 可以有多个。

在这里插入图片描述

2.7 renderer

Renderer发生在commit阶段, commit阶段遍历effectList执行对应的dom操作或部分生命周期。并执行真实dom节点的操作和一些生命周期, 不同的平台对应的Renderer不同, 浏览器对应的是react-dom。

commit阶段发生在commitRoot函数中, 遍历effectList, 三个函数来处理effectList上的节点, commitBeforeMutationEffects, commitMutationEffects, commitLayoutEffects。

在这里插入图片描述

2.8 concurrent

一类功能的合集(如fiber、schduler、lane、suspense), 目的是为了提高应用的响应速度, 使应用cpu密集型的更新不在那么卡顿, 核心是实现了一套异步可中断、带优先级的更新。

3.React源码调试

在这里插入图片描述

  • fixtures:为代码贡献者提供的测试React
  • packages:主要部分,包含Scheduler,reconciler等
  • scripts:react构建相关

在这里插入图片描述

  1. react:核心Api如:React.createElement、React.Component都在这

  2. 和平台相关render相关的文件夹:
    react-art:如canvas svg的渲染
    react-dom:浏览器环境
    react-native-renderer:原生相关 react-noop-renderer:调试或者fiber用

  3. 试验性的包
    react-server: ssr相关
    react-fetch: 请求相关
    react-interactions: 和事件如点击事件相关
    react-reconciler: 构建节点
    shared:包含公共方法和变量

  4. 辅助包:
    react-is : 判断类型
    react-client: 流相关
    react-fetch: 数据请求相关

  5. react-refresh: 热加载相关

  6. scheduler:调度器相关

  7. React-reconciler:在render阶段用它来构建fiber节点

相关文章:

[React源码解析] React的设计理念和源码架构 (一)

任务分割异步执行让出执法权 文章目录 1.React的设计理念1.1 Fiber1.2 Scheduler1.3 Lane1.4 代数效应 2.React的源码架构2.1 大概图示2.2 jsx2.3 Fiber双缓存2.4 scheduler2.5 Lane模型2.6 reconciler2.7 renderer2.8 concurrent 3.React源码调试 1.React的设计理念 Fiber: 即…...

[论文工具] LaTeX论文撰写常见用法及实战技巧归纳(持续更新)

祝大家中秋国庆双节快乐&#xff01; 回过头来&#xff0c;我们在编程过程中&#xff0c;经常会遇到各种各样的问题。然而&#xff0c;很多问题都无法解决&#xff0c;网上夹杂着各种冗余的回答&#xff0c;也缺乏系统的实战技巧归纳。为更好地从事科学研究和编程学习&#xff…...

多媒体应用设计师

1.多媒体技术基础 1.1.媒体与技术 1.1.媒体 维基百科&#xff1a;传播信息载体 国际电信联盟&#xff08;ITU-T&#xff09;&#xff1a;感知、表示、存储和传输的手段和方法。 两层含义&#xff1a;存储信息的实体&#xff0c;媒质。传递信息载体&#xff0c;媒介。 1.2.国…...

socket.error: [Errno 10049]错误

今天在pycharm运行rl_server_no_training.py欲启动服务器时&#xff0c;却出现如下错误 Traceback (most recent call last):File "xxx/rl_server_no_training.py", line 333, in <module>main()File "xxx/rl_server_no_training.py", line 326, in…...

二叉树的经典OJ题

对称二叉树 1.题目2.图形分析3.代码实现 1.题目 2.图形分析 3.代码实现 class Solution {public boolean isSymmetric(TreeNode root) {if(root null){return true;}return isSymmetricchild(root.left,root.right);}private boolean isSymmetricchild(TreeNode leftTree,Tre…...

统一建模语言UML(1~8章在线测试参考答案)

目录 UML概述 UML概念模型 参与者和用例 用例图之间的关系 用例模型 类图中的类 类图建模 顺序图的构成 UML概述 一 单项选择题(3分) 1、关于UML描述不正确的是()。(1分) UML是由信息系统和面向对象领域三位专家Grady Booch、James Rumbaugh和Ivar Jac…...

计算机竞赛 题目:基于FP-Growth的新闻挖掘算法系统的设计与实现

文章目录 0 前言1 项目背景2 算法架构3 FP-Growth算法原理3.1 FP树3.2 算法过程3.3 算法实现3.3.1 构建FP树 3.4 从FP树中挖掘频繁项集 4 系统设计展示5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于FP-Growth的新闻挖掘算法系统的设计与实现…...

String 类型的变量和常量做 “+” 运算时发生了什么?

先看看字符串不加 final 关键字拼接的情况&#xff08;jdk1.8&#xff09;: String str1 "str" String str2 "ing" String str3 "str" "ing" String str4 str1 str2 String str5 "string" System.out.println(str3 …...

【Java互联网技术】MinIO分布式文件存储服务

应用场景 互联网海量非结构化数据的存储 基本概念 Object&#xff1a;存储的基本对象&#xff0c;如文件、字节流等 Bucket&#xff1a;存储Object的逻辑空间&#xff0c;相当于顶层文件夹 Drive&#xff1a;存储数据的磁盘&#xff0c;在MinIO启动时&#xff0c;以参数的…...

在visual studio里配置Qt插件并运行Qt工程

Qt插件&#xff0c;也叫qt-vsaddin&#xff0c;它以*.vsix后缀名结尾。visual studio简称为VS&#xff0c;从visual studio 2010版本开始&#xff0c;VS支持Qt框架的开发&#xff0c;Qt以插件方式集成到VS里。这里简述在visual studio 2019里配置Qt 5.14.2插件&#xff0c;并配…...

【C语言】利用数组处理批量数据(字符数组)

前言:前面已经介绍了&#xff0c;字符数据是以字符的ASCII代码存储在存储单元中的&#xff0c;一般占一个字节。由于ASCII代码也属于整数形式&#xff0c;因此在C99标准中&#xff0c;把字符类型归纳为整型类型中的一种。 &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x…...

算法通过村第十二关-字符串|白银笔记|经典面试题

文章目录 前言1. 反转问题1.1 反转字符串1.2 k个一组反转1.3 仅仅反转字母1.3.1 采用栈实现操作1.3.2 采用双指针实现操作 1.4 反转字符串里面的单词1.4.1 使用语言提供的方法来解决(内置API)1.4.2 如何优雅自己实现上述功能 2. 验证回文串3. 字符串中的第一个唯一字符4. 判断是…...

《视觉 SLAM 十四讲》V2 第 5 讲 相机与图像

文章目录 相机 内参 && 外参5.1.2 畸变模型单目相机的成像过程5.1.3 双目相机模型5.1.4 RGB-D 相机模型 实践5.3.1 OpenCV 基础操作 【Code】OpenCV版本查看 5.3.2 图像去畸变 【Code】5.4.1 双目视觉 视差图 点云 【Code】5.4.2 RGB-D 点云 拼合成 地图【Code】 习题题…...

使用libmodbus库开发modbusTcp从站(支持多个主站连接)

使用libmodbus库开发modbusTcp从站&#xff08;支持多个主站连接&#xff09; Chapter1 使用libmodbus库开发modbusTcp从站(支持多个主站连接)rdsmodbusslave.hrdsmodbusslave.cppmain.cpp Chapter1 使用libmodbus库开发modbusTcp从站(支持多个主站连接) 参考链接&#xff1a…...

GPT系列论文解读:GPT-2

GPT系列 GPT&#xff08;Generative Pre-trained Transformer&#xff09;是一系列基于Transformer架构的预训练语言模型&#xff0c;由OpenAI开发。以下是GPT系列的主要模型&#xff1a; GPT&#xff1a;GPT-1是于2018年发布的第一个版本&#xff0c;它使用了12个Transformer…...

(四)激光线扫描-光平面标定

在上一章节,已经实现了对激光线条的中心线提取,并且在最开始已经实现了对相机的标定,那么相机标定的作用是什么呢? 就是将图像二维点和空间三维点之间进行互相转换。 1. 什么是光平面 激光发射器投射出一条线,形成的一个扇形区域平面就是光平面,也叫光刀面,与物体相交…...

妙不可言的Python之旅----(二)

Python基础语法 什么是字面量 字面量&#xff1a;在代码中&#xff0c;被写下来的的固定的值&#xff0c;称之为字面量 常用的值类型 类型 描述 说明 数字&#xff08;Number&#xff09; 支持 • 整数&#xff08;int&#xff09; • 浮点数&#xff08;float&#xff…...

cartographer(1)-运行

1.下载数据集 #1.下载数据集&#xff1a; mkdir /home/tang/bagfiles#2.开始二维建图 cd /home/tang/carto_ws/cartographer_detailed_comments_ws/install_isolated/source install_isolated/setup.bash rospack profile #新装的包索引地址存在ros的环境里 roslaunch ca…...

C++:模板进阶与继承

模板进阶与继承 模板进阶1.非类型的模板参数2.模板的特化2.1特化的概念2.2函数模板特化2.3类模板特化2.4全特化和偏特化2.4.1全特化2.4.2偏特化 3.模板的分离编译3.1同文件分离3.2不同文件下分离 继承1.继承的概念和定义1.1继承的概念1.2继承的定义1.2.1定义格式1.2.2继承关系和…...

vue-img-cutter 实现图片裁剪[vue 组件库]

借助 vue-img-cutter 可以在网页端实现图片裁剪功能&#xff0c;最终功能效果如下&#xff1a; 组件 npm 安装 npm install vue-img-cutter2 --save-dev # for vue2 npm install vue-img-cutter3 --save-dev # for vue3vue-img-cutter使用 template模板标签模块&#xff0c…...

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇&#xff0c;在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下&#xff1a; 【Note】&#xff1a;如果你已经完成安装等操作&#xff0c;可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作&#xff0c;重…...

Vim 调用外部命令学习笔记

Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

Java 8 Stream API 入门到实践详解

一、告别 for 循环&#xff01; 传统痛点&#xff1a; Java 8 之前&#xff0c;集合操作离不开冗长的 for 循环和匿名类。例如&#xff0c;过滤列表中的偶数&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

基于服务器使用 apt 安装、配置 Nginx

&#x1f9fe; 一、查看可安装的 Nginx 版本 首先&#xff0c;你可以运行以下命令查看可用版本&#xff1a; apt-cache madison nginx-core输出示例&#xff1a; nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

2.Vue编写一个app

1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

Reasoning over Uncertain Text by Generative Large Language Models

https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

2025季度云服务器排行榜

在全球云服务器市场&#xff0c;各厂商的排名和地位并非一成不变&#xff0c;而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势&#xff0c;对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析&#xff1a; 一、全球“三巨头”…...

【JavaSE】多线程基础学习笔记

多线程基础 -线程相关概念 程序&#xff08;Program&#xff09; 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序&#xff0c;比如我们使用QQ&#xff0c;就启动了一个进程&#xff0c;操作系统就会为该进程分配内存…...

基于Springboot+Vue的办公管理系统

角色&#xff1a; 管理员、员工 技术&#xff1a; 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能&#xff1a; 该办公管理系统是一个综合性的企业内部管理平台&#xff0c;旨在提升企业运营效率和员工管理水…...