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

Flutter-Widget-学习笔记

  Widget 是整个视图描述的基础。

 参考:https://docs.flutter.dev/resources/architectural-overview

 Widget 到底是什么呢?

Widget 是 Flutter 功能的抽象描述,是视图的配置信息,同样也是数据的映射,是 Flutter 开发框架中最基本的概念。前端框架中常见的名词,比如视图(View)、视图控制器(View Controller)、活动(Activity)、应用(Application)、布局(Layout)等,在 Flutter 中都是 Widget。

Flutter 的核心设计思想便是“一切皆 Widget”。

Widget 渲染过程

在进行 App 开发时,如何结构化地组织视图数据,提供给渲染引擎,最终完成界面显示。

通常情况下,不同的 UI 框架中会以不同的方式去处理这一问题,但无一例外地都会用到视图树(View Tree)的概念。而 Flutter 将视图树的概念进行了扩展,把视图数据的组织和渲染抽象为三部分,即 Widget,Element 和 RenderObject。

这三部分之间的关系:

 Widget

Widget 是 Flutter 世界里对视图的一种结构化描述,你可以把它看作是前端中的“控件”或“组件”。Widget 是控件实现的基本逻辑单位,里面存储的是有关视图渲染的配置信息,包括布局、渲染属性、事件响应信息等。

在页面渲染上,Flutter 将“Simple is best”这一理念做到了极致。为什么这么说呢?Flutter 将 Widget 设计成不可变的,所以当视图渲染的配置信息发生变化时,Flutter 会选择重建 Widget 树的方式进行数据更新,以数据驱动 UI 构建的方式简单高效。

但,这样做的缺点是,因为涉及到大量对象的销毁和重建,所以会对垃圾回收造成压力。不过,Widget 本身并不涉及实际渲染位图,所以它只是一份轻量级的数据结构,重建的成本很低。

另外,由于 Widget 的不可变性,可以以较低成本进行渲染节点复用,因此在一个真实的渲染树中可能存在不同的 Widget 对应同一个渲染节点的情况,这无疑又降低了重建 UI 的成本。

Element

Element 是 Widget 的一个实例化对象,它承载了视图构建的上下文数据,是连接结构化的配置信息到完成最终渲染的桥梁。

Flutter 渲染过程,可以分为这么三步:

首先,通过 Widget 树生成对应的 Element 树;

然后,创建相应的 RenderObject 并关联到 Element.renderObject 属性上;

最后,构建成 RenderObject 树,以完成最终的渲染。

可以看到,Element 同时持有 Widget 和 RenderObject。而无论是 Widget 还是 Element,其实都不负责最后的渲染,只负责发号施令,真正去干活儿的只有 RenderObject。那你可能会问,既然都是发号施令,那为什么需要增加中间的这层 Element 树呢?直接由 Widget 命令 RenderObject 去干活儿不好吗?

答案是,可以,但这样做会极大地增加渲染带来的性能损耗。

因为 Widget 具有不可变性,但 Element 却是可变的。实际上,Element 树这一层将 Widget 树的变化(类似 React 虚拟 DOM diff)做了抽象,可以只将真正需要修改的部分同步到真实的 RenderObject 树中,最大程度降低对真实渲染视图的修改,提高渲染效率,而不是销毁整个渲染视图树重建。

这,就是 Element 树存在的意义。

RenderObject

从其名字,我们就可以很直观地知道,RenderObject 是主要负责实现视图渲染的对象。

Flutter 通过控件树(Widget 树)中的每个控件(Widget)创建不同类型的渲染对象,组成渲染对象树。

而渲染对象树在 Flutter 的展示过程分为四个阶段,即布局、绘制、合成和渲染。 其中,布局和绘制在 RenderObject 中完成,Flutter 采用深度优先机制遍历渲染对象树,确定树中各个对象的位置和尺寸,并把它们绘制到不同的图层上。绘制完毕后,合成和渲染的工作则交给 Skia 搞定。

