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

工程化与框架系列(13)--虚拟DOM实现

虚拟DOM实现 🌳

虚拟DOM(Virtual DOM)是现代前端框架的核心技术之一,它通过在内存中维护UI的虚拟表示来提高渲染性能。本文将深入探讨虚拟DOM的实现原理和关键技术。

虚拟DOM概述 🌟

💡 小知识:虚拟DOM是对真实DOM的一种轻量级抽象表示,它以JavaScript对象的形式存在,通过diff算法计算最小更新路径,从而减少对实际DOM的操作。

为什么需要虚拟DOM

在现代前端开发中,虚拟DOM带来以下优势:

  1. 性能优化

    • 批量DOM更新
    • 最小化DOM操作
    • 跨平台渲染
    • 服务端渲染
  2. 开发体验

    • 声明式编程
    • 组件化开发
    • 状态驱动UI
    • 代码可维护性
  3. 跨平台能力

    • 浏览器渲染
    • 原生应用渲染
    • 服务端渲染
    • Canvas/WebGL渲染
  4. 调试能力

    • 状态追踪
    • 组件调试
    • 性能分析
    • 错误边界

核心实现 ⚡

虚拟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;
}

最佳实践建议 ⭐

性能优化建议

  1. 节点优化

    • 使用key标识
    • 提取静态节点
    • 避免深层嵌套
    • 合理使用v-show
  2. 更新优化

    • 批量更新
    • 异步更新
    • 合并操作
    • 缓存结果
  3. 渲染优化

    • 懒加载组件
    • 虚拟滚动
    • 时间切片
    • 优先级调度

开发建议

  1. 组件设计
// 好的实践
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)));}
});
  1. 更新处理
// 好的实践
function handleUpdates() {// 批量更新nextTick(() => {state.count++;state.total = calculateTotal();});
}// 避免频繁更新
function badUpdate() {state.count++;state.total = calculateTotal();// 直接触发DOM更新
}

结语 📝

虚拟DOM是现代前端框架的重要基石,通过本文,我们学习了:

  1. 虚拟DOM的核心概念
  2. DOM diff算法的实现
  3. 组件系统的设计
  4. 性能优化的策略
  5. 开发中的最佳实践

💡 学习建议:

  1. 深入理解虚拟DOM原理
  2. 掌握diff算法的优化
  3. 注重性能优化实践
  4. 遵循最佳实践指南
  5. 持续学习新的优化方案

如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇

终身学习,共同成长。

咱们下一期见

💻

相关文章:

工程化与框架系列(13)--虚拟DOM实现

虚拟DOM实现 &#x1f333; 虚拟DOM&#xff08;Virtual DOM&#xff09;是现代前端框架的核心技术之一&#xff0c;它通过在内存中维护UI的虚拟表示来提高渲染性能。本文将深入探讨虚拟DOM的实现原理和关键技术。 虚拟DOM概述 &#x1f31f; &#x1f4a1; 小知识&#xff1…...

Java实现pdf文件压缩(aspose-pdf实现压缩、itextpdf去除aspose-pdf版权水印)

Java实现pdf文件压缩 时间换空间&#xff0c;实现pdf文件无损压缩。 1、依赖准备 市面上操作pdf文件的组件有spire.pdf.free、itextpdf、openpdf、pdfbox等&#xff0c;它们各有千秋。我们主要完成的场景为压缩&#xff0c;减少文件大小去优化存储、传输等。 在这里选取的组件…...

生态安全相关

概念&#xff1a;生态安全指一个国家具有支撑国家生存发展的较为完整、不受威胁的生态系统&#xff0c;以及应对内外重大生态问题的能力。 &#xff08;1&#xff09;国外生态安全的研究进展 国际上对生态安全的研究是从“环境”与“安全”之间的关系展开的。开始的阶段&#x…...

通过 ANSYS Discovery 进行 CFD 分析,增强工程设计

