news 2026/10/16 7:01:30

esm.sh路径遍历漏洞CVE-2026-23644:URL解码与文件映射的安全风险

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
esm.sh路径遍历漏洞CVE-2026-23644:URL解码与文件映射的安全风险

1. 项目背景与核心价值

拿到CVE-2026-23644这个编号的时候,我第一反应是:esm.sh这种基础设施性质的CDN服务出路径遍历漏洞,影响面会比普通业务漏洞大得多。原因很简单——esm.sh承担的是npm包到ES Module的转换分发职责,全球大量前端项目和构建流程都直接依赖它,一旦这个环节可以被攻击者利用来读取任意文件,下游所有业务都得跟着排查。

先说清楚这个漏洞到底发生在哪一层。esm.sh的核心工作流程是:浏览器或构建工具请求某个npm包的ESM格式代码时,esm.sh从npm registry拉取对应包文件,实时转换为标准ES Module,再返回给调用方。它本质上是一个“请求入口”和“文件拉取器”的组合体。CVE-2026-23644的问题恰恰出在从请求URL到实际文件路径的映射环节——攻击者构造特殊编码的路径段,可以利用URL解析与实际文件系统路径读取之间的不一致,绕过正常路径约束,读取到服务端上本不该暴露的文件。

这类漏洞在CDN、文件代理、静态资源服务里算老熟人。几乎所有用Node.js或Go写的内容分发服务都踩过类似的坑。但为什么esm.sh的这次特别值得单独写一篇?因为它的请求路径解析不是单纯的服务端逻辑,它还要兼容npm包的name、version、文件路径三层信息,路径处理的复杂度比普通静态文件服务器高一个量级。想彻底理解这个漏洞,不能只盯着“路径遍历”四个字,得把esm.sh的URL设计、包转发现机制、缓存策略、以及不同部署形态下的文件读取逻辑都串起来看。

这篇文章适合三类人:第一是自建esm.sh服务或类似CDN代理服务的维护者,你需要定位自己是否受影响、怎么修;第二是前端基建或DevOps团队,你要评估供应链风险、在网关层面补拦截规则;第三是安全研究员或对Web服务漏洞原理感兴趣的开发者,这里面URL解码、路径规范化、黑名单过滤绕过这些知识点,值得反复推敲。

2. 路径遍历漏洞的前置认知——esm.sh的URL与文件映射机制

2.1 esm.sh的URL设计决定了路径解析的复杂度

要理解漏洞,先理解esm.sh是怎么把URL变成磁盘文件的。一个典型的esm.sh请求长这样:

https://esm.sh/react@18.2.0/?dev https://esm.sh/react@18.2.0/es2022/react.mjs https://esm.sh/stable/react-dom@18.2.0/es2022/react-dom.mjs

拆解一下路径段的含义:

  • 第一段是包名,比如react,也可以带stable/、experimental/这样的构建通道前缀
  • @18.2.0是版本号
  • 后面的es2022/react.mjs是包内的具体文件路径

但这里有个隐藏问题:react-dom这类带scope的包名实际形式是@scope/name,比如@vue/runtime-core,这类包请求URL长这样:

https://esm.sh/@vue/runtime-core@3.4.0

看明白问题了吗?URL里的@vue/runtime-core,第一段是@vue,第二段是runtime-core@3.4.0,esm.sh需要自己解析出完整的scope包名和版本号。像@babel/runtime、@types/node这类包名在URL里呈现出的路径结构和普通单级包完全不同。这就是为什么路径解析逻辑里塞满了各种split、startsWith、slice操作——每一层字符串处理,都可能给路径遍历留下机会。

2.2 版本号、文件名、构建参数——三个信息通道合并进同一个URL

esm.sh的URL后半段承载了更多信息。拿一个带构建参数的请求举例:

https://esm.sh/react@18.2.0/es2022/react.mjs?deps=react-dom@18.2.0&external=react-dom

路径部分react@18.2.0/es2022/react.mjs要解决三个问题:定位包(react@18.2.0)、定位模块格式目录(es2022)、定位具体文件(react.mjs)。这三个信息的拼接顺序和分隔方式完全依赖自定义解析规则,而不是简单的静态目录映射。

攻击者真正想利用的,是这套解析规则和底层fs模块读取文件时对路径的不同理解。换句话说:esm.sh解析完URL后得出的“包内相对路径”,和Node.jsfs.readFile实际访问的“磁盘路径”,可以不是同一个路径,但二者却共享一次规范化处理。这个落差就是路径遍历的立足点。

