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

React16源码: React中event事件对象的创建过程源码实现

event 对象


1 ) 概述

  • 在生产事件对象的过程当中,要去调用每一个 possiblePlugin.extractEvents 方法
  • 现在单独看下这里面的细节过程,即如何去生产这个事件对象的过程

2 )源码

定位到 packages/events/EventPluginHub.js#L172

function extractEvents(topLevelType: TopLevelType,targetInst: null | Fiber,nativeEvent: AnyNativeEvent,nativeEventTarget: EventTarget,
): Array<ReactSyntheticEvent> | ReactSyntheticEvent | null {let events = null;for (let i = 0; i < plugins.length; i++) {// Not every plugin in the ordering may be loaded at runtime.const possiblePlugin: PluginModule<AnyNativeEvent> = plugins[i];if (possiblePlugin) {// 这里要去调用 每个 plugin 的 extractEvents 方法const extractedEvents = possiblePlugin.extractEvents(topLevelType,targetInst,nativeEvent,nativeEventTarget,);if (extractedEvents) {events = accumulateInto(events, extractedEvents);}}}return events;
}

注意这里的 possiblePlugin.extractEvents 我们专门专注下 changeEvent 的这个方法
定位到 packages/react-dom/src/events/ChangeEventPlugin.js#L263

const ChangeEventPlugin = {eventTypes: eventTypes,_isInputEventSupported: isInputEventSupported,// 注意这里extractEvents: function(topLevelType,targetInst,nativeEvent,nativeEventTarget,) {// 在这个方法里面,拿到了 targetNode// 因为这边传进来的呢是一个发布对象, 所以要通过这种方法拿到它的 nodeconst targetNode = targetInst ? getNodeFromInstance(targetInst) : window;// 然后,要经过一系列的判断,主要去赋值了不同的 getTargetInstFunclet getTargetInstFunc, handleEventFunc;// 如果有返回这个instance,那么我们就可以去创建这个event了,这是什么意思呢?// 就是说我们这个 event plugin, 在所有的事件触发的过程当中,这个plugin都会被循环调用的// 它是没有通过事件名称来调用不同的plugin这么一个设置的// 所以这个判断是要放在每个pluggin里面自己去做。就是说根据这次触发的具体事件是什么?// 来判断我们要不要为它创建一个event,因为每个plugin在每次事件触发都会被调用// 如果我们都生成事件,那么明显是不对的,肯定要对自己这个 plugin 关心的事件来去为它生成这个事件if (shouldUseChangeEvent(targetNode)) {getTargetInstFunc = getTargetInstForChangeEvent;} else if (isTextInputElement(targetNode)) {if (isInputEventSupported) {getTargetInstFunc = getTargetInstForInputOrChangeEvent;} else {// polyfill 的这些先忽略getTargetInstFunc = getTargetInstForInputEventPolyfill;handleEventFunc = handleEventsForInputEventPolyfill;}} else if (shouldUseClickEvent(targetNode)) {getTargetInstFunc = getTargetInstForClickEvent;}// 基于类型,得到了最终的处理函数if (getTargetInstFunc) {const inst = getTargetInstFunc(topLevelType, targetInst);if (inst) {// 创建 eventconst event = createAndAccumulateChangeEvent(inst,nativeEvent,nativeEventTarget,);return event;}}if (handleEventFunc) {handleEventFunc(topLevelType, targetNode, targetInst);}// When blurring, set the value attribute for number inputsif (topLevelType === TOP_BLUR) {handleControlledInputBlur(targetNode);}},
};
  • 进入 shouldUseChangeEvent

    function shouldUseChangeEvent(elem) {const nodeName = elem.nodeName && elem.nodeName.toLowerCase();return (nodeName === 'select' || (nodeName === 'input' && elem.type === 'file'));
    }
    
    • 这个判断其实就是主要来判断一下我们这个节点上面是否有changeevent
    • 并且是否应该用 change event 来进行一个触发
    • 因为react当中的 onchange 事件,其实它是封装了各种不同的事件的
    • 比如说对于像我们输入文本的 input type=‘text’ 的一个情况
    • 正常来讲,应该绑定的是input事件,而不是change事件
    • 因为change事件在有些浏览器里面要等到这个输入框,blur的时候,才会真正触发这个change事件
    • 对于input事件是我们每次有输入变化的时候,都会触发的这个事件
    • 所以对于 select 还有 input type=‘file’, 它们的change是非常明显的,就是等到它们有内容变化的时候就会触发
    • 因为file是我们选择了一个文件之后,它就会触发change事件
    • 而select我们选择了某一个 option 之后,它也会触发这个change事件
    • 所以对于这种节点,我们可以直接使用onchange来进行一个绑定
    • 这时候, getTargetInstFunc = getTargetInstForChangeEvent; 进入 getTargetInstForChangeEvent
      function getTargetInstForChangeEvent(topLevelType, targetInst) {// TOP_CHANGE 就是 changeif (topLevelType === TOP_CHANGE) {return targetInst;}
      }// 注意 另外的文件中
      // packages/react-dom/src/events/DOMTopLevelEventTypes.js#L41
      export const TOP_CHANGE = unsafeCastStringToDOMTopLevelType('change');// packages/events/TopLevelEventTypes.js#L27
      export function unsafeCastStringToDOMTopLevelType(topLevelType: string,
      ): DOMTopLevelEventType {return topLevelType;
      }
      
  • 进入 isTextInputElement

    const supportedInputTypes: {[key: string]: true | void} = {color: true,date: true,datetime: true,'datetime-local': true,email: true,month: true,number: true,password: true,range: true,search: true,tel: true,text: true,time: true,url: true,week: true,
    };function isTextInputElement(elem: ?HTMLElement): boolean {// 获取 nodeNameconst nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();// 判断在 input 的时候,是否符合支持的type类型if (nodeName === 'input') {return !!supportedInputTypes[((elem: any): HTMLInputElement).type];}if (nodeName === 'textarea') {return true;}return false;
    }
    
    • 这里就是一个 boolean 类型的函数进行类型判断的
  • 进入 getTargetInstForInputOrChangeEvent

    // packages/react-dom/src/events/ChangeEventPlugin.js#L229
    function getTargetInstForInputOrChangeEvent(topLevelType, targetInst) {if (topLevelType === TOP_INPUT || topLevelType === TOP_CHANGE) {return getInstIfValueChanged(targetInst);}
    }// packages/react-dom/src/events/ChangeEventPlugin.js#L106
    function getInstIfValueChanged(targetInst) {const targetNode = getNodeFromInstance(targetInst);if (inputValueTracking.updateValueIfChanged(targetNode)) {return targetInst;}
    }// packages/react-dom/src/client/ReactDOMComponentTree.js#L69
    export function getNodeFromInstance(inst) {if (inst.tag === HostComponent || inst.tag === HostText) {// In Fiber this, is just the state node right now. We assume it will be// a host component or host text.return inst.stateNode;}// Without this first invariant, passing a non-DOM-component triggers the next// invariant for a missing parent, which is super confusing.invariant(false, 'getNodeFromInstance: Invalid argument.');
    }
    
  • 进入 shouldUseClickEvent

    /*** SECTION: handle `click` event*/// checkbox 和 radio 的特殊处理
    function shouldUseClickEvent(elem) {// Use the `click` event to detect changes to checkbox and radio inputs.// This approach works across all browsers, whereas `change` does not fire// until `blur` in IE8.const nodeName = elem.nodeName;return (nodeName &&nodeName.toLowerCase() === 'input' &&(elem.type === 'checkbox' || elem.type === 'radio'));
    }
    
  • 进入 getTargetInstForClickEvent

    function getTargetInstForClickEvent(topLevelType, targetInst) {if (topLevelType === TOP_CLICK) {return getInstIfValueChanged(targetInst);}
    }
    
  • 进入 createAndAccumulateChangeEvent 创建事件对象

    function createAndAccumulateChangeEvent(inst, nativeEvent, target) {const event = SyntheticEvent.getPooled(eventTypes.change,inst,nativeEvent,target,);event.type = 'change';// Flag this event loop as needing state restore.enqueueStateRestore(target);accumulateTwoPhaseDispatches(event);return event;
    }
    
    • 这个函数就是具体生成这个事件的一个过程
      • 可以看到这个事件,接收了一个 inst,然后传入了 nativeEvent,并且再传入 target
      • 然后,通过 SyntheticEvent.getPooled,就是说在react当中所有的事件对象是通过一个 pool 来进行一个存储的
      • 比如说我们为所有的event创建了十个event对象
      • 每一次有新的一个event进来的时候,从这个pool里面拿出一个设置一些事件以及对应的一些值之后
      • 去触发每一个事件的监听方法,然后去使用这个 event 对象
      • 这个 event 对象使用完了之后,又会归还到这个 pool 里面
      • 也就是一个 能够减少 对象声明 以及 对象回收 的一个性能开销
      • 然后拿到了这个 event 之后,给它设置了 type 是 change
      • 之后执行两个函数 enqueueStateRestoreaccumulateTwoPhaseDispatches
    • 进入 SyntheticEvent.getPooled
      // packages/events/SyntheticEvent.js#L335
      function addEventPoolingTo(EventConstructor) {EventConstructor.eventPool = [];EventConstructor.getPooled = getPooledEvent; // 注意这里EventConstructor.release = releasePooledEvent;
      }
      
      • 进入 getPooledEvent
        // packages/events/SyntheticEvent.js#L300
        function getPooledEvent(dispatchConfig, targetInst, nativeEvent, nativeInst) {const EventConstructor = this;// 存在pollif (EventConstructor.eventPool.length) {const instance = EventConstructor.eventPool.pop();EventConstructor.call(instance,dispatchConfig,targetInst,nativeEvent,nativeInst,);return instance;}// pool 里面没有,则创建一个新的return new EventConstructor(dispatchConfig,targetInst,nativeEvent,nativeInst,);
        }
        
        • 关于 EventConstructor
          • 首先在 packages/events/SyntheticEvent.js#L62 中的 SyntheticEvent 构造方法
          • 要理解这个过程, 首先在这个js里面先声明了一个叫做 SyntheticEvent 这么一个方法
          • 这个方法它是一个constructor 方法, 在这个方法里面去声明事件相关的各种属性
          • 重点关注它的事件的一个触发的过程以及生产的过程,所以只关心它的 pool 的处理过程
        • 这里的 this 就是 SyntheticEvent
          • 在这个构造方法的原型链上也有一大堆的东西,对事件对象进行一个封装和扩展
        • 注意这里的 EventConstructor.callnew EventConstructor 都达到同一个目的
    • 进入 enqueueStateRestore
      export function enqueueStateRestore(target: EventTarget): void {// 判断了这个 restoreTarget 公共变量是否存在if (restoreTarget) {if (restoreQueue) {restoreQueue.push(target);} else {restoreQueue = [target];}// restoreTarget 不存在,则对其进行赋值} else {restoreTarget = target;}
      }
      
      • 作用是处理,如果setState之后,这个 state 对应的 input 的 value 是不一样的
      • 要把这个值进行一个回滚
    • 进入 accumulateTwoPhaseDispatches 这个方法才是真正要去从每个节点上面去获取它的 listener 的一个过程
      // packages/events/EventPropagators.js#L115
      export function accumulateTwoPhaseDispatches(events) {// 其实就是对 events 这个数组里面,它的每一个节点去调用这个方法forEachAccumulated(events, accumulateTwoPhaseDispatchesSingle);
      }function accumulateTwoPhaseDispatchesSingle(event) {// 存在 phasedRegistrationNames 则调用 traverseTwoPhaseif (event && event.dispatchConfig.phasedRegistrationNames) {traverseTwoPhase(event._targetInst, accumulateDirectionalDispatches, event);}
      }// 在 event 对象上 插入 listener 的过程
      function accumulateDirectionalDispatches(inst, phase, event) {// 忽略if (__DEV__) {warningWithoutStack(inst, 'Dispatching inst must not be null');}// 获取 listenerconst listener = listenerAtPhase(inst, event, phase);if (listener) {// 注意这里 event._dispatchListeners 和 下面的 event._dispatchInstances 保持两者一一对应的关系event._dispatchListeners = accumulateInto(event._dispatchListeners,listener,);event._dispatchInstances = accumulateInto(event._dispatchInstances, inst);}
      }function listenerAtPhase(inst, event, propagationPhase: PropagationPhases) {const registrationName =event.dispatchConfig.phasedRegistrationNames[propagationPhase];return getListener(inst, registrationName);
      }// packages/events/EventPluginHub.js#L126
      export function getListener(inst: Fiber, registrationName: string) {let listener;// TODO: shouldPreventMouseEvent is DOM-specific and definitely should not// live here; needs to be moved to a better place soonconst stateNode = inst.stateNode;if (!stateNode) {// Work in progress (ex: onload events in incremental mode).return null;}const props = getFiberCurrentPropsFromNode(stateNode); // 从 dom tree上获取 propsif (!props) {// Work in progress.return null;}listener = props[registrationName];if (shouldPreventMouseEvent(registrationName, inst.type, props)) {return null;}invariant(!listener || typeof listener === 'function','Expected `%s` listener to be a function, instead got a value of `%s` type.',registrationName,typeof listener,);return listener;
      }// packages/shared/ReactTreeTraversal.js#L86
      export function traverseTwoPhase(inst, fn, arg) {const path = [];// 找到所有上层节点,并存入 pathwhile (inst) {path.push(inst);inst = getParent(inst);}// 下面是核心,执行两个阶段的回调,捕获和冒泡let i;for (i = path.length; i-- > 0; ) { // 注意这个 i 的顺序// path[i]:节点, arg:eventfn(path[i], 'captured', arg); // captured 是从 window 向下触发的}for (i = 0; i < path.length; i++) { // 注意这个 i 的顺序fn(path[i], 'bubbled', arg); // bubbled 是从下向 window 方向的}// 基于上面两个 循环// 这样的话,就不需要在 event 对象上面单独维护 capture 的这个事件的它的一个数组// 还有 bubble 的事件的一个速度,只需要放在同一个数组里面,然后按照这个数组的顺序去触发就可以了
      }
      // packages/shared/ReactTreeTraversal.js#L10
      function getParent(inst) {do {inst = inst.return;// TODO: If this is a HostRoot we might want to bail out.// That is depending on if we want nested subtrees (layers) to bubble// events to their parent. We could also go through parentNode on the// host node but that wouldn't work for React Native and doesn't let us// do the portal feature.} while (inst && inst.tag !== HostComponent);if (inst) {return inst; // 返回的 inst 是一个 HostComponent}return null;
      }
      
  • 以上,生产 event 对象,然后去挂载它的事件,这个过程是非常的复杂的

  • react 团队把整个事件系统去重新抽象的这么一个过程,而且设计的超级复杂

  • 这一套东西只是非常适合react,在其他框架要使用这类event库,会有很大的成本

  • 目前为止,通过 ChangeEventPlugin 来了解了整个 event 对象的处理过程

  • 后续其他的类似事件的处理逻辑到后面都是一样的

  • 但每一个 plugin 或多或少有一些自己的一些区别,这里不再赘述

相关文章:

React16源码: React中event事件对象的创建过程源码实现

event 对象 1 &#xff09; 概述 在生产事件对象的过程当中&#xff0c;要去调用每一个 possiblePlugin.extractEvents 方法现在单独看下这里面的细节过程&#xff0c;即如何去生产这个事件对象的过程 2 &#xff09;源码 定位到 packages/events/EventPluginHub.js#L172 f…...

深度学习(12)--Mnist分类任务

一.Mnist分类任务流程详解 1.1.引入数据集 Mnist数据集是官方的数据集&#xff0c;比较特殊&#xff0c;可以直接通过%matplotlib inline自动下载&#xff0c;博主此处已经完成下载&#xff0c;从本地文件中引入数据集。 设置数据路径 from pathlib import Path# 设置数据路…...

AI工具【OCR 01】Java可使用的OCR工具Tess4J使用举例(身份证信息识别核心代码及信息提取方法分享)

Java可使用的OCR工具Tess4J使用举例 1.简介1.1 简单介绍1.2 官方说明 2.使用举例2.1 依赖及语言数据包2.2 核心代码2.3 识别身份证信息2.3.1 核心代码2.3.2 截取指定字符2.3.3 去掉字符串里的非中文字符2.3.4 提取出生日期&#xff08;待优化&#xff09;2.3.5 实测 3.总结 1.简…...

【MySQL复制】半同步复制

介绍 除了内置的异步复制之外&#xff0c;MySQL 5.7 还支持通过插件实现的半同步复制接口。本节讨论半同步复制的概念及其工作原理。接下来的部分将涵盖与半同步复制相关的管理界面&#xff0c;以及如何安装、配置和监控它。 异步复制 MySQL 复制默认是异步的。源服务器将事…...

PHP面试知识点--echo、print、print_r、var_dump区别

echo、print、print_r、var_dump 区别 echo 输出单个或多个字符&#xff0c;多个使用逗号分隔无返回值 echo "String 1", "String 2";print 只可以输出单个字符返回1&#xff0c;因此可用于表达式 print "Hello"; if ($expr && pri…...

centos 7 部署若依前后端分离项目

目录 一、新建数据库 二、修改需求配置 1.修改数据库连接 2.修改Redis连接信息 3.文件路径 4.日志存储路径调整 三、编译后端项目 四、编译前端项目 1.上传项目 2.安装依赖 3.构建生产环境 五、项目部署 1.创建目录 2.后端文件上传 3. 前端文件上传 六、服务启…...

RFID手持终端_智能pda手持终端设备定制方案

手持终端是一款多功能、适用范围广泛的安卓产品&#xff0c;具有高性能、大容量存储、高端扫描头和全网通数据连接能力。它能够快速平稳地运行&#xff0c;并提供稳定的连接表现和快速的响应时&#xff0c;适用于医院、物流运输、零售配送、资产盘点等苛刻的环境。通过快速采集…...

51单片机学习——矩阵按键

目录 gitee链接 小程吃饭饭 (xiaocheng-has-a-meal) - Gitee.comhttps://gitee.com/xiaocheng-has-a-meal 1.图~突突突突突 矩阵键盘原理图 矩阵键盘的实物图 2.矩阵键盘 引入~啦啦啦啦啦 原理~沥沥沥沥沥 代码~嗷嗷嗷嗷嗷 【1】延时函数 【2】 LCD1602 【3】检测按…...

重写Sylar基于协程的服务器(1、日志模块的架构)

重写Sylar基于协程的服务器&#xff08;1、日志模块的架构&#xff09; 重写Sylar基于协程的服务器系列&#xff1a; 重写Sylar基于协程的服务器&#xff08;0、搭建开发环境以及项目框架 || 下载编译简化版Sylar&#xff09; 重写Sylar基于协程的服务器&#xff08;1、日志模…...

ElementUI Form:Radio 单选框

ElementUI安装与使用指南 Radio 单选框 点击下载learnelementuispringboot项目源码 效果图 el-radio.vue &#xff08;Radio 单选框&#xff09;页面效果图 项目里el-radio.vue代码 <script> export default {name: el_radio,data() {return {radio: 1,radio2: 2,ra…...

react-activation实现缓存,且部分页面刷新缓存,清除缓存

1.安装依赖 npm i -S react-activation2.使用AliveScope 包裹根组件 import { AliveScope } from "react-activation" <AliveScope><Router><Switch><Route exact path"/" render{() > <Redirect to"/login" push …...

idea 中 tomcat 乱码问题修复

之前是修改 Tomcat 目录下 conf/logging.properties 的配置&#xff0c;将 UTF-8 修改为 GBK&#xff0c;现在发现不用这样修改了。只需要修改 IDEA 中 Tomcat 的配置就可以了。 修改IDEA中Tomcat的配置&#xff1a;添加-Dfile.encodingUTF-8 本文结束...

Modbus协议学习第七篇之libmodbus库API介绍(modbus_write_bits等)

写在前面 在第六篇中我们介绍了基于libmodbus库的演示代码&#xff0c;那本篇博客就详细介绍一下第六篇的代码中使用的基于该库的API函数。另各位读者&#xff0c;Modbus相关知识受众较少&#xff0c;如果觉得我的专栏文章有帮助&#xff0c;请一定点个赞&#xff0c;在此跪谢&…...

第九节HarmonyOS 常用基础组件13-TimePicker

1、描述 时间选择组件&#xff0c;根据指定参数创建选择器&#xff0c;支持选择小时以及分钟。默认以24小时的时间区间创建滑动选择器。 2、接口 TimePicker(options?: {selected?: Date}) 3、参数 selected - Date - 设置选中项的时间。默认是系统当前的时间。 4、属性…...

力扣刷题-55.跳跃游戏

给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 class Solution { publ…...

Ruby安装演示教程

安装 Ruby 的过程会根据您的操作系统&#xff08;如 Windows、MacOS、Linux&#xff09;而有所不同。以下是在这些主要平台上安装 Ruby 的基本指南。 在 Windows 上安装 Ruby 下载 Ruby Installer&#xff1a;访问 RubyInstaller 官方网站下载适合您系统的 Ruby Installer 版…...

前端使用vue-simple-uploader进行分片上传

目录 一、安装vue-simple-uploader 二、在vue中使用 一、安装vue-simple-uploader npm install vue-simple-uploader --save main.js初始化vue-simple-uploader import uploader from vue-simple-uploaderVue.use(uploader) common/config文件 export const ACCEPT_CONF…...

Java 源代码中常见的数据类型

在Java源代码中&#xff0c;常见的数据类型包括基本数据类型&#xff08;Primitive Data Types&#xff09;和引用数据类型&#xff08;Reference Data Types&#xff09;。这些数据类型在Java中用于存储不同种类的数据&#xff0c;如整数、小数、字符、布尔值以及对象等。 1.…...

Web3行业研究逐步加强,“链上数据”缘何成为关注焦点?

据中国电子报报道&#xff0c;近日&#xff0c;由中关村区块链产业联盟指导&#xff0c;中国信息通信研究院牵头&#xff0c;欧科云链控股有限公司参与编写的《全球Web3产业全景与发展趋势研究报告&#xff08;2023年&#xff09;》正式发布。研究报告通过全面追踪国内外Web3产…...

逸学区块链【solidity】真随机数

参考Get a Random Number | Chainlink Documentation 但是很贵&#xff0c;价格 Gas Price&#xff1a;当前gas价格&#xff0c;根据网络状况而波动。Callback gas &#xff1a;返回您所请求的随机值时&#xff0c;回调请求消耗的gas 量。验证gas &#xff1a;量gas 用于验证…...

生成xcframework

打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式&#xff0c;可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查

在对接支付宝API的时候&#xff0c;遇到了一些问题&#xff0c;记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...

前端倒计时误差!

提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

嵌入式学习笔记DAY33(网络编程——TCP)

一、网络架构 C/S &#xff08;client/server 客户端/服务器&#xff09;&#xff1a;由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序&#xff0c;负责提供用户界面和交互逻辑 &#xff0c;接收用户输入&#xff0c;向服务器发送请求&#xff0c;并展示服务…...

QT3D学习笔记——圆台、圆锥

类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体&#xff08;对象或容器&#xff09;QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质&#xff08;定义颜色、反光等&#xff09;QFirstPersonC…...

深入浅出Diffusion模型:从原理到实践的全方位教程

I. 引言&#xff1a;生成式AI的黎明 – Diffusion模型是什么&#xff1f; 近年来&#xff0c;生成式人工智能&#xff08;Generative AI&#xff09;领域取得了爆炸性的进展&#xff0c;模型能够根据简单的文本提示创作出逼真的图像、连贯的文本&#xff0c;乃至更多令人惊叹的…...

用鸿蒙HarmonyOS5实现中国象棋小游戏的过程

下面是一个基于鸿蒙OS (HarmonyOS) 的中国象棋小游戏的实现代码。这个实现使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chinesechess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├──…...