概括 工程师使用计算流体动力学 (CFD) 分析来研究和优化各种应用中的流体流动和传热分析。ANSYS Discovery 是一个用户友好的软件平台&#xff0c;使工程师能够轻松设置和解决 CFD 模型&#xff0c;并能够通知设计修改 在这篇博文中&#xff0c;我们将重点介绍在 Ansys Disc…...

2. 在后端代码中加入日志记录模块

1. 说明 日志模块基本上是每一个软件系统开发中必不可少的&#xff0c;主要用于持久记录一些代码运行中的输出信息&#xff0c;辅助编码人员进行代码调试&#xff0c;以及后期软件上线运行报错分析。在Python中加入日志模块比较简单&#xff0c;只需要借助logging和RotatingFi…...

MySQL锁分类

一、按锁的粒度划分 全局锁 定义&#xff1a;锁定整个数据库实例&#xff0c;阻止所有写操作&#xff0c;确保数据备份一致性。加锁方式&#xff1a;通过FLUSH TABLES WITH READ LOCK实现&#xff0c;释放需执行UNLOCK TABLES。应用场景&#xff1a;适用于全库逻辑备份&#xf…...

软件测试之白盒测试知识总结

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 概念与定义 白盒测试&#xff1a;侧重于系统或部件内部机制的测试&#xff0c;类型分为分支测试&#xff08;判定节点测试&#xff09;、路径测试、语句测试…...

看视频学习方法总结

以下是提高教学视频吸收率的系统性方法&#xff0c;结合认知科学原理和实际学习场景&#xff0c;帮助您最大化学习效果&#xff1a; 一、观看前的黄金准备阶段 60秒快速扫描法 用1分钟快速浏览视频目录、章节标题和简介&#xff0c;建立知识框架。荷兰伊拉斯姆斯大学实验表明&…...

nvm的学习

学习 nvm&#xff08;Node Version Manager&#xff09; 是掌握 Node.js 开发的关键技能之一。以下是系统的学习路径和实战指南&#xff0c;涵盖从基础到进阶的内容&#xff1a; 一、基础入门 1. nvm 的核心作用 多版本共存&#xff1a;安装和管理多个 Node.js 版本&#xff…...

OpenCV计算摄影学(5)处理一系列图像(例如视频帧)的非局部均值去噪的函数fastNlMeansDenoisingColoredMulti()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 对用于彩色图像序列的 fastNlMeansDenoisingMulti 函数的修改。 cv::fastNlMeansDenoisingColoredMulti 函数是 OpenCV 中用于处理一系列图像&am…...

mysql安装教程,超详细图文教程(附安装包)MySQL8.0安装教程

文章目录 前言一、MySQL安装包下载二、mysql安装教程 前言 本教程旨在为大家提供详细、清晰的 mysql安装教程&#xff0c;帮助你轻松跨过安装门槛&#xff0c;顺利开启数据库管理与开发的精彩之旅。 一、MySQL安装包下载 MySQL 是一款备受欢迎的开源关系型数据库管理系统&…...

Spark内存迭代计算

一、宽窄依赖 窄依赖&#xff1a;父RDD的一个分区数据全部发往子RDD的一个分区 宽依赖&#xff1a;父RDD的一个分区数据发往子RDD的多个分区&#xff0c;也称为shuffle 二、Spark是如何进行内存计算的&#xff1f;DAG的作用&#xff1f;Stage阶段划分的作用&#xff1f; &a…...

Odoo免费开源CRM技术实战:从商机线索关联转化为售后工单的应用

文 / 开源智造 Odoo金牌服务 Odoo&#xff1a;功能强大且免费开源的CRM Odoo 引入了一种高效的客户支持管理方式&#xff0c;即将 CRM 线索转换为服务台工单。此功能确保销售和支持团队能够无缝协作&#xff0c;从而提升客户满意度并缩短问题解决时间。通过整合 CRM 模块与服…...

对seacmsv9进行sql注入,orderby,过滤information_schema

