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

ArkTS 中 @State 底层原理详解

ArkTS 中 @State 底层原理详解

📌 文档说明

本文档深入讲解 ArkTS 中 @State 装饰器的底层实现原理,包含技术原理详解和面试口述版本。适合用于:

  • 深入理解 ArkTS 响应式系统
  • 面试准备和技术交流
  • 排查状态管理相关问题

🔬 一、核心机制概述

@State 的底层实现基于响应式系统,核心流程可以概括为:

数据劫持(Proxy) → 依赖收集(Track) → 触发更新(Trigger)

核心公式

响应式数据 = Proxy(原始数据)
组件更新 = 依赖收集 + 变化监听 + 批量渲染

🎯 二、数据劫持(Data Hijacking)

2.1 Proxy 代理机制

当你使用 @State 装饰一个变量时,ArkTS 框架会将这个变量包装成一个 Proxy 对象

你写的代码
@State count: number = 0
底层实际做的事情(简化版)
class StateProxy {private _value: number = 0;private _subscribers: Set<Component> = new Set();// getter:读取时收集依赖get value(): number {// 依赖收集:记录是哪个组件在使用这个状态this.collectDependency();return this._value;}// setter:修改时触发更新set value(newValue: number) {if (this._value !== newValue) {this._value = newValue;// 触发所有订阅者(组件)重新渲染this.notifySubscribers();}}collectDependency() {// 记录当前正在执行的组件if (currentComponent) {this._subscribers.add(currentComponent);}}notifySubscribers() {// 通知所有使用了这个状态的组件重新渲染this._subscribers.forEach((subscriber) => {subscriber.markNeedsBuild(); // 标记需要重新构建});}
}

2.2 对象和数组的深度代理

对于对象和数组,框架会递归地进行代理,但仅限第一层

