Drizzle ORM 缓存机制完全指南:从 upstashCache 到自定义 Cache 实现
Drizzle ORM 缓存机制完全指南从 upstashCache 到自定义 Cache 实现【免费下载链接】drizzle-ormORM项目地址: https://gitcode.com/gh_mirrors/dr/drizzle-ormDrizzle ORM 默认不携带任何隐式缓存每条查询都会直达数据库行为透明可控。但当你希望对特定查询甚至全部查询引入缓存层、控制写入时如何失效缓存时Drizzle 在数据库实例层面提供了统一的cache选项配合内置的 Upstash Redis 缓存实现与可继承的Cache抽象类可以做到查询即缓存、写入即失效。读完本文你将掌握 Drizzle 缓存层的完整工作流如何用upstashCache()一行接入 Redis 缓存、如何通过$withCache()细粒度控制单条查询、如何自定义缓存存储实现以及缓存键生成、TTL 配置与表级/标签级失效的底层原理。Drizzle 缓存机制概述在开始使用前先明确 Drizzle 的缓存设计哲学默认零隐式行为。正如 缓存文档 所述Drizzle 默认对查询与结果映射不做任何隐含操作引擎盖下没有任何缓存——每条查询都会直接发送到数据库你可以真实看到它。只有在以下两种场景下你才需要引入缓存对特定查询实现简单缓存逻辑例如高频读、低变更的热点数据对所有查询全局启用缓存适合读多写少、可接受短暂延迟一致性的服务。Drizzle 的缓存机制本质上类似于一组在真实查询执行前被调用的beforeQuery钩子你可以定义缓存何时启用、如何存取数据以及当数据库执行写语句insert / update / delete时应采取什么动作通常是失效相关缓存。同时Drizzle 也提供了预定义的缓存逻辑——由 Drizzle 与 Upstash 团队联合构建的upstashCache()开箱即用。要启用缓存你需要在创建drizzle()实例时传入cache选项可以使用内置实现也可以传入任意自定义缓存类实例。快速上手使用 upstashCache 预定义缓存upstashCache()是 Drizzle 内置的、基于 Upstash Redisexport function upstashCache( { url, token, config, global false }: { url: string; token: string; config?: CacheConfig; global?: boolean }, ): UpstashCache { const redis new Redis({ url, token }); return new UpstashCache(redis, config, global); }最基本的接入方式只需一行const db drizzle(process.env.DB_URL!, { cache: upstashCache() })url与token是 Upstash Redis 的 REST 连接凭证。在不传入凭证与配置的情况下缓存实例已创建但不会全局作用于所有查询——只有显式声明使用缓存的查询才会命中缓存对应strategy()返回explicit。自定义缓存继承 Cache 抽象类除了内置实现Drizzle 允许你定义完全自定义的缓存行为。只需继承Cache抽象类并实现四个成员。抽象类的源码签名位于 core/cache.tsexport abstract class Cache { static readonly [entityKind]: string Cache; abstract strategy(): explicit | all; abstract get( key: string, tables: string[], isTag: boolean, isAutoInvalidate?: boolean, ): Promiseany[] | undefined; abstract put( hashedQuery: string, response: any, tables: string[], isTag: boolean, config?: CacheConfig, ): Promisevoid; abstract onMutate( params: MutationOption, ): Promisevoid; }四个待实现成员的含义如下成员签名要点职责strategy()返回explicit \| all声明缓存策略explicit表示只有. $withCache()显式标记的查询才使用缓存all表示所有查询全局缓存。默认行为是explicitget(key)接收缓存键根据查询与参数哈希出的key从缓存存储中取回响应值数组未命中返回undefinedput(key, response, tables, config?)接收键、响应、涉及表、配置将数据库返回的结果数组写入缓存并记录该键涉及的表供后续失效使用onMutate(params)接收{ tags, tables }在 insert / update / delete 执行时被调用用于失效与受影响表相关的缓存键或按标签失效MutationOption的类型定义为export type MutationOption { tags?: string | string[]; tables?: Tableany | Tableany[] | string | string[] };完整示例TestGlobalCache这是 Drizzle 官方为缓存测试套件编写的 NodeKV 实现基于Keyv完整演示了自定义缓存的写法const db drizzle(process.env.DB_URL!, { cache: new TestGlobalCache() })import Keyv from keyv; export class TestGlobalCache extends Cache { private globalTtl: number 1000; // This object will be used to store which query keys were used // for a specific table, so we can later use it for invalidation. private usedTablesPerKey: Recordstring, string[] {}; constructor(private kv: Keyv new Keyv()) { super(); } // For the strategy, we have two options: // - explicit: The cache is used only when .$withCache() is added to a query. // - all: All queries are cached globally. // The default behavior is explicit. override strategy(): explicit | all { return all; } // This function accepts query and parameters that cached into key param, // allowing you to retrieve response values for this query from the cache. override async get(key: string): Promiseany[] | undefined { const res await this.kv.get(key) ?? undefined; return res; } // This function accepts several options to define how cached data will be stored: // - key: A hashed query and parameters. // - response: An array of values returned by Drizzle from the database. // - tables: An array of tables involved in the select queries. This information is needed for cache invalidation. // // For example, if a query uses the users and posts tables, you can store this information. Later, when the app executes // any mutation statements on these tables, you can remove the corresponding key from the cache. // If youre okay with eventual consistency for your queries, you can skip this option. override async put(key: string, response: any, tables: string[], config?: CacheConfig): Promisevoid { await this.kv.set(key, response, config ? config.ex : this.globalTtl); for (const table of tables) { const keys this.usedTablesPerKey[table]; if (keys undefined) { this.usedTablesPerKey[table] [key]; } else { keys.push(key); } } } // This function is called when insert, update, or delete statements are executed. // You can either skip this step or invalidate queries that used the affected tables. // // The function receives an object with two keys: // - tags: Used for queries labeled with a specific tag, allowing you to invalidate by that tag. // - tables: The actual tables affected by the insert, update, or delete statements, // helping you track which tables have changed since the last cache update. override async onMutate(params: { tags: string | string[], tables: string | string[] | Tableany | Tableany[]}): Promisevoid { const tagsArray params.tags ? Array.isArray(params.tags) ? params.tags : [params.tags] : []; const tablesArray params.tables ? Array.isArray(params.tables) ? params.tables : [params.tables] : []; const keysToDelete new Setstring(); for (const table of tablesArray) { const tableName is(table, Table) ? getTableName(table) : table as string; const keys this.usedTablesPerKey[tableName] ?? []; for (const key of keys) keysToDelete.add(key); } if (keysToDelete.size 0 || tagsArray.length 0) { for (const tag of tagsArray) { await this.kv.delete(tag); } for (const key of keysToDelete) { await this.kv.delete(key); for (const table of tablesArray) { const tableName is(table, Table) ? getTableName(table) : table as string; this.usedTablesPerKey[tableName] []; } } } } }这段实现揭示了自定义缓存的核心套路键-值存储get/put只需对接任何 KV 存储Redis、Keyv、内存 Map 等表级索引usedTablesPerKey反向记录了每个表被哪些查询键使用这是onMutate精准失效的基础两种失效维度tags按查询标签失效tables按受影响表失效两者可同时使用。空实现 NoopCache仓库还提供了一个NoopCache见 core/cache.ts所有方法均为空操作、get永远返回undefined。它是缓存层的默认占位实现保证未配置cache时执行路径零开销。缓存配置CacheConfig 详解CacheConfig是 Drizzle 缓存层支持的完整配置类型定义于 core/types.tsexport type CacheConfig { /** * expire time, in seconds (a positive integer) */ ex?: number; /** * expire time, in milliseconds (a positive integer). */ px?: number; /** * Unix time at which the key will expire, in seconds (a positive integer). */ exat?: number; /** * Unix time at which the key will expire, in milliseconds (a positive integer) */ pxat?: number; /** * Retain the time to live associated with the key. */ keepTtl?: boolean; /** * Set an expiration (TTL or time to live) on one or more fields of a given hash key. * Used for HEXPIRE command */ hexOptions?: NX | nx | XX | xx | GT | gt | LT | lt; };各字段含义与适用范围字段类型含义说明exnumber过期时间秒正整数等价于 RedisEXPIRE ... EX语义也是 Upstash 内部配置的默认 TTL 来源pxnumber过期时间毫秒正整数等价于PEXPIRE语义exatnumber到期的 Unix 时间戳秒绝对过期时间等价于EXPIREATpxatnumber到期的 Unix 时间戳毫秒绝对过期时间等价于PEXPIREATkeepTtlboolean保留键原有的 TTL用于覆盖已存在键时不清除原有过期时间hexOptionsNX \| nx \| XX \| xx \| GT \| gt \| LT \| lt哈希字段级过期条件对应 RedisHEXPIRE命令的选项NX仅不存在时、XX仅存在时、GT/LT仅当大于/小于当前 TTL 时大写小写等价在UpstashCache内部toInternalConfig()会从CacheConfig中提取ex作为默认 TTL未配置时默认值为 1 秒并透传hexOptions见 upstash/cache.ts每条查询的 TTL 遵循单查询配置优先、否则回退全局配置的规则。配置缓存实例的三种形态创建drizzle()实例时cache选项支持以下组合1. 只配置凭证不全局启用缓存const db drizzle(process.env.DB_URL!, { cache: upstashCache({ url: , token: }) })此时缓存实例已可用但不会全局作用于所有查询只有显式调用.$withCache()的查询才会使用缓存。2. 配置凭证并全局启用缓存const db drizzle(process.env.DB_URL!, { cache: upstashCache({ url: , token: , global: true }) })global: true使strategy()返回all所有查询默认使用缓存。3. 配置凭证 全局缓存 自定义默认 TTL 配置const db drizzle(process.env.DB_URL!, { cache: upstashCache({ url: , token: , global: true, config: {} }) })config中填写的CacheConfig将成为所有查询的默认缓存配置除非被单条查询覆盖。缓存使用两种策略与 $withCache 细粒度控制配置好数据库实例后就可以在查询层面使用缓存了。Case 1global: false显式模式const db drizzle(process.env.DB_URL!, { cache: upstashCache({ url: , token: }) })此配置下普通查询不会使用缓存const res await db.select().from(users) // However, any mutate operation will trigger the onMutate function in the cache // and attempt to invalidate queries that used the tables involved in this mutation query. await db.insert(users).value({ email: cachemanupstash.com })注意即使当前查询没启用缓存任何写操作insert/update/delete依然会触发onMutate尝试失效与受影响表相关的缓存键——这一点保证了缓存在写入后的正确性。要让某条查询真正使用缓存需要链式调用.$withCache()const res await db.select().from(users).$withCache()$withCache接受一组选项用于管理该查询的缓存策略实现见 pg-core/query-builders/select.tsmysql / singlestore / sqlite / gel 的 select builder 均有同款实现// rewrite the global config options for this specific query .$withCache({ config: {} }) // give a query custom cache key instead of hashing queryparams under the hood .$withCache({ tag: custom_key }) // disable autoinvalidation for this query, if you are fine with eventual consistency for this specific query .$withCache({ autoInvalidate: false })三个选项的含义选项类型作用configCacheConfig覆盖全局配置为该查询单独指定 TTL 等过期参数tagstring为查询指定自定义缓存键替代默认的SQL 参数哈希同标签可用于批量失效autoInvalidateboolean关闭该查询的自动失效。为false时写操作不会删除该查询的缓存接受该查询的最终一致性从源码看$withCache()无参数调用时等价于{ config: {}, enable: true, autoInvalidate: true }传入false则enable: false即显式禁用该查询的缓存传入对象时默认开启autoInvalidate: true再与你的配置合并WithCacheConfig类型见 core/types.ts。Case 2global: true全局模式const db drizzle(process.env.DB_URL!, { cache: upstashCache({ url: , token: , global: true }) })此配置下普通查询默认使用缓存const res await db.select().from(users)如果希望某条特定查询跳过缓存则调用.$withCache(false)// cache is disabled for this query const res await db.select().from(users).$withCache(false)手动失效db.$cache?.invalidate无论采用哪种策略你都可以通过数据库实例上的$cache属性强制手动失效之前定义的特定表或标签// Invalidate all queries that use the users table. You can do this with the Drizzle instance. await db.$cache?.invalidate({ tables: users }); // or await db.$cache?.invalidate({ tables: [users, posts] }); // Invalidate all queries that use the usersTable. You can do this by using just the table name. await db.$cache?.invalidate({ tables: usersTable }); // or await db.$cache?.invalidate({ tables: [usersTable , postsTable ] }); // You can also invalidate custom tags defined in any previously executed select queries. await db.$cache?.invalidate({ tags: custom_key }); // or await db.$cache?.invalidate({ tags: [custom_key, custom_key1] });invalidate接受的对象与onMutate的MutationOption一致tables既可以是Table对象用is(table, Table)判断后取真实表名也可以是纯字符串表名tags则是你在$withCache({ tag })中定义过的任意自定义标签。源码原理缓存如何接入查询执行路径从源码结构看Drizzle 的缓存是贯穿数据库实例、Session 与查询构建器三层的能力实例层所有驱动pg、mysql、sqlite、gel、singlestore 等的drizzle()都接受cache选项例如 aws-data-api/pg/driver.ts、d1/driver.ts、bun-sql/driver.tsSession 构造时接收cache: Cache参数构建器层select 查询构建器通过$withCache()写入cacheConfig类型为WithCacheConfig随查询元数据传入执行层执行层Session 的prepareQuery在执行查询时统一调度缓存逻辑。以 pg-core/session.ts 为例执行路径的逻辑如下写语句insert/update/delete当语句涉及的表非空时真实查询与this.cache.onMutate({ tables: this.queryMetadata.tables })并行执行Promise.all即在写库的同时触发缓存失效无需等待失效完成后再响应未配置cacheConfig的读查询直接执行完全跳过缓存层保持 Drizzle 默认零隐式行为的语义配置了cacheConfig的 select 查询先计算缓存键——cacheConfig.tag优先否则调用hashQuery(queryString, params)生成哈希键调用cache.get(key, tables, isTag, autoInvalidate)尝试命中缓存命中非undefined则直接返回缓存结果不访问数据库未命中则执行真实查询随后调用cache.put(...)写回缓存put时若autoInvalidate为真则传入查询涉及的表数组否则传空数组意味着该查询不会被表级失效影响。缓存键生成hashQuery默认缓存键由 core/cache.ts 中的hashQuery生成export async function hashQuery(sql: string, params?: any[]) { const dataToHash ${sql}-${JSON.stringify(params)}; const encoder new TextEncoder(); const data encoder.encode(dataToHash); const hashBuffer await crypto.subtle.digest(SHA-256, data); const hashArray [...new Uint8Array(hashBuffer)]; const hashHex hashArray.map((b) b.toString(16).padStart(2, 0)).join(); return hashHex; }它将SQL 文本 序列化参数拼接后使用 Web Crypto API 做SHA-256摘要输出十六进制字符串。这意味着相同 SQL 与相同参数的查询会命中同一个缓存键而参数不同例如limit或where值变化则生成不同键。这也解释了为什么当缓存键由你自定义的tag接管时同标签的多个查询会共享同一份缓存值。UpstashCache 的底层数据结构复合表键与标签映射UpstashCache见 upstash/cache.ts是理解 Drizzle 缓存失效设计的范本。它在 Redis 中使用四类键组织数据常量值用途compositeTableSetPrefix__CTS__Set 前缀记录每个表被哪些复合表键引用如__CTS__table1中包含__CT__table1,table2、__CT__table1,table3compositeTablePrefix__CT__Hash 前缀键为排序后的表组合如table1,table2存储该组合下各查询键到结果的映射tagsMapKey__tagsMap__Hash记录标签 → 复合表键的映射用于按标签取缓存与失效nonAutoInvalidateTablePrefix__nonAutoInvalidate__Hash存放autoInvalidate: false的查询缓存使其免受表级写失效的影响核心逻辑包括复合表键getCompositeKey(tables)将查询涉及的表名排序后拼接__CT__table1,table2因此只查 users与联查 users,posts落在不同复合键下互不污染写入put使用 Redis pipeline 原子性地写入哈希值、设置HEXPIRE过期时间TTL 取单查询config.ex或内部默认值、将复合键加入每个表的__CTS__Set必要时写入__tagsMap__标签映射失效onMutate通过 Lua 脚本onMutateScript一次性完成——先按标签从__tagsMap__删除对应哈希字段再SUNION取受影响表的全部复合键并DEL删除避免多次网络往返按标签读取通过只读 Lua 脚本getByTagScript实现标签 → 复合表键 → 缓存值的两级哈希查找。这套表级索引 复合键 标签映射的设计使得 Drizzle 能够在任意写操作发生后以 O(1) 级别的 Redis 操作精准失效所有相关查询缓存而不是盲目清空整个缓存空间。使用建议与注意事项默认透明未配置cache选项时Drizzle 不会为任何查询引入缓存或额外开销NoopCache保证行为与旧版本完全一致一致性权衡autoInvalidate: false与手动invalidate都意味着接受查询结果的最终一致性——只应在可容忍短暂读到旧数据的场景使用如统计类查询、低频变更的配置表TTL 兜底即使不配置任何过期时间UpstashCache内部默认 TTL 为 1 秒为缓存数据提供了兜底过期保障写失效与读并行写语句执行时onMutate与数据库写入通过Promise.all并行触发失效逻辑不应阻塞业务写路径表名准确性onMutate与invalidate中传表对象时Drizzle 会通过is(table, Table)判断并解析真实表名OriginalName传字符串时按字面表名处理两种方式等价但需保证命名一致。至此你已掌握 Drizzle 缓存层的完整链路从upstashCache()的零配置接入到继承Cache自定义存储再到$withCache()的单查询控制与onMutate/db.$cache?.invalidate的双通道失效。相关的完整实现可继续在仓库中研读Cache 抽象类与哈希、配置类型、Upstash 实现、查询执行路径 以及 select 构建器。【免费下载链接】drizzle-ormORM项目地址: https://gitcode.com/gh_mirrors/dr/drizzle-orm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Prettier 处理 Markdown Wiki 链接([[...]])行尾换行的完整机制与源码解析