Flutter 通过引入 Widget、Element 与 RenderObject 这三个概念,把原本从视图数据到视图渲染的复杂构建过程拆分得更简单、直接,在易于集中治理的同时,保证了较高的渲染效率。

RenderObjectWidget 介绍

StatelessWidget 和 StatefulWidget 只是用来组装控件的容器,并不负责组件最后的布局和绘制。在 Flutter 中,布局和绘制工作实际上是在 Widget 的另一个子类 RenderObjectWidget 内完成的。

所以,在今天这篇文章的最后,我们再来看一下 RenderObjectWidget 的源码,来看看如何使用 Element 和 RenderObject 完成图形渲染工作。


abstract class RenderObjectWidget extends Widget {@overrideRenderObjectElement createElement();@protectedRenderObject createRenderObject(BuildContext context);@protectedvoid updateRenderObject(BuildContext context, covariant RenderObject renderObject) { }...
}

 RenderObjectWidget 是一个抽象类。我们通过源码可以看到,这个类中同时拥有创建 Element、RenderObject,以及更新 RenderObject 的方法。

但实际上,RenderObjectWidget 本身并不负责这些对象的创建与更新。

对于 Element 的创建,Flutter 会在遍历 Widget 树时,调用 createElement 去同步 Widget 自身配置,从而生成对应节点的 Element 对象。而对于 RenderObject 的创建与更新,其实是在 RenderObjectElement 类中完成的。


abstract class RenderObjectElement extends Element {RenderObject _renderObject;@overridevoid mount(Element parent, dynamic newSlot) {super.mount(parent, newSlot);_renderObject = widget.createRenderObject(this);attachRenderObject(newSlot);_dirty = false;}@overridevoid update(covariant RenderObjectWidget newWidget) {super.update(newWidget);widget.updateRenderObject(this, renderObject);_dirty = false;}...
}

在 Element 创建完毕后,Flutter 会调用 Element 的 mount 方法。在这个方法里,会完成与之关联的 RenderObject 对象的创建,以及与渲染树的插入工作,插入到渲染树后的 Element 就可以显示到屏幕中了。

如果 Widget 的配置数据发生了改变,那么持有该 Widget 的 Element 节点也会被标记为 dirty。在下一个周期的绘制时,Flutter 就会触发 Element 树的更新,并使用最新的 Widget 数据更新自身以及关联的 RenderObject 对象,接下来便会进入 Layout 和 Paint 的流程。而真正的绘制和布局过程,则完全交由 RenderObject 完成:


abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget {...void layout(Constraints constraints, { bool parentUsesSize = false }) {...}void paint(PaintingContext context, Offset offset) { }
}

 布局和绘制完成后,接下来的事情就交给 Skia 了。在 VSync 信号同步时直接从渲染树合成 Bitmap,然后提交给 GPU。

接下来,我以下面的界面示例为例,与你说明 Widget、Element 与 RenderObject 在渲染过程中的关系。在下面的例子中,一个 Row 容器放置了 4 个子 Widget,左边是 Image,而右边则是一个 Column 容器下排布的两个 Text。

那么,在 Flutter 遍历完 Widget 树,创建了各个子 Widget 对应的 Element 的同时,也创建了与之关联的、负责实际布局和绘制的 RenderObject。 

 

Widget Demo

AbsorbPointer class

参考:AbsorbPointer class - widgets library - Dart API

在命中测试期间吸收点击的小部件。

当 absorbing 为 true 时,此小部件通过终止自身的命中测试来 防止其子树接收点击事件。 它在布局期间仍然消耗空间并像往常一样绘制它的子View。 它只是防止其子项成为定位事件的目标,因为它从 RenderBox.hitTest 返回 true。

下面的示例有一个 AbsorbPointer 小部件将按钮包装在堆栈顶部,它吸收指针事件,防止其子按钮和堆栈中位于其下方的按钮接收指针事件。

