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

【Vue3源码解析】响应式原理

源码环境搭建

【Vue3源码解析】应用实例创建及页面渲染-CSDN博客

写文章时的Vue 版本:

"version": "3.5.13",

针对单个包进行开发环境打包、测试。

pnpm run dev reactivity

image-20250215180341644

image-20250215180401097

reactive 创建响应式对象

packages/reactivity/src/reactive.ts

export function reactive(target: object) {// if trying to observe a readonly proxy, return the readonly version.if (isReadonly(target)) {return target}/*** 通过调用 createReactiveObject 函数,创建一个响应式对象,并返回该对象。* 第一个参数是 目标对象* 第二个参数用来判断该对象是否是只读的* mutableHandlers 是一个对象,包含了响应式对象的一些操作方法,如get、set等。* mutableCollectionHandlers 是一个对象,包含了响应式集合对象的一些操作方法,如get、set等。* reactiveMap 是一个全局 WeakMap 对象,用于缓存 target 到 响应式对象的映射关系。*/return createReactiveObject(target,false,mutableHandlers,mutableCollectionHandlers,reactiveMap,)
}
function createReactiveObject(target: Target,isReadonly: boolean,baseHandlers: ProxyHandler<any>,collectionHandlers: ProxyHandler<any>,proxyMap: WeakMap<Target, any>,
) {if (!isObject(target)) {if (__DEV__) {warn(`value cannot be made ${isReadonly ? 'readonly' : 'reactive'}: ${String(target,)}`,)}return target}// target is already a Proxy, return it.// exception: calling readonly() on a reactive object// 如果已经是 Proxy 代理对象,则直接返回if (target[ReactiveFlags.RAW] &&!(isReadonly && target[ReactiveFlags.IS_REACTIVE])) {return target}// target already has corresponding Proxy// 如果 ProxyMap 中有 target 对应的 proxy 对象 则直接返回const existingProxy = proxyMap.get(target)if (existingProxy) {return existingProxy}// only specific value types can be observed.// 类型无效 则直接返回 targetconst targetType = getTargetType(target)if (targetType === TargetType.INVALID) {return target}// 创建 target 对应的 Proxy 代理对象 并传入 baseHandlersconst proxy = new Proxy(target,// COLLECTION => Map/Set/WeakMap/WeakSettargetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers,)proxyMap.set(target, proxy)return proxy
}

effect 和它的函数参数fn的作用

packages/reactivity/src/effect.ts

/*** 创建一个响应式副作用函数** 此函数用于追踪函数内的响应式数据访问和修改,以便在数据变化时自动重新执行该函数* 它是响应式系统的核心部分,使得开发者可以轻松创建响应式的应用程序** @param fn 要追踪的副作用函数,它会接收一个用于触发副作用的函数作为参数* @param options 可选的副作用选项,允许开发者自定义副作用的行为,例如是否应该停止追踪等* @returns 返回一个绑定了副作用函数的runner函数,用于手动触发副作用*/
export function effect<T = any>(fn: () => T,// 可以由用户自定义,用于定义副作用effect的行为,例如shouldTrack、onTrack、onTrigger等// 这样可以覆盖ReactiveEffect的默认scheduler而执行自己的scheduleroptions?: ReactiveEffectOptions,
): ReactiveEffectRunner<T> {// 检查传入的函数是否已经是一个ReactiveEffect的副作用函数,如果是,则使用其内部的副作用函数if ((fn as ReactiveEffectRunner).effect instanceof ReactiveEffect) {fn = (fn as ReactiveEffectRunner).effect.fn}// 调用一次effect函数,根据传入的fn创建一个新的ReactiveEffect实例对象:_effect// 一个fn 对应一个 _effect对象// fn 同时成为_effect 对象的fn属性(形成闭包)/*** effect(fn)* fn.effect => ReactiveEffect对象* ReactiveEffect对象.fn => fn*/// 创建一个新的ReactiveEffect实例/*** 当内部执行scheduler的时候,它会回头调用effect的run,·而run方法内部会调用fn* 意味着:scheduler()=>run()=>fn()* 如何执行:那么之后我们如果想要重新执行fn函数,·只需要执行scheduler就可以了*/const e = new ReactiveEffect(fn)// 如果提供了用户自定义的配置,则将配置合并到副作用实例中if (options) {extend(e, options)}// 尝试运行副作用函数,如果在执行过程中抛出错误,则停止追踪并重新抛出错误try {e.run()} catch (err) {e.stop()throw err}// 创建并返回一个副作用函数的runner,它绑定了当前的副作用实例const runner = e.run.bind(e) as ReactiveEffectRunnerrunner.effect = e// 返回副作用函数的runner,允许手动触发副作用 e.run()=> fn()return runner
}
/*** 执行当前的副作用函数** 此函数负责执行副作用操作,并在执行前后处理一些清理和准备依赖项的工作* 它还管理副作用的执行状态,确保在执行过程中不会被中断,并在完成后清理依赖项** @returns 返回副作用函数的执行结果*/
run(): T {// TODO cleanupEffect// 检查副作用是否处于非激活状态,如果是,则直接执行副作用函数if (!(this.flags & EffectFlags.ACTIVE)) {// stopped during cleanupreturn this.fn()}// 设置当前副作用为正在运行状态this.flags |= EffectFlags.RUNNING// 执行清理上一次执行的副作用cleanupEffect(this)// 准备依赖项收集prepareDeps(this)// 保存当前正在执行的副作用和是否应该跟踪的全局状态const prevEffect = activeSubconst prevShouldTrack = shouldTrack// 设置当前执行的副作用和全局跟踪状态activeSub = thisshouldTrack = truetry {// 执行副作用函数return this.fn()} finally {// 在开发环境下,确保当前执行的副作用被正确恢复if (__DEV__ && activeSub !== this) {warn('Active effect was not restored correctly - ' +'this is likely a Vue internal bug.',)}// 清理当前执行的副作用的依赖项cleanupDeps(this)// 恢复之前保存的正在执行的副作用和是否应该跟踪的全局状态activeSub = prevEffectshouldTrack = prevShouldTrack// 清除当前副作用的正在运行状态this.flags &= ~EffectFlags.RUNNING}
}