Prettier 处理 Markdown Wiki 链接([[...]])行尾换行的完整机制与源码解析

Prettier 处理 Markdown Wiki 链接([[...]])行尾换行的完整机制与源码解析 【免费下载链接】prettier Prettier is an opinionated code formatter. 项目地址: https://gitcode.com/gh_mirrors/pr/prettier 本文基于 Prettier 仓库中 tests/forma…

2026/9/19 7:57:34 阅读更多 →
WordPress子比主题超级插件:AI驱动的高效功能整合方案

WordPress子比主题超级插件:AI驱动的高效功能整合方案

1. 项目概述:子比主题超级插件的核心价值子比主题超级插件是一款专为WordPress网站设计的全能型功能扩展集合。作为一名在WordPress生态深耕多年的开发者,我亲测过市面上绝大多数主题插件,但像这样将AI功能与十余种实用模块深度整合的解决方案…

2026/9/19 7:57:34 阅读更多 →
Blender+Unity构建工业数字孪生产线:全流程实操与避坑指南

Blender+Unity构建工业数字孪生产线:全流程实操与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/19 7:56:33 阅读更多 →

最新新闻

基于残差信号与混合诊断的PMSM匝间短路故障识别

基于残差信号与混合诊断的PMSM匝间短路故障识别

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/19 8:52:00 阅读更多 →
PyPTO 最大公约数算子实战:pypto.Tensor.gcd 接口说明、TileShape 配置与逐元素 Euclid 算法实现剖析