import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {const MyApp({super.key});static const String _title = 'Flutter Code Sample';@overrideWidget build(BuildContext context) {return MaterialApp(title: _title,home: Scaffold(appBar: AppBar(title: const Text(_title)),body: const Center(child: MyStatelessWidget(),),),);}
}class MyStatelessWidget extends StatelessWidget {const MyStatelessWidget({super.key});@overrideWidget build(BuildContext context) {return Stack(alignment: AlignmentDirectional.center,children: <Widget>[SizedBox(width: 200.0,height: 100.0,child: ElevatedButton(onPressed: () {},child: null,),),SizedBox(width: 100.0,height: 200.0,child: AbsorbPointer(child: ElevatedButton(style: ElevatedButton.styleFrom(backgroundColor: Colors.blue.shade200,),onPressed: () {},child: null,),),),],);}
}

Align class

Align class - widgets library - Dart API

一个小部件,它在自身内部对齐其子项并根据子项的大小选择性地调整自身大小。

例如,要在右下角对齐一个框,您可以向此框传递一个比孩子的自然尺寸大的严格约束,对齐方式为 Alignment.bottomRight。

如果其尺寸受到限制且 widthFactor 和 heightFactor 为空,则此小部件将尽可能大。 如果维度不受约束并且相应的大小因子为空,则小部件将匹配其子项在该维度中的大小。 如果大小因子不为空,则此小部件的相应维度将是子维度和大小因子的乘积。 例如,如果 widthFactor 为 2.0,则此小部件的宽度将始终是其子部件宽度的两倍。

alignment 属性描述了孩子坐标系中的一个点和这个小部件坐标系中的一个不同点。 Align 小部件定位孩子,使两个点彼此对齐。

  • 本示例中的 Align 小部件使用 Alignment 中定义的常量之一,Alignment.topRight。 这会将 FlutterLogo 放置在父蓝色 Container 的右上角。

import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {const MyApp({super.key});@overrideWidget build(BuildContext context) {return Container(height: 120.0,width: 120.0,color: Colors.blue[50],child: const Align(alignment: Alignment.topRight,child: FlutterLogo(size: 60,),),);}
}

  • 以下示例中使用的 Alignment 定义了一个点:

(0.2 * FlutterLogo 的宽度/2 + FlutterLogo 的宽度/2, 0.6 * FlutterLogo 的高度/2 + FlutterLogo 的高度/2) = (36.0, 48.0)。
Alignment 类使用原点位于 Container 中心的坐标系,如上图所示。 Align会将FlutterLogo按照这个坐标系放置在(36.0, 48.0)。

import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {const MyApp({super.key});@overrideWidget build(BuildContext context) {return Center(child: Container(height: 120.0,width: 120.0,color: Colors.blue[50],child: const Align(alignment: Alignment(0.2, 0.6),child: FlutterLogo(size: 60,),),),);}
}

 

  • 以下示例中使用的 FractionalOffset 定义了两个点:

(0.2 * FlutterLogo 的宽度, 0.6 * FlutterLogo 的高度) = (12.0, 36.0) 在蓝色容器的坐标系中。
(0.2 * Align 的宽度, 0.6 * Align 的高度) = (24.0, 72.0) 在Align widget 的坐标系中。
Align 小部件定位 FlutterLogo,使两个点彼此重叠。 在此示例中,FlutterLogo 的左上角将放置在距 Align 小部件左上角的 (24.0, 72.0) - (12.0, 36.0) = (12.0, 36.0) 处。

FractionalOffset 类使用原点位于 Container 左上角的坐标系,这与上面示例中 Alignment 中使用的面向中心的系统不同。

import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {const MyApp({super.key});@overrideWidget build(BuildContext context) {return Center(child: Container(height: 120.0,width: 120.0,color: Colors.blue[50],child: const Align(alignment: FractionalOffset(0.2, 0.6),child: FlutterLogo(size: 60,),),),);}
}

 AnimatedAlign,它在给定的持续时间内平滑地动画对齐变化。