执行fn后响应式变量依赖的收集和触发

Vue3.4 版本之前最后的一层,也就是 nameDepMap 这一层是 Set 结构,但是从 3.4 版本开始,变成了 Map ,用来记录 track 的 id,然而Vue3.5对该部分又进行了重构,不再记录id,只能说学的速度永远跟不上技术更新的速度~

image-20250215181003656

packages/reactivity/src/baseHandlers.ts

/*** BaseReactiveHandler 类实现了 ProxyHandler 接口,用于处理响应式对象的代理操作。* 它主要负责拦截和处理对响应式对象的访问和操作,根据是否只读和是否浅层次来决定如何处理这些操作。** @param _isReadonly 是否只读,如果为 true,则该响应式对象不可被修改。* @param _isShallow 是否浅层次,如果为 true,则仅对外层属性进行响应式处理,不递归处理内部属性。*/
class BaseReactiveHandler implements ProxyHandler<Target> {constructor(protected readonly _isReadonly = false,protected readonly _isShallow = false,) {}/*** 拦截对目标对象属性的获取操作。* 此方法负责处理各种情况下的属性获取,包括处理特殊的 ReactiveFlags、数组的特殊情况、* 属性的追踪、以及返回值的处理等。** @param target 目标对象,即被代理的响应式对象。* @param key 要访问的属性名。* @param receiver 代理对象或继承链上的其他对象。* @returns 属性值或处理后的值。*/get(target: Target, key: string | symbol, receiver: object): any {// 处理特殊的 ReactiveFlags 属性if (key === ReactiveFlags.SKIP) return target[ReactiveFlags.SKIP]const isReadonly = this._isReadonly,isShallow = this._isShallow// 根据不同的 ReactiveFlags 返回相应的值if (key === ReactiveFlags.IS_REACTIVE) {return !isReadonly} else if (key === ReactiveFlags.IS_READONLY) {return isReadonly} else if (key === ReactiveFlags.IS_SHALLOW) {return isShallow} else if (key === ReactiveFlags.RAW) {if (receiver ===(isReadonly? isShallow? shallowReadonlyMap: readonlyMap: isShallow? shallowReactiveMap: reactiveMap).get(target) ||// receiver is not the reactive proxy, but has the same prototype// this means the receiver is a user proxy of the reactive proxyObject.getPrototypeOf(target) === Object.getPrototypeOf(receiver)) {return target}// early return undefinedreturn}const targetIsArray = isArray(target)// 处理非只读情况下的特殊属性和方法if (!isReadonly) {let fn: Function | undefinedif (targetIsArray && (fn = arrayInstrumentations[key])) {return fn}if (key === 'hasOwnProperty') {return hasOwnProperty}}// 获取属性值,考虑内置符号和非跟踪键的情况const res = Reflect.get(target,key,// if this is a proxy wrapping a ref, return methods using the raw ref// as receiver so that we don't have to call `toRaw` on the ref in all// its class methodsisRef(target) ? target : receiver,)if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {return res}// 在非只读情况下,跟踪属性的访问if (!isReadonly) {// 依赖追踪track(target, TrackOpTypes.GET, key)}// 根据是否浅层次和返回值的类型,决定是否需要进一步处理返回值if (isShallow) {return res}if (isRef(res)) {// ref unwrapping - skip unwrap for Array + integer key.return targetIsArray && isIntegerKey(key) ? res : res.value}if (isObject(res)) {// Convert returned value into a proxy as well. we do the isObject check// here to avoid invalid value warning. Also need to lazy access readonly// and reactive here to avoid circular dependency.return isReadonly ? readonly(res) : reactive(res)}return res}
}
class MutableReactiveHandler extends BaseReactiveHandler {constructor(isShallow = false) {super(false, isShallow)}/*** 设置一个响应式对象的属性。** 该方法重写了 `Proxy` 对象的默认 `set` 行为,实现了自定义的属性设置逻辑。* 它处理了各种情况,例如浅层响应式、只读属性和 ref 解包,以确保响应式系统的正确行为。** @param target - 被代理的原始对象。* @param key - 要设置的属性的名称或符号。* @param value - 属性的新值。* @param receiver - 代理对象本身。* @returns 返回设置属性的结果。*/set(target: Record<string | symbol, unknown>,key: string | symbol,value: unknown,receiver: object,): boolean {let oldValue = target[key]// 如果不是浅层响应式模式,需要进一步处理属性和值。if (!this._isShallow) {const isOldValueReadonly = isReadonly(oldValue)// 如果新旧值都不是浅层或只读的,将它们转换为原始状态。if (!isShallow(value) && !isReadonly(value)) {oldValue = toRaw(oldValue)value = toRaw(value)}// 如果目标不是数组且旧值是 ref 而新值不是 ref,需要特殊处理。if (!isArray(target) && isRef(oldValue) && !isRef(value)) {// 如果旧值是只读的,属性不能被设置。if (isOldValueReadonly) {return false} else {// 否则,直接设置旧值 ref 的值。oldValue.value = valuereturn true}}} else {// 在浅层模式下,对象按原样设置,无论是否是响应式的。}// 确定键是否已经存在。const hadKey =isArray(target) && isIntegerKey(key)? Number(key) < target.length: hasOwn(target, key)// 使用 Reflect.set 尝试设置属性。const result = Reflect.set(target,key,value,isRef(target) ? target : receiver,)// 如果目标是原始对象本身,根据属性是新增还是更新触发相应的副作用。if (target === toRaw(receiver)) {if (!hadKey) {// 触发新增属性的副作用。trigger(target, TriggerOpTypes.ADD, key, value)} else if (hasChanged(value, oldValue)) {// 触发更新属性的副作用。trigger(target, TriggerOpTypes.SET, key, value, oldValue)}}return result
}deleteProperty(target: Record<string | symbol, unknown>,key: string | symbol,): boolean {const hadKey = hasOwn(target, key)const oldValue = target[key]const result = Reflect.deleteProperty(target, key)if (result && hadKey) {trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)}return result}has(target: Record<string | symbol, unknown>, key: string | symbol): boolean {const result = Reflect.has(target, key)if (!isSymbol(key) || !builtInSymbols.has(key)) {track(target, TrackOpTypes.HAS, key)}return result}ownKeys(target: Record<string | symbol, unknown>): (string | symbol)[] {track(target,TrackOpTypes.ITERATE,isArray(target) ? 'length' : ITERATE_KEY,)return Reflect.ownKeys(target)}
}