2.3 为什么路径遍历常出现在“二次解码”场景

我在排查这类漏洞时有个习惯:先看开发者有没有对URL参数做过解码,再看他解码了多少次。很多服务会在路由层接一次解码,拿到路径后拼进文件系统调用前又解码一次,或者框架已经解码过一遍、业务代码出于“安全”又手动解码了一遍。

CVE-2026-23644涉及的两次解码,不是简单的%2e%2e%2f被解成../就完事,而是esm.sh内部在解析包名和文件路径时,分别在不同阶段对URL path做了decodeURIComponent或等效的解码处理。第一层解码在URL路由层(框架或网关完成),第二层解码在包路径解析层(esm.sh业务代码完成)。如果第一层解码后的字符串里还保留着编码形态的路径穿越序列,第二层解码就会把%2e%2e%2f变成真实路径分隔符。这时候如果在两轮解码之间做过路径合法性校验,校验看到的是“安全的转义字符串”,实际传给文件系统的却是穿越路径。

这个“校验与使用之间的不一致”,是所有路径遍历漏洞的通用根因。

3. 漏洞技术细节拆解——从URL到文件的跨越如何被构造

3.1 关键环节:包名解析处的目录拼接

esm.sh内部读取包文件的大致流程是:

  1. 解析URL path,提取包名、版本号、文件子路径
  2. 查询包在存储层的位置,通常是<cacheDir>/<packageName>@<version>/<subpath>
  3. 将subpath直接拼接在包根目录后,交给fs.readFile读取

漏洞就藏在第1步到第3步之间。如果第1步解析出的subpath里包含路径穿越序列,第3步拼接后就会跳出包根目录,指向缓存目录之外的区域。

CVE-2026-23644的构造方式,核心是利用了URL编码形态的路径穿越序列与解码时机错位。下面展示一个基于常见实践的构造原理推导(不针对任何特定版本,仅用于理解漏洞机理):

正常请求: GET /react@18.2.0/es2022/react.mjs 攻击请求: GET /react@18.2.0/%2e%2e/%2e%2e/%2e%2e/config/secret.json

如果服务端在解析版本号后的子路径时做了解码,那么%2e%2e/%2e%2e会变成../../,拼接到包缓存根目录后变成:

<cacheDir>/react@18.2.0/../../../../config/secret.json

路径规范化计算后,实际上指向的是缓存目录外部的<cacheDir>/config/secret.json——但注意,如果配置了对外映射关系,攻击者还可以用足够多的../一路跳转到宿主机的任意可读文件,比如/etc/passwd这类典型目标。

3.2 为什么单次编码的目录穿越会被拦,双层的却不会

很多服务都加了防目录穿越的校验,常见做法是:在解析路径后检查是否包含..或../。但校验的位置很关键。

如果校验发生在第一层解码之后、第二层解码之前,那么此时的请求路径里可能还带着编码形式,例如:

/react@18.2.0/%252e%252e/%252e%252e/%252e%252e/etc/passwd

第一层解码(URL路由层)把%25解成%,路径变成:

/react@18.2.0/%2e%2e/%2e%2e/%2e%2e/etc/passwd

如果此时做校验,看到的是%2e%2e这种字符串,它不等于..,校验放行。但随后esm.sh业务代码为了获取真实的文件路径,又做了一次解码,%2e%2e被还原成..,拼接进文件系统路径后,目录穿越生效。

这就是典型的“双重编码绕过单层检测”。如果服务端有两轮解码、但只有一轮检测,且检测的位置落在两次解码之间,就存在这个窗口。

3.3 影响的是什么数据

需要明确一点:CVE-2026-23644读取的范围取决于esm.sh运行时的文件系统权限。如果esm.sh以普通用户运行在容器里,攻击者能读取的是容器内该用户有读权限的文件,包括:

  • 环境变量文件(如果存在容器内文件形式的env配置)
  • 应用的配置文件(比如esm.sh自身的配置、npm缓存目录的元数据)
  • 挂载到容器内的敏感文件(例如Kubernetes的ServiceAccount token挂载路径)
  • 同进程用户权限范围内的系统文件

其中危害最大的是两类:一类是包含密钥、token的配置文件;另一类是npm缓存目录结构泄露——攻击者可以枚举出服务器缓存了哪些包、哪些版本,这是供应链攻击的侦察前置步骤。