对seacmsv9进行sql注入&#xff0c;orderby&#xff0c;过滤information_schema 1.对seacmsv9进行sql注入 海洋影视管理系统&#xff08;seacms&#xff0c;海洋cms&#xff09;是一套专为不同需求的站长而设计的视频点播系统&#xff0c;采用的是 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写数据的过程中&#xff0c;突然遇到了primary key memory usage exceeds the limit 问题&#xff0c;具体如下&#xff1a; java.lang.RuntimeException: com.starrocks.…...

【Java】I/O 流篇 —— 打印流与压缩流

目录 打印流概述字节打印流构造方法成员方法代码示例 字符打印流构造方法成员方法代码示例 打印流的应用场景 解压缩/压缩流解压缩流压缩流 Commons-io 工具包概述Commons-io 使用步骤Commons-io 常见方法代码示例 Hutool 工具包 打印流 概述 分类&#xff1a;打印流一般是指…...

刷屏(0和1)

计算机有一个“好东西”&#xff0c;它就是2进制&#xff0c;虽然无法快速转换&#xff0c;but可以通过编程来实现。 附代码&#xff1a; #include <bits/stdc.h> using namespace std; int main() {int a,b,c;cout<<" …...

keil主题(vscode风格)

#修改global.prop文件&#xff0c;重新打开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掉原来的服务&#xff0c;然后再启动新的包 有了这个脚本只需要把包替换掉&#xff0c;服务会自动kill 以6001 为例 完整的脚本如下 echo off REM 检查端口 6001 是否被占用 netstat -ano | findstr :6001 > nul IF %ERRORLE…...

前端实现OSS上传图片(Vue3+vant)

首先&#xff0c;下面这些信息从阿里云服务器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 中&#xff0c;组件是独立的、可复用的 Vue 实例。每个组件可以包含自己的模板、逻辑和样式。 // 定义一个简单的组件 Vue.component(my-component, {template: <div>这是一个自定义组件</div> });1.2 组件的使用…...

vulnhub靶场之【kioptrix-5】靶机

前言 靶机&#xff1a;kioptrix-5&#xff0c;IP地址为192.168.10.10 攻击&#xff1a;kali&#xff0c;IP地址为192.168.10.6 都采用VMware虚拟机&#xff0c;网卡为桥接模式 这里需要注意&#xff0c;在靶机安装后&#xff0c;先把原本的网卡删除&#xff0c;重新添加一个…...

#渗透测试#批量漏洞挖掘#(0day)某智能终端操作平台前台存在通用SQL注入漏洞(CVE-2022-21047)

免责声明 本教程仅为合法的教学目的而准备&#xff0c;严禁用于任何形式的违法犯罪活动及其他商业行为&#xff0c;在使用本教程前&#xff0c;您应确保该行为符合当地的法律法规&#xff0c;继续阅读即表示您需自行承担所有操作的后果&#xff0c;如有异议&#xff0c;请立即停…...

Linux系统中proc是做什么的?

在 Linux 系统中&#xff0c;/proc 是一个虚拟文件系统&#xff08;Virtual Filesystem&#xff09;&#xff0c;它不对应实际的磁盘文件&#xff0c;而是内核在内存中动态生成的。/proc 文件系统提供了一种与内核数据结构交互的方式&#xff0c;它主要用于存放与系统运行状态、…...

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语言中&#xff0c;变量都是有一定的生存周期的&#xff0c;所谓生存周期指的是从分配到释放的时间间隔。为变量分配内存相当于变量的诞生&#xff0c;释放其内存相当于变量的死亡。从诞生到死亡就是一个变量的生命周期。 根据定义方式的不同&#xff0c;变量的…...

执行git操作时报错:`remote: [session-b8xxxda3] Access denied ...`解决方案

问题描述&#xff1a; 执行git push -u origin "master"时报错&#xff1a; > remote: [session-b849cda3] Access denied > fatal: unable to access https://gitee.com/jyunee/maibobo.git/: The requested URL returned error: 403表示没有权限访问远程仓库…...