packages/reactivity/src/dep.ts

/*** 跟踪对响应式属性的访问。** 该函数会检查当前正在运行的效果(effect),并将其记录为依赖项(dep),* 这些依赖项记录了所有依赖于该响应式属性的效果。** @param target - 包含响应式属性的对象。* @param type - 定义对响应式属性的访问类型。* @param key - 要跟踪的响应式属性的标识符。*/
export function track(target: object, type: TrackOpTypes, key: unknown): void {// 仅在启用了跟踪且存在活动效果时进行跟踪。activeSub在3.4版本及之前为activeEffectif (shouldTrack && activeSub) {// 获取目标对象的依赖映射,如果不存在则创建一个新的映射。let depsMap = targetMap.get(target)if (!depsMap) {targetMap.set(target, (depsMap = new Map()))}// 获取指定属性的依赖项,如果不存在则创建一个新的依赖项。let dep = depsMap.get(key)if (!dep) {depsMap.set(key, (dep = new Dep()))dep.map = depsMapdep.key = key}// 在开发模式下记录依赖项,并包含详细信息。if (__DEV__) {dep.track({target,type,key,})} else {dep.track()}}
}
/*** 查找与目标对象(或特定属性)关联的所有依赖项,并触发存储在其中的效果。** @param target - 响应式对象。* @param type - 定义需要触发效果的操作类型。* @param key - 可用于指定目标对象中的特定响应式属性。* @param newValue - 新值,用于某些操作类型(如 SET)。* @param oldValue - 旧值,用于某些操作类型(如 SET)。* @param oldTarget - 旧的目标对象,用于某些操作类型(如 CLEAR)。*/
export function trigger(target: object,type: TriggerOpTypes,key?: unknown,newValue?: unknown,oldValue?: unknown,oldTarget?: Map<unknown, unknown> | Set<unknown>,
): void {const depsMap = targetMap.get(target)if (!depsMap) {// 从未被追踪过globalVersion++return}const run = (dep: Dep | undefined) => {if (dep) {if (__DEV__) {dep.trigger({target,type,key,newValue,oldValue,oldTarget,})} else {dep.trigger()}}}startBatch()if (type === TriggerOpTypes.CLEAR) {// 集合被清除// 触发目标对象的所有效果depsMap.forEach(run)} else {const targetIsArray = isArray(target)const isArrayIndex = targetIsArray && isIntegerKey(key)if (targetIsArray && key === 'length') {const newLength = Number(newValue)depsMap.forEach((dep, key) => {if (key === 'length' ||key === ARRAY_ITERATE_KEY ||(!isSymbol(key) && key >= newLength)) {run(dep)}})} else {// 调度 SET | ADD | DELETE 的运行if (key !== void 0 || depsMap.has(void 0)) {run(depsMap.get(key))}// 调度 ARRAY_ITERATE 对于任何数字键的变化(长度已在上方处理)if (isArrayIndex) {run(depsMap.get(ARRAY_ITERATE_KEY))}// 也对 ADD | DELETE | Map.SET 运行迭代键switch (type) {case TriggerOpTypes.ADD:if (!targetIsArray) {run(depsMap.get(ITERATE_KEY))if (isMap(target)) {run(depsMap.get(MAP_KEY_ITERATE_KEY))}} else if (isArrayIndex) {// 新索引添加到数组 -> 长度变化run(depsMap.get('length'))}breakcase TriggerOpTypes.DELETE:if (!targetIsArray) {run(depsMap.get(ITERATE_KEY))if (isMap(target)) {run(depsMap.get(MAP_KEY_ITERATE_KEY))}}breakcase TriggerOpTypes.SET:if (isMap(target)) {run(depsMap.get(ITERATE_KEY))}break}}}endBatch()
}
ITERATE_KEY))}} else if (isArrayIndex) {// 新索引添加到数组 -> 长度变化run(depsMap.get('length'))}breakcase TriggerOpTypes.DELETE:if (!targetIsArray) {run(depsMap.get(ITERATE_KEY))if (isMap(target)) {run(depsMap.get(MAP_KEY_ITERATE_KEY))}}breakcase TriggerOpTypes.SET:if (isMap(target)) {run(depsMap.get(ITERATE_KEY))}break}}}endBatch()
}