CustomSingleChildLayout,它使用委托来控制单个子项的布局。
Center,与 Align 相同,但对齐始终设置为 Alignment.center。
FractionallySizedBox,它根据自身大小的一部分调整其子项的大小,并根据 Alignment 值定位子项。
布局小部件的目录。

AspectRatio class

将子项调整为特定纵横比的小部件。

小部件首先尝试布局约束允许的最大宽度。 小部件的高度是通过将给定的纵横比应用于宽度来确定的,表示为宽度与高度的比率。

例如,宽高比为 16:9 的宽高比值为 16.0/9.0。 如果最大宽度是无限的,则初始宽度通过将宽高比应用于最大高度来确定。

  • 此示例展示了 AspectRatio 在其父级的宽度约束为无限时如何设置宽度。 由于其父项的允许高度是固定值,因此实际宽度通过给定的 AspectRatio 确定。由于本例中高度固定为 100.0,纵横比设置为 16 / 9,因此宽度应为 100.0 / 9 * 16。

import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {static const String _title = 'Flutter Code Sample';const MyApp({super.key});@overrideWidget build(BuildContext context) {return MaterialApp(title: _title,home: Scaffold(appBar: AppBar(title: const Text(_title),),body: const MyStatelessWidget(),),);}
}class MyStatelessWidget extends StatelessWidget {const MyStatelessWidget({super.key});@overrideWidget build(BuildContext context) {return Container(color: Colors.blue,alignment: Alignment.center,width: double.infinity,height: 100.0,child: AspectRatio(aspectRatio: 16 / 9,child: Container(color: Colors.green,),),);}
}

 现在考虑第二个示例,这次的纵横比为 2.0,布局约束要求宽度介于 0.0 和 100.0 之间,高度介于 0.0 和 100.0 之间。 我们将选择 100.0 的宽度(允许的最大宽度)和 50.0 的高度(以匹配纵横比)。

import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {const MyApp({super.key});static const String _title = 'Flutter Code Sample';@overrideWidget build(BuildContext context) {return MaterialApp(title: _title,home: Scaffold(appBar: AppBar(title: const Text(_title),),body: const MyStatelessWidget(),),);}
}class MyStatelessWidget extends StatelessWidget {const MyStatelessWidget({super.key});@overrideWidget build(BuildContext context) {return Container(color: Colors.blue,alignment: Alignment.center,width: 100.0,height: 100.0,child: AspectRatio(aspectRatio: 2.0,child: Container(width: 100.0,height: 50.0,color: Colors.green,),),);}
}

 在同样的情况下,如果宽高比是 0.5,我们也会选择 100.0 的宽度(仍然是允许的最大宽度)并且我们会尝试使用 200.0 的高度。 不幸的是,这违反了约束,因为孩子的高度最多为 100.0 像素。 然后小部件将采用该值并再次应用纵横比以获得 50.0 的宽度。 该宽度是约束允许的,子项接收到的宽度为 50.0,高度为 100.0。 如果不允许宽度,小部件将继续遍历约束。 如果widget在咨询了每一个约束后都没有找到一个可行的尺寸,widget最终会为child选择一个满足布局约束但不满足纵横比约束的尺寸。

import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {const MyApp({super.key});static const String _title = 'Flutter Code Sample';@overrideWidget build(BuildContext context) {return MaterialApp(title: _title,home: Scaffold(appBar: AppBar(title: const Text(_title),),body: const MyStatelessWidget(),),);}
}class MyStatelessWidget extends StatelessWidget {const MyStatelessWidget({super.key});@overrideWidget build(BuildContext context) {return Container(color: Colors.blue,alignment: Alignment.center,width: 100.0,height: 100.0,child: AspectRatio(aspectRatio: 0.5,child: Container(width: 100.0,height: 50.0,color: Colors.green,),),);}
}

相关文章:

Flutter-Widget-学习笔记

Widget 是整个视图描述的基础。 参考&#xff1a;https://docs.flutter.dev/resources/architectural-overview Widget 到底是什么呢&#xff1f; Widget 是 Flutter 功能的抽象描述&#xff0c;是视图的配置信息&#xff0c;同样也是数据的映射&#xff0c;是 Flutter 开发框…...

