工程化与框架系列(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概述 🌟 💡 小知识࿱…...

数据结构之各类排序算法代码及其详解
1. 排序的概念 排序是一种常见的算法概念,用于将一组数据按照特定的顺序进行排列。排序算法的目的是将一组数据按照递增或递减的顺序重新排列。常见的排序算法包括冒泡排序、插入排序、选择排序、快速排序、归并排序等。排序算法的选择通常取决于数据规模、数据分布…...

【洛谷贪心算法】P1090合并果子
为了使消耗的体力最小,每次都应该选择当前重量最小的两堆果子进行合并。可以使用优先队列(小根堆)来实现这个过程,优先队列可以自动维护元素的顺序,每次取出堆顶的两个元素(即最小的两个元素)进…...

【告别双日期面板!一招实现el-date-picker智能联动日期选择】
告别双日期面板!一招实现el-date-picker智能联动日期选择 1.需求背景2.DateTimePicker 现状图3.日期选择器实现代码4.日期选择器实现效果图5.日期时间选择器实现代码6.日期时间选择器实现效果图 1.需求背景 在用户使用时间查询时,我们经常需要按月份筛选…...

现今大语言模型性能(准确率)比较
现今大语言模型性能(准确率)比较 表头信息:表的标题为“大语言模型性能比较结果”(英文:Table 1: Large Language Model Performance Comparison Results),表明该表是用于对比不同大语言模型的性能。列信息: 模型:列出参与比较的不同大语言模型名称,包括LLAMA3(70B)…...

程序诗篇里的灵动笔触:指针绘就数据的梦幻蓝图(水文,勿三)
大家好啊,我是小象٩(๑ω๑)۶ 我的博客:Xiao Xiangζั͡ޓއއ 很高兴见到大家,希望能够和大家一起交流学习,共同进步。 这一节我们来学习指针的相关知识,学习内存和地址,指针变量和地址,包…...
在 UniApp 中实现中间凸起 TabBar 的完整指南
如何在 UniApp 中设置中间 TabBar 凸起效果 在移动应用开发中,TabBar 是常见的导航组件,而中间凸起的 TabBar 按钮则是一种流行的设计风格,常用于突出重要功能(如发布、拍照等)。UniApp 提供了 midButton 属性&#x…...
Redis大key
Redis大key基本概念,影响 Redis 大 key 指在 Redis 中存储了大量数据的键,它会对 Redis 的性能和内存管理产生影响。 大key的定义与value的大小和元素数量有关,但这个定义并不是绝对的,而是相对的,具体取决于系统的使用…...

WPF高级 | WPF 与数据库交互:连接、查询与数据更新
WPF高级 | WPF 与数据库交互:连接、查询与数据更新 前言一、数据库交互基础概念1.1 数据库简介1.2 数据访问技术 二、WPF 与数据库连接2.1 连接字符串2.2 建立连接 三、WPF 中的数据查询3.1 使用ADO.NET进行数据查询3.2 使用 Entity Framework 进行数据查询3.3 使用…...

CogBlobTool工具
CogBlobTool是一款专用于图像斑点检测于分析的 工具,通过灰度值阈值分割和特征过滤,帮助在复杂背景中提取目标区域,并计算几何属性。 效果图 注意:在这里只有一张图像可以不使用模板匹配工具 CogBlobTool工具的功能 斑点检测于…...

C# WinForm程序中如何调试dll接口
公司的SF系统是自主开发的。不同的机种会有不同数据记录保存的需求,尤其是客户SQE更是各种奇思妙想......于是做了一个接口,实践之下效果还不错呢。 每每总是忘记怎么调试接口,特记录下备查。首先要将, 1 DLL项目与WinForms项目…...
自然语言处理:词频-逆文档频率
介绍 大家好,博主又来给大家分享知识了。本来博主计划完成稠密向量表示的内容分享后,就开启自然语言处理中文本表示的讲解。可在整理分享资料的时候,博主发现还有个知识点,必须得单独拎出来好好说道说道。 这就是TF-IDF…...

【银河麒麟高级服务器操作系统】服务器测试业务耗时问题分析及处理全流程分享
更多银河麒麟操作系统产品及技术讨论,欢迎加入银河麒麟操作系统官方论坛 https://forum.kylinos.cn 了解更多银河麒麟操作系统全新产品,请点击访问 麒麟软件产品专区:https://product.kylinos.cn 开发者专区:https://developer…...

基于大数据的民宿旅馆消费数据分析系统
【大数据】基于大数据的民宿旅馆消费数据分析系统(完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 该系统可以揭示民宿市场的消费模式和价格分布情况,帮助理解消费者偏好、价格走势及…...
Spring-AI搭建企业专属知识库 一
环境介绍:Spring3.3.2 JDK 21 POM文件 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation&…...

极简本地体验deepseek大模型教程
一 题外随感:时代之问 就像狄更斯在双城记中所述,“这是最好的时代,这是最坏的时代”。每一代人都有其所处的时代,每一个时代都有其所谓好的一面和不那么好的一面。很多时候随口的一句大环境不好,就似乎给了自己一个最…...
RabbitMQ系列(五)基本概念之Queue
在 RabbitMQ 中,Queue(队列) 是存储消息的容器,也是消息传递的核心载体。以下是其核心特性与作用的全方位解析: 一、Queue 的定义与核心作用 消息存储容器 Queue 是 RabbitMQ 中实际存储消息的实体,生产者…...
【记录】成为创作者的第 730 天(两年)
收获 还是总在感叹走到今天收获的一切,都是自己曾经不敢想的。 无论是靠自己努力拿到的 Offer,还是在 CSDN 网站上结交的网友和前辈们,都是我莫大的荣幸和财富,感恩一切、感恩自己。 过去一年的收获真的数不胜数,抛…...

深度剖析数据分析职业成长阶梯
一、数据分析岗位剖析 目前,数据分析领域主要有以下几类岗位:业务数据分析师、商业数据分析师、数据运营、数据产品经理、数据工程师、数据科学家等,按照工作侧重点不同,本文将上述岗位分为偏业务和偏技术两大类,并对…...

【XSS】DVWA靶场XSS攻击
一、XSS攻击 1.1. XSS 攻击简介 XSS(Cross-Site Scripting,跨站脚本攻击)是一种常见的Web安全漏洞,它允许攻击者在受害者的浏览器中执行恶意脚本。攻击者通常通过在Web应用程序中注入恶意脚本代码(如JavaScript&…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...

Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
Java并发编程实战 Day 11:并发设计模式
【Java并发编程实战 Day 11】并发设计模式 开篇 这是"Java并发编程实战"系列的第11天,今天我们聚焦于并发设计模式。并发设计模式是解决多线程环境下常见问题的经典解决方案,它们不仅提供了优雅的设计思路,还能显著提升系统的性能…...
拟合问题处理
在机器学习中,核心任务通常围绕模型训练和性能提升展开,但你提到的 “优化训练数据解决过拟合” 和 “提升泛化性能解决欠拟合” 需要结合更准确的概念进行梳理。以下是对机器学习核心任务的系统复习和修正: 一、机器学习的核心任务框架 机…...
32位寻址与64位寻址
32位寻址与64位寻址 32位寻址是什么? 32位寻址是指计算机的CPU、内存或总线系统使用32位二进制数来标识和访问内存中的存储单元(地址),其核心含义与能力如下: 1. 核心定义 地址位宽:CPU或内存控制器用32位…...