相关文章:

【Vue3源码解析】响应式原理

源码环境搭建 【Vue3源码解析】应用实例创建及页面渲染-CSDN博客 写文章时的Vue 版本&#xff1a; "version": "3.5.13",针对单个包进行开发环境打包、测试。 pnpm run dev reactivityreactive 创建响应式对象 packages/reactivity/src/reactive.ts …...

servlet中的ServletContext

设置、获取ServletContext配置信息 与ServletConfig不同的是&#xff0c;所有Servlet共享一份ServletContext 在web.xml中设置配置信息 <?xml version"1.0" encoding"UTF-8"?> <web-app xmlns"https://jakarta.ee/xml/ns/jakartaee"x…...

第1825天 | 我的创作纪念日:缘起、成长经历、大方向

目录 缘起一、成为创作者的初心&#xff08;一&#xff09;好记性不如烂笔头&#xff08;二&#xff09;文档可以帮助多个人解决同一个问题&#xff08;三&#xff09;加深自己对问题的理解&#xff0c;对技术的研究 二、实战项目中的经验分享&#xff08;一&#xff09;项目背…...

如何在 Mac 上解决 Qt Creator 安装后应用程序无法找到的问题

在安装Qt时&#xff0c;遇到了一些问题&#xff0c;尤其是在Mac上安装Qt后&#xff0c;发现Qt Creator没有出现在应用程序中。通过一些搜索和操作&#xff0c;最终解决了问题。以下是详细的记录和解决方法。 1. 安装Qt后未显示Qt Creator 安装完成Qt后&#xff0c;启动应用程…...