PyPTO 最大公约数算子实战:pypto.Tensor.gcd 接口说明、TileShape 配置与逐元素 Euclid 算法实现剖析

PyPTO 最大公约数算子实战:pypto.Tensor.gcd 接口说明、TileShape 配置与逐元素 Euclid 算法实现剖析 【免费下载链接】pypto PyPTO(发音: pai p-t-o):Parallel Tensor/Tile Operation编程范式。 项目地址: https://gitcode.com…

2026/9/19 8:52:00 阅读更多 →
Rocky Linux迁移实战:CentOS替代方案与RHEL兼容性详解

Rocky Linux迁移实战:CentOS替代方案与RHEL兼容性详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/19 8:52:00 阅读更多 →
CST共面波导色散仿真:周期性边界与JDM求解器实战指南

CST共面波导色散仿真:周期性边界与JDM求解器实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/19 8:52:00 阅读更多 →
开源代码审查工作流:CLI驱动的Git+LLM语义审查方案

开源代码审查工作流:CLI驱动的Git+LLM语义审查方案

1. 项目概述:这不是一个“工具”,而是一套可落地的开源代码审查工作流“open-code-review”这个标题乍看像某个具体软件的名字,但实际它指向的是一种正在快速成型的新型工程实践——把大语言模型(LLM)深度嵌入到日常 G…