easyExcel 写复杂表头

写模板 模板图片&#xff1a; 实体类&#xff08;这里没有用Data 是因为Lombok和easyExcal的版本冲突&#xff0c;在导入读取的时候获取不到值&#xff09; package cn.iocoder.yudao.module.project.controller.admin.goods.vo;import com.alibaba.excel.annotation.ExcelI…...

关于线程池的执行流程和拒绝策略

使用线程池的好处为&#xff1a; 降低资源消耗&#xff1a;减少线程的创建和销毁带来的性能开销。 提高响应速度&#xff1a;当任务来时可以直接使用&#xff0c;不用等待线程创建 可管理性&#xff1a; 进行统一的分配&#xff0c;监控&#xff0c;避免大量的线程间因互相抢…...

【李忍考研传】二、约定

因为收学生证用了好些时间&#xff0c;李忍把学生证都交给班长后&#xff0c;就赶忙跑去食堂。远远地&#xff0c;他就看到那个瘦小的身影立在食堂正门前&#xff0c;那是他们约定每天午餐集合的地方。 “你咋这么慢啊……” “害&#xff01;帮班长收东西耽误了点时间&#…...

2023-2-19 刷题情况

修改两个元素的最小分数 题目描述 给你一个下标从 0 开始的整数数组 nums 。 nums 的 最小 得分是满足 0 < i < j < nums.length 的 |nums[i] - nums[j]| 的最小值。nums的 最大 得分是满足 0 < i < j < nums.length 的 |nums[i] - nums[j]| 的最大值。nu…...

LeetCode笔记:Weekly Contest 333

LeetCode笔记&#xff1a;Weekly Contest 333 1. 题目一 1. 解题思路2. 代码实现 2. 题目二 1. 解题思路2. 代码实现 3. 题目三 1. 解题思路2. 代码实现 4. 题目四 比赛链接&#xff1a;https://leetcode.com/contest/weekly-contest-333 1. 题目一 给出题目一的试题链接如下…...

元数据管理 1

1、关于元数据管理原则说法正确的是 (知识点: 三月份模拟题)A.确保员工了解如何访问和使用元数据。B.制定、实施和审核元数据标准&#xff0c;以简化元数据的集成和使用。C.创建反馈机制&#xff0c;以便数据使用者可以将错误或过时的元数据反馈给元数据管理团队。D.以上都对正…...

统计二进制中比特1的个数

快速统计比特1的数量int CountBitOnes(int32_t n) {int result 0;for(;n;result) {n & n-1;}return result; }原理很简单&#xff0c;n-1会将n中最靠近结尾的1减一&#xff0c;这样n&n-1&#xff0c;n中最靠近结尾的1就变成了0&#xff1b;假设n 0b xxxxxxxx100n - 1…...

第三方实现跑马灯和手写实现跑马灯

目录第三方实现跑马灯手写实现跑马灯手写实现跑马灯【整体代码】自己细心研究一下上述代码第三方实现跑马灯 https://vue3-marquee.vercel.app/guide.html#changes-from-v2https://evodiaaut.github.io/vue-marquee-text-component/ 手写实现跑马灯 CSS部分 <style>.m…...

React Native Cannot run program “node“问题

概述 前几天mac重装系统了&#xff0c;用Android studio重新构建React native项目时&#xff0c;报Cannot run program "node"错误。 电脑系统为macOS 12.6.3 (Monterey)&#xff0c;M1 Pro芯片。设备信息如下图所示&#xff1a; 完整错误信息如下图所示&#xff…...

python基于vue微信小程序 房屋租赁出租系统

目录 1 绪论 1 1.1课题背景 1 1.2课题研究现状 1 1.3初步设计方法与实施方案 2 1.4本文研究内容 2 2 系统开发环境 4 2.1 2.2MyEclipse环境配置 4 2.3 B/S结构简介 4 2.4MySQL数据库 5 2. 3 系统分析 6 3.1系统可行性分析 6 3.1.1经济可行性 6 3.1.2技术可行性 6 3.1.3运行可行…...