Java 设计模式之迭代器模式

文章目录 Java 设计模式之迭代器模式概述UML代码实现Java的迭代器 Java 设计模式之迭代器模式 概述 迭代器模式(Iterator)&#xff0c;提供一种方法顺序访问一个聚合对象中的各个元素&#xff0c;而又不暴露该对象的内部表示。 UML Iterator&#xff1a;迭代器接口&#xff…...

登录演示和功能拆解

登录演示和功能拆解 表单基础校验实现 1. 基础双向绑定 <template><el-form><el-form-item label"账号"><el-input v-model"formData.username" /></el-form-item><el-form-item label"密码"><el-inpu…...

DeepSeek深度求索API多线程批量写原创文章软件-ai痕迹极低

DeepSeek是一款由国内人工智能公司研发的大型语言模型&#xff0c;拥有强大的自然语言处理能力&#xff0c;能够理解并回答问题&#xff0c;还能辅助写代码、整理资料和解决复杂的数学问题。 与OpenAI开发的ChatGPT相比&#xff0c;DeepSeek不仅率先实现了媲美OpenAI-o1模型的…...

Redis进阶使用

在日常工作中&#xff0c;使用Redis有什么需要注意的&#xff1f; 设置合适的过期时间。尽量避免大key问题&#xff0c;避免用字符串存储过大的数据&#xff1b;避免集合的数据量太大&#xff0c;要定期清除。 常用的数据结构有哪些&#xff1f;用在什么地方&#xff1f; 按…...

Python常见面试题的详解6

1. 按字典 value 值排序 要点&#xff1a;对于给定字典&#xff0c;使用 sorted() 函数结合 items() 方法&#xff0c;依据 value 进行排序&#xff0c;也可以定义一个通用函数&#xff0c;支持按 value 升序或降序排序。示例&#xff1a; python d {a: 1, b: 2, c: 3, d: …...

Linux基础之文件权限的八进制表示法

1. Linux 文件权限概述 在 Linux 中&#xff0c;每个文件或目录都有三种基本权限&#xff0c;分别是&#xff1a; 读权限 - r&#xff1a;允许查看文件内容。写权限 - w&#xff1a;允许修改文件内容。执行权限 - x&#xff1a;允许执行文件或进入目录。 每个文件或目录的权…...

数据结构与算法面试专题——堆排序

完全二叉树 完全二叉树中如果每棵子树的最大值都在顶部就是大根堆 完全二叉树中如果每棵子树的最小值都在顶部就是小根堆 设计目标&#xff1a;完全二叉树的设计目标是高效地利用存储空间&#xff0c;同时便于进行层次遍历和数组存储。它的结构使得每个节点的子节点都可以通过简…...

《On Java进阶卷》阅读笔记(五)

第7章 IO系统 I/O流&#xff1a; IO有很多不同的来源和去处&#xff0c;如文件、控制台网络连接等&#xff0c;而且还涉及需求以很多种方式&#xff0c;如顺序读取、随机访问、缓冲、字符、按行读取、按字读取等。 Java8的函数式流相关的类和IO流之间并无关联。 IO流隐藏了…...

《代码随想录》刷题笔记——回溯篇【java实现】

文章目录 组合组合总和 III电话号码的字母组合组合总和组合总和II思路代码实现 分割回文串※思路字符串分割回文串判断效率优化※ 复原 IP 地址优化版本 子集子集 II使用usedArr辅助去重不使用usedArr辅助去重 递增子序列※全排列全排列 II重新安排行程题意代码 N 皇后解数独直…...

数值积分:通过复合梯形法计算