我遇到过真实事件里,攻击者通过这类漏洞读取到的是部署平台自动注入的云服务凭证,后续直接横向移动到了对象存储桶。所以不要觉得“只是读文件”,读文件的下一步往往就是权限提升。

4. 影响面分析与版本边界

4.1 受影响版本与修复版本的判断

根据CVE-2026-23644的披露信息,漏洞影响的是esm.sh在2026年初的某个版本区间。这里我给一个通用的判断方法论,因为实际版本范围需要结合你使用的部署版本对照公告:

  1. 检查当前esm.sh版本号,确认是否落在披露公告的受影响区间
  2. 如果没有明确版本区间,直接检查CHANGELOG里是否有路径解析、URL解码、安全过滤相关的提交记录
  3. 最稳妥的办法:看修复commit涉及的文件,如果包含parseURL、resolvePath、decodePath这类模块,基本可以确定你的版本如果早于该commit就受影响

4.2 不同部署形态的暴露差异

esm.sh有两种使用方式,漏洞暴露面完全不同:

  • 官方公共CDN服务(esm.sh):用户请求直接打到公网服务,任何人都可以构造恶意请求。影响面是全球所有使用该服务的站点。但修复速度也快,官方在收到报告后一般会立即热修复。
  • 自建esm.sh实例:如果实例部署在内网,暴露面相对小;但如果自建实例配置了公网入口(比如给公司内部前端项目做的公共构建缓存服务),风险等同公共CDN。更危险的是,很多自建实例版本老旧、无人维护,漏洞可能长期存在。

另一个角度是“通过esm.sh加载npm包的下游业务”。即使官方快速修复,浏览器端和构建工具也会缓存旧的响应内容。如果攻击者在你缓存清理前已经利用漏洞获取了敏感文件,你无法通过更新上游服务来追溯泄露面。这也是供应链类漏洞最让人头疼的地方——你不可能控制所有下游节点的行为。

4.3 与其他路径遍历漏洞的横向对比

拿同样出现在CDN类服务的几个历史漏洞做对比(抽象描述,不特指具体CVE):

漏洞类型根因常见触发位置修复难度
单次编码目录穿越缺少..过滤文件路径拼接低,加过滤即可
双重编码目录穿越多次解码+校验时机错位包名解析、子路径拼接中,需要重构解码流程
存储型路径遍历包名/版本号直接作为目录名缓存路径构造中高,涉及目录命名策略
符号链接穿越解压后的包内包含symlinknpm包解压落地高,需要检查包内容

CVE-2026-23644属于“双重编码目录穿越”与“包解析逻辑缺陷”的结合体。单独用黑名单过滤../解决不了问题,必须从解码流程上重新设计。

5. 复现与验证——从理论到本地最小环境的确认过程

5.1 搭建最小复现环境

我不建议直接用生产环境或公共实例做验证,正确做法是在本地起一个最小复现环境。步骤如下:

  1. 拉取受影响版本的esm.sh源码
  2. 使用Node.js运行本地服务,不需要完整配置外部依赖(esm.sh核心的包转换流程可以mock掉,只保留URL解析和文件读取逻辑)
  3. 准备一个测试目录结构如下:
/tmp/esm-lab/ ├── cache/ │ └── react@18.2.0/ │ ├── index.mjs │ └── package.json └── secret/ └── flag.txt

其中secret/flag.txt用于验证是否读取成功。

5.2 本地服务验证脚本

一个适合做原理验证的简化Node.js脚本思路:

const http = require('http'); const fs = require('fs'); const path = require('path'); const CACHE_ROOT = '/tmp/esm-lab/cache'; function decodePath(segment) { // 模拟第一层解析时的部分解码 return decodeURIComponent(segment); } http.createServer((req, res) => { const pathname = req.url.split('?')[0]; // 提取包名和子路径(简化版) // 第一个/后到@版本号之间是包名 // @版本号之后是子路径 const segments = pathname.split('/'); // 模拟两层解码 const firstDecoded = segments.map(decodeURIComponent); // 模拟校验:禁止包含.. if (firstDecoded.some(s => s === '..')) { res.writeHead(403); res.end('blocked'); return; } // 第二层解码 const finalPath = segments.map(decodePath).join('/'); const safePath = path.join(CACHE_ROOT, finalPath); fs.readFile(safePath, (err, data) => { if (err) { res.writeHead(404); res.end('not found'); return; } res.end(data); }); }).listen(8787);

5.3 验证结果预期

分别请求以下URL观察响应:

# 正常请求,预期返回react.mjs内容 http://localhost:8787/react@18.2.0/index.mjs # 单次编码穿越,预期被403拦截 http://localhost:8787/react@18.2.0/%2e%2e/%2e%2e/secret/flag.txt # 双重编码穿越,如果漏洞存在,预期返回flag.txt内容 http://localhost:8787/react@18.2.0/%252e%252e/%252e%252e/secret/flag.txt

观察的关键点是:第三次请求是否绕过了第一次解码后的..检测,但在第二次解码后成功读取到文件。如果返回了flag.txt的内容,说明漏洞原理在这个环境里成立。

我在实际验证这类问题时,会在第二次解码前后各打一条日志,打印路径变化过程,这样能清楚看到“哪一步解码把路径变成了穿越序列”。对整个链路了然于胸后,再回到真实环境做修复验证,心态会完全不一样。

6. 修复方案全解——从上游升级到多层防御

6.1 上游升级:最快、最彻底的修复路径

如果你使用的是官方esm.sh服务,不需要做任何操作,官方修复即生效。但如果你自己部署了esm.sh实例,第一步永远是升级到修复版本。

实际操作顺序:

  1. 备份现有配置(主要关注缓存目录、白名单配置、自定义域名映射)
  2. 拉取最新版本源码,对比修复commit,确认改动了哪些解析逻辑
  3. 升级后跑一遍回归测试,同时观察日志中是否有异常请求(%密集、路径段数异常等)

一个容易忽略的点:升级后必须清空缓存。如果旧缓存里存有被污染的文件,或者缓存键的设计基于旧路径解析逻辑,可能残留旧的错误映射。生产环境清缓存要挑低峰期,注意缓存重建对源站的压力。

6.2 自建服务端的代码级加固

如果因为某种原因暂时无法升级(比如二次开发过、依赖了旧版本的内部接口),可以通过代码级加固缓解风险。核心思路是统一解码时机、消除校验与使用之间的窗口:

  1. 在入口处只解码一次:所有URL路径解码集中在最外层完成,内部逻辑拿到的必须是解码后的最终路径,不允许二次解码
  2. 将路径校验移到最终路径拼接之后:不要在校验通过后再次修改路径字符串,确保校验的就是最终传给文件系统的值
  3. 使用白名单校验替代黑名单过滤:不要只过滤..,而是限制路径段只能包含包名字符集。npm包名的合法字符是有限的——小写字母、数字、-、_、.、~、/(用于scope边界),超出这个范围的直接拒绝请求,比尝试去拦截各种编码形态的..可靠得多

下面是一个更健壮的路径解析思路片段:

function resolvePackagePath(packageNameRaw, subpathRaw, version) { const packageName = decodeURIComponent(packageNameRaw); const subpath = decodeURIComponent(subpathRaw); // 对包名做严格白名单校验 if (!/^(@[a-z0-9-]+(\/)?)?[a-z0-9-_.~]+$/.test(packageName)) { throw new Error('invalid package name'); } // 对子路径做严格白名单校验,只允许正常文件路径 // 禁止任何形式的..(包括编码形态) if (subpath === '' || subpath.includes('..') || subpath.includes('\0') || !/^[a-zA-Z0-9/._-]+$/.test(subpath)) { throw new Error('invalid subpath'); } const fullPath = path.join(CACHE_ROOT, packageName + '@' + version, subpath); // 最终校验路径前缀 const resolvedRoot = path.resolve(CACHE_ROOT); const resolvedFull = path.resolve(fullPath); if (!resolvedFull.startsWith(resolvedRoot + path.sep)) { throw new Error('path escape detected'); } return resolvedFull; }

注意这里的关键设计:解码后的字符串直接进入正则校验,校验通过后的字符串不再做任何修改直接拼接。这样校验与使用完全一致,没有中间窗口。

6.3 网关与WAF层的纵深防御

即使代码层修复了,我还是建议在网关层补一道拦截规则,作为纵深防御。尤其对于自建实例,前面挂Nginx或云WAF是常见架构,可以在不碰代码的情况下快速止血。

一个有效的Nginx拦截配置思路:

# 在server块中增加 if ($request_uri ~ "(%2e%2e|%252e|\.\./|\.\.\\)") { return 403; }

更精确的做法是使用map指令定义需要拦截的恶意模式:

map $request_uri $bad_path { default 0; "~*%2e%2e" 1; "~*%252e" 1; "~*\.\./" 1; "~*\.\.\\" 1; } server { if ($bad_path) { return 403; } }

注意正则不能写得太宽,避免误伤正常请求。比如%2e是合法的URL编码,有些包名或参数里可能正常包含,所以重点拦截%2e%2e(编码形态的..)而不是单独拦截%2e。

WAF规则层面,核心拦截点三个:

  • ../直接形态
  • %2e%2e/单层编码形态
  • %252e%252e/双层编码形态

同时建议开启URL解码检测策略,让WAF在解码前和解码后各做一次匹配。

6.4 业务侧对第三方依赖的治理

最后一步是站在“esm.sh使用方”的角度加固。

如果你在前端项目里直接引用了https://esm.sh/xxx的URL,那么你没法控制上游服务的安全性,能做的是:

  1. 将依赖锁定到具体版本号,不要用stable、latest这类浮动版本——一旦上游缓存被污染,浮动版本会比锁定版本更容易拉取到异常内容
  2. 对构建流程中拉取的esm.sh响应做完整性校验,比如固定integrity属性(类似SRI的做法),确保返回内容没有被篡改
  3. 部署一个私有的esm.sh代理或者预构建产物缓存,把外网依赖变成内网受控依赖,从根源上消除供应链不可控风险

我见过不少大团队的做法是:“开发期用官方esm.sh方便,生产期用私有代理锁版本”。这个思路在成本可控的情况下是比较稳妥的选择。

7. 踩坑记录与排查指引

7.1 常见误判:不是所有403都是路径穿越被拦截

修复路径遍历漏洞时经常遇到一类误报:加了网关过滤规则后,部分正常请求开始返回403。排查后发现,问题不出在路径穿越,而是正则写得太宽了。比如前面提到的拦截%2e的模式,会误伤URL里包含编码点号的包名。query-string@8.1.0这种包名本身没有点号,但有些包的构建参数里可能有合法的编码点号。

误报发现后怎么定位:看403请求的完整URL,对比是不是包名里含有点号、文件名里含有点号,还是真的出现了..序列。我的建议是分两条规则写:一条拦编码形态的连续点号对,一条拦直接形态的斜杠穿越,不要混在一起。

7.2 日志排查的关键字段

确认漏洞是否被利用过,比修复漏洞本身更紧迫。如果攻击者已经利用过了,你需要知道他从哪里进来的、读了什么。

排查esm.sh日志时重点关注:

  • request_uri字段里包含%编码密集的请求(特别是%25、%2e组合)
  • 响应状态码反常地出现大量200(如果是敏感文件读取,攻击者会快速扫路径)
  • 同一来源IP在短时间内请求了多个不同路径段组合,这是路径遍历扫描的典型特征

实操建议:直接对访问日志跑一条过滤命令,把可疑请求捞出来:

grep -E "%2e|%25|\.\./" access.log | grep "esm.sh" | awk '{print $1, $7, $9}' | head -50

看到来源IP、请求URL、响应码三条信息,就能判断攻击者是否尝试过穿越。

7.3 修复后的回归测试要点

修复不是把代码改完就结束,回归测试要覆盖到三类场景:

正常功能回归:随便挑几个常见包(react、vue、lodash-es),确认正常版本请求返回内容正确,线上业务不受影响。

恶意请求回归:依次请求单层编码穿越、双层编码穿越、混合编码穿越(%2e%2e%2f这种三字符组合)、以及包含空字节的变体,全部应该返回403或404,而不是内容。

边界场景回归:合法但特殊的请求不能被误杀。比如@scope/name@1.0.0带scope的包、文件名包含点号的包(index.module.js)、路径包含连字符的包(my-utils),这些都要跑通。

我习惯把回归测试用例固化成脚本,每次升级esm.sh或修改网关规则时都自动跑一遍,比人工点几个链接靠谱得多。

7.4 关于漏洞披露时间线的思考

这次CVE的披露节奏属于“先修复后公开”的负责任披露流程。官方收到报告后先静默修复,留出一定时间窗口给下游升级,然后才公开细节。这给所有跑在公共基础设施上的团队提了个醒:依赖第三方公共服务的风险,不只是“服务挂了怎么办”,还包括“第三方服务出安全事件时,你是否能感知、是否能快速响应”。

如果你所在团队的供应链里有esm.sh这类第三方服务,建议做两件事:一是订阅安全公告渠道,第一时间感知漏洞披露;二是建立应急预案——假设esm.sh明天不可用了,你的构建流程怎么兜底。这个问题想清楚了,遇到类似CVE时你就不会慌。

8. 最后再分享一个实操心得

我在搭建复现环境时踩过一个坑:直接在完整esm.sh源码环境里跑验证,结果被它复杂的依赖安装流程折腾了半天。后来换成最小化模拟脚本,把URL解析和文件读取逻辑单独抽出来,一分钟内就跑通了漏洞触发链路。

这也是一条通用经验:分析外部项目漏洞时,不要一上来就追求“完整复现”,先梳理清楚漏洞链路的最小必要组件,搭一个能验证核心逻辑的最小环境,效率会高很多。等原理验证通过、影响范围摸清之后,再回到完整环境里做修复验证,思路就清晰了。

路径遍历这类漏洞,本质上是“信任边界”的问题。服务端对用户输入的路径段给予了过多的信任,又在处理过程中引入了不一致的解码和校验。通过这次CVE-2026-23644的完整拆解,希望你能把“解码时机”和“校验与使用一致性”这两件事刻在脑子里,以后再看任何文件读取、路径映射类代码,第一反应就是去找这两处关键点。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 7:01:09

渗透测试空白页突破口:七种信息发现思路全解析

一个登录页&#xff0c;一张只有公司简介的官网首页&#xff0c;甚至是一段“系统建设中”的占位字符——做渗透测试的人&#xff0c;最熟悉的就是这种开场。你手里拿着授权书&#xff0c;目标就在眼前&#xff0c;可浏览器打开之后什么都没有&#xff0c;连一个像样的功能入口…

作者头像 李华
网站建设 2026/10/16 7:01:04

Git分支策略与提交规范:从Git Flow到GitHub Flow的工程实践

Git 用久了你会发现一个现象&#xff1a;命令本身其实不难&#xff0c;真正让团队协作变难受的&#xff0c;往往是分支策略、Commit 规范和 rebase/merge 这些需要统一认识的问题。我见过不少项目&#xff0c;一开始大家想怎么建分支就怎么建&#xff0c;结果就是 develop、tes…

作者头像 李华
网站建设 2026/10/16 7:01:01

业务用例视角下绘制流程图的实战指南:从需求分析到评审落地

从需求评审会出来&#xff0c;我经常感叹&#xff1a;如果每个业务方都愿意把流程画成一张图再说事&#xff0c;会议时间至少能砍掉一半。刚接手某跨部门系统的需求梳理时&#xff0c;我几乎每周都要参加各种评审&#xff0c;而大部分争论最后都会落到同一个问题——"你说…

作者头像 李华
网站建设 2026/10/16 7:00:21

VB6无Acrobat依赖生成PDF的工业级方案

简介&#xff1a;本资源是面向VB6&#xff08;Visual Basic 6&#xff09;开发者的PDF功能集成实战指南&#xff0c;专为需在传统桌面应用中嵌入PDF读写、显示、打印及编辑能力的中高级开发者设计。压缩包内含完整可运行示例代码、组件调用说明与分场景实现逻辑&#xff0c;覆盖…

作者头像 李华
网站建设 2026/10/16 6:59:42

Flutter适配OpenHarmony实战:武器统计应用开发全记录

1. 项目定位与需求拆解1.1 为什么做武器统计功能先说说这个项目的来龙去脉。作为一个习惯用Flutter做跨平台应用的开发者&#xff0c;我一直在关注OpenHarmony生态的发展。有一天拿到了一台搭载OpenHarmony的开发板&#xff0c;就想试试把Flutter应用跑上去&#xff0c;但光做个…

作者头像 李华
网站建设 2026/10/16 6:59:35

Vue3+TS+Three.js全景看房系统实战:空间锚定与真VR交互

简介&#xff1a;这是一套基于Vue3TypeScriptThree.js实现的轻量级全景家居VR看房系统&#xff0c;面向前端开发者、Web 3D初学者及房地产数字化展示需求者&#xff0c;解决传统静态户型图交互性弱、沉浸感不足的问题。资源包共125个文件&#xff0c;含90张JPG/PNG全景素材&…

作者头像 李华