// 你的代码
@State user: User = { name: 'Tom', age: 20 }// 底层处理(简化)
function createReactiveObject(obj: any, depth: number = 0) {if (typeof obj !== 'object' || obj === null) {return obj}return new Proxy(obj, {get(target, key) {// 收集依赖track(target, key)const value = target[key]// 如果值是对象,递归代理(但只代理第一层!)if (depth === 0 && typeof value === 'object' && value !== null) {return createReactiveObject(value, depth + 1)}return value},set(target, key, value) {const oldValue = target[key]if (oldValue !== value) {target[key] = value// 触发更新trigger(target, key)}return true}})
}

这就是为什么 @State 只能观察第一层属性变化的原因!


📊 三、依赖收集(Dependency Collection)

3.1 依赖收集的数据结构

// 全局变量
let currentComponent: Component | null = null;// 依赖映射:WeakMap<目标对象, Map<属性名, Set<组件>>>
const targetMap = new WeakMap<any, Map<string, Set<Component>>>();

为什么用 WeakMap?

  • 键是弱引用,对象被销毁时会自动清理,防止内存泄漏
  • 不会阻止垃圾回收

3.2 依赖收集函数

// 依赖收集函数
function track(target: any, key: string) {if (!currentComponent) return;// 获取或创建目标对象的依赖映射let depsMap = targetMap.get(target);if (!depsMap) {depsMap = new Map();targetMap.set(target, depsMap);}// 获取或创建属性的依赖集合let deps = depsMap.get(key);if (!deps) {deps = new Set();depsMap.set(key, deps);}// 添加当前组件到依赖集合deps.add(currentComponent);
}

3.3 组件渲染时的处理

class Component {private stateVars: Map<string, any> = new Map();build() {// 设置当前组件currentComponent = this;try {// 执行 build 方法,期间所有的状态访问都会被 trackthis.renderContent();} finally {// 清除当前组件currentComponent = null;}}renderContent() {// 你的 build 方法内容// 每次访问 this.count 时,都会调用 track()}
}

3.4 依赖收集的时机

@Entry
@Component
struct Demo {@State count: number = 0build() {Column() {// ✅ 这里访问 count,会收集依赖Text(`计数: ${this.count}`)Button('增加').onClick(() => {// ❌ 这里修改 count,不会收集依赖(因为不在 build 中)this.count++})}}
}

关键点

  • 依赖收集只在 build() 方法执行时发生
  • 事件回调中修改状态不会收集依赖,而是触发更新

🔄 四、触发更新(Trigger Update)

4.1 触发更新函数

// 触发更新函数
function trigger(target: any, key: string) {const depsMap = targetMap.get(target);if (!depsMap) return;const deps = depsMap.get(key);if (!deps) return;// 批量更新:避免重复渲染const effectsToRun = new Set<Component>();deps.forEach((component) => {if (component !== currentComponent) {// 避免在渲染中触发渲染effectsToRun.add(component);}});// 将更新任务加入微任务队列queueMicrotask(() => {effectsToRun.forEach((component) => {component.scheduleUpdate();});});
}

4.2 组件的更新调度

class Component {private updateScheduled: boolean = false;scheduleUpdate() {if (this.updateScheduled) return; // 防止重复调度this.updateScheduled = true;// 使用 requestAnimationFrame 或类似机制批量更新requestAnimationFrame(() => {this.updateScheduled = false;this.performUpdate();});}performUpdate() {// 执行 Diff 算法,最小化 DOM 操作const newVNode = this.build();const patches = diff(this.oldVNode, newVNode);patch(this.element, patches);this.oldVNode = newVNode;}
}

4.3 批量更新机制

// 多次修改只触发一次渲染
this.count++; // 标记需要更新
this.message = "hello"; // 标记需要更新
this.visible = true; // 标记需要更新// 在下一个 requestAnimationFrame 中统一更新
// 避免了多次渲染,提升性能

🔁 五、完整的工作流程

5.1 流程图

初始化阶段:@State count = 0↓创建 Proxy 对象渲染阶段:执行 build()↓设置 currentComponent↓访问 this.count (触发 getter)↓执行 track() 收集依赖↓记录:Component A 依赖 count更新阶段:this.count++ (触发 setter)↓执行 trigger()↓查找依赖 count 的组件↓调度 Component A 更新↓下一帧:重新执行 build()↓UI 更新完成

5.2 代码示例

@Entry
@Component
struct CounterDemo {@State count: number = 0  // ① 初始化:创建 Proxybuild() {  // ② build 执行:开始依赖收集Column() {// ③ 访问 count:触发 getter,收集依赖Text(`计数: ${this.count}`)Button('增加').onClick(() => {// ④ 修改 count:触发 setterthis.count++// ⑤ setter 内部://    - 比较新旧值//    - 调用 trigger()//    - 找到所有依赖的组件//    - 调度更新// ⑥ 下一帧://    - 重新执行 build()//    - 重新收集依赖//    - 计算 VNode diff//    - 更新 UI})}}
}

5.3 时序图

用户操作 → setter → trigger → scheduleUpdate → performUpdate → build → render↑                                                                      ↓└──────────────────────── 等待下一次用户操作 ────────────────────────┘

⚙️ 六、性能优化机制

6.1 批量更新

问题:如果每次状态变化都立即渲染,性能会很差。

解决:收集所有变化,在下一帧统一更新。

// 示例
this.count++; // 标记更新
this.message = "hi"; // 标记更新
this.visible = false; // 标记更新// 只触发一次渲染(在下一个 RAF)

6.2 精确更新

问题:父组件状态变化,是否需要更新所有子组件?

解决:只更新真正使用了该状态的组件。

@Component
struct Parent {@State parentData: string = 'parent'build() {Column() {Text(this.parentData)  // ✅ 只有这里会在 parentData 变化时更新Child()                // ❌ Child 不会因为 parentData 变化而更新}}
}

6.3 浅比较优化

问题:即使值没变,也触发更新?

解决:setter 中进行浅比较。

set value(newValue: any) {// 浅比较:如果值没变,不触发更新if (this._value === newValue) {return}this._value = newValuethis.notifySubscribers()
}

❓ 七、常见问题解析

7.1 为什么只能观察第一层属性?

问题示例
@State user: User = {name: 'Tom',      // ✅ 第一层:被代理address: {city: '北京'    // ❌ 第二层:没有被代理}
}// 修改第一层:触发更新
this.user.name = 'Jerry'  // ✅ Proxy 的 setter 被调用// 修改第二层:不触发更新
this.user.address.city = '上海'  // ❌ 访问的是普通对象,没有 Proxy
原因分析
  1. 性能考虑:深度代理开销大

    • 每个嵌套对象都需要创建 Proxy
    • 深层嵌套会导致性能下降
  2. 复杂度问题:需要递归处理所有嵌套对象

    • 循环引用检测
    • 数组变化追踪
    • 动态属性添加
  3. 内存占用:每个 Proxy 都需要维护依赖关系

解决方案

使用 @Observed + @ObjectLink 对需要深度观察的对象单独处理:

@Observed
class Address {city: string = ''street: string = ''
}@Observed
class User {name: string = ''address: Address = new Address()
}@State user: User = new User()// 现在嵌套属性也能触发更新了
this.user.address.city = '上海'  // ✅

7.2 为什么数组要用特定方法修改?

问题示例
@State items: string[] = ['a', 'b', 'c']// ❌ 错误:不会触发更新
this.items[0] = 'd'// ✅ 正确:使用数组方法
this.items.splice(0, 1, 'd')// ✅ 正确:创建新数组
this.items = [...this.items]
原因分析
  1. 索引赋值的问题

    • 虽然 Proxy 能拦截 items[0] = 'd' 这个操作
    • 但数组的引用地址没有变化
    • 框架的浅比较可能认为数组没变
  2. 数组方法的优势

    • pushsplice 等方法会触发完整的响应式流程
    • 框架能正确识别数组的变化
    • 确保 UI 正确更新
最佳实践
// ✅ 推荐方法1:使用数组方法
this.items.push("new");
this.items.splice(index, 1);
this.items.unshift("new");// ✅ 推荐方法2:创建新数组
this.items = [...this.items, "new"];
this.items = this.items.filter((item) => item !== "old");
this.items = this.items.map((item) => transform(item));

7.3 更新是同步还是异步?

数据更新是同步的,UI 渲染是异步的。

@State count: number = 0Button('点击').onClick(() => {console.log('更新前:', this.count)  // 0this.count++console.log('更新后:', this.count)  // 1 ✅ 立即看到新值// 但此时 UI 还没更新,要等到下一帧})

原因

  • 数据同步更新:保证逻辑正确性
  • UI 异步渲染:批量处理,提升性能

🆚 八、与其他框架对比

8.1 对比表格

框架响应式原理更新粒度手动触发深度观察
ArkTS @StateProxy 代理组件级精确更新需要 @Observed
Vue 3Proxy 代理组件级更新自动深度代理
React无代理(手动触发)组件树更新
AngularZone.js 脏检查组件树更新需要配置

8.2 与 Vue 3 的异同

相同点

  • 都基于 Proxy 实现响应式
  • 都是声明式 UI 框架
  • 都支持组件化开发

不同点

  • 深度代理:Vue 3 自动深度代理,ArkTS 需要 @Observed
  • 更新策略:Vue 3 有模板编译优化,ArkTS 依赖依赖收集
  • API 设计:Vue 3 有 ref/reactive,ArkTS 统一用装饰器

8.3 与 React 的区别

响应式机制

  • ArkTS:自动追踪依赖,数据变化自动更新
  • React:手动调用 setState 触发更新

心智模型

  • ArkTS:数据驱动,类似 Vue
  • React:状态管理,需要理解 Fiber 协调

代码对比

// ArkTS
@State count: number = 0
this.count++  // 自动更新// React
const [count, setCount] = useState(0)
setCount(count + 1)  // 手动触发

🎤 九、面试口述版本

9.1 基础版本(30 秒)

"@State 的底层原理主要基于响应式系统,核心是三个步骤:数据劫持、依赖收集和触发更新

当我们用 @State 装饰一个变量时,框架会用 Proxy 把它包装成一个代理对象。当组件的 build 方法执行时,访问这个状态变量会触发 getter,框架就会记录下’哪个组件用了这个状态’,这叫依赖收集

当我们修改状态变量时,会触发 setter,框架就会通知所有依赖这个状态的组件重新渲染,这样就实现了数据驱动 UI 自动更新的效果。"

9.2 进阶版本(1-2 分钟)

开场

“好的,我从底层机制来讲解一下 @State。它的核心是响应式系统,可以用一个公式概括:数据劫持 + 依赖收集 + 派发更新 = 响应式。”

第一点:数据劫持

"首先是数据劫持。当我们声明 @State count: number = 0 时,框架底层会用 ES6 的 Proxy 对象把这个变量包装起来。

Proxy 可以拦截对象的读写操作,所以当我们访问 this.count 时会触发 getter,当我们修改 this.count++ 时会触发 setter。这就给了框架一个’钩子’,让它能感知到状态的读取和修改。"

第二点:依赖收集

"第二步是依赖收集。这个过程发生在组件的 build 方法执行时。

框架内部有一个全局变量记录’当前正在渲染的组件是谁’。当 build 方法执行,代码访问到 this.count 时,getter 就会被触发,这时框架就会记录:‘当前这个组件依赖了 count 这个状态’。

这个依赖关系会存储在一个 WeakMap 数据结构里,形成一个映射:状态 → 使用它的组件集合。"

第三点:触发更新

"第三步是触发更新。当我们在事件回调中修改状态,比如 this.count++,setter 就会被触发。

setter 里面会做两件事:

  1. 浅比较:先对比新旧值是否相同,如果相同就不触发更新,这是一个性能优化
  2. 派发更新:如果值确实变了,就从刚才的依赖映射表里找出所有依赖这个状态的组件,然后通知它们’你需要重新渲染了’

这里还有一个优化:框架会批量更新。如果我连续修改多个状态,框架不会立即渲染多次,而是把这些更新任务收集起来,在下一个 requestAnimationFrame 的时候统一渲染,这样可以避免重复渲染,提高性能。"

总结

“所以整个流程就是:初始化时用 Proxy 劫持数据 → 渲染时收集依赖关系 → 修改时精确通知相关组件更新。这样就实现了数据变化自动驱动 UI 更新的效果。”

9.3 深度版本(追问回答)

如果问:为什么只能观察第一层属性变化?

"这是因为性能和复杂度的权衡

当我们声明一个对象状态时,框架只会对第一层属性进行 Proxy 代理。如果要递归代理所有嵌套对象,会带来以下问题:

  1. 性能开销大:每个嵌套对象都要创建 Proxy,深层嵌套会导致性能下降
  2. 内存占用高:每个 Proxy 对象都需要维护依赖关系
  3. 复杂度高:需要处理循环引用、数组变化等边界情况

所以 ArkTS 的设计是:浅层响应式 + 按需深度观察。如果确实需要观察嵌套对象,可以使用 @Observed 和 @ObjectLink 装饰器,对特定的类进行深度代理。这样既保证了性能,又提供了灵活性。"

如果问:和 Vue/React 有什么区别?

"从响应式原理来说:

ArkTS 和 Vue 3 都是基于 Proxy 实现响应式,但更新粒度和实现细节不同:

  • ArkTS 是组件级精确更新:只更新用到该状态的组件
  • Vue 3 也是组件级更新,但会自动深度代理嵌套对象

React 则完全不同:

  • React 没有响应式系统,需要手动调用 setState
  • 更新机制是基于 Fiber 架构的协调算法
  • 采用虚拟 DOM diff 来决定更新范围

从开发者角度看,ArkTS 的 @State 更像 Vue 的响应式,写起来更简洁,不需要像 React 那样手动管理状态更新。"

如果问:数组修改为什么要用特定方法?

"这涉及到 JavaScript 的特性和 Proxy 的拦截机制。

当我们直接通过索引修改数组元素,比如 this.items[0] = 'new',虽然 Proxy 能拦截到这个 set 操作,但这个操作不会改变数组的引用地址

框架在做依赖追踪时,如果发现引用地址没变,为了性能考虑,可能会认为数组没有变化(浅比较)。所以推荐使用数组的变更方法,比如:

  • pushsplicepop 等会触发完整的响应式更新
  • 或者创建新数组 this.items = [...this.items],改变引用地址

这样能确保框架正确识别到数据变化并触发更新。"

9.4 加分项(展现深度思考)

"从设计模式角度看,@State 的实现综合运用了:

  • 代理模式(Proxy):拦截数据访问
  • 观察者模式:组件订阅状态变化
  • 发布-订阅模式:状态变化时通知订阅者

从架构角度看,这种响应式设计的优势是:

  1. 开发效率高:不需要手动操作 DOM
  2. 心智负担低:只需关注数据,UI 自动同步
  3. 性能可控:通过批量更新和精确更新优化性能

实际应用中需要注意

  • 避免在 build 中修改状态(会导致无限循环)
  • 理解同步数据更新 vs 异步 UI 渲染
  • 合理使用计算属性减少不必要的状态"

💡 十、常见面试追问及回答

Q1: 更新是同步的还是异步的?

回答

"数据更新是同步的,UI 渲染是异步的

当我执行 this.count++,count 的值立即就变了,马上 console.log(this.count) 能看到新值。但 UI 更新要等到下一个动画帧(requestAnimationFrame),这是为了批量处理多个状态变化,避免频繁渲染导致性能问题。"

代码示例

this.count++;
console.log(this.count); // ✅ 立即输出新值
// 但此时 UI 还没更新

Q2: 如何优化性能?

回答

"主要有三个方向:

  1. 减少状态数量:能计算得出的就不要存成状态
  2. 合理拆分组件:让组件只依赖必要的状态,减少不必要的重渲染
  3. 避免深层嵌套:深层对象用 @Observed,不要让一个大对象触发整个组件更新"

代码示例

// ✅ 好的做法
@State items: Item[] = []getTotal() {return this.items.length  // 计算属性
}// ❌ 不好的做法
@State items: Item[] = []
@State total: number = 0  // 冗余状态

Q3: 能手动触发更新吗?

回答

"ArkTS 的设计理念是数据驱动,不需要也不推荐手动触发更新。如果发现 UI 没更新,通常是因为:

  • 修改了嵌套对象的深层属性
  • 直接修改了数组的索引

解决方法是使用正确的数据修改方式,或者使用 @Observed/@ObjectLink。"

Q4: Proxy 和 Object.defineProperty 有什么区别?

回答

"这是 Vue 2 和 Vue 3 响应式实现的主要区别:

Object.defineProperty(Vue 2):

  • 只能劫持已有属性
  • 无法检测属性的添加和删除
  • 数组需要特殊处理
  • 需要遍历对象的每个属性

Proxy(Vue 3、ArkTS):

  • 可以劫持整个对象
  • 可以检测属性的添加和删除
  • 原生支持数组
  • 性能更好,惰性观察

这就是为什么现代框架都选择 Proxy 的原因。"

Q5: 如何避免不必要的重渲染?

回答

"有以下几个技巧:

  1. 拆分组件:让组件只依赖必要的状态
  2. 使用计算属性:避免在 build 中进行复杂计算
  3. 合理使用 @Prop:展示型组件用 @Prop,不需要响应式
  4. 避免大对象:大对象的任何属性变化都会触发更新

核心思想是:最小化依赖,精确化更新。"


⚠️ 十一、实际应用注意事项

11.1 避免在 build 中修改状态

// ❌ 错误:会导致无限循环
build() {Column() {if (this.count < 10) {this.count++  // 触发重新渲染 → 再次执行这里 → 无限循环}}
}// ✅ 正确:在事件回调中修改
build() {Column() {Button('增加').onClick(() => {if (this.count < 10) {this.count++  // ✅ 正确}})}
}

11.2 理解更新时机

this.count++;
console.log(this.count); // ✅ 立即看到新值
console.log(this.getUIText()); // ❌ UI 还没更新// 如果需要在 UI 更新后执行操作
requestAnimationFrame(() => {console.log("UI 已更新");
});

11.3 数组操作的正确方式

@State items: string[] = ['a', 'b']// ❌ 错误方式
this.items[0] = 'c'                     // 不会触发更新
this.items.length = 0                   // 不会触发更新// ✅ 正确方式
this.items.splice(0, 1, 'c')            // 使用数组方法
this.items = [...this.items]            // 创建新数组
this.items = this.items.filter(...)     // 返回新数组的方法

11.4 对象修改的正确方式

@State user: User = { name: 'Tom', age: 20 }// ❌ 错误方式(某些情况下)
this.user.name = 'Jerry'  // 第一层可以,但不推荐// ✅ 推荐方式
this.user = { ...this.user, name: 'Jerry' }  // 创建新对象// 对于嵌套对象
this.user = {...this.user,address: {...this.user.address,city: '上海'}
}

11.5 性能优化技巧

// ✅ 使用计算属性
@State items: Item[] = []getTotal(): number {return this.items.reduce((sum, item) => sum + item.price, 0)
}build() {Text(`总价: ${this.getTotal()}`)  // 调用方法
}// ❌ 在 build 中直接计算(每次渲染都会执行)
build() {Text(`总价: ${this.items.reduce((sum, item) => sum + item.price, 0)}`)
}

📚 十二、设计模式分析

12.1 代理模式(Proxy Pattern)

作用:拦截和控制对目标对象的访问。

const proxy = new Proxy(target, {get(target, key) {// 拦截读取操作console.log(`读取 ${key}`);return target[key];},set(target, key, value) {// 拦截写入操作console.log(`设置 ${key} = ${value}`);target[key] = value;return true;},
});

12.2 观察者模式(Observer Pattern)

作用:对象之间的一对多依赖关系。

class Subject {private observers: Set<Observer> = new Set();subscribe(observer: Observer) {this.observers.add(observer);}notify() {this.observers.forEach((observer) => observer.update());}
}

12.3 发布-订阅模式(Pub-Sub Pattern)

作用:通过事件中心解耦发布者和订阅者。

class EventBus {private events: Map<string, Set<Function>> = new Map();on(event: string, callback: Function) {if (!this.events.has(event)) {this.events.set(event, new Set());}this.events.get(event)!.add(callback);}emit(event: string, data: any) {this.events.get(event)?.forEach((callback) => callback(data));}
}

🎯 十三、核心要点总结

13.1 三大核心机制

  1. 数据劫持(Proxy)

    • 拦截 get/set 操作
    • 给框架提供感知数据变化的能力
  2. 依赖收集(Track)

    • 在 build 执行时记录依赖关系
    • 建立状态与组件的映射
  3. 触发更新(Trigger)

    • 状态变化时通知相关组件
    • 批量更新提升性能

13.2 关键特性

  • ✅ 自动追踪依赖
  • ✅ 精确更新组件
  • ✅ 批量渲染优化
  • ✅ 浅比较避免无效更新
  • ⚠️ 只观察第一层属性

13.3 最佳实践

  1. 状态最小化:能计算的不要存
  2. 合理拆分组件:减少不必要的依赖
  3. 正确修改数据:使用数组方法或创建新对象
  4. 避免深层嵌套:使用 @Observed/@ObjectLink
  5. 使用计算属性:提取复杂计算逻辑

✅ 面试回答检查清单

在面试前,确保你能清楚回答以下问题:

  • 什么是 Proxy?它如何工作?
  • 依赖收集发生在什么时候?如何实现?
  • 为什么能实现自动更新?原理是什么?
  • 为什么只能观察第一层属性?
  • 批量更新是怎么实现的?
  • 和 Vue/React 的响应式有什么区别?
  • 数据更新是同步还是异步?
  • 如何优化性能?
  • 常见的错误用法有哪些?

📖 十四、参考资料

14.1 相关概念

  • Proxy:ES6 元编程特性
  • WeakMap:弱引用映射表
  • requestAnimationFrame:浏览器渲染 API
  • Virtual DOM:虚拟 DOM 树
  • Diff Algorithm:差异比对算法

14.2 延伸学习

  • Vue 3 响应式原理
  • React Fiber 架构
  • 观察者模式与发布-订阅模式
  • 前端性能优化
  • 组件设计模式

🎬 总结

@State 的底层原理可以用一句话概括

通过 Proxy 劫持数据访问,在 build 执行时收集依赖,在数据变化时精确通知相关组件,最后通过批量渲染机制更新 UI,从而实现了数据驱动 UI 自动更新的声明式编程模型。

核心价值

  1. 提升开发效率:不需要手动操作 DOM
  2. 降低心智负担:只需关注数据逻辑
  3. 保证性能:精确更新 + 批量渲染
  4. 易于维护:数据流清晰,易于调试

记住:理解 @State 的底层原理,不仅能帮助你写出更好的代码,也能在面试中展现你的技术深度!


祝你面试顺利!加油! 🚀

http://www.dtcms.com/a/531743.html

相关文章:

  • Post-training-of-llms TASK05
  • 项目实战复盘:基于仓颉语言的鸿蒙智能导航助手(HarmonyNav)
  • Datawhale AI秋训营|RAG 多模态相关 TASK1 /Task 2 Baseline笔记(待优化)
  • 龙华新区城市建设局网站网页布局是指什么
  • 【系统分析师】高分论文:论需求分析在项目中的应用(智慧市场监管项目)
  • LeetCode128. 最长连续序列
  • go-stream(一些常用命令介绍,以及在go-tcp中使用)
  • 中职 网站建设与管理海口快速建站公司推荐
  • Qt TCP 网络通信详解(笔记)
  • RandLA-net-pytorch 复现
  • Rocky 9 安装 Elasticsearch分布式集群基于非安全特性
  • 使用现代C++构建高效日志系统的分步指南
  • Nacos 环境搭建:从单机开发到集群生产部署
  • OpenWrt | 实现限制只有指定设备才能访问 luci 和 使用 SSH 等方式管理设备的方法
  • 数据库圣经-----最终章JDBC
  • 小贷做网站客户推广渠道有哪些
  • SAP SD交货单明细查询接口分享
  • Java Spring原理 --- Bean的作用域,Bean的生命周期,Spring自动配置
  • TCP三次握手与四次挥手通俗理解
  • 电商网站如何设计内容能源产品网站建设多少钱
  • 门户网站的发布特点网站子栏目设计
  • 赣州企业网站建设公司苏州网站定制公司哪家好
  • 网页设计与网站建设的课后习题答案外贸 企业网站 建设
  • 呼伦贝尔网站建设平台海口网站运营托管费用
  • 网站外包费用怎么做分录个人做广播网站需要注意什么
  • 教育网站设计欣赏建一个公司网站要多少钱
  • 广州快速建站公司推荐做关于植物的网站
  • 厦门网站建设哪家专业黔东南小程序开发公司
  • 网站开发公司安心加盟苏州排名搜索优化
  • 响应式网站公司怎么做网站