Redis版本的EOL策略与升级路径(刷到别划走)

各位看官&#xff0c;刷到就点进来&#xff0c;大数据已经抓到你喽&#xff5e;&#x1f60a; 前言 在软件行业做服务端开发的我们&#xff0c;多多少少都会接触到Redis&#xff0c;用它来缓存数据、实现分布式锁等&#xff0c;相关八股文烂熟于心&#xff0c;但是往往会忽略具…...

算法题:数组中的第 K 个最大元素(中等难度)

一、题目 给定整数数组 nums 和整数 k&#xff0c;请返回数组中第 k 个最大的元素。 请注意&#xff0c;你需要找的是数组排序后的第 k 个最大的元素&#xff0c;而不是第 k 个不同的元素。 示例 1&#xff1a; 输入&#xff1a;nums [3,2,1,5,6,4], k 2 输出&#xff1a;…...

进行性核上性麻痹患者的生活护理指南

进行性核上性麻痹是一种神经系统退行性疾病&#xff0c;合理的生活护理能有效改善症状&#xff0c;提高生活质量。 居家环境要安全。移除地面杂物&#xff0c;铺设防滑垫&#xff0c;安装扶手&#xff0c;降低跌倒风险。在浴室、厨房等湿滑区域要特别加强防护措施。建议在床边、…...

Python大战Java:AI时代的编程语言‘复仇者联盟‘能否换C位?

背景 当Java程序员在咖啡机前念叨’Python凭什么抢我饭碗’时&#xff0c;AI实验室里的Python工程师正用5行代码召唤出神经网络——这场编程语言的’权力的游戏’&#xff0c;胜负可能比你想象的更魔幻&#xff01;" 一、茶水间里的战争&#xff1a;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). 选择四个图像&#xff1a; 从数据集中随机选择四张图像。这四张图像是用来组合成一个新图像的基础。 (2) 确定拼接位置&#xff1a; 设计一个新的画布(输入size的2倍)&#xff0c;在指定范围内找出一个随机点&#xff08;如…...

数据集笔记:新加坡LTA MRT 车站出口、路灯 等位置数据集

1 MRT 车站出口 data.gov.sg &#xff08;geojson格式&#xff09; 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 自行车停车架&#xff…...

Highcharts 配置语法详解

Highcharts 配置语法详解 引言 Highcharts 是一个功能强大的图表库&#xff0c;广泛应用于数据可视化领域。本文将详细介绍 Highcharts 的配置语法&#xff0c;帮助您快速上手并制作出精美、实用的图表。 高级配置结构 Highcharts 的配置对象通常包含以下几部分&#xff1a…...

Python 项目安全实战:工具应用、规范制定、数据防护与架构加固

Python 项目安全实战&#xff1a;工具应用、规范制定、数据防护与架构加固 本文聚焦 Python 项目安全&#xff0c;深入介绍安全工具如 Bandit、OWASP ZAP 的实战操作&#xff0c;涵盖对特定模块扫描及 Web 测试进阶应用。详细阐述团队如何制定并持续更新安全编码规范&#xff…...

linux ununtu通过nginx-1.6.2.tar.gz安装nginx并安装在自定义目录XXX下 的步骤

Ubuntu 下通过源码安装 Nginx 1.6.2 到自定义目录 /home/aot/nginx 的步骤 以下是将 Nginx 1.6.2 源码包离线安装到自定义目录的详细流程&#xff0c;包含依赖管理、编译配置和服务管理&#xff1a; 一、准备工作 1. 下载源码包和依赖&#xff08;需联网环境准备&#xff09;…...

《Python百练成仙》31-40章(不定时更新)

第卅一章 函数结丹def开紫府 罗酆山的鬼门关吞吐着猩红的变量阴风&#xff0c;每个风眼都涌动着作用域混乱的灵力乱流。叶军手握薛香遗留的丹田玉简&#xff0c;玉简表面浮现出残缺的函数符文&#xff1a; def 凝聚金丹(灵气):道基 灵气 * 0.618print(金丹品质) # 作用域外变…...