ThreadPoolExecutor管理异步线程笔记

为什么使用线程池&#xff1f; 线程的创建和销毁都需要不小的系统开销&#xff0c;不加以控制管理容易发生OOM错误。避免线程并发抢占系统资源导致系统阻塞。具备一定的线程管理能力&#xff08;数量、存活时间&#xff0c;任务管理&#xff09; new ThreadPoolExecutor(int …...

MotoSimEG-VRC教程:动态输送带创建以及示教编程与仿真运行

目录 任务描述 简易输送带外部设备创建 输送带模型添加与配置 工件安装到输送带 输送带输送工件程序编写与仿真运行 任务描述 在MotoSimEG-VRC中创建1条输送带&#xff0c;并且能够实现将工件从输送带起始点位置处输送到结束点位置处。 简易输送带外部设备创建 在MotoS…...

PyTorch 并行训练 DistributedDataParallel完整代码示例

使用大型数据集训练大型深度神经网络 (DNN) 的问题是深度学习领域的主要挑战。 随着 DNN 和数据集规模的增加&#xff0c;训练这些模型的计算和内存需求也会增加。 这使得在计算资源有限的单台机器上训练这些模型变得困难甚至不可能。 使用大型数据集训练大型 DNN 的一些主要挑…...

Golang实现ttl机制保存内存数据

ttl(time-to-live) 数据存活时间&#xff0c;我们这里指数据在内存中保存一段时间&#xff0c;超过期限则不能被读取到&#xff0c;与Redis的ttl机制类似。本文仅实现ttl部分&#xff0c;不考虑序列化和反序列化。 获取当前时间 涉及时间计算&#xff0c;这里首先介绍如何获取…...

js中数字运算结果与预期不一致的问题和解决方案

本文主要是和大家聊聊关于js中经常出现数字运算结果与预期结果不一致的问题&#xff0c;与及解决该问题的的方案。 一、问题现象 如&#xff1a;0.1 0.2的预期结果是0.3&#xff0c;但是在js中得到的计算结果却是0.30000000000000004&#xff0c;如下图所示 如&#xff1a;0…...

C++ Primer Plus 学习笔记(一)——基本类型

字节与字符 计算机内存的基本单位是位&#xff08;bit&#xff09;&#xff0c;字节&#xff08;byte&#xff09;通常指的是8位的内存单元&#xff0c;从这个意义上来说&#xff0c;字节指的就是描述计算机内存量的度量单位。 C对字节的定义则有些不同&#xff0c;C字节由至…...

ChatGpt与Google 谁能给出最好的回答

ChatGPT由于其先进的会话和技术功能而越来越受欢迎。你可以问聊天机器人任何你想问的问题&#xff0c;它会在几秒钟内输出答案。虽然它不是一个搜索引擎&#xff0c;你应该使用ChatGPT作为你的信息来源而不是谷歌&#xff0c;百度吗? 我们来根据国外的一场测试来看一下 ChatG…...

【Redis】一、CentOS64 安装 Redis

1.下载redis https://download.redis.io/releases/2.将 redis 安装包拷贝到 /opt/ 目录 最好自己创建一个文件夹 3.解压 tar -zvxf redis-6.2.1.tar.gz4. 安装gcc yum install gcc5. 进入目录 cd /opt/redis/redis-6.2.1/6. 编译 make7.执行 make install 进行安装 8. …...

Redis底层原理(持久化+分布式锁)

Redis底层原理 持久化 Redis虽然是个内存数据库&#xff0c;但是Redis支持RDB和AOF &#xff08;Redis Database Backup file&#xff08;Redis数据备份文件&#xff09;&#xff0c;也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中 &#xff1b;Appen…...

thinkphp8.1 调用巨量广告API接口,刷新token

