工程化与框架系列(13)--虚拟DOM实现
虚拟DOM实现 🌳
虚拟DOM(Virtual DOM)是现代前端框架的核心技术之一,它通过在内存中维护UI的虚拟表示来提高渲染性能。本文将深入探讨虚拟DOM的实现原理和关键技术。
虚拟DOM概述 🌟
💡 小知识:虚拟DOM是对真实DOM的一种轻量级抽象表示,它以JavaScript对象的形式存在,通过diff算法计算最小更新路径,从而减少对实际DOM的操作。
为什么需要虚拟DOM
在现代前端开发中,虚拟DOM带来以下优势:
-
性能优化
- 批量DOM更新
- 最小化DOM操作
- 跨平台渲染
- 服务端渲染
-
开发体验
- 声明式编程
- 组件化开发
- 状态驱动UI
- 代码可维护性
-
跨平台能力
- 浏览器渲染
- 原生应用渲染
- 服务端渲染
- Canvas/WebGL渲染
-
调试能力
- 状态追踪
- 组件调试
- 性能分析
- 错误边界
核心实现 ⚡
虚拟DOM节点定义
// vnode.ts
export type VNodeType = string | Component;export interface VNode {type: VNodeType;props: Record<string, any>;children: (VNode | string)[];key?: string | number;el?: HTMLElement | Text;
}export interface Component {render: () => VNode;props?: Record<string, any>;setup?: (props: Record<string, any>) => Record<string, any>;
}export function h(type: VNodeType,props: Record<string, any> = {},children: (VNode | string)[] = []
): VNode {return {type,props,children,key: props.key};
}// JSX类型定义
declare global {namespace JSX {interface Element extends VNode {}interface IntrinsicElements {[elemName: string]: any;}}
}// 创建文本节点
export function createTextVNode(text: string): VNode {return {type: 'text',props: {},children: [text]};
}// 创建Fragment
export function Fragment(props: Record<string, any>): VNode {return {type: 'fragment',props,children: props.children || []};
}
DOM渲染实现
// renderer.ts
export class Renderer {private container: HTMLElement;constructor(container: HTMLElement) {this.container = container;}render(vnode: VNode | null) {if (vnode === null) {// 卸载if (this.container.firstChild) {this.container.innerHTML = '';}return;}// 挂载或更新const prevVNode = this.container._vnode;if (!prevVNode) {// 首次挂载this.mount(vnode, this.container);} else {// 更新this.patch(prevVNode, vnode, this.container);}this.container._vnode = vnode;}private mount(vnode: VNode, container: HTMLElement, anchor?: Node | null) {const { type, props, children } = vnode;if (typeof type === 'string') {// 创建元素const el = document.createElement(type);vnode.el = el;// 设置属性this.patchProps(el, {}, props);// 挂载子节点children.forEach(child => {if (typeof child === 'string') {el.appendChild(document.createTextNode(child));} else {this.mount(child, el);}});// 插入到容器container.insertBefore(el, anchor || null);} else if (typeof type === 'function') {// 挂载组件this.mountComponent(vnode, container, anchor);}}private mountComponent(vnode: VNode,container: HTMLElement,anchor?: Node | null) {const component = vnode.type as Component;// 执行setuplet setupResult = {};if (component.setup) {setupResult = component.setup(vnode.props);}// 执行renderconst renderVNode = component.render.call(setupResult);// 挂载渲染结果this.mount(renderVNode, container, anchor);vnode.el = renderVNode.el;}private patch(n1: VNode,n2: VNode,container: HTMLElement,anchor?: Node | null) {if (n1.type !== n2.type) {// 类型不同,直接替换this.unmount(n1);this.mount(n2, container, anchor);return;}if (typeof n2.type === 'string') {// 更新元素const el = (n2.el = n1.el as HTMLElement);// 更新属性this.patchProps(el, n1.props, n2.props);// 更新子节点this.patchChildren(n1, n2, el);} else if (typeof n2.type === 'function') {// 更新组件this.patchComponent(n1, n2, container);}}private patchProps(el: HTMLElement,oldProps: Record<string, any>,newProps: Record<string, any>) {// 移除旧属性for (const key in oldProps) {if (!(key in newProps)) {if (key.startsWith('on')) {const event = key.slice(2).toLowerCase();el.removeEventListener(event, oldProps[key]);} else {el.removeAttribute(key);}}}// 设置新属性for (const key in newProps) {const newValue = newProps[key];const oldValue = oldProps[key];if (newValue !== oldValue) {if (key.startsWith('on')) {// 事件处理const event = key.slice(2).toLowerCase();if (oldValue) {el.removeEventListener(event, oldValue);}el.addEventListener(event, newValue);} else if (key === 'style') {// 样式处理if (typeof newValue === 'string') {el.style.cssText = newValue;} else {for (const styleKey in newValue) {el.style[styleKey] = newValue[styleKey];}}} else if (key === 'class') {// 类名处理if (Array.isArray(newValue)) {el.className = newValue.join(' ');} else {el.className = newValue;}} else {// 其他属性el.setAttribute(key, newValue);}}}}private patchChildren(n1: VNode, n2: VNode, container: HTMLElement) {const oldChildren = n1.children;const newChildren = n2.children;// 处理文本节点if (typeof newChildren[0] === 'string') {if (typeof oldChildren[0] === 'string') {// 文本节点更新if (newChildren[0] !== oldChildren[0]) {container.textContent = newChildren[0];}} else {// 替换为文本节点container.textContent = newChildren[0];}return;}// 处理子节点数组const oldLen = oldChildren.length;const newLen = newChildren.length;const commonLen = Math.min(oldLen, newLen);// 更新公共部分for (let i = 0; i < commonLen; i++) {this.patch(oldChildren[i] as VNode,newChildren[i] as VNode,container);}if (newLen > oldLen) {// 添加新节点for (let i = commonLen; i < newLen; i++) {this.mount(newChildren[i] as VNode, container);}} else if (oldLen > newLen) {// 移除多余节点for (let i = commonLen; i < oldLen; i++) {this.unmount(oldChildren[i] as VNode);}}}private unmount(vnode: VNode) {if (typeof vnode.type === 'string') {vnode.el?.parentNode?.removeChild(vnode.el);} else if (typeof vnode.type === 'function') {// 组件卸载if (vnode.el) {vnode.el.parentNode?.removeChild(vnode.el);}}}
}
Diff算法实现
// diff.ts
interface KeyToIndexMap {[key: string]: number;
}export function patchKeyedChildren(oldChildren: VNode[],newChildren: VNode[],container: HTMLElement
) {let oldStartIdx = 0;let oldEndIdx = oldChildren.length - 1;let newStartIdx = 0;let newEndIdx = newChildren.length - 1;let oldStartVNode = oldChildren[oldStartIdx];let oldEndVNode = oldChildren[oldEndIdx];let newStartVNode = newChildren[newStartIdx];let newEndVNode = newChildren[newEndIdx];const keyToIndexMap: KeyToIndexMap = {};while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {if (!oldStartVNode) {oldStartVNode = oldChildren[++oldStartIdx];} else if (!oldEndVNode) {oldEndVNode = oldChildren[--oldEndIdx];} else if (isSameVNode(oldStartVNode, newStartVNode)) {// 头部节点相同patch(oldStartVNode, newStartVNode, container);oldStartVNode = oldChildren[++oldStartIdx];newStartVNode = newChildren[++newStartIdx];} else if (isSameVNode(oldEndVNode, newEndVNode)) {// 尾部节点相同patch(oldEndVNode, newEndVNode, container);oldEndVNode = oldChildren[--oldEndIdx];newEndVNode = newChildren[--newEndIdx];} else if (isSameVNode(oldStartVNode, newEndVNode)) {// 老头和新尾相同patch(oldStartVNode, newEndVNode, container);container.insertBefore(oldStartVNode.el!,oldEndVNode.el!.nextSibling);oldStartVNode = oldChildren[++oldStartIdx];newEndVNode = newChildren[--newEndIdx];} else if (isSameVNode(oldEndVNode, newStartVNode)) {// 老尾和新头相同patch(oldEndVNode, newStartVNode, container);container.insertBefore(oldEndVNode.el!, oldStartVNode.el!);oldEndVNode = oldChildren[--oldEndIdx];newStartVNode = newChildren[++newStartIdx];} else {// 处理其他情况if (!keyToIndexMap) {// 生成旧节点的key映射for (let i = oldStartIdx; i <= oldEndIdx; i++) {const key = oldChildren[i].key;if (key != null) {keyToIndexMap[key] = i;}}}// 在旧节点中寻找新头节点const idxInOld = keyToIndexMap[newStartVNode.key!];if (idxInOld === undefined) {// 新节点mount(newStartVNode, container, oldStartVNode.el!);} else {// 移动节点const vnodeToMove = oldChildren[idxInOld];patch(vnodeToMove, newStartVNode, container);container.insertBefore(vnodeToMove.el!, oldStartVNode.el!);oldChildren[idxInOld] = undefined as any;}newStartVNode = newChildren[++newStartIdx];}}// 处理剩余节点if (oldStartIdx > oldEndIdx) {// 添加新节点const anchor = newChildren[newEndIdx + 1]? newChildren[newEndIdx + 1].el: null;for (let i = newStartIdx; i <= newEndIdx; i++) {mount(newChildren[i], container, anchor);}} else if (newStartIdx > newEndIdx) {// 移除多余节点for (let i = oldStartIdx; i <= oldEndIdx; i++) {if (oldChildren[i]) {unmount(oldChildren[i]);}}}
}function isSameVNode(n1: VNode, n2: VNode): boolean {return n1.type === n2.type && n1.key === n2.key;
}
组件系统实现 🏗️
组件定义
// component.ts
export interface ComponentOptions {name?: string;props?: Record<string, PropOptions>;setup?: (props: Record<string, any>,context: SetupContext) => Record<string, any>;render?: () => VNode;
}interface PropOptions {type: any;required?: boolean;default?: any;validator?: (value: any) => boolean;
}interface SetupContext {attrs: Record<string, any>;slots: Record<string, (...args: any[]) => VNode[]>;emit: (event: string, ...args: any[]) => void;
}export function defineComponent(options: ComponentOptions) {return {name: options.name,props: options.props,setup: options.setup,render: options.render,// 组件实例创建create(props: Record<string, any>) {// 创建组件实例const instance = {props: shallowReactive(props),attrs: {},slots: {},emit: (event: string, ...args: any[]) => {const handler = props[`on${capitalize(event)}`];if (handler) {handler(...args);}}};// 执行setupif (options.setup) {const setupContext = {attrs: instance.attrs,slots: instance.slots,emit: instance.emit};const setupResult = options.setup(instance.props,setupContext);if (typeof setupResult === 'function') {// setup返回渲染函数instance.render = setupResult;} else if (typeof setupResult === 'object') {// setup返回状态对象instance.setupState = proxyRefs(setupResult);}}// 渲染函数instance.render = options.render || instance.render;return instance;}};
}// 工具函数
function capitalize(str: string): string {return str.charAt(0).toUpperCase() + str.slice(1);
}
生命周期实现
// lifecycle.ts
export const enum LifecycleHooks {BEFORE_CREATE = 'beforeCreate',CREATED = 'created',BEFORE_MOUNT = 'beforeMount',MOUNTED = 'mounted',BEFORE_UPDATE = 'beforeUpdate',UPDATED = 'updated',BEFORE_UNMOUNT = 'beforeUnmount',UNMOUNTED = 'unmounted'
}export function injectHook(type: LifecycleHooks,hook: Function,target: any
): Function | undefined {if (target) {const hooks = target[type] || (target[type] = []);const wrappedHook = (...args: any[]) => {hook.call(target, ...args);};hooks.push(wrappedHook);return wrappedHook;}
}export const createHook = (lifecycle: LifecycleHooks) => {return (hook: Function, target: any = currentInstance) =>injectHook(lifecycle, hook, target);
};export const onBeforeMount = createHook(LifecycleHooks.BEFORE_MOUNT);
export const onMounted = createHook(LifecycleHooks.MOUNTED);
export const onBeforeUpdate = createHook(LifecycleHooks.BEFORE_UPDATE);
export const onUpdated = createHook(LifecycleHooks.UPDATED);
export const onBeforeUnmount = createHook(LifecycleHooks.BEFORE_UNMOUNT);
export const onUnmounted = createHook(LifecycleHooks.UNMOUNTED);
性能优化 ⚡
静态节点优化
// optimize.ts
interface StaticNodeAnalysis {isStatic: boolean;staticRoot: boolean;
}export function optimizeNode(node: VNode): StaticNodeAnalysis {if (typeof node.type === 'string') {// 分析静态节点const analysis: StaticNodeAnalysis = {isStatic: true,staticRoot: false};// 检查属性for (const key in node.props) {if (key === 'v-if' ||key === 'v-for' ||key === 'v-model' ||key.startsWith(':') ||key.startsWith('@')) {analysis.isStatic = false;break;}}// 检查子节点if (analysis.isStatic && node.children.length > 0) {let allChildrenStatic = true;let staticChildCount = 0;for (const child of node.children) {if (typeof child === 'object') {const childAnalysis = optimizeNode(child);if (!childAnalysis.isStatic) {allChildrenStatic = false;break;}if (childAnalysis.staticRoot) {staticChildCount++;}}}analysis.staticRoot =allChildrenStatic && staticChildCount > 0;}return analysis;}return {isStatic: false,staticRoot: false};
}// 标记静态根节点
export function markStaticRoots(node: VNode, analysis: StaticNodeAnalysis) {if (analysis.staticRoot) {node.staticRoot = true;// 缓存静态子树node.staticChildren = [...node.children];}// 递归处理子节点for (const child of node.children) {if (typeof child === 'object') {const childAnalysis = optimizeNode(child);markStaticRoots(child, childAnalysis);}}
}
更新优化
// update-optimization.ts
export class UpdateOptimizer {private static readonly BATCH_SIZE = 1000;private updates: Set<VNode> = new Set();private updating = false;queueUpdate(vnode: VNode) {this.updates.add(vnode);if (!this.updating) {this.updating = true;requestAnimationFrame(() => this.processUpdates());}}private processUpdates() {const updates = Array.from(this.updates);this.updates.clear();this.updating = false;// 批量处理更新for (let i = 0; i < updates.length; i += this.BATCH_SIZE) {const batch = updates.slice(i, i + this.BATCH_SIZE);this.processBatch(batch);}}private processBatch(vnodes: VNode[]) {// 按照组件层级排序vnodes.sort((a, b) => getDepth(a) - getDepth(b));// 合并同层级更新const updateMap = new Map<number, VNode[]>();for (const vnode of vnodes) {const depth = getDepth(vnode);if (!updateMap.has(depth)) {updateMap.set(depth, []);}updateMap.get(depth)!.push(vnode);}// 按层级处理更新for (const [depth, nodes] of updateMap) {this.processDepthUpdates(nodes);}}private processDepthUpdates(vnodes: VNode[]) {// 处理同一层级的更新for (const vnode of vnodes) {if (vnode.staticRoot) {// 跳过静态根节点continue;}// 更新节点patch(vnode, vnode, vnode.el!.parentNode);}}
}function getDepth(vnode: VNode): number {let depth = 0;let current = vnode;while (current.parent) {depth++;current = current.parent;}return depth;
}
最佳实践建议 ⭐
性能优化建议
-
节点优化
- 使用key标识
- 提取静态节点
- 避免深层嵌套
- 合理使用v-show
-
更新优化
- 批量更新
- 异步更新
- 合并操作
- 缓存结果
-
渲染优化
- 懒加载组件
- 虚拟滚动
- 时间切片
- 优先级调度
开发建议
- 组件设计
// 好的实践
const GoodComponent = defineComponent({name: 'GoodComponent',props: {items: {type: Array,required: true}},setup(props) {// 提取复杂逻辑const state = reactive({selectedIndex: -1});// 计算属性const filteredItems = computed(() =>props.items.filter(item => item.visible));return {state,filteredItems};}
});// 避免这样做
const BadComponent = defineComponent({render() {// 渲染函数中包含复杂逻辑const items = this.items.filter(item => {return item.visible && this.complexCheck(item);});return h('div', {}, items.map(item =>h('div', { key: item.id }, item.name)));}
});
- 更新处理
// 好的实践
function handleUpdates() {// 批量更新nextTick(() => {state.count++;state.total = calculateTotal();});
}// 避免频繁更新
function badUpdate() {state.count++;state.total = calculateTotal();// 直接触发DOM更新
}
结语 📝
虚拟DOM是现代前端框架的重要基石,通过本文,我们学习了:
- 虚拟DOM的核心概念
- DOM diff算法的实现
- 组件系统的设计
- 性能优化的策略
- 开发中的最佳实践
💡 学习建议:
- 深入理解虚拟DOM原理
- 掌握diff算法的优化
- 注重性能优化实践
- 遵循最佳实践指南
- 持续学习新的优化方案
如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇
终身学习,共同成长。
咱们下一期见
💻
相关文章:
工程化与框架系列(13)--虚拟DOM实现
虚拟DOM实现 🌳 虚拟DOM(Virtual DOM)是现代前端框架的核心技术之一,它通过在内存中维护UI的虚拟表示来提高渲染性能。本文将深入探讨虚拟DOM的实现原理和关键技术。 虚拟DOM概述 🌟 💡 小知识࿱…...
Java实现pdf文件压缩(aspose-pdf实现压缩、itextpdf去除aspose-pdf版权水印)
Java实现pdf文件压缩 时间换空间,实现pdf文件无损压缩。 1、依赖准备 市面上操作pdf文件的组件有spire.pdf.free、itextpdf、openpdf、pdfbox等,它们各有千秋。我们主要完成的场景为压缩,减少文件大小去优化存储、传输等。 在这里选取的组件…...
生态安全相关
概念:生态安全指一个国家具有支撑国家生存发展的较为完整、不受威胁的生态系统,以及应对内外重大生态问题的能力。 (1)国外生态安全的研究进展 国际上对生态安全的研究是从“环境”与“安全”之间的关系展开的。开始的阶段&#x…...
通过 ANSYS Discovery 进行 CFD 分析,增强工程设计
概括 工程师使用计算流体动力学 (CFD) 分析来研究和优化各种应用中的流体流动和传热分析。ANSYS Discovery 是一个用户友好的软件平台,使工程师能够轻松设置和解决 CFD 模型,并能够通知设计修改 在这篇博文中,我们将重点介绍在 Ansys Disc…...
2. 在后端代码中加入日志记录模块
1. 说明 日志模块基本上是每一个软件系统开发中必不可少的,主要用于持久记录一些代码运行中的输出信息,辅助编码人员进行代码调试,以及后期软件上线运行报错分析。在Python中加入日志模块比较简单,只需要借助logging和RotatingFi…...
MySQL锁分类
一、按锁的粒度划分 全局锁 定义:锁定整个数据库实例,阻止所有写操作,确保数据备份一致性。加锁方式:通过FLUSH TABLES WITH READ LOCK实现,释放需执行UNLOCK TABLES。应用场景:适用于全库逻辑备份…...
软件测试之白盒测试知识总结
🍅 点击文末小卡片 ,免费获取软件测试全套资料,资料在手,涨薪更快 概念与定义 白盒测试:侧重于系统或部件内部机制的测试,类型分为分支测试(判定节点测试)、路径测试、语句测试…...
看视频学习方法总结
以下是提高教学视频吸收率的系统性方法,结合认知科学原理和实际学习场景,帮助您最大化学习效果: 一、观看前的黄金准备阶段 60秒快速扫描法 用1分钟快速浏览视频目录、章节标题和简介,建立知识框架。荷兰伊拉斯姆斯大学实验表明&…...
nvm的学习
学习 nvm(Node Version Manager) 是掌握 Node.js 开发的关键技能之一。以下是系统的学习路径和实战指南,涵盖从基础到进阶的内容: 一、基础入门 1. nvm 的核心作用 多版本共存:安装和管理多个 Node.js 版本ÿ…...
OpenCV计算摄影学(5)处理一系列图像(例如视频帧)的非局部均值去噪的函数fastNlMeansDenoisingColoredMulti()的使用
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 对用于彩色图像序列的 fastNlMeansDenoisingMulti 函数的修改。 cv::fastNlMeansDenoisingColoredMulti 函数是 OpenCV 中用于处理一系列图像&am…...
mysql安装教程,超详细图文教程(附安装包)MySQL8.0安装教程
文章目录 前言一、MySQL安装包下载二、mysql安装教程 前言 本教程旨在为大家提供详细、清晰的 mysql安装教程,帮助你轻松跨过安装门槛,顺利开启数据库管理与开发的精彩之旅。 一、MySQL安装包下载 MySQL 是一款备受欢迎的开源关系型数据库管理系统&…...
Spark内存迭代计算
一、宽窄依赖 窄依赖:父RDD的一个分区数据全部发往子RDD的一个分区 宽依赖:父RDD的一个分区数据发往子RDD的多个分区,也称为shuffle 二、Spark是如何进行内存计算的?DAG的作用?Stage阶段划分的作用? &a…...
Odoo免费开源CRM技术实战:从商机线索关联转化为售后工单的应用
文 / 开源智造 Odoo金牌服务 Odoo:功能强大且免费开源的CRM Odoo 引入了一种高效的客户支持管理方式,即将 CRM 线索转换为服务台工单。此功能确保销售和支持团队能够无缝协作,从而提升客户满意度并缩短问题解决时间。通过整合 CRM 模块与服…...
对seacmsv9进行sql注入,orderby,过滤information_schema
对seacmsv9进行sql注入,orderby,过滤information_schema 1.对seacmsv9进行sql注入 海洋影视管理系统(seacms,海洋cms)是一套专为不同需求的站长而设计的视频点播系统,采用的是 php5.Xmysql 的架构 seacm…...
跨AWS账户共享SQS队列以实现消息传递
在现代分布式系统中,不同的服务和组件通常需要进行通信和协作。Amazon Simple Queue Service (SQS)提供了一种可靠、可扩展且完全托管的消息队列服务,可以帮助您构建分布式应用程序。本文将介绍如何在一个AWS账户(账户A)中创建SQS队列,并授权另一个AWS账户(账户B)中的用户和角色…...
Starrocks 写入报错 primary key memory usage exceeds the limit
背景 本文基于 StarRocks 3.3.5 单个Starrocks BE配置是 16CU 32GB 在Flink Yaml CDC 任务往 Starrocks写数据的过程中,突然遇到了primary key memory usage exceeds the limit 问题,具体如下: java.lang.RuntimeException: com.starrocks.…...
【Java】I/O 流篇 —— 打印流与压缩流
目录 打印流概述字节打印流构造方法成员方法代码示例 字符打印流构造方法成员方法代码示例 打印流的应用场景 解压缩/压缩流解压缩流压缩流 Commons-io 工具包概述Commons-io 使用步骤Commons-io 常见方法代码示例 Hutool 工具包 打印流 概述 分类:打印流一般是指…...
刷屏(0和1)
计算机有一个“好东西”,它就是2进制,虽然无法快速转换,but可以通过编程来实现。 附代码: #include <bits/stdc.h> using namespace std; int main() {int a,b,c;cout<<" …...
keil主题(vscode风格)
#修改global.prop文件,重新打开keil即可 # Keil uVision Global Properties File # This file is used to customize the appearance of the editor# Editor Font editor.font.nameConsolas editor.font.size10 editor.font.style0# Editor Colors editor.backgro…...
windows服务器更新jar包脚本
【需求】Java每次发布新的版本都需要先kill掉原来的服务,然后再启动新的包 有了这个脚本只需要把包替换掉,服务会自动kill 以6001 为例 完整的脚本如下 echo off REM 检查端口 6001 是否被占用 netstat -ano | findstr :6001 > nul IF %ERRORLE…...
前端实现OSS上传图片(Vue3+vant)
首先,下面这些信息从阿里云服务器OSS管理中获取 aliyun:oss:file:endpoint: "oss-cn-beijing.aliyuncs.com"keyid: "xxxxxxxxx"keysecret: "xxxxxxxxxxxx"bucketname: "xxxx"一、安装OSS npm install ali-oss 二、以下步…...
Vue.js 组件开发指南:实现、传值与优缺点分析
1. 组件的实现与使用 1.1 组件的定义 在 Vue.js 中,组件是独立的、可复用的 Vue 实例。每个组件可以包含自己的模板、逻辑和样式。 // 定义一个简单的组件 Vue.component(my-component, {template: <div>这是一个自定义组件</div> });1.2 组件的使用…...
vulnhub靶场之【kioptrix-5】靶机
前言 靶机:kioptrix-5,IP地址为192.168.10.10 攻击:kali,IP地址为192.168.10.6 都采用VMware虚拟机,网卡为桥接模式 这里需要注意,在靶机安装后,先把原本的网卡删除,重新添加一个…...
#渗透测试#批量漏洞挖掘#(0day)某智能终端操作平台前台存在通用SQL注入漏洞(CVE-2022-21047)
免责声明 本教程仅为合法的教学目的而准备,严禁用于任何形式的违法犯罪活动及其他商业行为,在使用本教程前,您应确保该行为符合当地的法律法规,继续阅读即表示您需自行承担所有操作的后果,如有异议,请立即停…...
Linux系统中proc是做什么的?
在 Linux 系统中,/proc 是一个虚拟文件系统(Virtual Filesystem),它不对应实际的磁盘文件,而是内核在内存中动态生成的。/proc 文件系统提供了一种与内核数据结构交互的方式,它主要用于存放与系统运行状态、…...
el-table修改表格颜色
文章目录 一、el-table属性修改表格颜色1.1、header-row-class-name修改表头行颜色1.2、header-row-style修改表头样式1.3、row-class-name修改行颜色 二、el-table-column属性修改表格颜色2.1、class-name修改整列的颜色2.2、label-class-name修改列标题颜色 本文讲解vue修改e…...
014存储期(时间)
一、基本概念 C语言中,变量都是有一定的生存周期的,所谓生存周期指的是从分配到释放的时间间隔。为变量分配内存相当于变量的诞生,释放其内存相当于变量的死亡。从诞生到死亡就是一个变量的生命周期。 根据定义方式的不同,变量的…...
执行git操作时报错:`remote: [session-b8xxxda3] Access denied ...`解决方案
问题描述: 执行git push -u origin "master"时报错: > remote: [session-b849cda3] Access denied > fatal: unable to access https://gitee.com/jyunee/maibobo.git/: The requested URL returned error: 403表示没有权限访问远程仓库…...
Redis版本的EOL策略与升级路径(刷到别划走)
各位看官,刷到就点进来,大数据已经抓到你喽~😊 前言 在软件行业做服务端开发的我们,多多少少都会接触到Redis,用它来缓存数据、实现分布式锁等,相关八股文烂熟于心,但是往往会忽略具…...
算法题:数组中的第 K 个最大元素(中等难度)
一、题目 给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。 请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。 示例 1: 输入:nums [3,2,1,5,6,4], k 2 输出:…...
进行性核上性麻痹患者的生活护理指南
进行性核上性麻痹是一种神经系统退行性疾病,合理的生活护理能有效改善症状,提高生活质量。 居家环境要安全。移除地面杂物,铺设防滑垫,安装扶手,降低跌倒风险。在浴室、厨房等湿滑区域要特别加强防护措施。建议在床边、…...
Python大战Java:AI时代的编程语言‘复仇者联盟‘能否换C位?
背景 当Java程序员在咖啡机前念叨’Python凭什么抢我饭碗’时,AI实验室里的Python工程师正用5行代码召唤出神经网络——这场编程语言的’权力的游戏’,胜负可能比你想象的更魔幻!" 一、茶水间里的战争:Java和Python的相爱相…...
SpringBoot AI + PgVector向量库 + Openai Embedding模型
Spring Boot 项目引入 下载仓库地址 <dependencyManagement><dependencies><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-bom</artifactId><version>${spring-ai.version}</version>&l…...
目标检测——数据处理
1. Mosaic 数据增强 Mosaic 数据增强步骤: (1). 选择四个图像: 从数据集中随机选择四张图像。这四张图像是用来组合成一个新图像的基础。 (2) 确定拼接位置: 设计一个新的画布(输入size的2倍),在指定范围内找出一个随机点(如…...
数据集笔记:新加坡LTA MRT 车站出口、路灯 等位置数据集
1 MRT 车站出口 data.gov.sg (geojson格式) 1.1 kml格式 data.gov.sg 2 路灯 data.govsg ——geojson data.gov.sg——kml 版本 3 道路摄像头数据集 data.gov.sg 4 自行车道网络 data.gov.sg 5 学校区域 data.gov.sg 6 自行车停车架ÿ…...
Highcharts 配置语法详解
Highcharts 配置语法详解 引言 Highcharts 是一个功能强大的图表库,广泛应用于数据可视化领域。本文将详细介绍 Highcharts 的配置语法,帮助您快速上手并制作出精美、实用的图表。 高级配置结构 Highcharts 的配置对象通常包含以下几部分:…...
Python 项目安全实战:工具应用、规范制定、数据防护与架构加固
Python 项目安全实战:工具应用、规范制定、数据防护与架构加固 本文聚焦 Python 项目安全,深入介绍安全工具如 Bandit、OWASP ZAP 的实战操作,涵盖对特定模块扫描及 Web 测试进阶应用。详细阐述团队如何制定并持续更新安全编码规范ÿ…...
linux ununtu通过nginx-1.6.2.tar.gz安装nginx并安装在自定义目录XXX下 的步骤
Ubuntu 下通过源码安装 Nginx 1.6.2 到自定义目录 /home/aot/nginx 的步骤 以下是将 Nginx 1.6.2 源码包离线安装到自定义目录的详细流程,包含依赖管理、编译配置和服务管理: 一、准备工作 1. 下载源码包和依赖(需联网环境准备)…...
《Python百练成仙》31-40章(不定时更新)
第卅一章 函数结丹def开紫府 罗酆山的鬼门关吞吐着猩红的变量阴风,每个风眼都涌动着作用域混乱的灵力乱流。叶军手握薛香遗留的丹田玉简,玉简表面浮现出残缺的函数符文: def 凝聚金丹(灵气):道基 灵气 * 0.618print(金丹品质) # 作用域外变…...
Python--内置模块和开发规范(上)
1. 内置模块 1.1 JSON 模块 核心功能 序列化:Python 数据类型 → JSON 字符串 import json data [{"id": 1, "name": "武沛齐"}, {"id": 2, "name": "Alex"}] json_str json.dumps(data, ensure_a…...
使用DeepSeek实现自动化编程:类的自动生成
目录 简述 1. 通过注释生成C类 1.1 模糊生成 1.2 把控细节,让结果更精准 1.3 让DeepSeek自动生成代码 2. 验证DeepSeek自动生成的代码 2.1 安装SQLite命令行工具 2.2 验证DeepSeek代码 3. 测试代码下载 简述 在现代软件开发中,自动化编程工具如…...
植物大战僵尸金铲铲版 v1.1.6(windows+安卓)
游戏简介 《植物大战僵尸金铲铲版》是由“古见xzz”、“对不起贱笑了”、“是怪哉吖”等联合开发的民间魔改版本,融合了原版塔防玩法与《金铲铲之战》的自走棋元素,属于非官方同人作品。 游戏特点 合成升星机制:三个相同低星植物可合成更高…...
LeetCode 热题 100_寻找两个正序数组的中位数(68_4_困难_C++)(二分查找)(先合并再挑选中位数;划分数组(二分查找))
LeetCode 热题 100_寻找两个正序数组的中位数(68_4) 题目描述:输入输出样例:题解:解题思路:思路一(先合并再挑选中位数):思路二(划分数组(二分查找…...
酒店管理系统(代码+数据库+LW)
摘 要 时代的发展带来了巨大的生活改变,很多事务从传统手工管理转变为自动管理。自动管理是利用科技的发展开发的新型管理系统,这类管理系统可以帮助人完成基本的繁琐的反复工作。酒店是出门的必需品,无论出差还是旅游都需要酒店的服务。由…...
关于C/C++的输入和输出
目录 一、C语言中的scanf 有关scanf()的例子 二、C语言中的printf 有关printf()的例子 三、C中的cin、cout 四、字符的输入 1、cin.get() 2、cin.get() 3、cin.getline() 4、getline() 5、getchar() 五、string类型字符串长度 1、length() 2、size() 一、C语言中…...
袋鼠数据库工具 6.4 AI 版已上线
袋鼠数据库工具 6.4 AI 版已于 2025 年 2 月 26 日上线1。以下是该版本的一些新特性1: 地图支持:支持坐标定位并支持缩放动画;支持路线图,可在路线位置之间跳转;支持图层切换、标记和路线图图层切换;支持新…...
【AGI】DeepSeek开源周:The whale is making waves!
DeepSeek开源周:The whale is making waves! 思维火花引言一、DeepSeek模型体系的技术演进1. 通用语言模型:DeepSeek-V3系列2. 推理优化模型:DeepSeek-R1系列3. 多模态模型:Janus系列 二、开源周三大工具库的技术解析1…...
【无人机】无人机飞行日志下载及分析,飞行日志分析软件的使用
目录 一、飞行日志下载 1.1 通过地面站下载 1.1.1 QGroundControl(QGC)地面站 1.1.2 Mission Planner 地面站 1.2 通过内存卡读卡器下载 1.3 通过数传模块下载(数传日志) 二、飞行日志分析 2.1 使用 Flight Review 分析 …...
【朝夕教育】《鸿蒙原生应用开发从零基础到多实战》003-TypeScript 中的类
标题详情作者简介愚公搬代码头衔华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,CSDN商业化专家,阿里云专家博主,阿里云签约作者,腾讯云优秀博主&…...
Java并发编程之可见性、原子性和有序性
引言 CPU缓存与内存产生的一致性问题(可见性) CPU时间片切换产生的原子性问题 CPU指令编译优化产生的有序性问题 并发编程问题的根源 CPU、内存、I/O设备三者速度差异一直是 核心矛盾 三者速度差异可形象描述为:天上一天(CPU),…...