Python--内置模块和开发规范(上)

1. 内置模块 1.1 JSON 模块 核心功能 序列化&#xff1a;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 把控细节&#xff0c;让结果更精准 1.3 让DeepSeek自动生成代码 2. 验证DeepSeek自动生成的代码 2.1 安装SQLite命令行工具 2.2 验证DeepSeek代码 3. 测试代码下载 简述 在现代软件开发中&#xff0c;自动化编程工具如…...

植物大战僵尸金铲铲版 v1.1.6(windows+安卓)

游戏简介 《植物大战僵尸金铲铲版》是由“古见xzz”、“对不起贱笑了”、“是怪哉吖”等联合开发的民间魔改版本&#xff0c;融合了原版塔防玩法与《金铲铲之战》的自走棋元素&#xff0c;属于非官方同人作品。 游戏特点 合成升星机制&#xff1a;三个相同低星植物可合成更高…...

LeetCode 热题 100_寻找两个正序数组的中位数(68_4_困难_C++)(二分查找)(先合并再挑选中位数;划分数组(二分查找))

LeetCode 热题 100_寻找两个正序数组的中位数&#xff08;68_4&#xff09; 题目描述&#xff1a;输入输出样例&#xff1a;题解&#xff1a;解题思路&#xff1a;思路一&#xff08;先合并再挑选中位数&#xff09;&#xff1a;思路二&#xff08;划分数组&#xff08;二分查找…...

酒店管理系统(代码+数据库+LW)

摘 要 时代的发展带来了巨大的生活改变&#xff0c;很多事务从传统手工管理转变为自动管理。自动管理是利用科技的发展开发的新型管理系统&#xff0c;这类管理系统可以帮助人完成基本的繁琐的反复工作。酒店是出门的必需品&#xff0c;无论出差还是旅游都需要酒店的服务。由…...

关于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&#xff1a; 地图支持&#xff1a;支持坐标定位并支持缩放动画&#xff1b;支持路线图&#xff0c;可在路线位置之间跳转&#xff1b;支持图层切换、标记和路线图图层切换&#xff1b;支持新…...

【AGI】DeepSeek开源周:The whale is making waves!

DeepSeek开源周&#xff1a;The whale is making waves&#xff01; 思维火花引言一、DeepSeek模型体系的技术演进1. 通用语言模型&#xff1a;DeepSeek-V3系列2. 推理优化模型&#xff1a;DeepSeek-R1系列3. 多模态模型&#xff1a;Janus系列 二、开源周三大工具库的技术解析1…...

【无人机】无人机飞行日志下载及分析,飞行日志分析软件的使用

目录 一、飞行日志下载 1.1 通过地面站下载 1.1.1 QGroundControl&#xff08;QGC&#xff09;地面站 1.1.2 Mission Planner 地面站 1.2 通过内存卡读卡器下载 1.3 通过数传模块下载&#xff08;数传日志&#xff09; 二、飞行日志分析 2.1 使用 Flight Review 分析 …...

【朝夕教育】《鸿蒙原生应用开发从零基础到多实战》003-TypeScript 中的类

标题详情作者简介愚公搬代码头衔华为云特约编辑&#xff0c;华为云云享专家&#xff0c;华为开发者专家&#xff0c;华为产品云测专家&#xff0c;CSDN博客专家&#xff0c;CSDN商业化专家&#xff0c;阿里云专家博主&#xff0c;阿里云签约作者&#xff0c;腾讯云优秀博主&…...

Java并发编程之可见性、原子性和有序性

引言 CPU缓存与内存产生的一致性问题&#xff08;可见性&#xff09; CPU时间片切换产生的原子性问题 CPU指令编译优化产生的有序性问题 并发编程问题的根源 CPU、内存、I/O设备三者速度差异一直是 核心矛盾 三者速度差异可形象描述为&#xff1a;天上一天(CPU)&#xff0c;…...