在物理学和工程学中&#xff0c;很多问题都可以通过数值积分来求解&#xff0c;特别是当我们无法得到解析解时。数值积分是通过计算积分区间内离散点的函数值来近似积分的结果。在这篇博客中&#xff0c;我将讨论如何使用 复合梯形法 来进行数值积分&#xff0c;并以一个简单的…...

AcWing——3624. 三值字符串

双指针解法 #include<iostream> #include<unordered_map> using namespace std; int main() {int n; cin >> n;while(n--){unordered_map<char, int> tree;string s; cin >> s;int ans 0x7fffffff; for(int i 0, j 0; j < (int)s.size();…...

【JavaEE进阶】验证码案例

目 &#x1f332;实现说明 &#x1f384;Hutool介绍 &#x1f333;准备工作 &#x1f334;约定前后端交互接口 &#x1f6a9;接口定义 &#x1f6a9;实现服务器后端代码 &#x1f6a9;前端代码 &#x1f6a9;整体测试 &#x1f332;实现说明 随着安全性的要求越来越⾼…...

Uniapp 短视频去水印解析工具开发实现

最近搞了一个有意思的小工具——短视频去水印解析器&#xff01;这玩意儿可以把短视频中的水印给抹掉&#xff0c;还能提取视频、封面等资源。整个项目用了 Uniapp 开发&#xff0c;做完后体验了一下&#xff0c;发现还挺顺手。今天就来跟大家聊聊实现思路和代码细节~ 需求分析…...

计算机网络-八股-学习摘要

一&#xff1a;HTTP的基本概念 全称&#xff1a; 超文本传输协议 从三个方面介绍HTTP协议 1&#xff0c;超文本&#xff1a;我们先来理解「文本」&#xff0c;在互联网早期的时候只是简单的字符文字&#xff0c;但现在「文本」的涵义已经可以扩展为图片、视频、压缩包等&am…...

编程速递-庆祝Delphi诞生30周年!

庆祝Delphi 30周年纪念是一个特别的时刻。 回到1995年&#xff0c;也就是30年前&#xff0c;在微软Windows和互联网时代的曙光初现之时&#xff0c;Borland Delphi的创建者们无法想象&#xff0c;当时使用Borland Delphi构建的应用程序至今仍在运行——为全世界数十亿人服务。…...

每天五分钟深度学习框架pytorch:搭建谷歌的Inception网络模块

本文重点 前面我们学习了VGG,从现在开始我们将学习谷歌公司推出的GoogLeNet。当年ImageNet竞赛的第二名是VGG,而第一名就是GoogLeNet,它的模型设计拥有很多的技巧,这个model证明了一件事:用更多的卷积,更深的层次可以得到更好的结构 GoogLeNet的网络结构 如图所示就是Go…...

智慧医疗能源事业线深度画像分析(上)

引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

Mac软件卸载指南,简单易懂!

刚和Adobe分手&#xff0c;它却总在Library里给你写"回忆录"&#xff1f;卸载的Final Cut Pro像电子幽灵般阴魂不散&#xff1f;总是会有残留文件&#xff0c;别慌&#xff01;这份Mac软件卸载指南&#xff0c;将用最硬核的方式教你"数字分手术"&#xff0…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

探索Selenium:自动化测试的神奇钥匙

目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...

MySQL:分区的基本使用

目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区&#xff08;Partitioning&#xff09;是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分&#xff08;分区&#xff09;可以独立存储、管理和优化&#xff0c;…...

Python竞赛环境搭建全攻略

Python环境搭建竞赛技术文章大纲 竞赛背景与意义 竞赛的目的与价值Python在竞赛中的应用场景环境搭建对竞赛效率的影响 竞赛环境需求分析 常见竞赛类型&#xff08;算法、数据分析、机器学习等&#xff09;不同竞赛对Python版本及库的要求硬件与操作系统的兼容性问题 Pyth…...

区块链技术概述

区块链技术是一种去中心化、分布式账本技术&#xff0c;通过密码学、共识机制和智能合约等核心组件&#xff0c;实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点&#xff1a;数据存储在网络中的多个节点&#xff08;计算机&#xff09;&#xff0c;而非…...

【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL

ETABS API实战:导出框架元素数据到Excel 在结构工程师的日常工作中,经常需要从ETABS模型中提取框架元素信息进行后续分析。手动复制粘贴不仅耗时,还容易出错。今天我们来用简单的VBA代码实现自动化导出。 🎯 我们要实现什么? 一键点击,就能将ETABS中所有框架元素的基…...