1、在mysql中建立表sys_token; CREATE TABLE sys_token (id int UNSIGNED NOT NULL,access_token varchar(50) COLLATE utf8mb4_general_ci NOT NULL,expires_in datetime NOT NULL,refresh_token varchar(50) COLLATE utf8mb4_general_ci NOT NULL,refresh_token_expires_in …...

Python应用break初解

大家好!作为 Python 初学者&#xff0c;控制循环的执行是编程中的基础技能之一。在本文中&#xff0c;我们将深入探讨break语句的用途和用法&#xff0c;帮助您更好地理解和掌握这一强大的工具。 定义: break是 Python 中的一个保留关键字&#xff0c;用于在循环中提前终止循环…...

Spring Boot 类加载机制深度解析

Spring Boot 类加载机制深度解析 前言 在 Java 应用开发中&#xff0c;类加载机制是一个重要且复杂的话题。Spring Boot 作为现代 Java 开发的主流框架&#xff0c;其类加载机制更是值得深入了解。本文将从基础概念到实际应用&#xff0c;全面解析 Spring Boot 的类加载机制。…...

在Docker里面运行Docker

Docker 凭借其轻量级和可移植的容器,无疑改变了软件开发和部署的世界。但如果我告诉你 Docker 本身可以在另一个 Docker 容器中运行,你会怎么想?没错!这个概念通常被称为“Docker Inside Docker”或“DinD”,它为开发人员和系统管理员开辟了一个全新的可能性领域。在这篇博…...

深入了解JavaScript当中如何确定值的类型

JavaScript是一种弱类型语言&#xff0c;当你给一个变量赋了一个值&#xff0c;该值是什么类型的&#xff0c;那么该变量就是什么类型的&#xff0c;并且你还可以给一个变量赋多种类型的值&#xff0c;也不会报错&#xff0c;这就是JavaScript的内部机制所决定的&#xff0c;那…...

C++ --- vector

C --- vector的使用 前言1、构造函数1.1默认构造1.2n个val值构造1.3迭代器区间构造1.4拷贝构造1.4初始化列表构造 2、遍历方式2.1[ ] 下标2.2迭代器2.3范围for 3、常用方法或重载&#xff08;1&#xff09;增push_back()insert()assign() &#xff08;2&#xff09;删erase()c…...

打造高效多模态RAG系统:原理与评测方法详解

引言 随着信息检索与生成式AI的深度融合&#xff0c;检索增强生成&#xff08;RAG, Retrieval-Augmented Generation&#xff09; 已成为AI领域的重要技术方向。传统RAG系统主要依赖文本数据&#xff0c;但真实世界中的信息往往包含图像、表格等多模态内容。多模态RAG&#xf…...

【基于阿里云搭建数据仓库(离线)】Data Studio创建资源与函数

Data Studio支持在您的数据分析代码中引用自定义的资源和函数&#xff08;支持MaxCompute、EMR、CDH、Flink&#xff09;&#xff0c;您需要先创建或上传资源、函数至目标工作空间&#xff0c;上传后才可在该工作空间的任务中使用。您可参考本文了解如何使用DataWorks可视化方式…...

dvwa10——XSS(DOM)

XSS攻击&#xff1a; DOM型XSS 只在浏览器前端攻击触发&#xff1a;修改url片段代码不存储 反射型XSS 经过服务器攻击触发&#xff1a;可能通过提交恶意表单&#xff0c;连接触发代码不存储 存储型XSS 经由服务器攻击触发&#xff1a;可能通过提交恶意表单&#xff0c;连…...

RabbitMQ 监控与调优实战指南(二)

五、调优策略与实战&#xff1a;对症下药提升性能 5.1 配置参数调优 在 RabbitMQ 的性能优化中&#xff0c;合理调整配置参数是关键的一环&#xff0c;这些参数涉及内存、磁盘、网络等多个资源层面&#xff0c;对 RabbitMQ 的整体性能有着深远的影响。 内存相关配置&#xf…...