2026/9/19 8:52:00 阅读更多 →
CMSIS-4老工程迁移指南:从结构尽调到AC6实战避坑

CMSIS-4老工程迁移指南:从结构尽调到AC6实战避坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/19 8:51:00 阅读更多 →

日新闻

BP神经网络时序预测:滑窗长度与多窗口平均策略

BP神经网络时序预测:滑窗长度与多窗口平均策略

简介:面向机器学习、深度学习与数据建模学习者的一份完整研究文献,聚焦BP神经网络在农业产量预测中的应用。文档以1980—2018年全国棉花产量为样本,系统讲解数据归一化处理、激活函数原理、多层神经网络结构搭建及训练流程,展示敏…

2026/9/19 0:00:30 阅读更多 →
Transformer训练实时监控实战:基于MindSpore的损失曲线可视化方案

Transformer训练实时监控实战:基于MindSpore的损失曲线可视化方案

上个月调一个Deformable DETR模型,在单卡上要跑将近两天。第二天早上我下意识打开终端翻日志,发现loss从凌晨两点就开始往上爬,一路从0.8涨到1.35,整整六个小时没人发现。那六个小时的训练不仅白跑,还霸占着卡——等于…

2026/9/19 0:00:30 阅读更多 →
OpenCloud 中的 Go 类型安全转换库 spf13/cast:从零值回退到泛型 API 的完整实战指南

OpenCloud 中的 Go 类型安全转换库 spf13/cast:从零值回退到泛型 API 的完整实战指南

OpenCloud 中的 Go 类型安全转换库 spf13/cast:从零值回退到泛型 API 的完整实战指南 【免费下载链接】opencloud 🌤️ OpenCloud is the open source platform for file management, sharing and collaboration. Simple and sovereign. 项目地址: htt…

2026/9/19 0:00:30 阅读更多 →

周新闻

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验 【免费下载链接】ai The AI Toolkit for TypeScript. From the creators of Next.js, the AI SDK is a free open-source library for building AI-powered applications and ag…

2026/9/19 3:59:36 阅读更多 →
Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化

Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化

Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化 【免费下载链接】refine A React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility. 项目地址: https://gitcode.com/GitH…

2026/9/19 3:53:08 阅读更多 →
Flutter应用改名全指南:从Android到iOS的配置与工具实践

Flutter应用改名全指南:从Android到iOS的配置与工具实践

刚接一个外包项目时,甲方要求把工程里临时用的应用名改成正式产品名。我本来觉得“改名”这种小事,打开配置文件改一行不就完了?结果真动手才发现,Flutter项目里“应用名称”根本不是一处配置,而是一整套散落在 Androi…

2026/9/19 4:02:43 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/16 22:31:27 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/15 21:39:18 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/16 22:32:59 阅读更多 →