<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/rss/feed.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>ACの博客</title><description>你已消耗一次免费次数</description><link>https://koharu.ac.ren</link><item><title>做产品前，先别急着写代码：我是怎么判断一个点子值不值得做的</title><link>https://koharu.ac.ren/post/juejin/%E5%81%9A%E4%BA%A7%E5%93%81%E5%89%8D_%E5%85%88%E5%88%AB%E6%80%A5%E7%9D%80%E5%86%99%E4%BB%A3%E7%A0%81_%E6%88%91%E6%98%AF%E6%80%8E%E4%B9%88%E5%88%A4%E6%96%AD%E4%B8%80%E4%B8%AA%E7%82%B9%E5%AD%90%E5%80%BC%E4%B8%8D%E5%80%BC%E5%BE%97%E5%81%9A%E7%9A%84</link><guid isPermaLink="false">juejin/做产品前_先别急着写代码_我是怎么判断一个点子值不值得做的</guid><description>做产品前，先别急着写代码：我是怎么判断一个点子值不值得做的  作者: 前端AC | 原文: https://juejin.cn/post/7628874305050443814  以前我总觉得，做产品最重要的是执行力。后来我慢慢发现，真正拉开差距的，往往不是谁做得快，而是谁一开始选对了问题。很多项目...</description><pubDate>Wed, 15 Apr 2026 07:00:32 GMT</pubDate><content:encoded>&lt;h1&gt;做产品前，先别急着写代码：我是怎么判断一个点子值不值得做的&lt;a href=&quot;#做产品前先别急着写代码我是怎么判断一个点子值不值得做的&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;作者: 前端AC | 原文: &lt;a href=&quot;https://juejin.cn/post/7628874305050443814&quot;&gt;https://juejin.cn/post/7628874305050443814&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;以前我总觉得，做产品最重要的是执行力。后来我慢慢发现，真正拉开差距的，往往不是谁做得快，而是谁一开始选对了问题。很多项目做不起来，不是因为技术不够强，而是因为从第一步就选错了方向。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;为什么很多人做产品，最后都做成了 “自己觉得有用”？&lt;a href=&quot;#为什么很多人做产品最后都做成了-自己觉得有用&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;我见过很多技术人做产品，起手都很像：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;做一个 AI 工具&lt;/li&gt;
&lt;li&gt;做一个效率 App&lt;/li&gt;
&lt;li&gt;做一个知识管理产品&lt;/li&gt;
&lt;li&gt;做一个 “感觉很多人都会需要” 的平台&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;然后就开始设计页面、写接口、做功能、调体验。&lt;/p&gt;
&lt;p&gt;最后上线之后，结果往往并不理想：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用户可能会来看看，但不会留下&lt;/li&gt;
&lt;li&gt;有人会觉得 “还不错”，但不会持续使用&lt;/li&gt;
&lt;li&gt;更现实一点，几乎没人愿意付费&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我后来越来越认同一个判断：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;很多产品不是做得不够好，而是一开始就不值得做。&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这件事对技术人尤其容易发生。因为我们太容易把注意力放在 “怎么做” 上，却忽略了 “为什么做”“值不值得做”。&lt;/p&gt;
&lt;p&gt;所以现在我再看一个产品点子，第一反应已经不是 “这个能不能实现”，而是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;这是不是一个真需求？&lt;/li&gt;
&lt;li&gt;这是谁的需求？&lt;/li&gt;
&lt;li&gt;这个人为什么今天就想解决它？&lt;/li&gt;
&lt;li&gt;他会不会为了解决它付钱？&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;我现在判断一个点子，主要看三件事&lt;a href=&quot;#我现在判断一个点子主要看三件事&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;如果要把 “怎么找点子” 这件事讲清楚，我觉得最重要的其实就三点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;先判断这是不是一个真需求&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不要服务所有人，要找到最痛的那群人&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不要闭门做产品，要先低成本验证&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这三点，比 “灵感” 重要得多。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;第一件事：先判断这是不是一个真需求&lt;a href=&quot;#第一件事先判断这是不是一个真需求&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;我以前很容易被一种感觉误导：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;这个东西挺有用的，那应该可以做。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;后来我发现，“有用” 和 “值得做” 之间差得非常远。&lt;/p&gt;
&lt;p&gt;因为很多东西确实有用，但不够痛、不够急、不够刚需，最后也不值得用户付费，更不值得用户改变习惯。&lt;/p&gt;
&lt;p&gt;所以我现在判断一个需求，主要看三个标准：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用户愿不愿意付费&lt;/li&gt;
&lt;li&gt;用户愿不愿意为它改变行为&lt;/li&gt;
&lt;li&gt;如果不解决，用户会不会持续承受损失&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;只有这三个条件同时成立，我才会把它当成一个真正值得看的方向。&lt;/p&gt;
&lt;h3&gt;我会把需求分成三层&lt;a href=&quot;#我会把需求分成三层&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;为了让自己判断更清楚，我通常会把需求粗分成三类：&lt;/p&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;类型&lt;/th&gt;&lt;th&gt;特点&lt;/th&gt;&lt;th&gt;用户状态&lt;/th&gt;&lt;th&gt;做产品的优先级&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;痛点&lt;/td&gt;&lt;td&gt;不解决就难受&lt;/td&gt;&lt;td&gt;焦虑、麻烦、持续付出代价&lt;/td&gt;&lt;td&gt;高&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;爽点&lt;/td&gt;&lt;td&gt;解决了会很爽&lt;/td&gt;&lt;td&gt;更高效、更方便&lt;/td&gt;&lt;td&gt;中&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;痒点&lt;/td&gt;&lt;td&gt;有了更好，没有也行&lt;/td&gt;&lt;td&gt;可替代、可忽略&lt;/td&gt;&lt;td&gt;低&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;我现在基本认定一个排序：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;痛点 &amp;gt; 爽点 &amp;gt; 痒点&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这个排序非常重要，因为技术人最容易踩的坑，就是拿 “痒点” 当 “痛点” 做。&lt;/p&gt;
&lt;p&gt;比如这些方向，看起来都挺合理：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;喝水提醒&lt;/li&gt;
&lt;li&gt;更好看的待办工具&lt;/li&gt;
&lt;li&gt;更精致的记账产品&lt;/li&gt;
&lt;li&gt;自动整理知识卡片&lt;/li&gt;
&lt;li&gt;一个新的习惯追踪工具&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些方向不能说完全没价值，但问题是：它们往往不够痛。&lt;/p&gt;
&lt;p&gt;用户可能会说：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;听起来不错&lt;/li&gt;
&lt;li&gt;好像有点意思&lt;/li&gt;
&lt;li&gt;有空可以试试&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但真正到付费那一步，很多人就停住了。因为没有它，生活照样能过。&lt;/p&gt;
&lt;p&gt;所以我现在看一个想法，先不问 “功能是不是酷”，而是先问一句：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;如果没有这个产品，用户现在到底在承受什么？&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果答案只是 “不太方便”，那大概率还不够。&lt;/p&gt;
&lt;p&gt;如果答案是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每天都在为这个问题花很多时间&lt;/li&gt;
&lt;li&gt;经常犯错&lt;/li&gt;
&lt;li&gt;很焦虑&lt;/li&gt;
&lt;li&gt;有损失&lt;/li&gt;
&lt;li&gt;有风险&lt;/li&gt;
&lt;li&gt;影响工作或生活质量&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;那我才会继续往下看。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;第二件事：不要服务所有人，要找到最痛的那群人&lt;a href=&quot;#第二件事不要服务所有人要找到最痛的那群人&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;我现在越来越不相信 “大而全” 的点子了。&lt;/p&gt;
&lt;p&gt;一旦一个点子听起来像这样：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;健身 App&lt;/li&gt;
&lt;li&gt;记账 App&lt;/li&gt;
&lt;li&gt;新闻助手&lt;/li&gt;
&lt;li&gt;校园二手平台&lt;/li&gt;
&lt;li&gt;学习工具&lt;/li&gt;
&lt;li&gt;效率工具&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我第一反应就会比较警惕。&lt;/p&gt;
&lt;p&gt;不是因为这些方向不能做，而是因为这种说法太泛了。只要你面向 “所有人”，基本就意味着你会直接撞进一个已经很拥挤的市场。&lt;/p&gt;
&lt;p&gt;对个人开发者或者小团队来说，这种打法通常很难赢。&lt;/p&gt;
&lt;p&gt;所以我现在更倾向于这样想问题：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;不是我要做一个什么工具，而是我要帮哪一类人，在什么具体场景下，解决一个特别痛的问题。&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这是一个非常大的思维切换。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;我怎么切分人群？&lt;a href=&quot;#我怎么切分人群&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;我的做法很简单：先把一个大需求拆成人群，再去看哪一群最痛、最愿意付费、最容易切进去。&lt;/p&gt;
&lt;p&gt;比如 “记账” 这个需求，如果不切分，它只是一个很普通的方向。&lt;/p&gt;
&lt;p&gt;但只要一切，你就会发现完全不是一回事：&lt;/p&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;人群&lt;/th&gt;&lt;th&gt;表面上都在 “记账”&lt;/th&gt;&lt;th&gt;实际问题完全不同&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;普通上班族&lt;/td&gt;&lt;td&gt;记录消费&lt;/td&gt;&lt;td&gt;懒得记，坚持不下来&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;自由职业者&lt;/td&gt;&lt;td&gt;管收入支出&lt;/td&gt;&lt;td&gt;收入不稳定，现金流焦虑&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;小微企业主&lt;/td&gt;&lt;td&gt;管日常支出&lt;/td&gt;&lt;td&gt;个人和公司花销混在一起&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;留学生家长&lt;/td&gt;&lt;td&gt;看海外消费&lt;/td&gt;&lt;td&gt;不知道孩子的钱到底花去哪了&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;这时候你会发现，真正值得做的可能不是 “记账” 这个动作本身，而是其中某种更深层的情绪和问题。&lt;/p&gt;
&lt;p&gt;比如对留学生家长来说，他们的核心痛点根本不是 “记账”，而是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;失控感&lt;/li&gt;
&lt;li&gt;不透明&lt;/li&gt;
&lt;li&gt;不安心&lt;/li&gt;
&lt;li&gt;不知道孩子是不是超支了&lt;/li&gt;
&lt;li&gt;不知道钱花到了什么地方&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;于是产品定位就发生了变化。&lt;/p&gt;
&lt;p&gt;它不再是一个 “自动记账 App”，而更像是：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;留学资金管家&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;一旦这么重构，你卖的就不再只是功能，而是结果：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;实时同步&lt;/li&gt;
&lt;li&gt;超支提醒&lt;/li&gt;
&lt;li&gt;月度分析&lt;/li&gt;
&lt;li&gt;风险预警&lt;/li&gt;
&lt;li&gt;家长心里有数&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这就是我理解的：&lt;strong&gt;不要做泛需求，要切到最痛的那群人。&lt;/strong&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7628874305050443814/img_0.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; /&gt;&lt;/figure&gt;
&lt;hr /&gt;
&lt;h2&gt;第三件事：不要闭门造车，要先验证&lt;a href=&quot;#第三件事不要闭门造车要先验证&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;我现在对 “想得很完整再开始做” 这件事已经比较警惕了。&lt;/p&gt;
&lt;p&gt;因为很多时候，你越往后做，沉没成本越高，最后越难承认方向不对。&lt;/p&gt;
&lt;p&gt;所以我现在更愿意早点验证，早点暴露问题。&lt;/p&gt;
&lt;h3&gt;我判断一个点子，通常会先做 5 件事&lt;a href=&quot;#我判断一个点子通常会先做-5-件事&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;h4&gt;1. 找到 10 个真实用户聊&lt;a href=&quot;#1-找到-10-个真实用户聊&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;不是泛泛地问朋友，而是尽量找到真正会遇到这个问题的人。&lt;/p&gt;
&lt;h4&gt;2. 问他们现在怎么解决&lt;a href=&quot;#2-问他们现在怎么解决&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;我不会问：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;你会不会用这个产品？&lt;/li&gt;
&lt;li&gt;你觉得这个 idea 怎么样？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因为这种问题太容易得到礼貌性答案。&lt;/p&gt;
&lt;p&gt;我更关心的是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;你现在怎么解决这个问题？&lt;/li&gt;
&lt;li&gt;最近一周这个问题困扰了你几次？&lt;/li&gt;
&lt;li&gt;你为了处理它花了多少钱、多少时间？&lt;/li&gt;
&lt;li&gt;你觉得现有方案哪里最难受？&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;3. 看现有替代方案是不是足够差&lt;a href=&quot;#3-看现有替代方案是不是足够差&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;用户现在有没有用别的方法凑合？&lt;/p&gt;
&lt;p&gt;比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Excel&lt;/li&gt;
&lt;li&gt;手工记录&lt;/li&gt;
&lt;li&gt;多个工具拼凑&lt;/li&gt;
&lt;li&gt;忍着不解决&lt;/li&gt;
&lt;li&gt;用成熟产品但很不满意&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果用户已经用一个成熟产品，而且非常满意，那机会通常不大。&lt;/p&gt;
&lt;p&gt;但如果用户现在是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用笨办法硬扛&lt;/li&gt;
&lt;li&gt;用多个工具勉强拼起来&lt;/li&gt;
&lt;li&gt;明明有产品，但体验很差&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这种反而更值得看。&lt;/p&gt;
&lt;h4&gt;4. 做一个最小可验证版本&lt;a href=&quot;#4-做一个最小可验证版本&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;我现在不会一上来做完整产品，而会先做最小验证。&lt;/p&gt;
&lt;p&gt;比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个 landing page&lt;/li&gt;
&lt;li&gt;一个飞书表单&lt;/li&gt;
&lt;li&gt;一个 Notion 页面&lt;/li&gt;
&lt;li&gt;一个微信群&lt;/li&gt;
&lt;li&gt;一个原型页&lt;/li&gt;
&lt;li&gt;一个极简 demo&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我验证的重点不是 “功能齐不齐”，而是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有没有人愿意点进去&lt;/li&gt;
&lt;li&gt;有没有人愿意留联系方式&lt;/li&gt;
&lt;li&gt;有没有人愿意进一步沟通&lt;/li&gt;
&lt;li&gt;有没有人愿意先付一点钱&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;5. 尽量让验证靠近 “真实付费”&lt;a href=&quot;#5-尽量让验证靠近-真实付费&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;这一点我现在特别看重。&lt;/p&gt;
&lt;p&gt;因为 “愿意试试” 和 “愿意掏钱” 完全不是一回事。&lt;/p&gt;
&lt;p&gt;所以如果条件允许，我会尽早做这些动作：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;预约&lt;/li&gt;
&lt;li&gt;预售&lt;/li&gt;
&lt;li&gt;定金&lt;/li&gt;
&lt;li&gt;小额试用费&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;能不能拿到第一笔钱，往往比一百句 “听起来不错” 更有价值。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;我现在找点子，通常按这个顺序走&lt;a href=&quot;#我现在找点子通常按这个顺序走&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;如果把上面的思路再收敛一下，我自己现在找点子的流程，大概是这样的：&lt;/p&gt;
&lt;h3&gt;第一步：从自己熟悉的问题开始&lt;a href=&quot;#第一步从自己熟悉的问题开始&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;我尽量从这些地方找线索：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;我自己长期遇到的问题&lt;/li&gt;
&lt;li&gt;我身边人反复抱怨的问题&lt;/li&gt;
&lt;li&gt;我能持续接触到的人群问题&lt;/li&gt;
&lt;li&gt;我比别人更理解的场景问题&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因为点子的本质，不只是 “想到”，更是 “理解到位”。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;第二步：把大需求切成人群&lt;a href=&quot;#第二步把大需求切成人群&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;我会先列出 3 到 5 类可能的人群，然后逐个判断：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;谁最痛&lt;/li&gt;
&lt;li&gt;谁最愿意付费&lt;/li&gt;
&lt;li&gt;谁最容易接触&lt;/li&gt;
&lt;li&gt;谁的竞争没那么激烈&lt;/li&gt;
&lt;li&gt;我对谁理解最深&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;第三步：写出这个用户的一天&lt;a href=&quot;#第三步写出这个用户的一天&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;这是我现在很常用的方法。&lt;/p&gt;
&lt;p&gt;因为很多痛点，如果你不落到真实场景里，是看不出来的。&lt;/p&gt;
&lt;p&gt;比如 “产后恢复” 这个方向，如果只说概念，会很空。&lt;/p&gt;
&lt;p&gt;但只要写成一天，你就很容易看到真实问题：&lt;/p&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;时间&lt;/th&gt;&lt;th&gt;场景&lt;/th&gt;&lt;th&gt;表面问题&lt;/th&gt;&lt;th&gt;深层情绪&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;早上&lt;/td&gt;&lt;td&gt;宝宝刚睡，有 30 分钟空闲&lt;/td&gt;&lt;td&gt;不知道该做什么动作&lt;/td&gt;&lt;td&gt;恐惧&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;上午&lt;/td&gt;&lt;td&gt;抱娃很久，腰酸背痛&lt;/td&gt;&lt;td&gt;没空系统训练&lt;/td&gt;&lt;td&gt;焦虑&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;下午&lt;/td&gt;&lt;td&gt;想恢复，但身体状态不稳定&lt;/td&gt;&lt;td&gt;担心练错&lt;/td&gt;&lt;td&gt;无助&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;晚上&lt;/td&gt;&lt;td&gt;终于有空照镜子&lt;/td&gt;&lt;td&gt;身材变化大&lt;/td&gt;&lt;td&gt;自卑、孤独&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;这样一写你就会发现，这类用户真正需要的，不只是一个 “健身工具”。&lt;/p&gt;
&lt;p&gt;她需要的是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;安全感&lt;/li&gt;
&lt;li&gt;恢复方案&lt;/li&gt;
&lt;li&gt;专属指导&lt;/li&gt;
&lt;li&gt;情绪支持&lt;/li&gt;
&lt;li&gt;被理解&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;于是这个产品就不该叫 “健身 App”，而更像是：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;产后恢复助手&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这就是从 “一个功能” 升级为 “一个解决方案” 的过程。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;第四步：把功能重构成结果&lt;a href=&quot;#第四步把功能重构成结果&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;我现在做定位时，会强迫自己从 “功能语言” 切到 “结果语言”。&lt;/p&gt;
&lt;p&gt;比如：&lt;/p&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;功能表达&lt;/th&gt;&lt;th&gt;结果表达&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;记账工具&lt;/td&gt;&lt;td&gt;资金管家&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;新闻聚合&lt;/td&gt;&lt;td&gt;情报官&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;番茄钟&lt;/td&gt;&lt;td&gt;工作证明工具&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;健身课程&lt;/td&gt;&lt;td&gt;恢复助手&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;这个转变非常关键，因为用户最终买的不是功能，而是：&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;安心&lt;/li&gt;
&lt;li&gt;节省时间&lt;/li&gt;
&lt;li&gt;降低风险&lt;/li&gt;
&lt;li&gt;提升掌控感&lt;/li&gt;
&lt;li&gt;缓解焦虑&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;第五步：先做最小验证，再决定要不要做大&lt;a href=&quot;#第五步先做最小验证再决定要不要做大&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;这是我现在最看重的一步。&lt;/p&gt;
&lt;p&gt;我不太相信 “先做出来再说”。&lt;/p&gt;
&lt;p&gt;我更相信：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;先验证，再投入。&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;哪怕验证结果不理想，也是一件好事。因为这意味着你用最低成本排除了一个错误方向。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;三个我觉得特别典型的例子&lt;a href=&quot;#三个我觉得特别典型的例子&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;hr /&gt;
&lt;h2&gt;例子一：从 “健身 App” 到 “产后恢复助手”&lt;a href=&quot;#例子一从-健身-app-到-产后恢复助手&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;一开始的想法&lt;a href=&quot;#一开始的想法&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;做一个健身 App，提供训练课程、打卡、记录和社区。&lt;/p&gt;
&lt;h3&gt;这个想法的问题&lt;a href=&quot;#这个想法的问题&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;方向太泛，而且竞争太强。如果你只是做一个更普通的健身工具，用户很难有切换动力。&lt;/p&gt;
&lt;h3&gt;更好的切法&lt;a href=&quot;#更好的切法&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;把 “想健身的人” 拆开看：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;普通健身者&lt;/li&gt;
&lt;li&gt;增肌人群&lt;/li&gt;
&lt;li&gt;糖尿病患者&lt;/li&gt;
&lt;li&gt;产后妈妈&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果继续往下挖，会发现产后妈妈这个群体的需求很典型：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;时间非常碎片化&lt;/li&gt;
&lt;li&gt;身体状态特殊&lt;/li&gt;
&lt;li&gt;容易焦虑&lt;/li&gt;
&lt;li&gt;对 “安全” 和 “专业” 极度敏感&lt;/li&gt;
&lt;li&gt;愿意为恢复付费&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;重构后的产品&lt;a href=&quot;#重构后的产品&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;不是健身 App，而是：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;产后恢复助手&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;它提供的价值也不只是课程，而是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;分阶段恢复方案&lt;/li&gt;
&lt;li&gt;适合碎片时间的训练&lt;/li&gt;
&lt;li&gt;动作安全指导&lt;/li&gt;
&lt;li&gt;情绪支持和陪伴&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;例子二：从 “记账 App” 到 “留学资金管家”&lt;a href=&quot;#例子二从-记账-app-到-留学资金管家&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;一开始的想法&lt;a href=&quot;#一开始的想法-1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;做一个自动记账工具，帮用户分类消费。&lt;/p&gt;
&lt;h3&gt;这个想法的问题&lt;a href=&quot;#这个想法的问题-1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;市场太成熟，普通用户替代品太多。&lt;/p&gt;
&lt;h3&gt;更好的切法&lt;a href=&quot;#更好的切法-1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;切到 “留学生家长” 这个人群。&lt;/p&gt;
&lt;p&gt;这类人的真实需求不是 “我要记账”，而是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;我想知道孩子的钱花到哪了&lt;/li&gt;
&lt;li&gt;我想知道有没有超支&lt;/li&gt;
&lt;li&gt;我想知道消费结构是否异常&lt;/li&gt;
&lt;li&gt;我想减少那种不透明带来的不安&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;重构后的产品&lt;a href=&quot;#重构后的产品-1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;留学资金管家&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;核心不再是记账，而是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;资金透明&lt;/li&gt;
&lt;li&gt;超支提醒&lt;/li&gt;
&lt;li&gt;异常消费感知&lt;/li&gt;
&lt;li&gt;家长掌控感&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;例子三：从 “新闻助手” 到 “投研情报官”&lt;a href=&quot;#例子三从-新闻助手-到-投研情报官&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;一开始的想法&lt;a href=&quot;#一开始的想法-2&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;做一个新闻聚合工具，方便看资讯。&lt;/p&gt;
&lt;h3&gt;这个想法的问题&lt;a href=&quot;#这个想法的问题-2&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;普通用户并不缺新闻入口，大平台已经做得很好。&lt;/p&gt;
&lt;h3&gt;更好的切法&lt;a href=&quot;#更好的切法-2&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;切到金融分析师、投研从业者。&lt;/p&gt;
&lt;p&gt;这类人的真实需求不是 “多看一点新闻”，而是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不要漏掉关键行业动态&lt;/li&gt;
&lt;li&gt;不要错过目标公司的公告&lt;/li&gt;
&lt;li&gt;能够快速理解信息变化&lt;/li&gt;
&lt;li&gt;提升判断效率&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;重构后的产品&lt;a href=&quot;#重构后的产品-2&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;投研情报官&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;它卖的不是资讯，而是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;信息雷达&lt;/li&gt;
&lt;li&gt;关键信号提取&lt;/li&gt;
&lt;li&gt;公告追踪&lt;/li&gt;
&lt;li&gt;辅助决策&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7628874305050443814/img_1.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; /&gt;&lt;/figure&gt;
&lt;hr /&gt;
&lt;h2&gt;AI 在 “找点子” 这件事上，我是怎么用的？&lt;a href=&quot;#ai-在-找点子-这件事上我是怎么用的&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;我觉得 AI 很适合在下面这些环节帮忙：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;帮我重新切分人群&lt;/li&gt;
&lt;li&gt;帮我补全用户场景&lt;/li&gt;
&lt;li&gt;帮我重写产品定位&lt;/li&gt;
&lt;li&gt;帮我规划 MVP&lt;/li&gt;
&lt;li&gt;帮我生成用户访谈问题&lt;/li&gt;
&lt;li&gt;帮我整理验证指标&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但我不会把 “判断需求真假” 这件事完全交给 AI。&lt;/p&gt;
&lt;p&gt;因为最关键的部分其实还是你自己：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;你是不是真的理解这个人群&lt;/li&gt;
&lt;li&gt;你有没有见过那个场景&lt;/li&gt;
&lt;li&gt;你有没有接触到真实用户&lt;/li&gt;
&lt;li&gt;用户是不是真的愿意付钱&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以我现在更愿意把 AI 当成一个 “打磨器”，而不是 “点子生成器”。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;这是我自己会直接拿来用的提示词&lt;a href=&quot;#这是我自己会直接拿来用的提示词&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;暂时无法在飞书文档外展示此内容&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;最后，我现在对 “找点子” 的理解就一句话&lt;a href=&quot;#最后我现在对-找点子-的理解就一句话&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;不要先问 “我能做什么”，要先问 “谁现在真的很痛，而且愿意为解决这个痛付钱”。&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;真正靠谱的点子，通常不是靠脑暴拍出来的，而是沿着这条路径慢慢筛出来的：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;找真需求&lt;/li&gt;
&lt;li&gt;切细分人群&lt;/li&gt;
&lt;li&gt;深挖场景和情绪&lt;/li&gt;
&lt;li&gt;把功能重构成解决方案&lt;/li&gt;
&lt;li&gt;用最小成本验证&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;对技术人来说，最可惜的从来不是 “做不出来”。&lt;/p&gt;
&lt;p&gt;而是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;你明明很能做，却把最宝贵的时间花在了一个不值得做的方向上。&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;所以现在如果你问我，做产品最重要的一步是什么。我的答案不是写代码，也不是画原型。&lt;/p&gt;
&lt;p&gt;而是：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;先找到那个真正值得做的问题。&lt;/strong&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;真正的好点子，不是 “这个功能有没有人会用”，而是 “这群人现在是不是已经痛到愿意付费”。做产品前，先别急着写代码，先把问题找对。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded><category>category:掘金同步</category><category>category:前端</category><category>tag:前端</category></item><item><title>想偷卷？但微信不支持md文档？这个软件助你！</title><link>https://koharu.ac.ren/post/juejin/%E6%83%B3%E5%81%B7%E5%8D%B7_%E4%BD%86%E5%BE%AE%E4%BF%A1%E4%B8%8D%E6%94%AF%E6%8C%81md%E6%96%87%E6%A1%A3_%E8%BF%99%E4%B8%AA%E8%BD%AF%E4%BB%B6%E5%8A%A9%E4%BD%A0_</link><guid isPermaLink="false">juejin/想偷卷_但微信不支持md文档_这个软件助你_</guid><description>想偷卷？但微信不支持md文档？这个软件助你！  作者: 前端AC | 原文: https://juejin.cn/post/7563473808994549814  📝 Markdown 查看器  现代化的文档预览工具  一个基于 React 19 + TypeScript 构建的现代化 Markd...</description><pubDate>Tue, 21 Oct 2025 16:20:59 GMT</pubDate><content:encoded>&lt;h1&gt;想偷卷？但微信不支持md文档？这个软件助你！&lt;a href=&quot;#想偷卷但微信不支持md文档这个软件助你&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;作者: 前端AC | 原文: &lt;a href=&quot;https://juejin.cn/post/7563473808994549814&quot;&gt;https://juejin.cn/post/7563473808994549814&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt; Markdown 查看器 - 现代化的文档预览工具&lt;a href=&quot;#-markdown-查看器---现代化的文档预览工具&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;一个基于 React 19 + TypeScript 构建的现代化 Markdown 文档查看器，支持实时预览、语法高亮、数学公式渲染等功能。&lt;/p&gt;
&lt;p&gt;在微信或浏览器上打开此编辑器，上传你的md文档可以上课偷偷看自己写的博客哈哈，这个是我解决微信这个没有md预览的痛点，自己用ai搞了一个小工具出来，效果还不错，还有图片可以借助图床工具：&lt;a href=&quot;https://img.scdn.io/&quot;&gt;图床 - 简单、快速、免费的图床&lt;/a&gt;把自己图片上传到这里，就不会导致路径问题了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;项目概述&lt;a href=&quot;#项目概述&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;项目背景&lt;a href=&quot;#项目背景&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;在日常开发和写作中，我们经常需要预览 Markdown 文档的渲染效果。虽然市面上有很多 Markdown 编辑器，但大多数要么功能过于复杂，要么界面不够现代化。因此，我开发了这个轻量级、功能完整的 Markdown 查看器。&lt;/p&gt;
&lt;h3&gt;核心特性&lt;a href=&quot;#核心特性&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;现代化 UI 设计&lt;/strong&gt; - 基于 Tailwind CSS 的响应式设计&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多主题支持&lt;/strong&gt; - 亮色/暗色/护眼模式，适应不同使用场景&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;完美适配移动端&lt;/strong&gt; - 响应式布局，手机平板都能完美使用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;强大的 Markdown 渲染&lt;/strong&gt; - 支持 GFM、数学公式、代码高亮&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;智能本地存储&lt;/strong&gt; - 自动保存文档，刷新不丢失&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;极速加载体验&lt;/strong&gt; - 优化的构建配置，秒开应用&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;技术架构&lt;a href=&quot;#技术架构&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;前端技术栈&lt;a href=&quot;#前端技术栈&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;React 19.1.1          # 最新的 React 版本，性能更优&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;TypeScript 5.9.3      # 类型安全，开发体验更好&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Vite (Rolldown)       # 下一代构建工具，构建速度极快&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Tailwind CSS 3.4.10   # 原子化 CSS 框架&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;核心依赖&lt;a href=&quot;#核心依赖&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;react-markdown 9.0.1   # Markdown 渲染引擎&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;highlight.js 11.9.0    # 代码语法高亮&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;katex 0.16.9          # 数学公式渲染&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;lucide-react 0.400.0  # 现代化图标库&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;项目结构&lt;a href=&quot;#项目结构&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;src/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── components/        # 组件目录&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── Header.tsx    # 顶部导航栏&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── Sidebar.tsx   # 侧边栏文件管理&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   └── MainContent.tsx # 主内容区域&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── stores/           # 状态管理&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   └── useAppStore.ts # Zustand 全局状态&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── utils/            # 工具函数&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── api.ts       # API 接口&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── cache.ts     # 缓存管理&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   └── markdown.ts  # Markdown 处理&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;└── types/            # TypeScript 类型定义&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    └── index.ts&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;项目地址&lt;/strong&gt;: &lt;a href=&quot;https://github.com/ac666666666/markdown-view&quot;&gt;https://github.com/ac666666666/markdown-view&lt;/a&gt;&lt;br /&gt;
&lt;strong&gt;在线预览&lt;/strong&gt;: &lt;a href=&quot;http://129.204.12.129:9080/&quot;&gt;http://129.204.12.129:9080/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;pc端：
&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;/images/juejin/7563473808994549814/img_0.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; /&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;移动端：
&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;/images/juejin/7563473808994549814/img_1.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; /&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;如果这个项目对你有帮助，欢迎 Star ⭐ 支持！&lt;/p&gt;</content:encoded><category>category:掘金同步</category><category>category:前端</category><category>tag:前端</category><category>tag:JavaScript</category></item><item><title>新手小白如何开发练手项目？</title><link>https://koharu.ac.ren/post/juejin/%E6%96%B0%E6%89%8B%E5%B0%8F%E7%99%BD%E5%A6%82%E4%BD%95%E5%BC%80%E5%8F%91%E7%BB%83%E6%89%8B%E9%A1%B9%E7%9B%AE_</link><guid isPermaLink="false">juejin/新手小白如何开发练手项目_</guid><description>新手小白如何开发练手项目？  作者: 前端AC | 原文: https://juejin.cn/post/7560580887253188650  前言 朱波也是好久没更新了，最近也是在疯狂的实习面试哈，也是拿到4个offer，不过都不怎么喜欢就没去了，最近也是实验室的师弟师妹们刚开始项目，也是有点...</description><pubDate>Tue, 14 Oct 2025 03:41:08 GMT</pubDate><content:encoded>&lt;h1&gt;新手小白如何开发练手项目？&lt;a href=&quot;#新手小白如何开发练手项目&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;作者: 前端AC | 原文: &lt;a href=&quot;https://juejin.cn/post/7560580887253188650&quot;&gt;https://juejin.cn/post/7560580887253188650&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;前言&lt;a href=&quot;#前言&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;朱波也是好久没更新了，最近也是在疯狂的实习面试哈，也是拿到4个offer，不过都不怎么喜欢就没去了，最近也是实验室的师弟师妹们刚开始项目，也是有点不懂的地方，所以我想出一个我自己做项目的一个经验，怎么从0-1的搭建好一个项目架构，从产品-UI-开发-测试-部署上线这几个部分来。&lt;/p&gt;
&lt;p&gt;我将举例阳江智慧养鸡项目来开始：&lt;/p&gt;
&lt;h1&gt;1-产品分析&lt;a href=&quot;#1-产品分析&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;我们前端呢也是需要去学习一些开发以外的东西，毕竟业务为王嘛，但是我们拿到一个练习项目怎么去开始写内容呢？脑子一直想不出来写啥？AI的流行，我们可以借助ai来辅助我们去分析一个产品需要有什么内容，一个好的项目我们需要花很多时间去分析它的内容。&lt;/p&gt;
&lt;p&gt;那么我们怎么去借助ai去分析呢？这里我使用GPT去分析，不能翻墙的同学可以使用国内的ai也行。&lt;/p&gt;
&lt;p&gt;第一步，我们分析我们的项目，《智慧养鸡》&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_0.png&quot; alt=&quot;image-20251014102843927.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014102843927.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;给出的结果我们来进行分析：&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_1.png&quot; alt=&quot;image-20251014103214754.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014103214754.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_2.png&quot; alt=&quot;image-20251014103246501.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014103246501.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;上面就是告诉我们要做多端的项目，比如web项目（浏览器PC端）、还有手机的小程序或者h5、或者手机跨端app项目，大家可以根据自己的技术、业务的范围去定。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_3.png&quot; alt=&quot;image-20251014103436402.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014103436402.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;这里ai说智慧方面我们可以围绕硬件（传感器）、yolo算法、天气预报（网上有一些api可以去调用的如和风天气）等进行结合，数字孪生鸡舍技术的话结合3d技术如Three.js/Babylon.js/cannon.js去进行开发。&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_4.png&quot; alt=&quot;image-20251014103633503.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014103633503.png&lt;/figcaption&gt;&lt;/figure&gt;
围绕鸡出发，分析鸡可以写什么内容： 1-溯源系统（养殖→屠宰→销售） 2-记录一个生长档案系统（后台表单的crud） 3-ai健康指数（算法分析鸡的生长情况）&lt;p&gt;&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_5.png&quot; alt=&quot;image-20251014103747654.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014103747654.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;围绕在阳江地理方面，可以结合当地一个地理位置和文化优势，当地特色就是阳江土鸡，需要数据的话可以去当地的养殖场采集数据。或者做一个前台的文化宣传平台如：&lt;a href=&quot;http://129.204.12.129:9092/&quot;&gt;http://129.204.12.129:9092/&lt;/a&gt;&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_6.png&quot; alt=&quot;image-20251014105735570.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014105735570.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_7.png&quot; alt=&quot;image-20251014110033554.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014110033554.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_8.png&quot; alt=&quot;image-20251014110044772.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014110044772.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;这个是给的一些页面大概设计，根据自己的需求来，如果自己技术不行的就选一些自己可以的，然后没有想法的可以借鉴，不一定跟着来.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_9.png&quot; alt=&quot;image-20251014110056649.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014110056649.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;这个是给的大概的技术栈，可以借鉴一下。现在我们就清楚我们要写什么内容了，接下来我举例pc端的一个后台或者一个前台展示页面，教大家如何去UI设计页面&lt;/p&gt;
&lt;h1&gt;2-UI设计&lt;a href=&quot;#2-ui设计&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;前端不如那些UI设计一样懂页面设计，但是我们可以借助ai软件，市场上有很多ai： 1-扣子：&lt;a href=&quot;https://space.coze.cn/&quot;&gt;https://space.coze.cn/&lt;/a&gt; 2-bolt：&lt;a href=&quot;https://bolt.new/&quot;&gt;https://bolt.new/&lt;/a&gt; 3-v0：(&lt;a href=&quot;https://v0.dev/&quot;&gt;https://v0.dev/&lt;/a&gt;) 4-蓝湖：(&lt;a href=&quot;https://mastergo.com/files/home&quot;&gt;https://mastergo.com/files/home&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;接下来我举例蓝湖设计我的简单页面：&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_10.png&quot; alt=&quot;image-20251014110525922.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014110525922.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_11.png&quot; alt=&quot;image-20251014110536825.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014110536825.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_12.png&quot; alt=&quot;image-20251014110649897.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014110649897.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;然后复制到ai页面,顺便选择我们的技术栈，这里我使用vue：&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_13.png&quot; alt=&quot;image-20251014110734225.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014110734225.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_14.png&quot; alt=&quot;image-20251014110849788.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014110849788.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;然后就开始生成我们的内容，你可以根据需要去调整，然后就开始设计我们的大概页面，大家就可以把这个原型图扔给我们的ai模型，开始去写代码了哈哈。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_15.png&quot; alt=&quot;image-20251014111439528.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014111439528.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h1&gt;3-搭建前端项目&lt;a href=&quot;#3-搭建前端项目&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;我之前就发过一篇vue的项目搭建，具体可看：&lt;a href=&quot;https://juejin.cn/post/7486024544240730162&quot;&gt;从0到1配置vue项目跟git仓库连接！前言 小编心血来潮，想出一集vue的项目配置，包括git和项目环境搭配，如下： - 掘金&lt;/a&gt;，里面有教如何创建vue3项目，配置git，配置简单路由。&lt;/p&gt;
&lt;h1&gt;4-接口文档&lt;a href=&quot;#4-接口文档&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;在开发过程中，前后端协调会以一个接口文档来统一我们的字段，接口，请求方式等，后端一般会给我们一个接口文档，然后我们前端调接口拿到数据去放到页面上。我们前端也可以使用mock去模拟请求，减少开发周期，不懂的可以看官方文档：&lt;a href=&quot;http://mockjs.com/&quot;&gt;Mock.js&lt;/a&gt;。测试接口可以使用：postman、aipost，Apifox等。我习惯是使用aipost：&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_16.png&quot; alt=&quot;image-20251014111536964.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014111536964.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h1&gt;5-部署（宝塔快速部署）&lt;a href=&quot;#5-部署宝塔快速部署&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;项目完成之后就可以打包部署了，常见指令：&lt;code&gt;npm run build&lt;/code&gt;，打包成一个dist文件，然后我们可以选择一个云服务商：腾讯云、阿里云、七牛云等，这里我经常使用的是腾讯云，可以去购买一个轻量型云服务器（新用户有试用期一个月），然后就部署我们的前端项目。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_17.png&quot; alt=&quot;image-20251014111812384.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014111812384.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_18.png&quot; alt=&quot;image-20251014111840423.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014111840423.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;这里我以我的一个无敌服务器（其实一大堆危险）为例，点击三个点…查看应用详情&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_19.png&quot; alt=&quot;image-20251014111950051.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014111950051.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;复制我们的命令，然后点击登录&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_20.png&quot; alt=&quot;image-20251014112003568.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014112003568.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;然后执行：&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_21.png&quot; alt=&quot;image-20251014112046822.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014112046822.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_22.png&quot; alt=&quot;image-20251014112109559.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014112109559.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;拿到我们进入宝塔的一个链接，进去之后（选择高级），可能不安全，不管他，然后输入账号密码进去页面：&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_23.png&quot; alt=&quot;image-20251014112235337.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014112235337.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;然后点击文件菜单，把我们的dist文件上传上去。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_24.png&quot; alt=&quot;image-20251014112308685.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014112308685.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;然后点击网站&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_25.png&quot; alt=&quot;image-20251014113134756.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014113134756.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_26.png&quot; alt=&quot;image-20251014112320161.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014112320161.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;来网站这边选择php项目/html项目，添加站点&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_27.png&quot; alt=&quot;image-20251014113013432.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014113013432.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;然后把dist目录路径选对&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_28.png&quot; alt=&quot;image-20251014113038737.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014113038737.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;然后网站名就是我们的ip地址，然后配置我们的nginx，配置好端口/404页面&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_29.png&quot; alt=&quot;image-20251014113134756.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014113134756.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;然后放行我们的防火墙9095端口&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_30.png&quot; alt=&quot;image-20251014113247482.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014113247482.png&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;然后重启一下nginx&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;/images/juejin/7560580887253188650/img_31.png&quot; alt=&quot;image-20251014113153344.png&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;image-20251014113153344.png&lt;/figcaption&gt;&lt;/figure&gt;
我们的项目就可以上线演示咯！&lt;p&gt;&lt;/p&gt;</content:encoded><category>category:掘金同步</category><category>category:前端</category><category>tag:前端</category><category>tag:JavaScript</category><category>tag:前端框架</category></item><item><title>27拓扑前端日常实习面经</title><link>https://koharu.ac.ren/post/juejin/27%E6%8B%93%E6%89%91%E5%89%8D%E7%AB%AF%E6%97%A5%E5%B8%B8%E5%AE%9E%E4%B9%A0%E9%9D%A2%E7%BB%8F</link><guid isPermaLink="false">juejin/27拓扑前端日常实习面经</guid><description>27拓扑前端日常实习面经  作者: 前端AC | 原文: https://juejin.cn/post/7545794742550855730 没有笔试题 ，但是会有情景题数学题 1面试官进来就先介绍他们的业务还有技术还有招的人数，主要是在业务这方面聊天差不多20分钟 2自我介绍 3es6新特性 4...</description><pubDate>Thu, 04 Sep 2025 05:18:50 GMT</pubDate><content:encoded>&lt;h1&gt;27拓扑前端日常实习面经&lt;a href=&quot;#27拓扑前端日常实习面经&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;作者: 前端AC | 原文: &lt;a href=&quot;https://juejin.cn/post/7545794742550855730&quot;&gt;https://juejin.cn/post/7545794742550855730&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;没有笔试题 ，但是会有情景题数学题
1-面试官进来就先介绍他们的业务还有技术还有招的人数，主要是在业务这方面聊天差不多20分钟
2-自我介绍
3-es6新特性
4-讲讲promise
5-讲一下v-for的key吧，为什么要用，然后涉及到虚拟dom的什么原理
6-前端工程化
7-问我开发有没有ai习惯，是ai主导还是自己主导，然后跟我讲了现在中小厂都是ai开发的
8-前端未来方向是超级程序员，啥都干，要跳出前端，业务为主，业务就是秦始皇
反问
1-项目大了怎么维护呢？&lt;/p&gt;</content:encoded><category>category:掘金同步</category><category>category:面试</category><category>tag:面试</category></item><item><title>前端常见安全问题 + 防御方法 + 面试回答</title><link>https://koharu.ac.ren/post/juejin/%E5%89%8D%E7%AB%AF%E5%B8%B8%E8%A7%81%E5%AE%89%E5%85%A8%E9%97%AE%E9%A2%98___%E9%98%B2%E5%BE%A1%E6%96%B9%E6%B3%95___%E9%9D%A2%E8%AF%95%E5%9B%9E%E7%AD%94</link><guid isPermaLink="false">juejin/前端常见安全问题___防御方法___面试回答</guid><description>前端常见安全问题 + 防御方法 + 面试回答  作者: 前端AC | 原文: https://juejin.cn/post/7543981652944273460  1. XSS（跨站脚本攻击）  定义 XSS（CrossSite Scripting）是一种 通过注入恶意脚本执行非预期操作 的攻击。...</description><pubDate>Sun, 31 Aug 2025 09:48:02 GMT</pubDate><content:encoded>&lt;h1&gt;前端常见安全问题 + 防御方法 + 面试回答&lt;a href=&quot;#前端常见安全问题--防御方法--面试回答&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;作者: 前端AC | 原文: &lt;a href=&quot;https://juejin.cn/post/7543981652944273460&quot;&gt;https://juejin.cn/post/7543981652944273460&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;1. XSS（跨站脚本攻击）&lt;a href=&quot;#1-xss跨站脚本攻击&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;定义&lt;a href=&quot;#定义&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;XSS（Cross-Site Scripting）是一种 &lt;strong&gt;通过注入恶意脚本执行非预期操作&lt;/strong&gt; 的攻击。&lt;br /&gt;
分为三类：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;存储型&lt;/strong&gt;：代码存储在数据库中，用户访问时执行（评论区、论坛）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;反射型&lt;/strong&gt;：代码通过 URL 参数返回执行。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DOM 型&lt;/strong&gt;：前端 JS 动态插入数据时执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;危害&lt;a href=&quot;#危害&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;窃取 &lt;strong&gt;Cookie、Token&lt;/strong&gt;，盗取用户身份。&lt;/li&gt;
&lt;li&gt;伪造请求（转账、发帖）。&lt;/li&gt;
&lt;li&gt;植入恶意脚本、广告、木马。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;防御手段&lt;a href=&quot;#防御手段&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;输入过滤 + 输出转义&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CSP（Content Security Policy）&lt;/strong&gt; 限制脚本来源。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HttpOnly Cookie&lt;/strong&gt;，防止通过 JS 获取。&lt;/li&gt;
&lt;li&gt;前端框架（React/Vue）默认转义 HTML。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;示例&lt;a href=&quot;#示例&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;h4&gt;❌ 漏洞代码&lt;a href=&quot;#-漏洞代码&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;document.body.innerHTML = location.search; &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;访问：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;http://site.com?msg=&amp;lt;script&amp;gt;alert(1)&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;✅ 安全代码&lt;a href=&quot;#-安全代码&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;document.body.textContent = location.search;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2. CSRF（跨站请求伪造）&lt;a href=&quot;#2-csrf跨站请求伪造&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;定义&lt;a href=&quot;#定义-1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;CSRF（Cross-Site Request Forgery）利用 &lt;strong&gt;用户已登录状态&lt;/strong&gt;，诱导其访问恶意链接，伪造合法请求。&lt;br /&gt;
浏览器会自动带上用户 Cookie，从而冒充用户身份。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;危害&lt;a href=&quot;#危害-1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;转账、盗刷。&lt;/li&gt;
&lt;li&gt;修改密码、邮箱。&lt;/li&gt;
&lt;li&gt;批量操作（删帖、发帖）。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;防御手段&lt;a href=&quot;#防御手段-1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;CSRF Token 验证&lt;/strong&gt;（每个请求附带随机 Token）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SameSite Cookie&lt;/strong&gt;：阻止跨站请求携带 Cookie。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;验证码 / 二次确认&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Referer / Origin 校验&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h3&gt;示例&lt;a href=&quot;#示例-1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;h4&gt;❌ 漏洞&lt;a href=&quot;#-漏洞&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;img src=&quot;http://bank.com/transfer?to=attacker&amp;amp;amount=1000&quot;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;✅ 安全（CSRF Token）&lt;a href=&quot;#-安全csrf-token&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;form method=&quot;POST&quot; action=&quot;/transfer&quot;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;input type=&quot;hidden&quot; name=&quot;csrfToken&quot; value=&quot;random123&quot;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;input name=&quot;to&quot;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;input name=&quot;amount&quot;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;button&amp;gt;提交&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. SQL 注入&lt;a href=&quot;#3-sql-注入&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;定义&lt;a href=&quot;#定义-2&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;SQL 注入是攻击者 &lt;strong&gt;在输入中注入恶意 SQL 代码&lt;/strong&gt;，拼接后数据库直接执行。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;危害&lt;a href=&quot;#危害-2&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;绕过登录验证。&lt;/li&gt;
&lt;li&gt;窃取/篡改数据。&lt;/li&gt;
&lt;li&gt;删除数据库。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;防御手段&lt;a href=&quot;#防御手段-2&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;参数化查询 / 预编译（Prepared Statement）&lt;/strong&gt; ✅&lt;/li&gt;
&lt;li&gt;输入校验（过滤 &lt;code&gt;&apos;&lt;/code&gt;, &lt;code&gt;--&lt;/code&gt;, &lt;code&gt;;&lt;/code&gt; 等特殊符号）。&lt;/li&gt;
&lt;li&gt;最小权限（避免删库）。&lt;/li&gt;
&lt;li&gt;使用 &lt;strong&gt;ORM 框架&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h3&gt;示例&lt;a href=&quot;#示例-2&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;h4&gt;❌ 漏洞&lt;a href=&quot;#-漏洞-1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const sql = `SELECT * FROM users WHERE username=&apos;${username}&apos; AND password=&apos;${password}&apos;`;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;db.query(sql);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输入：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&apos; OR &apos;1&apos;=&apos;1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;✅ 安全&lt;a href=&quot;#-安全&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const sql = &apos;SELECT * FROM users WHERE username=? AND password=?&apos;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;db.query(sql, [username, password]);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. 文件上传漏洞&lt;a href=&quot;#4-文件上传漏洞&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;定义&lt;a href=&quot;#定义-3&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;文件上传漏洞是指：&lt;strong&gt;攻击者上传恶意文件（如 WebShell），服务器未严格校验，导致代码执行或 DoS 攻击&lt;/strong&gt;。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;危害&lt;a href=&quot;#危害-3&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;上传脚本木马，控制服务器。&lt;/li&gt;
&lt;li&gt;上传恶意文档，攻击用户。&lt;/li&gt;
&lt;li&gt;上传大文件，导致拒绝服务。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;防御手段&lt;a href=&quot;#防御手段-3&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;白名单文件类型&lt;/strong&gt;（校验扩展名 + MIME + 文件头）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重命名文件&lt;/strong&gt;（避免保留原始扩展名）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;存储到非 Web 目录&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;限制大小/数量&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;前端校验（次要）+ 后端强制校验（核心）&lt;/strong&gt; 。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h3&gt;示例&lt;a href=&quot;#示例-3&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;h4&gt;❌ 漏洞&lt;a href=&quot;#-漏洞-2&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;if (substr($_FILES[&apos;file&apos;][&apos;name&apos;], -4) === &quot;.jpg&quot;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    move_uploaded_file($_FILES[&apos;file&apos;][&apos;tmp_name&apos;], &quot;/uploads/&quot; . $_FILES[&apos;file&apos;][&apos;name&apos;]);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;攻击者上传 &lt;code&gt;evil.php.jpg&lt;/code&gt;。&lt;/p&gt;
&lt;h4&gt;✅ 安全（Node.js）&lt;a href=&quot;#-安全nodejs&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const upload = multer({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  storage,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  fileFilter: (req, file, cb) =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const allowed = [&apos;image/jpeg&apos;, &apos;image/png&apos;];&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if (!allowed.includes(file.mimetype)) return cb(new Error(&apos;Invalid type&apos;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    cb(null, true);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  limits: { fileSize: 1024 * 1024 * 2 }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. 其他前端常见安全问题&lt;a href=&quot;#5-其他前端常见安全问题&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;5.1 点击劫持（Clickjacking）&lt;a href=&quot;#51-点击劫持clickjacking&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;定义：攻击者在透明 iframe 中嵌套目标站点，引导用户点击。&lt;/li&gt;
&lt;li&gt;防御：&lt;code&gt;X-Frame-Options: DENY&lt;/code&gt; 或 &lt;code&gt;Content-Security-Policy: frame-ancestors &apos;none&apos;;&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5.2 敏感信息泄露&lt;a href=&quot;#52-敏感信息泄露&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;问题：前端暴露 API Key、JWT、调试接口。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;防御：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不在前端存储敏感信息。&lt;/li&gt;
&lt;li&gt;使用环境变量 + 后端代理。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5.3 依赖安全漏洞&lt;a href=&quot;#53-依赖安全漏洞&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;问题：前端依赖（npm 包）可能被注入恶意代码。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;防御：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用 &lt;code&gt;npm audit&lt;/code&gt;/&lt;code&gt;yarn audit&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;锁定依赖版本。&lt;/li&gt;
&lt;li&gt;关注供应链安全（supply chain attack）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;6. 面试常见问答&lt;a href=&quot;#6-面试常见问答&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;Q1: 什么是 XSS？怎么防御？&lt;a href=&quot;#q1-什么是-xss怎么防御&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;跨站脚本攻击，注入恶意 JS。&lt;/li&gt;
&lt;li&gt;危害：窃取 Cookie、执行恶意操作。&lt;/li&gt;
&lt;li&gt;防御：输入过滤 + 输出转义 + CSP + HttpOnly Cookie。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Q2: 什么是 CSRF？怎么防御？&lt;a href=&quot;#q2-什么是-csrf怎么防御&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;跨站请求伪造，利用用户 Cookie 发起伪造请求。&lt;/li&gt;
&lt;li&gt;防御：CSRF Token、SameSite Cookie、验证码、Referer 校验。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Q3: 什么是 SQL 注入？怎么防御？&lt;a href=&quot;#q3-什么是-sql-注入怎么防御&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;在输入中拼接 SQL 语句篡改数据库。&lt;/li&gt;
&lt;li&gt;防御：预编译、参数化查询、最小权限、ORM 框架。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Q4: 文件上传漏洞如何防御？&lt;a href=&quot;#q4-文件上传漏洞如何防御&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;白名单校验文件类型（MIME + 文件头）。&lt;/li&gt;
&lt;li&gt;上传后重命名，存储到非 Web 目录。&lt;/li&gt;
&lt;li&gt;限制大小和数量，多层校验。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Q5: 点击劫持如何防御？&lt;a href=&quot;#q5-点击劫持如何防御&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;使用 &lt;code&gt;X-Frame-Options&lt;/code&gt; 或 &lt;code&gt;CSP frame-ancestors&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>category:掘金同步</category><category>category:前端</category><category>tag:前端</category><category>tag:面试</category><category>tag:JavaScript</category></item><item><title>吉比特（雷霆游戏）前端二面问题总结</title><link>https://koharu.ac.ren/post/juejin/%E5%90%89%E6%AF%94%E7%89%B9_%E9%9B%B7%E9%9C%86%E6%B8%B8%E6%88%8F_%E5%89%8D%E7%AB%AF%E4%BA%8C%E9%9D%A2%E9%97%AE%E9%A2%98%E6%80%BB%E7%BB%93</link><guid isPermaLink="false">juejin/吉比特_雷霆游戏_前端二面问题总结</guid><description>吉比特（雷霆游戏）前端二面问题总结  作者: 前端AC | 原文: https://juejin.cn/post/7542953832671592467  一、岗位与经历相关 1.  介绍在吉比特雷霆游戏岗位负责的具体工作内容（如营销活动、H5页面等）。 1.  岗位要求中提到需要哪些技术能力（如前...</description><pubDate>Wed, 27 Aug 2025 09:53:13 GMT</pubDate><content:encoded>&lt;h1&gt;吉比特（雷霆游戏）前端二面问题总结&lt;a href=&quot;#吉比特雷霆游戏前端二面问题总结&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;作者: 前端AC | 原文: &lt;a href=&quot;https://juejin.cn/post/7542953832671592467&quot;&gt;https://juejin.cn/post/7542953832671592467&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;一、岗位与经历相关&lt;a href=&quot;#一岗位与经历相关&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;介绍在吉比特雷霆游戏岗位负责的具体工作内容（如营销活动、H5页面等）。&lt;/li&gt;
&lt;li&gt;岗位要求中提到需要哪些技术能力（如前端、服务端基础、小游戏经验等）？&lt;/li&gt;
&lt;li&gt;介绍你的教育背景（学校、专业）及前端学习经历（Vue/React掌握程度）。&lt;/li&gt;
&lt;li&gt;讲讲你在东田数码和中科院的两段实习分别做了什么？&lt;/li&gt;
&lt;li&gt;你近期参与了哪些开源项目（如OMI框架）？具体贡献了什么？&lt;/li&gt;
&lt;li&gt;你开发过哪些小工具或技术探索项目（如Markdown预览器、Three.js游戏）？&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;二、技术问题（深入探讨）&lt;a href=&quot;#二技术问题深入探讨&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;h5&gt;1. 开源与工具开发&lt;a href=&quot;#1-开源与工具开发&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;你参与的开源编辑器（模仿BigMall）是为了解决什么问题？腾讯为什么要做这个编辑器？&lt;/li&gt;
&lt;li&gt;这个编辑器的底层引擎是什么？开发模式是怎样的（如团队分工、流程）？&lt;/li&gt;
&lt;/ol&gt;
&lt;h5&gt;2. 性能优化&lt;a href=&quot;#2-性能优化&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;你在东田实习时如何优化动画S5编辑器的webpack打包性能？具体用了什么方案？&lt;/li&gt;
&lt;li&gt;你提到的SMR热更新是如何实现的（核心机制）？&lt;/li&gt;
&lt;li&gt;移动端适配你用了rem和CSS媒体查询，吴湖生建议用PX+Webpack插件转rem，你怎么看？&lt;/li&gt;
&lt;li&gt;水产养殖系统里为什么把PNG图片换成Webp？还用了哪些优化手段（如CDN/SSR）？&lt;/li&gt;
&lt;li&gt;前端单页面应用和SSR在首屏渲染上有什么区别？从SEO角度为什么推荐SSR？&lt;/li&gt;
&lt;li&gt;大屏可视化项目中3D图表卡顿，你是怎么优化的（如何切换2D/3D）？&lt;/li&gt;
&lt;li&gt;前端盒子模型有哪两种（标准/怪异）？它们的宽高计算有什么区别？&lt;/li&gt;
&lt;/ol&gt;
&lt;h5&gt;3. 前端基础与原理&lt;a href=&quot;#3-前端基础与原理&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;Vue 2的响应式原理是什么（如何实现依赖收集和更新）？有哪些缺点（如数组、对象操作）？&lt;/li&gt;
&lt;li&gt;Vue 3的响应式原理和Vue 2有什么区别（Proxy的作用）？&lt;/li&gt;
&lt;li&gt;Vue 2依赖收集时用的数据结构是什么（吴湖生问但未答出）？&lt;/li&gt;
&lt;li&gt;Vue 2中数组索引变化和新增/删除对象属性为什么监听不到？怎么解决（deep/watch）？&lt;/li&gt;
&lt;li&gt;Vue 2中是如何重写数组方法（如push）来实现新增项通知依赖的？具体怎么实现？&lt;/li&gt;
&lt;li&gt;webpack打包的完整流程是什么（从入口到输出）？生产环境优化方案有哪些（如Tree Shaking）？&lt;/li&gt;
&lt;li&gt;webpack中loader的作用是什么？它的执行顺序是怎样的（从上到下/从右到左）？&lt;/li&gt;
&lt;/ol&gt;
&lt;h5&gt;4. 其他技术&lt;a href=&quot;#4-其他技术&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;HTTPS和HTTP有什么区别（加密/安全性）？HTTPS的实现原理是什么（握手过程）？&lt;/li&gt;
&lt;li&gt;你认为前端性能优化的核心指标有哪些（如首屏加载、渲染时间）？具体优化方法是什么（如懒加载、CDN）？&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;三、代码实践&lt;a href=&quot;#三代码实践&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;用递归实现一个深拷贝（现场编码，考察逻辑与实现细节）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;四、面试流程相关&lt;a href=&quot;#四面试流程相关&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;你之前技术面的表现如何？接下来可能有HR面吗？&lt;/li&gt;
&lt;li&gt;你知道吉比特（雷霆游戏）的办公地点在深圳吗？&lt;/li&gt;
&lt;/ol&gt;</content:encoded><category>category:掘金同步</category><category>category:前端</category><category>tag:前端</category><category>tag:面试</category></item><item><title>吉比特27前端实习面试</title><link>https://koharu.ac.ren/post/juejin/%E5%90%89%E6%AF%94%E7%89%B927%E5%89%8D%E7%AB%AF%E5%AE%9E%E4%B9%A0%E9%9D%A2%E8%AF%95</link><guid isPermaLink="false">juejin/吉比特27前端实习面试</guid><description>吉比特27前端实习面试  作者: 前端AC | 原文: https://juejin.cn/post/7538769988296900660  前言 朱波误闯GBT，面试官挺好的，居然后面说也是同校师兄，给了一些面评还有简历修改意见，下面是询问的一些内容，有一些忘记了。  一、技术问题复盘  算法题...</description><pubDate>Sat, 16 Aug 2025 07:40:02 GMT</pubDate><content:encoded>&lt;h1&gt;吉比特27前端实习面试&lt;a href=&quot;#吉比特27前端实习面试&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;作者: 前端AC | 原文: &lt;a href=&quot;https://juejin.cn/post/7538769988296900660&quot;&gt;https://juejin.cn/post/7538769988296900660&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;前言&lt;a href=&quot;#前言&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;朱波误闯GBT，面试官挺好的，居然后面说也是同校师兄，给了一些面评还有简历修改意见，下面是询问的一些内容，有一些忘记了。&lt;/p&gt;
&lt;h2&gt;一、技术问题复盘&lt;a href=&quot;#一技术问题复盘&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;算法题&lt;a href=&quot;#算法题&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1. 无重复字符的最长子串（LeetCode 原题）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;问题&lt;/strong&gt;：求字符串中不含重复字符的最长子串长度&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;反思&lt;/strong&gt;：需掌握滑动窗口解法（双指针+哈希表记录字符索引），理解时间复杂度 O(n) 的优化逻辑&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;项目拷打 &amp;amp; 实习经历&lt;a href=&quot;#项目拷打--实习经历&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2. 项目拷打 / 实习拷打&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;问题&lt;/strong&gt;：对实习项目和课程项目的细节挖掘（技术实现、难点解决、量化结果）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;反思&lt;/strong&gt;：回答过于浅层，需明确「使用了什么技术 → 解决了什么问题 → 达成了什么效果（数据量化）」&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;前端核心原理&lt;a href=&quot;#前端核心原理&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;3. 响应式原理&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;问题&lt;/strong&gt;：Vue/React 响应式实现机制（如 Vue2 的 Object.defineProperty、Vue3 的 Proxy；React 的 setState 调度）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;反思&lt;/strong&gt;：需区分框架差异，结合依赖收集、派发更新等流程详细说明&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;4. OMI 是什么？你做了什么？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;问题&lt;/strong&gt;：对自研/使用的前端框架（如 OMI，类 Web Components 方案）的理解与实践&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;反思&lt;/strong&gt;：需清晰描述 OMI 的核心特性（如基于 Custom Elements 的封装），以及个人在其中的开发贡献（如组件开发、性能优化等）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;5. 封装组件如何实现 React 和 Vue 兼容？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;问题&lt;/strong&gt;：跨框架组件设计的通用方案（如通过 Web Components 或 props/event 标准化）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;反思&lt;/strong&gt;：需提及适配层设计（如统一事件通信、属性传递规范）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;6. 如何封装组件？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;问题&lt;/strong&gt;：组件的设计原则（单一职责、可复用性）与实现步骤（Props 定义、状态管理、事件暴露）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;反思&lt;/strong&gt;：需结合具体场景（如业务组件/通用组件）说明封装思路&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;7. 组件通信&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;问题&lt;/strong&gt;：父子/跨组件通信方式（如 Props/Events、Context、Vuex/Pinia、Redux、事件总线等）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;反思&lt;/strong&gt;：需区分场景说明适用方案&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;JavaScript 基础&lt;a href=&quot;#javascript-基础&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;8. 闭包&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;问题&lt;/strong&gt;：函数与词法环境的绑定关系（如闭包的形成条件、应用场景、内存泄漏风险）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;反思&lt;/strong&gt;：需通过代码示例解释（如计数器、模块化封装）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;9. HTTP 和 HTTPS&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;问题&lt;/strong&gt;：两者区别（明文传输 vs 加密传输）、HTTPS 的必要性&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;10. HTTPS 加密过程&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;问题&lt;/strong&gt;：SSL/TLS 握手流程（非对称加密协商密钥 → 对称加密传输数据），涉及证书校验、密钥交换算法（如 RSA/ECDHE）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;反思&lt;/strong&gt;：需分步骤说明（客户端 Hello → 服务端响应 → 密钥交换 → 加密通信）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;11. 继承&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;问题&lt;/strong&gt;：JavaScript 实现继承的方式（原型链继承、构造函数继承、组合继承、寄生组合继承）及优缺点&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;12. 原型链及其优点&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;问题&lt;/strong&gt;：原型链的查找机制（&lt;strong&gt;proto&lt;/strong&gt; 与 prototype 的关系）、通过原型实现方法复用的优势&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;前端性能与图形&lt;a href=&quot;#前端性能与图形&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;13. WebGL 和 ECharts-GL 怎么切换的？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;问题&lt;/strong&gt;：WebGL 原生开发与 ECharts-GL（基于 WebGL 的图表库）的适配逻辑（如场景需求决定技术选型）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;14. Three.js 性能优化&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;问题&lt;/strong&gt;：渲染优化（如实例化渲染、LOD、合并几何体）、资源管理（如纹理压缩、模型减面）、动画优化（如减少重绘）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;15. WebP 渐进式加载如何兼容浏览器？降级如何处理？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;问题&lt;/strong&gt;：通过 &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; 标签或 JavaScript 检测浏览器支持（如 &lt;code&gt;Image&lt;/code&gt; 对象探测），降级至 JPEG/PNG&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;实习功能实现&lt;a href=&quot;#实习功能实现&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;16. 实习中如何实现视频/图片动态文本叠加功能？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;问题&lt;/strong&gt;：技术实现细节（如 Canvas/WebGL 叠加、动态文本渲染、交互控制），需说明具体方案与难点&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;浏览器原理&lt;a href=&quot;#浏览器原理&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;17. 浏览器的渲染原理&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;问题&lt;/strong&gt;：从 HTML 解析到页面显示的全流程（DOM 树 → CSSOM 树 → 渲染树 → 布局 → 绘制 → 合成），重绘与回流的影响因素&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;18. CSS 是怎么解析的？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;问题&lt;/strong&gt;：CSS 解析流程（选择器匹配 → 样式计算 → 层叠优先级），以及与渲染流程的关联&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;二、面试面评总结&lt;a href=&quot;#二面试面评总结&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;主要问题&lt;a href=&quot;#主要问题&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;基础不扎实&lt;/strong&gt;：核心原理（如响应式、原型链、HTTPS 加密）理解停留在表面，缺乏细节推导能力。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;项目回答过浅&lt;/strong&gt;：仅描述「做了什么」，未说明「为什么做 → 怎么解决 → 效果如何」，量化结果缺失。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;表达能力不足&lt;/strong&gt;：技术描述逻辑混乱，未能清晰传递关键信息。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;改进方向&lt;a href=&quot;#改进方向&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;算法&lt;/strong&gt;：针对性刷题（重点 LeetCode 热题 100），每题需总结「解题思路 → 代码实现 → 时间复杂度分析」。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;基础知识&lt;/strong&gt;：重新梳理前端核心原理（响应式、原型链、浏览器渲染、HTTP/HTTPS），结合图示理解流程。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;项目复盘&lt;/strong&gt;：精简简历项目描述，保留「技术栈 → 核心功能 → 解决的问题 → 量化效果」结构（如：通过虚拟列表优化长列表渲染，首屏加载时间减少 40%）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;表达训练&lt;/strong&gt;：用「STAR 法则」练习技术问题回答（Situation-背景, Task-任务, Action-行动, Result-结果）。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;三、简历优化建议&lt;a href=&quot;#三简历优化建议&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;内容精简&lt;/strong&gt;：控制在 1 页内，删除冗余信息（如移动端 rem 适配等基础技能）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重点突出&lt;/strong&gt;：保留 1-2 个高含金量奖项（如国家级竞赛），项目聚焦「性能优化」「复杂功能实现」等亮点。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;项目描述&lt;/strong&gt;：按「技术栈 → 核心功能 → 解决的问题 → 效果提升」结构编写（示例：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;项目名称&lt;/strong&gt;：XXX 动态文本叠加系统
&lt;ul&gt;
&lt;li&gt;技术栈：Canvas/WebGL + Vue.js&lt;/li&gt;
&lt;li&gt;功能：实现视频/图片与动态文本的实时叠加（支持字体、位置、动画调整）&lt;/li&gt;
&lt;li&gt;解决问题：通过离屏渲染优化文本绘制性能，解决多图层叠加时的闪烁问题&lt;/li&gt;
&lt;li&gt;效果：渲染帧率从 30fps 提升至 60fps，用户操作响应延迟降低 50%&lt;br /&gt;
）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;</content:encoded><category>category:掘金同步</category><category>category:前端</category><category>tag:前端</category><category>tag:JavaScript</category><category>tag:面试</category></item><item><title>纷享销客前端实习一面</title><link>https://koharu.ac.ren/post/juejin/%E7%BA%B7%E4%BA%AB%E9%94%80%E5%AE%A2%E5%89%8D%E7%AB%AF%E5%AE%9E%E4%B9%A0%E4%B8%80%E9%9D%A2</link><guid isPermaLink="false">juejin/纷享销客前端实习一面</guid><description>纷享销客前端实习一面  作者: 前端AC | 原文: https://juejin.cn/post/7535495474457182242  自我介绍 学校+专业+姓名+什么时候开学前端+前端经验+实习公司+项目  1. 前端是怎么学的？ 我一开始是通过 B 站、掘金、官网文档等资源自学 HTML、...</description><pubDate>Thu, 07 Aug 2025 07:33:33 GMT</pubDate><content:encoded>&lt;h1&gt;纷享销客前端实习一面&lt;a href=&quot;#纷享销客前端实习一面&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;作者: 前端AC | 原文: &lt;a href=&quot;https://juejin.cn/post/7535495474457182242&quot;&gt;https://juejin.cn/post/7535495474457182242&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;自我介绍&lt;a href=&quot;#自我介绍&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;学校+专业+姓名+什么时候开学前端+前端经验+实习公司+项目&lt;/p&gt;
&lt;h2&gt;1. 前端是怎么学的？&lt;a href=&quot;#1-前端是怎么学的&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;我一开始是通过 B 站、掘金、官网文档等资源自学 HTML、CSS 和 JavaScript，后面通过做一些小项目巩固知识，比如实现简易的 todoList、天气查询、图形拖拽等。后来我系统性地学习了 Vue 和 React，掌握了组件化开发、路由、状态管理、前端工程化等内容，并在实习中参与真实项目，不断积累实践经验。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. 为什么对前端感兴趣？&lt;a href=&quot;#2-为什么对前端感兴趣&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;我喜欢前端是因为它“看得见”，可以快速地将代码变成界面和交互体验，并且它紧跟技术潮流、新东西不断出现，很有挑战和成就感。同时，我也喜欢把复杂的逻辑通过交互变得简单直观，提升用户体验。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. 和别人相比，你觉得自己有什么优势？&lt;a href=&quot;#3-和别人相比你觉得自己有什么优势&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;学习能力强&lt;/strong&gt;：我能快速掌握新框架新技术。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;实践动手能力强&lt;/strong&gt;：不仅看理论，也会动手做项目实践。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;注重细节和用户体验&lt;/strong&gt;：对 UI 和交互的细节比较敏感。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;团队协作意识强&lt;/strong&gt;：在实习和合作项目中习惯使用 git 协作，能主动沟通推进任务。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;4. 城市地铁规划使用了什么算法？有用到遍历吗？&lt;a href=&quot;#4-城市地铁规划使用了什么算法有用到遍历吗&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;地铁规划本质是图论问题，地铁站是节点、线路是边，常用算法包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;最短路径算法&lt;/strong&gt;：如 Dijkstra（用于求最短行驶路线）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;最小生成树算法&lt;/strong&gt;：如 Prim、Kruskal（构建最优成本线路）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;遍历算法&lt;/strong&gt;：如 DFS/BFS（查找所有路径或最少换乘路径）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A&lt;/strong&gt;*：启发式最优路径（更智能）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;网络流算法&lt;/strong&gt;：分析高峰期站点流量瓶颈&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以遍历是必须要用的，尤其是在路径搜索、换乘优化等场景中。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. 举一个时间复杂度的例子&lt;a href=&quot;#5-举一个时间复杂度的例子&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; printPairs&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;arr&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; i &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;; i &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; arr.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;; i&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; j &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;; j &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; arr.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;; j&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(arr[i], arr[j]);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度是 &lt;code&gt;O(n^2)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;因为有两个嵌套循环，每个都跑 &lt;code&gt;n&lt;/code&gt; 次。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;6. Promise实例？ promise.all 第一个执行后面的还会执行吗？为什么？&lt;a href=&quot;#6-promise实例-promiseall-第一个执行后面的还会执行吗为什么&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;1. 创建普通 Promise 实例&lt;a href=&quot;#1-创建普通-promise-实例&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; p&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Promise&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;reject&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  setTimeout&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    resolve&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;成功&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    // 或 reject(&quot;失败&quot;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }, &lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;2. Promise.resolve()&lt;a href=&quot;#2-promiseresolve&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;快速创建一个成功的 Promise&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const p = Promise.resolve(&quot;立即成功&quot;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;p.then(console.log); // 立即成功&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;3. Promise.reject()&lt;a href=&quot;#3-promisereject&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;快速创建一个失败的 Promise&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const p = Promise.reject(&quot;立即失败&quot;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;p.catch(console.error); // 立即失败&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;4. Promise.all()&lt;a href=&quot;#4-promiseall&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;等待&lt;strong&gt;所有 Promise 成功&lt;/strong&gt;，否则立刻失败&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;Promise.all([&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  Promise.resolve(1),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  Promise.resolve(2)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;]).then(res =&amp;gt; console.log(res)); // [1, 2]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;5. Promise.race()&lt;a href=&quot;#5-promiserace&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;谁先完成（成功或失败），就返回谁的结果&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;Promise.race([&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  new Promise(r =&amp;gt; setTimeout(() =&amp;gt; r(&quot;A&quot;), 500)),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  new Promise(r =&amp;gt; setTimeout(() =&amp;gt; r(&quot;B&quot;), 1000))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;]).then(console.log); // A&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;6. Promise.allSettled()&lt;a href=&quot;#6-promiseallsettled&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;等待所有完成（不管成功或失败），返回每个的状态&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;Promise.allSettled([&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  Promise.resolve(&quot;yes&quot;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  Promise.reject(&quot;no&quot;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;]).then(console.log);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/*&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  { status: &apos;fulfilled&apos;, value: &apos;yes&apos; },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  { status: &apos;rejected&apos;, reason: &apos;no&apos; }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;7. Promise.any()（ES2021）&lt;a href=&quot;#7-promiseanyes2021&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;只要有一个成功就返回结果；全部失败才报错&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;Promise.any([&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  Promise.reject(&quot;fail&quot;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  Promise.resolve(&quot;success&quot;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;]).then(console.log); // success&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;8. async/await 与 Promise&lt;a href=&quot;#8-asyncawait-与-promise&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;async function fetchData() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const data = await Promise.resolve(&quot;结果&quot;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  console.log(data); // 结果&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;二、promise.then 返回什么？&lt;a href=&quot;#二promisethen-返回什么&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;1. 返回一个新的 Promise&lt;a href=&quot;#1-返回一个新的-promise&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const p = Promise.resolve(1);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const p2 = p.then(val =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return val + 1;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;p2.then(console.log); // 2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;then()&lt;/code&gt; 返回的是一个&lt;strong&gt;新的 Promise 实例&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;可以链式调用，不会影响原 Promise&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;2. 返回值分类&lt;a href=&quot;#2-返回值分类&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;返回内容&lt;/th&gt;&lt;th&gt;结果&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;普通值（如字符串、数字）&lt;/td&gt;&lt;td&gt;包装成一个成功的 Promise&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Promise 对象&lt;/td&gt;&lt;td&gt;等待它的状态&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;抛出异常 / 返回 &lt;code&gt;throw&lt;/code&gt;&lt;/td&gt;&lt;td&gt;返回失败的 Promise&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr /&gt;
&lt;h3&gt;3. 示例：then 返回普通值&lt;a href=&quot;#3-示例then-返回普通值&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;Promise.resolve(&quot;a&quot;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .then(val =&amp;gt; val + &quot;b&quot;) // 返回的是 &quot;ab&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .then(console.log); // ab&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;4. 示例：then 返回一个 Promise&lt;a href=&quot;#4-示例then-返回一个-promise&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;Promise.resolve(&quot;a&quot;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .then(val =&amp;gt; Promise.resolve(val + &quot;b&quot;))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .then(console.log); // ab&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;5. 示例：then 抛异常&lt;a href=&quot;#5-示例then-抛异常&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;Promise.resolve(&quot;x&quot;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .then(val =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    throw new Error(&quot;出错了&quot;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .catch(err =&amp;gt; console.error(err.message)); // 出错了&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const p1 = new Promise(resolve =&amp;gt; setTimeout(() =&amp;gt; resolve(&apos;A&apos;), 1000));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const p2 = new Promise(resolve =&amp;gt; setTimeout(() =&amp;gt; resolve(&apos;B&apos;), 2000));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Promise.all([p1, p2]).then(res =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  console.log(res); // [&apos;A&apos;, &apos;B&apos;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;是的，后面的会继续执行。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Promise.all&lt;/code&gt; 会等所有 Promise 都成功，才返回结果。&lt;/li&gt;
&lt;li&gt;即使第一个已经执行完，后面的也会照常执行，不会提前停止。&lt;/li&gt;
&lt;li&gt;如果其中&lt;strong&gt;有一个失败&lt;/strong&gt;（&lt;code&gt;reject&lt;/code&gt;），&lt;code&gt;Promise.all&lt;/code&gt; 会立刻 &lt;code&gt;reject&lt;/code&gt;，&lt;strong&gt;后续结果不会返回&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;7. reduce 是什么？使用示例？&lt;a href=&quot;#7-reduce-是什么使用示例&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;reduce&lt;/code&gt; 是数组的一个高阶函数，用于累计处理数组值。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const arr = [1, 2, 3, 4];&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const sum = arr.reduce((acc, cur) =&amp;gt; acc + cur, 0); // 10&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也可以将数组转对象：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const people = [{ id: 1, name: &apos;Tom&apos; }, { id: 2, name: &apos;Jerry&apos; }];&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const map = people.reduce((acc, cur) =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  acc[cur.id] = cur.name;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return acc;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}, {});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;8. nextTick 是什么？&lt;a href=&quot;#8-nexttick-是什么&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;在 Vue 中，&lt;code&gt;nextTick&lt;/code&gt; 用于等待 DOM 更新完成后再执行某个逻辑。&lt;/li&gt;
&lt;li&gt;在 Node.js 中，&lt;code&gt;process.nextTick&lt;/code&gt; 是一种微任务，比 &lt;code&gt;setTimeout&lt;/code&gt; 还早执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;Vue.nextTick(() =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  console.log(&apos;DOM updated!&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;9. 数组扁平化有几种方式？&lt;a href=&quot;#9-数组扁平化有几种方式&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;方法一：&lt;code&gt;flat()&lt;/code&gt;（ES2019+）&lt;a href=&quot;#方法一flates2019&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;[1, [2, [3]]].flat(Infinity) // [1, 2, 3]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;方法二：递归&lt;a href=&quot;#方法二递归&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;function flatten(arr) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return arr.reduce((acc, cur) =&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    Array.isArray(cur) ? acc.concat(flatten(cur)) : acc.concat(cur), []);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;方法三：栈（迭代模拟递归）&lt;a href=&quot;#方法三栈迭代模拟递归&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;function flatten(arr) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const stack = [...arr];&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const result = [];&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  while (stack.length) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const val = stack.pop();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if (Array.isArray(val)) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      stack.push(...val);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    } else {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      result.unshift(val);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return result;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;10. 在项目中，Vue 和 React 有什么区别？&lt;a href=&quot;#10-在项目中vue-和-react-有什么区别&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;








































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;对比项&lt;/th&gt;&lt;th&gt;Vue&lt;/th&gt;&lt;th&gt;React&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;编程范式&lt;/td&gt;&lt;td&gt;更接近模板 + 配置式&lt;/td&gt;&lt;td&gt;更偏函数式、JSX 写法&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;数据绑定&lt;/td&gt;&lt;td&gt;响应式系统（双向绑定）&lt;/td&gt;&lt;td&gt;单向数据流，需要自己用 state 和 props 控制&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;组件写法&lt;/td&gt;&lt;td&gt;options API / composition API&lt;/td&gt;&lt;td&gt;函数组件 / class 组件（已弃用）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;状态管理&lt;/td&gt;&lt;td&gt;Vuex / Pinia&lt;/td&gt;&lt;td&gt;Redux / Zustand / Context&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;生命周期命名&lt;/td&gt;&lt;td&gt;比较直观（如 &lt;code&gt;mounted&lt;/code&gt;）&lt;/td&gt;&lt;td&gt;更底层（如 &lt;code&gt;useEffect&lt;/code&gt;）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;上手难度&lt;/td&gt;&lt;td&gt;上手快&lt;/td&gt;&lt;td&gt;更灵活但学习曲线稍陡&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr /&gt;</content:encoded><category>category:掘金同步</category><category>category:前端</category><category>tag:前端</category><category>tag:JavaScript</category><category>tag:面试</category></item><item><title>字节前端面试知识点总结</title><link>https://koharu.ac.ren/post/juejin/%E5%AD%97%E8%8A%82%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E7%9F%A5%E8%AF%86%E7%82%B9%E6%80%BB%E7%BB%93</link><guid isPermaLink="false">juejin/字节前端面试知识点总结</guid><description>字节前端面试知识点总结  作者: 前端AC | 原文: https://juejin.cn/post/7531664455932231722  前言 主包总结了前端字节经常出现的真题，也是其他面试也经常会问的题目，答案是询问gpt的，有需要自取  1. 闭包是什么？ 定义： 闭包是函数和其词法作用域...</description><pubDate>Sun, 27 Jul 2025 09:28:51 GMT</pubDate><content:encoded>&lt;h1&gt;字节前端面试知识点总结&lt;a href=&quot;#字节前端面试知识点总结&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;作者: 前端AC | 原文: &lt;a href=&quot;https://juejin.cn/post/7531664455932231722&quot;&gt;https://juejin.cn/post/7531664455932231722&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;前言&lt;a href=&quot;#前言&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;主包总结了前端字节经常出现的真题，也是其他面试也经常会问的题目，答案是询问gpt的，有需要自取&lt;/p&gt;
&lt;h2&gt;1. 闭包是什么？&lt;a href=&quot;#1-闭包是什么&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;定义：&lt;/strong&gt; 闭包是函数和其词法作用域的组合，即一个函数可以“记住”其定义时所处的作用域，即使在函数在其作用域外执行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原理：&lt;/strong&gt; 函数在定义时会“捕获”其外部作用域变量，即便函数在外部执行，这些变量仍然可以访问。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;应用场景：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;数据隐藏（如创建私有变量）&lt;/li&gt;
&lt;li&gt;函数柯里化&lt;/li&gt;
&lt;li&gt;缓存（memoization）&lt;/li&gt;
&lt;li&gt;防抖 / 节流函数&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; createCounter&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; count &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; function&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    count&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; count&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; counter&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; createCounter&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;counter&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;// 1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;counter&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;// 2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;面试话术：&lt;/strong&gt; 闭包的本质是函数作用域的记忆，它能保存状态、做私有封装，是 JS 函数式编程的核心能力。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. 防抖函数&lt;a href=&quot;#2-防抖函数&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;定义：&lt;/strong&gt; 设定一个固定时间 delay，事件触发后 delay 时间内不再触发才执行函数。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;应用：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;输入框搜索&lt;/li&gt;
&lt;li&gt;resize 监听&lt;/li&gt;
&lt;li&gt;scroll 滚动优化&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;实现：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; debounce&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;delay&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; timer &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; null&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    clearTimeout&lt;/span&gt;&lt;span&gt;(timer)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    timer &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; setTimeout&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      fn.&lt;/span&gt;&lt;span&gt;apply&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;, args)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }, delay)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;通俗解释：&lt;/strong&gt; 比如你敲键盘，每次敲一个字母，后台就发请求——太浪费了。防抖就是“等你停 500ms 再发”。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;面试话术：&lt;/strong&gt; 防抖通过闭包保存定时器，避免频繁调用，是前端事件性能优化中最常见的应用。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Flex 布局项目中如何使用&lt;a href=&quot;#3-flex-布局项目中如何使用&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;核心轴：&lt;/strong&gt; 主轴（默认横向）+ 交叉轴（默认纵向）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;常用属性：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;容器属性（display: flex, justify-content, align-items）&lt;/li&gt;
&lt;li&gt;项目属性（flex, order, align-self）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt; 横向居中：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.parent&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;flex&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  justify-content&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;center&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  align-items&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;center&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;项目中应用：&lt;/strong&gt; 页面布局、导航栏、左右结构、弹性容器都可用 flex 实现响应式适配。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;面试话术：&lt;/strong&gt; Flex 布局简洁强大，适合一维布局场景，主流页面都离不开它。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. 跨域是什么？如何解决？&lt;a href=&quot;#4-跨域是什么如何解决&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;定义：&lt;/strong&gt; 浏览器同源策略限制，不允许从一个域向另一个域发起 AJAX 请求。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;判断跨域标准：&lt;/strong&gt; 协议、域名、端口三者只要有一个不同就属于跨域。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;常见解决方案：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CORS（后端设置 &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;JSONP（仅 GET 请求）&lt;/li&gt;
&lt;li&gt;代理转发（前端开发服务器配置 proxy）&lt;/li&gt;
&lt;li&gt;iframe + postMessage 通信&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;面试话术：&lt;/strong&gt; 跨域是浏览器安全机制导致，生产中主要使用 CORS 或 proxy。JSONP 是早期解决方案。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. JSONP 和 postMessage&lt;a href=&quot;#5-jsonp-和-postmessage&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;JSONP：&lt;/strong&gt; 利用 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; 标签无跨域限制，通过 URL 传参数，后端返回 JS 调用函数。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// 前端：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; handleData&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(data)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; script&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;createElement&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;script&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;script.src &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;http://example.com/data?callback=handleData&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;document.body.&lt;/span&gt;&lt;span&gt;appendChild&lt;/span&gt;&lt;span&gt;(script)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;缺点：&lt;/strong&gt; 仅支持 GET，请求不安全，不推荐用于敏感数据。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;postMessage：&lt;/strong&gt; 跨域 iframe/窗口之间通信的安全机制。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// 子页面：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;window.parent.&lt;/span&gt;&lt;span&gt;postMessage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;hello&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;*&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// 父页面：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;window.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;message&apos;&lt;/span&gt;&lt;span&gt;, (&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(e.data)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;面试话术：&lt;/strong&gt; postMessage 更现代、安全，推荐用在 iframe 跨域、OAuth 登录、前端微服务通信场景。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. WebSocket 重连接与消息重发&lt;a href=&quot;#6-websocket-重连接与消息重发&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;定义：&lt;/strong&gt; WebSocket 是双向通信协议，可实现浏览器和服务器的实时通信。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;断线重连实现思路：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用 &lt;code&gt;onclose&lt;/code&gt; 监听断开&lt;/li&gt;
&lt;li&gt;延迟一段时间尝试 reconnect&lt;/li&gt;
&lt;li&gt;设置最大重试次数避免无限重连&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;发送消息重试：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;发送前检测 &lt;code&gt;socket.readyState&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;若未连接好，将消息暂存，连接建立后 flush 消息队列&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例代码片段：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; socket&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; msgQueue &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; initSocket&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  socket &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; WebSocket&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;wss://server&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  socket.&lt;/span&gt;&lt;span&gt;onopen&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    msgQueue.&lt;/span&gt;&lt;span&gt;forEach&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;msg&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; socket.&lt;/span&gt;&lt;span&gt;send&lt;/span&gt;&lt;span&gt;(msg))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    msgQueue &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  socket.&lt;/span&gt;&lt;span&gt;onclose&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    setTimeout&lt;/span&gt;&lt;span&gt;(initSocket, &lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; send&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;msg&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (socket.readyState &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;) socket.&lt;/span&gt;&lt;span&gt;send&lt;/span&gt;&lt;span&gt;(msg)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  else&lt;/span&gt;&lt;span&gt; msgQueue.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(msg)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;面试话术：&lt;/strong&gt; WebSocket 是实时通信首选协议，断线重连和消息重发机制对稳定性要求较高的业务尤为重要。&lt;/p&gt;
&lt;h2&gt;7. npm / yarn / pnpm 区别&lt;a href=&quot;#7-npm--yarn--pnpm-区别&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;

































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;工具&lt;/th&gt;&lt;th&gt;安装速度&lt;/th&gt;&lt;th&gt;node_modules 结构&lt;/th&gt;&lt;th&gt;支持 monorepo&lt;/th&gt;&lt;th&gt;特点&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;npm&lt;/td&gt;&lt;td&gt;普通&lt;/td&gt;&lt;td&gt;扁平&lt;/td&gt;&lt;td&gt;部分支持&lt;/td&gt;&lt;td&gt;官方默认，稳定性好&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;yarn&lt;/td&gt;&lt;td&gt;快&lt;/td&gt;&lt;td&gt;扁平&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;td&gt;缓存机制好，lock 文件稳定&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;pnpm&lt;/td&gt;&lt;td&gt;更快&lt;/td&gt;&lt;td&gt;严格嵌套 + 软链&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;td&gt;节省磁盘空间，速度极快，软链接&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;面试话术&lt;/strong&gt;：在大多数现代项目中，推荐使用 pnpm 或 yarn 替代 npm，尤其是配合 monorepo 管理大型代码仓库时。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;8. Monorepo 是什么？&lt;a href=&quot;#8-monorepo-是什么&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;定义&lt;/strong&gt;：多个项目/包统一放在一个仓库中维护的方式（mono=单，repo=仓库）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;优势&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;模块之间联动方便&lt;/li&gt;
&lt;li&gt;依赖统一管理&lt;/li&gt;
&lt;li&gt;适合组件库或多个产品线统一维护&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;工具：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pnpm workspaces&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Lerna&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Turborepo&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;面试话术&lt;/strong&gt;：Monorepo 能简化大型项目协同开发、版本管理及构建发布过程，在中大型前端团队中很常见。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;9. VueRouter 实现原理（手写版）&lt;a href=&quot;#9-vuerouter-实现原理手写版&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;核心机制&lt;/strong&gt;：监听 URL 变化（hash/history）→ 渲染对应组件&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;基本手写实现（Hash 模式）&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; routes&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &apos;/&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;首页&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &apos;/about&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;关于页&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; render&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; path&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; location.hash.&lt;/span&gt;&lt;span&gt;slice&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &apos;/&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  document.&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;view&apos;&lt;/span&gt;&lt;span&gt;).innerText &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; routes[path] &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &apos;404&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;window.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;hashchange&apos;&lt;/span&gt;&lt;span&gt;, render)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;关键点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用 &lt;code&gt;location.hash&lt;/code&gt; 获取路由&lt;/li&gt;
&lt;li&gt;页面中预留 &lt;code&gt;&amp;lt;div id=&quot;view&quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt; 容器渲染内容&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;面试话术&lt;/strong&gt;：VueRouter 本质是监听 URL 变化，然后根据路由映射渲染对应视图，可通过自定义组件结合响应式完成 router-view 效果。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;10. Tree Shaking 与按需加载&lt;a href=&quot;#10-tree-shaking-与按需加载&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Tree Shaking 原理&lt;/strong&gt;：ES Module 静态结构分析 + 标记未使用代码 + 删除&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;工具支持&lt;/strong&gt;：Webpack、Rollup、Vite 都支持 Tree Shaking&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;按需加载组件库方式&lt;/strong&gt;（如按需加载 Element Plus）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { ElButton } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;element-plus&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或使用插件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// babel-plugin-import&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;libraryName&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;element-plus&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;libraryDirectory&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;es&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;style&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;面试话术&lt;/strong&gt;：Tree Shaking 能有效减少产物体积，前提是使用 ESModule 且编译工具支持。按需加载组件库也是一种 Tree Shaking 的实际运用。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;11. 打包流程与图片资源处理&lt;a href=&quot;#11-打包流程与图片资源处理&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;整体打包流程（以 Webpack 为例）：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;解析入口文件（entry）&lt;/li&gt;
&lt;li&gt;构建模块依赖图（import/require）&lt;/li&gt;
&lt;li&gt;编译代码（Babel、TS、CSS loader）&lt;/li&gt;
&lt;li&gt;Tree shaking / 压缩优化&lt;/li&gt;
&lt;li&gt;输出 bundle 到 dist 文件夹&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;图片处理：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;小图（&amp;lt;8kb）：转 base64 内联到 JS 中，减少请求数&lt;/li&gt;
&lt;li&gt;大图：复制到 dist，生成 hash 文件名，自动替换路径&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  module: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    rules: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        test:&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt;\.&lt;/span&gt;&lt;span&gt;png&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        type: &lt;/span&gt;&lt;span&gt;&apos;asset&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        parser: { dataUrlCondition: { maxSize: &lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; 1024&lt;/span&gt;&lt;span&gt; } },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;面试话术&lt;/strong&gt;：打包的核心是将源码模块化、标准化处理为浏览器可识别的 JS/CSS/HTML/图片资源，优化性能、缓存与部署流程。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;12. 浏览器缓存机制&lt;a href=&quot;#12-浏览器缓存机制&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;两类缓存：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;强缓存（状态码 200 from cache）：Expires、Cache-Control&lt;/li&gt;
&lt;li&gt;协商缓存（状态码 304 Not Modified）：Last-Modified、ETag&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;强缓存示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;Cache-Control&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; max-age=31536000&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;协商缓存示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;ETag&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &quot;abc123&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;If-None-Match&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &quot;abc123&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;面试话术&lt;/strong&gt;：合理配置 HTTP 缓存头可显著提升网站性能与加载速度，需兼顾更新与命中率。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;13. this 的指向、new 创建过程&lt;a href=&quot;#13-this-的指向new-创建过程&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;this 取决于调用方式：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;普通函数：window（或 undefined 严格模式）&lt;/li&gt;
&lt;li&gt;对象调用：this 指向调用者&lt;/li&gt;
&lt;li&gt;bind/call/apply 修改 this&lt;/li&gt;
&lt;li&gt;箭头函数：this 不变，取决于父作用域&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;new 做了哪些事？&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;创建空对象&lt;/li&gt;
&lt;li&gt;this 指向该对象&lt;/li&gt;
&lt;li&gt;执行构造函数&lt;/li&gt;
&lt;li&gt;返回对象（显式返回对象则替换）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; Person&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  this&lt;/span&gt;&lt;span&gt;.name &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; p&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Person&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;Tom&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;面试话术&lt;/strong&gt;：this 是 JS 中最容易出错的机制，理解调用上下文尤为重要。new 是构造函数实例化对象的过程。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;14. 原型链与类的区别&lt;a href=&quot;#14-原型链与类的区别&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;原型链定义：&lt;/strong&gt;
每个对象都有 &lt;code&gt;__proto__&lt;/code&gt;，指向其构造函数的 prototype，最终形成一条链。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;类的实现（ES6 class）：&lt;/strong&gt;
语法糖，本质仍是构造函数 + prototype 实现继承&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;区别总结：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;类是语法层面结构&lt;/li&gt;
&lt;li&gt;原型链是对象查找属性的方法机制&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;面试话术&lt;/strong&gt;：理解原型链是理解继承、作用域、 instanceof 的关键，类是对原型的封装，便于组织代码结构。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;15. Vue3 响应式原理&lt;a href=&quot;#15-vue3-响应式原理&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;**Vue2 响应式缺陷：**基于 Object.defineProperty，数组无法监听新增属性&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vue3 使用 Proxy 实现响应式：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;捕捉 get、set 操作&lt;/li&gt;
&lt;li&gt;结合依赖收集与触发更新（effect）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例核心代码：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; reactive&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; obj&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Proxy&lt;/span&gt;&lt;span&gt;(obj, {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;target&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    track&lt;/span&gt;&lt;span&gt;(target, key) &lt;/span&gt;&lt;span&gt;// 依赖收集&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; Reflect.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(target, key)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;target&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; res&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; Reflect.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(target, key, value)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    trigger&lt;/span&gt;&lt;span&gt;(target, key) &lt;/span&gt;&lt;span&gt;// 触发更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; res&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;面试话术&lt;/strong&gt;：Vue3 相比 Vue2，在响应式系统更具灵活性和性能，Proxy 的优势在于可监听新增、删除、数组索引等所有操作。&lt;/p&gt;
&lt;h2&gt;16. 虚拟列表与 IntersectionObserver 实现懒加载&lt;a href=&quot;#16-虚拟列表与-intersectionobserver-实现懒加载&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;虚拟列表定义&lt;/strong&gt;：只渲染可视区域的数据，提升长列表性能。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;实现方式：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;固定高度列表 + 滚动监听&lt;/li&gt;
&lt;li&gt;滚动时计算渲染区间索引，动态替换 DOM&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;IntersectionObserver 实现图片懒加载：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; observer&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; IntersectionObserver&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;entries&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  entries.&lt;/span&gt;&lt;span&gt;forEach&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;entry&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (entry.isIntersecting) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      const&lt;/span&gt;&lt;span&gt; img&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; entry.target&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      img.src &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; img.dataset.src&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      observer.&lt;/span&gt;&lt;span&gt;unobserve&lt;/span&gt;&lt;span&gt;(img)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;document.&lt;/span&gt;&lt;span&gt;querySelectorAll&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;img[data-src]&apos;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;forEach&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  observer.&lt;/span&gt;&lt;span&gt;observe&lt;/span&gt;&lt;span&gt;(img)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;面试话术&lt;/strong&gt;：虚拟列表提高了性能，避免一次性渲染大量 DOM。IntersectionObserver 是现代浏览器优化滚动监听的首选。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;17. 发布订阅模式&lt;a href=&quot;#17-发布订阅模式&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;定义&lt;/strong&gt;：一种解耦通信机制，发布者发事件，订阅者监听响应。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;手写实现：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; EventBus&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  constructor&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    this&lt;/span&gt;&lt;span&gt;.events &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  on&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    (&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.events[event] &lt;/span&gt;&lt;span&gt;||=&lt;/span&gt;&lt;span&gt; []).&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(fn)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  emit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    this&lt;/span&gt;&lt;span&gt;.events[event]?.&lt;/span&gt;&lt;span&gt;forEach&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; fn&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;args))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;应用场景：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;组件通信（兄弟组件）&lt;/li&gt;
&lt;li&gt;Vue 的 &lt;span&gt;&lt;span&gt;emit/emit / &lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;mi&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;on 本质&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;面试话术&lt;/strong&gt;：发布订阅用于组件解耦或异步通知，是前端常用架构思想之一。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;18. Composition API 与 Options API&lt;a href=&quot;#18-composition-api-与-options-api&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;特性&lt;/th&gt;&lt;th&gt;Options API&lt;/th&gt;&lt;th&gt;Composition API&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;组织方式&lt;/td&gt;&lt;td&gt;分散（data、methods）&lt;/td&gt;&lt;td&gt;聚合（setup 函数中）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;复用逻辑&lt;/td&gt;&lt;td&gt;Mixin&lt;/td&gt;&lt;td&gt;可组合函数（Composable）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;类型推导支持&lt;/td&gt;&lt;td&gt;较弱&lt;/td&gt;&lt;td&gt;TypeScript 更友好&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;面试话术&lt;/strong&gt;：Composition API 更适合复杂逻辑组合与 TS 项目，Options API 更适合初学者。Vue3 推荐使用 Composition。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;19. computed 与 watch 区别&lt;a href=&quot;#19-computed-与-watch-区别&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;特性&lt;/th&gt;&lt;th&gt;computed&lt;/th&gt;&lt;th&gt;watch&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;概念&lt;/td&gt;&lt;td&gt;派生状态，自动缓存&lt;/td&gt;&lt;td&gt;监听响应式数据，手动触发回调&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;使用场景&lt;/td&gt;&lt;td&gt;模板渲染的衍生值&lt;/td&gt;&lt;td&gt;监听异步操作、副作用处理&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;返回值&lt;/td&gt;&lt;td&gt;返回值&lt;/td&gt;&lt;td&gt;无返回值&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;面试话术&lt;/strong&gt;：computed 是依赖驱动、自动缓存的计算属性，而 watch 更像是监听器，用于观察数据变化并做副作用操作。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;20. React18 特性&lt;a href=&quot;#20-react18-特性&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;核心变化：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;自动批处理（无需手动包裹 setTimeout）&lt;/li&gt;
&lt;li&gt;并发特性（startTransition）&lt;/li&gt;
&lt;li&gt;useId（防止 SSR 问题）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;新 API 示例：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { startTransition } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;startTransition&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  setValue&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;new&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;面试话术&lt;/strong&gt;：React18 最大亮点是并发渲染与自动批处理机制，大幅提升响应能力与开发体验。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;21. let / const / var 区别&lt;a href=&quot;#21-let--const--var-区别&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;特性&lt;/th&gt;&lt;th&gt;var&lt;/th&gt;&lt;th&gt;let&lt;/th&gt;&lt;th&gt;const&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;作用域&lt;/td&gt;&lt;td&gt;函数作用域&lt;/td&gt;&lt;td&gt;块级作用域&lt;/td&gt;&lt;td&gt;块级作用域&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;提升&lt;/td&gt;&lt;td&gt;有（值为 undefined）&lt;/td&gt;&lt;td&gt;有（不初始化）&lt;/td&gt;&lt;td&gt;有（不初始化）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;重复声明&lt;/td&gt;&lt;td&gt;允许&lt;/td&gt;&lt;td&gt;报错&lt;/td&gt;&lt;td&gt;报错&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;可修改&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;td&gt;是&lt;/td&gt;&lt;td&gt;否（基本类型）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;面试话术&lt;/strong&gt;：let/const 是 ES6 提供的更安全、作用域明确的声明方式，建议默认使用 const。var 已逐步淘汰。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;22. Vuex 与 Pinia 区别&lt;a href=&quot;#22-vuex-与-pinia-区别&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;特性&lt;/th&gt;&lt;th&gt;Vuex&lt;/th&gt;&lt;th&gt;Pinia&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;API 风格&lt;/td&gt;&lt;td&gt;module + mutations/actions&lt;/td&gt;&lt;td&gt;setup 语法，函数式组织&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;类型支持&lt;/td&gt;&lt;td&gt;差&lt;/td&gt;&lt;td&gt;优秀（TS 支持一流）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;使用成本&lt;/td&gt;&lt;td&gt;多&lt;/td&gt;&lt;td&gt;少（无需冗余代码）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Devtools&lt;/td&gt;&lt;td&gt;Vue Devtools&lt;/td&gt;&lt;td&gt;支持更好&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;面试话术&lt;/strong&gt;：Pinia 是 Vue3 官方推荐状态库，简洁、支持 Composition API、类型推导强，是 Vuex 的未来替代品。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;23. HTTP 与 HTTPS 区别&lt;a href=&quot;#23-http-与-https-区别&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;项目&lt;/th&gt;&lt;th&gt;HTTP&lt;/th&gt;&lt;th&gt;HTTPS&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;安全性&lt;/td&gt;&lt;td&gt;明文传输，易被窃听&lt;/td&gt;&lt;td&gt;加密传输（SSL/TLS）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;端口&lt;/td&gt;&lt;td&gt;80&lt;/td&gt;&lt;td&gt;443&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;证书要求&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;需 CA 签发的数字证书&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;面试话术&lt;/strong&gt;：HTTPS 是 HTTP + SSL/TLS 加密，防止中间人攻击与数据泄露，现代站点必备。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;24. HTTP 状态码分类&lt;a href=&quot;#24-http-状态码分类&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;1xx：信息（如 101 Switching Protocols）&lt;/li&gt;
&lt;li&gt;2xx：成功（如 200 OK，204 No Content）&lt;/li&gt;
&lt;li&gt;3xx：重定向（如 301 永久重定向，302 临时重定向）&lt;/li&gt;
&lt;li&gt;4xx：客户端错误（如 404 Not Found，403 Forbidden）&lt;/li&gt;
&lt;li&gt;5xx：服务端错误（如 500 内部错误，502 Bad Gateway）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;面试话术&lt;/strong&gt;：重点掌握 200、301、302、304、403、404、500、503 等状态含义。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;25. HTTP/2 特性&lt;a href=&quot;#25-http2-特性&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;多路复用（一个连接并发多个请求）&lt;/li&gt;
&lt;li&gt;头部压缩（减少重复 header 体积）&lt;/li&gt;
&lt;li&gt;Server Push（服务端主动推送资源）&lt;/li&gt;
&lt;li&gt;二进制协议（比文本更快）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;面试话术&lt;/strong&gt;：HTTP/2 在性能上是 HTTP1 的升级，极大减少了连接数量与传输体积。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;26. TCP 三次握手&lt;a href=&quot;#26-tcp-三次握手&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;过程说明：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;客户端：发送 SYN&lt;/li&gt;
&lt;li&gt;服务端：收到后返回 SYN + ACK&lt;/li&gt;
&lt;li&gt;客户端：收到后回复 ACK，连接建立&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;为什么三次？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;防止已失效的连接请求重复到达造成错误&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;面试话术&lt;/strong&gt;：三次握手确保双方具备收发能力并保持同步，防止半连接攻击。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;27. HTTP 缓存控制方式&lt;a href=&quot;#27-http-缓存控制方式&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;强缓存：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Expires&lt;/code&gt;（绝对时间）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Cache-Control: max-age=3600&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;协商缓存：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Last-Modified + If-Modified-Since&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ETag + If-None-Match&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;流程图：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有强缓存 → 直接使用&lt;/li&gt;
&lt;li&gt;无强缓存 → 发请求，命中协商缓存返回 304&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;面试话术&lt;/strong&gt;：合理缓存配置可极大提升前端性能和用户体验。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;28. DNS 使用的协议&lt;a href=&quot;#28-dns-使用的协议&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;DNS 查询过程涉及多个协议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;应用层协议：DNS&lt;/li&gt;
&lt;li&gt;传输层协议：UDP（默认）或 TCP（大于 512 字节）&lt;/li&gt;
&lt;li&gt;网络层协议：IP&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;面试话术&lt;/strong&gt;：DNS 查询是 Web 加载的第一步，通常使用 UDP 提高效率，大包或可靠性要求则转为 TCP。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;29. 应用层协议&lt;a href=&quot;#29-应用层协议&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;常见应用层协议包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTTP / HTTPS&lt;/li&gt;
&lt;li&gt;FTP / SFTP&lt;/li&gt;
&lt;li&gt;SMTP / POP3 / IMAP&lt;/li&gt;
&lt;li&gt;WebSocket&lt;/li&gt;
&lt;li&gt;DNS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;面试话术&lt;/strong&gt;：OSI 七层模型最顶层是应用层，主要面向用户交互与数据格式处理。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;30. UDP 协议&lt;a href=&quot;#30-udp-协议&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;特点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;面向无连接&lt;/li&gt;
&lt;li&gt;不保证顺序、不保证可靠&lt;/li&gt;
&lt;li&gt;发送快、开销小&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;典型应用：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;视频直播&lt;/li&gt;
&lt;li&gt;DNS 查询&lt;/li&gt;
&lt;li&gt;实时游戏&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;与 TCP 区别：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TCP 可靠但慢，UDP 快但不可靠&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;面试话术&lt;/strong&gt;：UDP 常用于对时效性要求高、能容忍少量丢包的场景。&lt;/p&gt;</content:encoded><category>category:掘金同步</category><category>category:面试</category><category>tag:面试</category><category>tag:前端</category></item><item><title>08-刚体约束 Constraint</title><link>https://koharu.ac.ren/post/juejin/08-%E5%88%9A%E4%BD%93%E7%BA%A6%E6%9D%9F_constraint</link><guid isPermaLink="false">juejin/08-刚体约束_constraint</guid><description>08刚体约束 Constraint  作者: 前端AC | 原文: https://juejin.cn/post/7530180210872811570  刚体约束 Constraint  本节目标 学习如何使用 cannones 的约束（Constraint）系统，让多个刚体之间具有物理连接，如铰...</description><pubDate>Wed, 23 Jul 2025 16:26:37 GMT</pubDate><content:encoded>&lt;h1&gt;08-刚体约束 Constraint&lt;a href=&quot;#08-刚体约束-constraint&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;作者: 前端AC | 原文: &lt;a href=&quot;https://juejin.cn/post/7530180210872811570&quot;&gt;https://juejin.cn/post/7530180210872811570&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;刚体约束 Constraint&lt;a href=&quot;#刚体约束-constraint&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;h2&gt;本节目标&lt;a href=&quot;#本节目标&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;学习如何使用 &lt;code&gt;cannon-es&lt;/code&gt; 的约束（Constraint）系统，让多个刚体之间具有物理连接，如铰链、点对点连接等。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;基础知识讲解&lt;a href=&quot;#基础知识讲解&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Cannon.js 中的约束用于在两个刚体之间建立某种规则限制，使它们表现出类似于物理连接（链条、铰链、弹簧等）的行为。&lt;/p&gt;
&lt;h3&gt;常见约束类型&lt;a href=&quot;#常见约束类型&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;约束类型&lt;/th&gt;&lt;th&gt;描述&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PointToPointConstraint&lt;/code&gt;&lt;/td&gt;&lt;td&gt;点对点连接（类似绳子连接两点）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;HingeConstraint&lt;/code&gt;&lt;/td&gt;&lt;td&gt;铰链连接（门的转动效果）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;LockConstraint&lt;/code&gt;&lt;/td&gt;&lt;td&gt;锁定两个刚体之间的所有自由度&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;DistanceConstraint&lt;/code&gt;&lt;/td&gt;&lt;td&gt;固定两个点的距离（距离保持不变）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;示例：点对点约束连接两个小球&lt;a href=&quot;#示例点对点约束连接两个小球&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;我们将创建两个小球，并使用 &lt;code&gt;PointToPointConstraint&lt;/code&gt; 将它们连接在一起，实现“弹簧绳索”的效果。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;示例代码（Vue3 + cannon-es）&lt;a href=&quot;#示例代码vue3--cannon-es&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;vue复制编辑&lt;/span&gt;&lt;span&gt;&amp;lt;!-- App.vue --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;canvas&lt;/span&gt;&lt;span&gt; ref&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;canvasRef&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;canvas&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; setup&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { ref, onMounted } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;vue&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; THREE &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;three&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; CANNON &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;cannon-es&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { OrbitControls } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;three/examples/jsm/controls/OrbitControls&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; canvasRef&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; ref&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;onMounted&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 创建 Three.js 场景&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; scene&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Scene&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; camera&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;PerspectiveCamera&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;75&lt;/span&gt;&lt;span&gt;, window.innerWidth &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; window.innerHeight, &lt;/span&gt;&lt;span&gt;0.1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  camera.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;15&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; renderer&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;WebGLRenderer&lt;/span&gt;&lt;span&gt;({ canvas: canvasRef.value })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  renderer.&lt;/span&gt;&lt;span&gt;setSize&lt;/span&gt;&lt;span&gt;(window.innerWidth, window.innerHeight)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; controls&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; OrbitControls&lt;/span&gt;&lt;span&gt;(camera, renderer.domElement)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  controls.enableDamping &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 添加光源&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; light&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;DirectionalLight&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0xffffff&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  light.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(light)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 初始化物理世界&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; world&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;World&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.gravity.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;9.82&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 创建地面&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundBody&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Body&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    mass: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    shape: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Plane&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  groundBody.quaternion.&lt;/span&gt;&lt;span&gt;setFromEuler&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Math.&lt;/span&gt;&lt;span&gt;PI&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.&lt;/span&gt;&lt;span&gt;addBody&lt;/span&gt;&lt;span&gt;(groundBody)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; ground&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Mesh&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;PlaneGeometry&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;MeshStandardMaterial&lt;/span&gt;&lt;span&gt;({ color: &lt;/span&gt;&lt;span&gt;0x999999&lt;/span&gt;&lt;span&gt;, side: &lt;/span&gt;&lt;span&gt;THREE&lt;/span&gt;&lt;span&gt;.DoubleSide })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  )&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ground.rotation.x &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt;Math.&lt;/span&gt;&lt;span&gt;PI&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(ground)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 创建两个球体&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; sphereGeo&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;SphereGeometry&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;32&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;32&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; sphereMat&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;MeshStandardMaterial&lt;/span&gt;&lt;span&gt;({ color: &lt;/span&gt;&lt;span&gt;0x00aaff&lt;/span&gt;&lt;span&gt; })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; createSphere&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;y&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; mesh&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Mesh&lt;/span&gt;&lt;span&gt;(sphereGeo, sphereMat)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    mesh.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(x, y, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(mesh)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; body&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Body&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      mass: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      shape: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Sphere&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      position: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Vec3&lt;/span&gt;&lt;span&gt;(x, y, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    world.&lt;/span&gt;&lt;span&gt;addBody&lt;/span&gt;&lt;span&gt;(body)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; { mesh, body }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; ballA&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; createSphere&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; ballB&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; createSphere&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 添加点对点约束（连接 ballA 和 ballB）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; constraint&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;PointToPointConstraint&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ballA.body, &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Vec3&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ballB.body, &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Vec3&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  )&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.&lt;/span&gt;&lt;span&gt;addConstraint&lt;/span&gt;&lt;span&gt;(constraint)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 动画循环&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; clock&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Clock&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  function&lt;/span&gt;&lt;span&gt; animate&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    requestAnimationFrame&lt;/span&gt;&lt;span&gt;(animate)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; delta&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; clock.&lt;/span&gt;&lt;span&gt;getDelta&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    world.&lt;/span&gt;&lt;span&gt;step&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 60&lt;/span&gt;&lt;span&gt;, delta)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    // 同步位置&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    [ballA, ballB].&lt;/span&gt;&lt;span&gt;forEach&lt;/span&gt;&lt;span&gt;(({ &lt;/span&gt;&lt;span&gt;mesh&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt; }) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      mesh.position.&lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt;(body.position)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      mesh.quaternion.&lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt;(body.quaternion)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    controls.&lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    renderer.&lt;/span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;(scene, camera)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  animate&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7530180210872811570/img_0.png&quot; alt=&quot;2025-07-23T16_13_19.635Z-496341.gif&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;2025-07-23T16_13_19.635Z-496341.gif&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2&gt;重点解释&lt;a href=&quot;#重点解释&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;代码片段&lt;/th&gt;&lt;th&gt;解释&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PointToPointConstraint&lt;/code&gt;&lt;/td&gt;&lt;td&gt;将两个球体通过世界坐标系中的特定点连接起来，模拟绳索连接的效果。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;new CANNON.Vec3(0.5, 0, 0)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;约束在 ballA 的局部空间的连接点&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;new CANNON.Vec3(-0.5, 0, 0)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;约束在 ballB 的局部空间的连接点&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;world.addConstraint(...)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;将该约束注册到物理世界中&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;你可以尝试的拓展&lt;a href=&quot;#你可以尝试的拓展&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;使用 &lt;code&gt;DistanceConstraint&lt;/code&gt; 来实现固定距离效果&lt;/li&gt;
&lt;li&gt;将其中一个球体 &lt;code&gt;mass: 0&lt;/code&gt; 变为固定点&lt;/li&gt;
&lt;li&gt;尝试加入多个球体，实现链条式连接（类似吊桥）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;总结&lt;a href=&quot;#总结&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;刚体约束是 Cannon.js 中实现刚体间连接的重要方式&lt;/li&gt;
&lt;li&gt;本节展示了点对点约束的用法&lt;/li&gt;
&lt;li&gt;可以用来实现链条、吊桥、布料物理等效果&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>category:掘金同步</category><category>category:前端</category><category>tag:前端</category><category>tag:JavaScript</category></item><item><title>07-监听碰撞事件和获取碰撞信息</title><link>https://koharu.ac.ren/post/juejin/07-%E7%9B%91%E5%90%AC%E7%A2%B0%E6%92%9E%E4%BA%8B%E4%BB%B6%E5%92%8C%E8%8E%B7%E5%8F%96%E7%A2%B0%E6%92%9E%E4%BF%A1%E6%81%AF</link><guid isPermaLink="false">juejin/07-监听碰撞事件和获取碰撞信息</guid><description>07监听碰撞事件和获取碰撞信息  作者: 前端AC | 原文: https://juejin.cn/post/7530119090058084378  监听碰撞事件和获取碰撞信息 在物理仿真中，了解刚体之间的碰撞信息是非常关键的。Cannon.js 提供了 collide 事件，让我们能够在碰撞发生...</description><pubDate>Wed, 23 Jul 2025 16:25:37 GMT</pubDate><content:encoded>&lt;h1&gt;07-监听碰撞事件和获取碰撞信息&lt;a href=&quot;#07-监听碰撞事件和获取碰撞信息&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;作者: 前端AC | 原文: &lt;a href=&quot;https://juejin.cn/post/7530119090058084378&quot;&gt;https://juejin.cn/post/7530119090058084378&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;监听碰撞事件和获取碰撞信息&lt;a href=&quot;#监听碰撞事件和获取碰撞信息&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;在物理仿真中，了解刚体之间的碰撞信息是非常关键的。Cannon.js 提供了 &lt;code&gt;collide&lt;/code&gt; 事件，让我们能够在碰撞发生时捕获相关信息，比如碰撞对象、冲击速度等。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;一、监听碰撞事件&lt;a href=&quot;#一监听碰撞事件&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;body.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;collide&apos;&lt;/span&gt;&lt;span&gt;, (&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;发生碰撞！&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;碰撞到的刚体:&apos;&lt;/span&gt;&lt;span&gt;, event.body)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;碰撞强度:&apos;&lt;/span&gt;&lt;span&gt;, event.contact.&lt;/span&gt;&lt;span&gt;getImpactVelocityAlongNormal&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;字段&lt;/th&gt;&lt;th&gt;含义&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;event.body&lt;/code&gt;&lt;/td&gt;&lt;td&gt;与当前刚体发生碰撞的刚体&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;event.contact&lt;/code&gt;&lt;/td&gt;&lt;td&gt;碰撞联系信息&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;getImpactVelocityAlongNormal()&lt;/code&gt;&lt;/td&gt;&lt;td&gt;获取法向方向上的冲击速度（衡量碰撞强度）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;二、完整示例&lt;a href=&quot;#二完整示例&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;球体从空中落下与地面发生碰撞，并在控制台打印碰撞信息：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;!-- App.vue --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; setup&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { onMounted, ref } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;vue&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; THREE &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;three&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; CANNON &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;cannon-es&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { OrbitControls } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;three/examples/jsm/controls/OrbitControls&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; canvasRef&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; ref&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;onMounted&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 创建 Three.js 场景与相机&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; scene&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Scene&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; camera&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;PerspectiveCamera&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;75&lt;/span&gt;&lt;span&gt;, window.innerWidth &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; window.innerHeight, &lt;/span&gt;&lt;span&gt;0.1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  camera.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 渲染器和轨道控制器&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; renderer&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;WebGLRenderer&lt;/span&gt;&lt;span&gt;({ canvas: canvasRef.value })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  renderer.&lt;/span&gt;&lt;span&gt;setSize&lt;/span&gt;&lt;span&gt;(window.innerWidth, window.innerHeight)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; controls&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; OrbitControls&lt;/span&gt;&lt;span&gt;(camera, renderer.domElement)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  controls.enableDamping &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 灯光&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; light&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;DirectionalLight&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0xffffff&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  light.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(light)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 创建物理世界&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; world&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;World&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.gravity.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;9.82&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 创建地面刚体&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundBody&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Body&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    mass: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    shape: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Plane&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  groundBody.quaternion.&lt;/span&gt;&lt;span&gt;setFromEuler&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Math.&lt;/span&gt;&lt;span&gt;PI&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.&lt;/span&gt;&lt;span&gt;addBody&lt;/span&gt;&lt;span&gt;(groundBody)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 创建地面网格&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundGeo&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;PlaneGeometry&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundMat&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;MeshStandardMaterial&lt;/span&gt;&lt;span&gt;({ color: &lt;/span&gt;&lt;span&gt;0x999999&lt;/span&gt;&lt;span&gt;, side: &lt;/span&gt;&lt;span&gt;THREE&lt;/span&gt;&lt;span&gt;.DoubleSide })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundMesh&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Mesh&lt;/span&gt;&lt;span&gt;(groundGeo, groundMat)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  groundMesh.rotation.x &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt;Math.&lt;/span&gt;&lt;span&gt;PI&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(groundMesh)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 创建小球&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; radius&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 0.5&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; sphereGeo&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;SphereGeometry&lt;/span&gt;&lt;span&gt;(radius, &lt;/span&gt;&lt;span&gt;32&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;32&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; sphereMat&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;MeshStandardMaterial&lt;/span&gt;&lt;span&gt;({ color: &lt;/span&gt;&lt;span&gt;0x0077ff&lt;/span&gt;&lt;span&gt; })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; sphereMesh&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Mesh&lt;/span&gt;&lt;span&gt;(sphereGeo, sphereMat)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  sphereMesh.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(sphereMesh)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; sphereBody&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Body&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    mass: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    shape: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Sphere&lt;/span&gt;&lt;span&gt;(radius),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    position: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Vec3&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.&lt;/span&gt;&lt;span&gt;addBody&lt;/span&gt;&lt;span&gt;(sphereBody)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  //  碰撞事件监听&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  sphereBody.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;collide&apos;&lt;/span&gt;&lt;span&gt;, (&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos; 碰撞发生！&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;与谁发生碰撞:&apos;&lt;/span&gt;&lt;span&gt;, event.body)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;冲击速度:&apos;&lt;/span&gt;&lt;span&gt;, event.contact.&lt;/span&gt;&lt;span&gt;getImpactVelocityAlongNormal&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;toFixed&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 动画更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; clock&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Clock&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; timeStep&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 60&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  function&lt;/span&gt;&lt;span&gt; animate&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    requestAnimationFrame&lt;/span&gt;&lt;span&gt;(animate)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; delta&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; clock.&lt;/span&gt;&lt;span&gt;getDelta&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    world.&lt;/span&gt;&lt;span&gt;step&lt;/span&gt;&lt;span&gt;(timeStep, delta)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    sphereMesh.position.&lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt;(sphereBody.position)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    sphereMesh.quaternion.&lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt;(sphereBody.quaternion)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    controls.&lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    renderer.&lt;/span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;(scene, camera)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  animate&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;canvas&lt;/span&gt;&lt;span&gt; ref&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;canvasRef&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;canvas&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7530119090058084378/img_0.png&quot; alt=&quot;2025-07-23T13_47_28.405Z-885294.gif&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;2025-07-23T13_47_28.405Z-885294.gif&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2&gt;三、碰撞信息可以做什么？&lt;a href=&quot;#三碰撞信息可以做什么&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;用途&lt;/th&gt;&lt;th&gt;描述&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;播放音效&lt;/td&gt;&lt;td&gt;根据碰撞强度播放不同音效&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;触发逻辑&lt;/td&gt;&lt;td&gt;如“命中目标”后触发销毁或得分&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;记录日志&lt;/td&gt;&lt;td&gt;用于物理调试或分析仿真结果&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;可视化反应&lt;/td&gt;&lt;td&gt;冲击越强，颜色变化越剧烈&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;小技巧&lt;a href=&quot;#小技巧&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;若想检测多个刚体的碰撞，分别为它们绑定监听器。&lt;/li&gt;
&lt;li&gt;想获取更详细的碰撞点信息，可查看 &lt;code&gt;event.contact.ri&lt;/code&gt; 和 &lt;code&gt;event.contact.rj&lt;/code&gt;（相对接触点）。&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>category:掘金同步</category><category>category:前端</category><category>tag:前端</category><category>tag:JavaScript</category><category>tag:面试</category></item><item><title>06 - 物体休眠与休眠事件详解（Cannon.js）</title><link>https://koharu.ac.ren/post/juejin/06_-_%E7%89%A9%E4%BD%93%E4%BC%91%E7%9C%A0%E4%B8%8E%E4%BC%91%E7%9C%A0%E4%BA%8B%E4%BB%B6%E8%AF%A6%E8%A7%A3_cannonjs_</link><guid isPermaLink="false">juejin/06_-_物体休眠与休眠事件详解_cannonjs_</guid><description>06  物体休眠与休眠事件详解（Cannon.js）  作者: 前端AC | 原文: https://juejin.cn/post/7530106154313433098  06  物体休眠与休眠事件详解（Cannon.js） Cannon.js 提供了物体的休眠机制（Sleep），当刚体长时间处于...</description><pubDate>Wed, 23 Jul 2025 16:24:47 GMT</pubDate><content:encoded>&lt;h1&gt;06 - 物体休眠与休眠事件详解（Cannon.js）&lt;a href=&quot;#06---物体休眠与休眠事件详解cannonjs&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;作者: 前端AC | 原文: &lt;a href=&quot;https://juejin.cn/post/7530106154313433098&quot;&gt;https://juejin.cn/post/7530106154313433098&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;06 - 物体休眠与休眠事件详解（Cannon.js）&lt;a href=&quot;#06---物体休眠与休眠事件详解cannonjs-1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Cannon.js 提供了物体的休眠机制（Sleep），当刚体长时间处于静止或低速状态时，会自动进入“休眠”状态，从而 &lt;strong&gt;节省计算资源&lt;/strong&gt;。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;一、核心知识点&lt;a href=&quot;#一核心知识点&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;概念&lt;/th&gt;&lt;th&gt;说明&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;allowSleep&lt;/code&gt;&lt;/td&gt;&lt;td&gt;是否允许该物体休眠（默认 false）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;sleepSpeedLimit&lt;/code&gt;&lt;/td&gt;&lt;td&gt;速度低于该值会开始休眠计时（默认 0.1）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;sleepTimeLimit&lt;/code&gt;&lt;/td&gt;&lt;td&gt;静止多久后才真正休眠（默认 1 秒）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;sleepState&lt;/code&gt;&lt;/td&gt;&lt;td&gt;当前休眠状态（&lt;code&gt;AWAKE&lt;/code&gt;, &lt;code&gt;SLEEPY&lt;/code&gt;, &lt;code&gt;SLEEPING&lt;/code&gt;）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;事件&lt;/td&gt;&lt;td&gt;&lt;code&gt;sleep&lt;/code&gt;, &lt;code&gt;wakeup&lt;/code&gt; 可监听进入或唤醒状态&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;二、常量说明（&lt;code&gt;CANNON.Body.SLEEPING&lt;/code&gt;）&lt;a href=&quot;#二常量说明cannonbodysleeping&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;状态&lt;/th&gt;&lt;th&gt;值&lt;/th&gt;&lt;th&gt;含义&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;CANNON.Body.AWAKE&lt;/code&gt;&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;活跃状态&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;CANNON.Body.SLEEPY&lt;/code&gt;&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;正在准备休眠&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;CANNON.Body.SLEEPING&lt;/code&gt;&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;已休眠&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;三、完整示例（Vue3 + Cannon.js）&lt;a href=&quot;#三完整示例vue3--cannonjs&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;!-- App.vue --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; setup&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { onMounted, ref } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;vue&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; THREE &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;three&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; CANNON &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;cannon-es&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { OrbitControls } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;three/examples/jsm/controls/OrbitControls&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; canvasRef&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; ref&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;onMounted&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 场景、相机&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; scene&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Scene&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; camera&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;PerspectiveCamera&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;75&lt;/span&gt;&lt;span&gt;, window.innerWidth &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; window.innerHeight, &lt;/span&gt;&lt;span&gt;0.1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  camera.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 渲染器与轨道控制&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; renderer&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;WebGLRenderer&lt;/span&gt;&lt;span&gt;({ canvas: canvasRef.value })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  renderer.&lt;/span&gt;&lt;span&gt;setSize&lt;/span&gt;&lt;span&gt;(window.innerWidth, window.innerHeight)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; controls&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; OrbitControls&lt;/span&gt;&lt;span&gt;(camera, renderer.domElement)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  controls.enableDamping &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 灯光&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; light&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;DirectionalLight&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0xffffff&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  light.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(light)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 物理世界&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; world&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;World&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.gravity.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;9.82&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 地面刚体&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundBody&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Body&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    mass: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    shape: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Plane&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  groundBody.quaternion.&lt;/span&gt;&lt;span&gt;setFromEuler&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Math.&lt;/span&gt;&lt;span&gt;PI&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.&lt;/span&gt;&lt;span&gt;addBody&lt;/span&gt;&lt;span&gt;(groundBody)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 地面可视化&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundGeo&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;PlaneGeometry&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundMat&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;MeshStandardMaterial&lt;/span&gt;&lt;span&gt;({ color: &lt;/span&gt;&lt;span&gt;0x999999&lt;/span&gt;&lt;span&gt;, side: &lt;/span&gt;&lt;span&gt;THREE&lt;/span&gt;&lt;span&gt;.DoubleSide })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundMesh&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Mesh&lt;/span&gt;&lt;span&gt;(groundGeo, groundMat)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  groundMesh.rotation.x &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt;Math.&lt;/span&gt;&lt;span&gt;PI&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(groundMesh)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 创建一个立方体（可休眠）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; boxSize&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; boxGeo&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;BoxGeometry&lt;/span&gt;&lt;span&gt;(boxSize, boxSize, boxSize)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; boxMat&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;MeshStandardMaterial&lt;/span&gt;&lt;span&gt;({ color: &lt;/span&gt;&lt;span&gt;0x44aa88&lt;/span&gt;&lt;span&gt; })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; boxMesh&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Mesh&lt;/span&gt;&lt;span&gt;(boxGeo, boxMat)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  boxMesh.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(boxMesh)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; boxBody&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Body&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    mass: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    shape: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Box&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Vec3&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;)),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    position: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Vec3&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // ✅ 允许休眠&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  boxBody.allowSleep &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  boxBody.sleepSpeedLimit &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0.1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  boxBody.sleepTimeLimit &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 1.0&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.&lt;/span&gt;&lt;span&gt;addBody&lt;/span&gt;&lt;span&gt;(boxBody)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  //  监听休眠与唤醒事件&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  boxBody.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;sleep&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos; 物体进入休眠状态&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    boxMesh.material.color.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0xaaaaaa&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  boxBody.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;wakeup&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos; 物体被唤醒&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    boxMesh.material.color.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0x44aa88&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 动画循环&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; clock&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Clock&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; timeStep&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 60&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  function&lt;/span&gt;&lt;span&gt; animate&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    requestAnimationFrame&lt;/span&gt;&lt;span&gt;(animate)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; delta&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; clock.&lt;/span&gt;&lt;span&gt;getDelta&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    world.&lt;/span&gt;&lt;span&gt;step&lt;/span&gt;&lt;span&gt;(timeStep, delta)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    boxMesh.position.&lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt;(boxBody.position)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    boxMesh.quaternion.&lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt;(boxBody.quaternion)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    controls.&lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    renderer.&lt;/span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;(scene, camera)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  animate&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;canvas&lt;/span&gt;&lt;span&gt; ref&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;canvasRef&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;canvas&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;四、常见用途&lt;a href=&quot;#四常见用途&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;性能优化&lt;/strong&gt;：大量静态物体（如积木塔）静止后进入休眠，不再参与模拟。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;状态切换提示&lt;/strong&gt;：休眠时更换颜色、显示文字、播放音效等。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;唤醒机制&lt;/strong&gt;：被碰撞、手动移动物体时自动唤醒。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;小结&lt;a href=&quot;#小结&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;优点&lt;/th&gt;&lt;th&gt;描述&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;减少 CPU 计算开销&lt;/td&gt;&lt;td&gt;休眠体不会参与碰撞检测和力更新&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;适用于静态场景&lt;/td&gt;&lt;td&gt;如地面上的箱子、稳定的堆叠结构&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;事件驱动更直观&lt;/td&gt;&lt;td&gt;&lt;code&gt;sleep&lt;/code&gt; / &lt;code&gt;wakeup&lt;/code&gt; 能帮助构建游戏逻辑&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr /&gt;</content:encoded><category>category:掘金同步</category><category>category:前端</category><category>tag:前端</category><category>tag:JavaScript</category></item><item><title>05-碰撞与碰撞组</title><link>https://koharu.ac.ren/post/juejin/05-%E7%A2%B0%E6%92%9E%E4%B8%8E%E7%A2%B0%E6%92%9E%E7%BB%84</link><guid isPermaLink="false">juejin/05-碰撞与碰撞组</guid><description>05碰撞与碰撞组  作者: 前端AC | 原文: https://juejin.cn/post/7530141922515795978  碰撞与碰撞组  一、核心知识点讲解 在 Cannon.js 中，我们可以使用 碰撞过滤 来控制哪些物体可以彼此发生碰撞。主要依赖以下两个属性： | 属性     ...</description><pubDate>Wed, 23 Jul 2025 16:24:10 GMT</pubDate><content:encoded>&lt;h1&gt;05-碰撞与碰撞组&lt;a href=&quot;#05-碰撞与碰撞组&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;作者: 前端AC | 原文: &lt;a href=&quot;https://juejin.cn/post/7530141922515795978&quot;&gt;https://juejin.cn/post/7530141922515795978&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;碰撞与碰撞组&lt;a href=&quot;#碰撞与碰撞组&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;hr /&gt;
&lt;h2&gt;一、核心知识点讲解&lt;a href=&quot;#一核心知识点讲解&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;在 &lt;code&gt;Cannon.js&lt;/code&gt; 中，我们可以使用 &lt;strong&gt;碰撞过滤&lt;/strong&gt; 来控制哪些物体可以彼此发生碰撞。主要依赖以下两个属性：&lt;/p&gt;




















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;属性&lt;/th&gt;&lt;th&gt;类型&lt;/th&gt;&lt;th&gt;说明&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;collisionFilterGroup&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;Number&lt;/code&gt;&lt;/td&gt;&lt;td&gt;表示刚体所属的组（可以多个组按位或组合）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;collisionFilterMask&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;Number&lt;/code&gt;&lt;/td&gt;&lt;td&gt;表示刚体可以与哪些组发生碰撞（按位与判断）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;类似 bitmask 位运算控制。比如：&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;A 属于组 1，只想和组 2 碰撞，则：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;A.collisionFilterGroup = 1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;A.collisionFilterMask = 2&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;二、示例：三个方块，控制不同的碰撞组&lt;a href=&quot;#二示例三个方块控制不同的碰撞组&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;地面：属于组 1，允许与组 2 和组 4 碰撞&lt;/li&gt;
&lt;li&gt;红色方块：属于组 2，只碰地面&lt;/li&gt;
&lt;li&gt;绿色方块：属于组 4，只碰地面&lt;/li&gt;
&lt;li&gt;蓝色方块：属于组 8，&lt;strong&gt;不碰任何人&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;示例代码&lt;a href=&quot;#示例代码&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; setup&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { onMounted, ref } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;vue&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; THREE &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;three&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; CANNON &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;cannon-es&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { OrbitControls } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;three/examples/jsm/controls/OrbitControls&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; canvasRef&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; ref&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;onMounted&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // ------------------- 初始化场景、相机、渲染器 -------------------&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; scene&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Scene&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; camera&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;PerspectiveCamera&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;75&lt;/span&gt;&lt;span&gt;, window.innerWidth &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; window.innerHeight, &lt;/span&gt;&lt;span&gt;0.1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  camera.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;15&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; renderer&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;WebGLRenderer&lt;/span&gt;&lt;span&gt;({ canvas: canvasRef.value })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  renderer.&lt;/span&gt;&lt;span&gt;setSize&lt;/span&gt;&lt;span&gt;(window.innerWidth, window.innerHeight)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; controls&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; OrbitControls&lt;/span&gt;&lt;span&gt;(camera, renderer.domElement)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  controls.enableDamping &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // ------------------- 光照 -------------------&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; light&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;DirectionalLight&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0xffffff&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  light.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(light)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // ------------------- 创建物理世界 -------------------&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; world&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;World&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.gravity.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;9.82&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // ------------------- 创建地面刚体 -------------------&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundShape&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Plane&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundBody&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Body&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    mass: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    shape: groundShape,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    collisionFilterGroup: &lt;/span&gt;&lt;span&gt;0b0001&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// 属于组 1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    collisionFilterMask: &lt;/span&gt;&lt;span&gt;0b1111&lt;/span&gt;&lt;span&gt;   // 可与所有组碰撞&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  groundBody.quaternion.&lt;/span&gt;&lt;span&gt;setFromEuler&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Math.&lt;/span&gt;&lt;span&gt;PI&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.&lt;/span&gt;&lt;span&gt;addBody&lt;/span&gt;&lt;span&gt;(groundBody)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundGeo&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;PlaneGeometry&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundMat&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;MeshStandardMaterial&lt;/span&gt;&lt;span&gt;({ color: &lt;/span&gt;&lt;span&gt;0x888888&lt;/span&gt;&lt;span&gt;, side: &lt;/span&gt;&lt;span&gt;THREE&lt;/span&gt;&lt;span&gt;.DoubleSide })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundMesh&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Mesh&lt;/span&gt;&lt;span&gt;(groundGeo, groundMat)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  groundMesh.rotation.x &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt;Math.&lt;/span&gt;&lt;span&gt;PI&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(groundMesh)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // ------------------- 创建球体函数 -------------------&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  function&lt;/span&gt;&lt;span&gt; createBall&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;color&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;group&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;mask&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; radius&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 0.5&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; geometry&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;SphereGeometry&lt;/span&gt;&lt;span&gt;(radius, &lt;/span&gt;&lt;span&gt;32&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;32&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; material&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;MeshStandardMaterial&lt;/span&gt;&lt;span&gt;({ color })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; mesh&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Mesh&lt;/span&gt;&lt;span&gt;(geometry, material)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    mesh.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(x, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(mesh)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; shape&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Sphere&lt;/span&gt;&lt;span&gt;(radius)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; body&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Body&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      mass: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      shape,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      position: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Vec3&lt;/span&gt;&lt;span&gt;(x, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      collisionFilterGroup: group,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      collisionFilterMask: mask&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    world.&lt;/span&gt;&lt;span&gt;addBody&lt;/span&gt;&lt;span&gt;(body)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; { mesh, body }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // ------------------- 创建三个球体 -------------------&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; redBall&lt;/span&gt;&lt;span&gt;   =&lt;/span&gt;&lt;span&gt; createBall&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0xff0000&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0b0010&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0b0001&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;// 红球：只与地面碰撞&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; greenBall&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; createBall&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0x00ff00&lt;/span&gt;&lt;span&gt;,  &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0b0100&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0b1111&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;// 绿球：与所有碰撞&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; blueBall&lt;/span&gt;&lt;span&gt;  =&lt;/span&gt;&lt;span&gt; createBall&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0x0000ff&lt;/span&gt;&lt;span&gt;,  &lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0b1000&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0b0100&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;// 蓝球：只与绿球碰撞&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; balls&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; [redBall, greenBall, blueBall] &lt;/span&gt;&lt;span&gt;// 避免 undefined&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // ------------------- 动画更新 -------------------&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; clock&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Clock&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; fixedTimeStep&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 60&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  function&lt;/span&gt;&lt;span&gt; animate&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    requestAnimationFrame&lt;/span&gt;&lt;span&gt;(animate)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; delta&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; clock.&lt;/span&gt;&lt;span&gt;getDelta&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    world.&lt;/span&gt;&lt;span&gt;step&lt;/span&gt;&lt;span&gt;(fixedTimeStep, delta)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;mesh&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;of&lt;/span&gt;&lt;span&gt; balls) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      if&lt;/span&gt;&lt;span&gt; (mesh &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; body) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        mesh.position.&lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt;(body.position)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        mesh.quaternion.&lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt;(body.quaternion)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    controls.&lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    renderer.&lt;/span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;(scene, camera)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  animate&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;canvas&lt;/span&gt;&lt;span&gt; ref&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;canvasRef&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;canvas&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;三、效果说明&lt;a href=&quot;#三效果说明&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;红色方块和绿色方块都能落在地面上（因为它们的组和掩码与地面对上了）&lt;/li&gt;
&lt;li&gt;蓝色方块&lt;strong&gt;直接穿透地面&lt;/strong&gt;（它没有任何碰撞掩码匹配地面）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;四、小贴士&lt;a href=&quot;#四小贴士&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;碰撞组默认是 &lt;code&gt;1&lt;/code&gt;，所有物体都在组 &lt;code&gt;1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;掩码为 &lt;code&gt;-1&lt;/code&gt; 时表示&lt;strong&gt;与所有组碰撞&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;可用 &lt;code&gt;1 &amp;lt;&amp;lt; N&lt;/code&gt; 来表示不同的位（如 &lt;code&gt;1 &amp;lt;&amp;lt; 3 = 8&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>category:掘金同步</category><category>category:前端</category><category>tag:前端</category><category>tag:JavaScript</category></item><item><title>04 - 弹性与接触材质详解</title><link>https://koharu.ac.ren/post/juejin/04_-_%E5%BC%B9%E6%80%A7%E4%B8%8E%E6%8E%A5%E8%A7%A6%E6%9D%90%E8%B4%A8%E8%AF%A6%E8%A7%A3</link><guid isPermaLink="false">juejin/04_-_弹性与接触材质详解</guid><description>04  弹性与接触材质详解  作者: 前端AC | 原文: https://juejin.cn/post/7530141922515779594  04  弹性与接触材质详解  本节目标    理解 restitution（弹性系数）的意义    设置两种材质之间的弹跳效果    对比高弹性与低弹性...</description><pubDate>Wed, 23 Jul 2025 16:22:12 GMT</pubDate><content:encoded>&lt;h1&gt;04 - 弹性与接触材质详解&lt;a href=&quot;#04---弹性与接触材质详解&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;作者: 前端AC | 原文: &lt;a href=&quot;https://juejin.cn/post/7530141922515779594&quot;&gt;https://juejin.cn/post/7530141922515779594&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;04 - 弹性与接触材质详解&lt;a href=&quot;#04---弹性与接触材质详解-1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;h2&gt;本节目标&lt;a href=&quot;#本节目标&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;理解 &lt;code&gt;restitution&lt;/code&gt;（弹性系数）的意义&lt;/li&gt;
&lt;li&gt;设置两种材质之间的弹跳效果&lt;/li&gt;
&lt;li&gt;对比高弹性与低弹性物体的物理行为差异&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;什么是弹性系数（&lt;code&gt;restitution&lt;/code&gt;）？&lt;a href=&quot;#什么是弹性系数restitution&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;restitution&lt;/code&gt; 表示两个物体碰撞时的“反弹程度”：&lt;/p&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;restitution&lt;/th&gt;&lt;th&gt;效果&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;完全不弹跳&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;完全弹回原高度&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;0 ~ 1&lt;/td&gt;&lt;td&gt;部分弹跳&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;它控制的是动能的保留程度。数值越高，动能损耗越少，弹跳越多。&lt;/p&gt;
&lt;p&gt;在 Cannon.js 中，它在 &lt;code&gt;ContactMaterial&lt;/code&gt; 中设置：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ContactMaterial&lt;/span&gt;&lt;span&gt;(matA, matB, {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  restitution: &lt;/span&gt;&lt;span&gt;0.9&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;示例：高弹与低弹两个球的落地行为&lt;a href=&quot;#示例高弹与低弹两个球的落地行为&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;我们将创建两个球，一个弹性系数为 0（完全不弹），另一个为 1（高度弹跳），观察它们的表现差异。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;完整 Vue3 示例（使用 cannon-es）&lt;a href=&quot;#完整-vue3-示例使用-cannon-es&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; setup&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { ref, onMounted } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;vue&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; THREE &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;three&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; CANNON &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;cannon-es&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { OrbitControls } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;three/examples/jsm/controls/OrbitControls&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; canvasRef&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; ref&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;onMounted&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 1. Three.js 场景、相机、渲染器&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; scene&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Scene&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; camera&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;PerspectiveCamera&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;60&lt;/span&gt;&lt;span&gt;, window.innerWidth &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; window.innerHeight, &lt;/span&gt;&lt;span&gt;0.1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  camera.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;15&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  camera.&lt;/span&gt;&lt;span&gt;lookAt&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; renderer&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;WebGLRenderer&lt;/span&gt;&lt;span&gt;({ canvas: canvasRef.value, antialias: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  renderer.&lt;/span&gt;&lt;span&gt;setSize&lt;/span&gt;&lt;span&gt;(window.innerWidth, window.innerHeight)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 2. 轨道控制器&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; controls&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; OrbitControls&lt;/span&gt;&lt;span&gt;(camera, renderer.domElement)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  controls.enableDamping &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;span&gt; // 阻尼，惯性效果&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  controls.dampingFactor &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0.05&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 3. 光照&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;AmbientLight&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0xffffff&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.7&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; dirLight&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;DirectionalLight&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0xffffff&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  dirLight.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(dirLight)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 4. 地面网格&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundGeo&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;BoxGeometry&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundMat&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;MeshStandardMaterial&lt;/span&gt;&lt;span&gt;({ color: &lt;/span&gt;&lt;span&gt;0x888888&lt;/span&gt;&lt;span&gt; })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundMesh&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Mesh&lt;/span&gt;&lt;span&gt;(groundGeo, groundMat)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  groundMesh.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(groundMesh)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 5. 物理世界初始化&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; world&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;World&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.gravity.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;9.82&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 6. 地面物理体&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundBody&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Body&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    mass: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    shape: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Box&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Vec3&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;)),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  groundBody.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.&lt;/span&gt;&lt;span&gt;addBody&lt;/span&gt;&lt;span&gt;(groundBody)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 7. 创建材质&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundMaterial&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Material&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;ground&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; softMaterial&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Material&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;soft&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; bouncyMaterial&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Material&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;bouncy&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  groundBody.material &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; groundMaterial&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 8. 接触材质&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.&lt;/span&gt;&lt;span&gt;addContactMaterial&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ContactMaterial&lt;/span&gt;&lt;span&gt;(groundMaterial, softMaterial, {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    friction: &lt;/span&gt;&lt;span&gt;0.4&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    restitution: &lt;/span&gt;&lt;span&gt;0.0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.&lt;/span&gt;&lt;span&gt;addContactMaterial&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ContactMaterial&lt;/span&gt;&lt;span&gt;(groundMaterial, bouncyMaterial, {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    friction: &lt;/span&gt;&lt;span&gt;0.4&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    restitution: &lt;/span&gt;&lt;span&gt;0.9&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 9. 两个球体（视觉+物理）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; sphereGeo&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;SphereGeometry&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;32&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;32&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; softMesh&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Mesh&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    sphereGeo,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;MeshStandardMaterial&lt;/span&gt;&lt;span&gt;({ color: &lt;/span&gt;&lt;span&gt;0xff5555&lt;/span&gt;&lt;span&gt; })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  )&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  softMesh.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(softMesh)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; softBody&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Body&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    mass: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    shape: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Sphere&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    position: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Vec3&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    material: softMaterial,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.&lt;/span&gt;&lt;span&gt;addBody&lt;/span&gt;&lt;span&gt;(softBody)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; bouncyMesh&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Mesh&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    sphereGeo,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;MeshStandardMaterial&lt;/span&gt;&lt;span&gt;({ color: &lt;/span&gt;&lt;span&gt;0x55ff55&lt;/span&gt;&lt;span&gt; })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  )&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  bouncyMesh.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(bouncyMesh)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; bouncyBody&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Body&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    mass: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    shape: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Sphere&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    position: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Vec3&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    material: bouncyMaterial,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.&lt;/span&gt;&lt;span&gt;addBody&lt;/span&gt;&lt;span&gt;(bouncyBody)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 10. 动画循环&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; fixedTimeStep&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 60&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  function&lt;/span&gt;&lt;span&gt; animate&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    requestAnimationFrame&lt;/span&gt;&lt;span&gt;(animate)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    world.&lt;/span&gt;&lt;span&gt;step&lt;/span&gt;&lt;span&gt;(fixedTimeStep)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    softMesh.position.&lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt;(softBody.position)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    softMesh.quaternion.&lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt;(softBody.quaternion)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    bouncyMesh.position.&lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt;(bouncyBody.position)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    bouncyMesh.quaternion.&lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt;(bouncyBody.quaternion)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    controls.&lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    renderer.&lt;/span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;(scene, camera)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  animate&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 11. 窗口尺寸变化处理&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  window.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;resize&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    camera.aspect &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; window.innerWidth &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; window.innerHeight&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    camera.&lt;/span&gt;&lt;span&gt;updateProjectionMatrix&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    renderer.&lt;/span&gt;&lt;span&gt;setSize&lt;/span&gt;&lt;span&gt;(window.innerWidth, window.innerHeight)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;canvas&lt;/span&gt;&lt;span&gt; ref&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;canvasRef&quot;&lt;/span&gt;&lt;span&gt; style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;display: block; width: 100vw; height: 100vh;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;canvas&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;p&gt;观察结果&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7530141922515779594/img_0.png&quot; alt=&quot;2025-07-23T13_02_28.319Z-592165.gif&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;2025-07-23T13_02_28.319Z-592165.gif&lt;/figcaption&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;红色小球（restitution = 0）：一落地即停止&lt;/li&gt;
&lt;li&gt;绿色小球（restitution = 0.9）：反复弹跳好几次后才慢慢停止&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;你也可以试试修改 &lt;code&gt;restitution = 1.0&lt;/code&gt;，观察是否可以无限弹跳（理论上动能完全保留，不会停下）。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;小结&lt;a href=&quot;#小结&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;restitution&lt;/code&gt; 决定了刚体之间碰撞的反弹程度&lt;/li&gt;
&lt;li&gt;设置在 &lt;code&gt;ContactMaterial&lt;/code&gt; 中控制材质对之间的表现&lt;/li&gt;
&lt;li&gt;弹跳模拟可以用于球类、橡胶类、弹簧类等物理对象&lt;/li&gt;
&lt;li&gt;实际模拟中弹性和摩擦常常需要搭配调试&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>category:掘金同步</category><category>category:前端</category><category>tag:前端</category><category>tag:JavaScript</category><category>tag:面试</category></item><item><title>03 - Cannon 材质与摩擦系数设置</title><link>https://koharu.ac.ren/post/juejin/03_-_cannon_%E6%9D%90%E8%B4%A8%E4%B8%8E%E6%91%A9%E6%93%A6%E7%B3%BB%E6%95%B0%E8%AE%BE%E7%BD%AE</link><guid isPermaLink="false">juejin/03_-_cannon_材质与摩擦系数设置</guid><description>03  Cannon 材质与摩擦系数设置  作者: 前端AC | 原文: https://juejin.cn/post/7530105407846563890  03  Cannon 材质与摩擦系数设置  本节目标    理解什么是物理材质（Material）    学会设置摩擦系数 frictio...</description><pubDate>Wed, 23 Jul 2025 16:20:58 GMT</pubDate><content:encoded>&lt;h1&gt;03 - Cannon 材质与摩擦系数设置&lt;a href=&quot;#03---cannon-材质与摩擦系数设置&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;作者: 前端AC | 原文: &lt;a href=&quot;https://juejin.cn/post/7530105407846563890&quot;&gt;https://juejin.cn/post/7530105407846563890&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;03 - Cannon 材质与摩擦系数设置&lt;a href=&quot;#03---cannon-材质与摩擦系数设置-1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;h2&gt;本节目标&lt;a href=&quot;#本节目标&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;理解什么是物理材质（Material）&lt;/li&gt;
&lt;li&gt;学会设置摩擦系数 &lt;code&gt;friction&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;使用 &lt;code&gt;ContactMaterial&lt;/code&gt; 控制材质间交互&lt;/li&gt;
&lt;li&gt;观察摩擦力对运动的影响&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;材质与摩擦介绍&lt;a href=&quot;#材质与摩擦介绍&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;在 Cannon.js 中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Material&lt;/code&gt; 是一个&lt;strong&gt;刚体的物理属性载体&lt;/strong&gt;，包括摩擦、弹性等。&lt;/li&gt;
&lt;li&gt;只有在 &lt;code&gt;ContactMaterial&lt;/code&gt; 中组合两个 &lt;code&gt;Material&lt;/code&gt; 才能生效。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ContactMaterial&lt;/code&gt; 可定义：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;friction&lt;/code&gt; 摩擦系数&lt;/li&gt;
&lt;li&gt;&lt;code&gt;restitution&lt;/code&gt; 弹性系数（可反弹）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;举个例子：&lt;a href=&quot;#举个例子&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; wood&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Material&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;wood&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; ice&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Material&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;ice&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; contactMat&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ContactMaterial&lt;/span&gt;&lt;span&gt;(wood, ice, {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  friction: &lt;/span&gt;&lt;span&gt;0.05&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  restitution: &lt;/span&gt;&lt;span&gt;0.1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;world.&lt;/span&gt;&lt;span&gt;addContactMaterial&lt;/span&gt;&lt;span&gt;(contactMat)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;示例：三个立方体摩擦不同的滑动对比&lt;a href=&quot;#示例三个立方体摩擦不同的滑动对比&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;我们创建一个倾斜的平面，然后放上三个摩擦系数不同的立方体，观察它们滑动速度的不同。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;完整 Vue3 示例（使用 cannon-es）&lt;a href=&quot;#完整-vue3-示例使用-cannon-es&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; setup&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { onMounted, ref } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;vue&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; THREE &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;three&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; CANNON &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;cannon-es&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { OrbitControls } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;three/examples/jsm/controls/OrbitControls&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; canvasRef&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; ref&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;onMounted&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; scene&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Scene&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; camera&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;PerspectiveCamera&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;75&lt;/span&gt;&lt;span&gt;, window.innerWidth &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; window.innerHeight, &lt;/span&gt;&lt;span&gt;0.1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  camera.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;15&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; renderer&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;WebGLRenderer&lt;/span&gt;&lt;span&gt;({ canvas: canvasRef.value })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  renderer.&lt;/span&gt;&lt;span&gt;setSize&lt;/span&gt;&lt;span&gt;(window.innerWidth, window.innerHeight)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; controls&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; OrbitControls&lt;/span&gt;&lt;span&gt;(camera, renderer.domElement)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  controls.enableDamping &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; world&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;World&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.gravity.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;9.82&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 创建基础材质&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundMaterial&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Material&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;ground&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; matLow&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Material&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;low&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; matMid&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Material&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;mid&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; matHigh&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Material&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;high&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 设置接触材质（摩擦系数不同）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.&lt;/span&gt;&lt;span&gt;addContactMaterial&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ContactMaterial&lt;/span&gt;&lt;span&gt;(groundMaterial, matLow, { friction: &lt;/span&gt;&lt;span&gt;0.0&lt;/span&gt;&lt;span&gt; }))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.&lt;/span&gt;&lt;span&gt;addContactMaterial&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ContactMaterial&lt;/span&gt;&lt;span&gt;(groundMaterial, matMid, { friction: &lt;/span&gt;&lt;span&gt;0.3&lt;/span&gt;&lt;span&gt; }))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.&lt;/span&gt;&lt;span&gt;addContactMaterial&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ContactMaterial&lt;/span&gt;&lt;span&gt;(groundMaterial, matHigh, { friction: &lt;/span&gt;&lt;span&gt;1.0&lt;/span&gt;&lt;span&gt; }))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 添加地面（倾斜）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundGeo&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;BoxGeometry&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundMat&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;MeshStandardMaterial&lt;/span&gt;&lt;span&gt;({ color: &lt;/span&gt;&lt;span&gt;0x888888&lt;/span&gt;&lt;span&gt; })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundMesh&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Mesh&lt;/span&gt;&lt;span&gt;(groundGeo, groundMat)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  groundMesh.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  groundMesh.rotation.z &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt;0.3&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(groundMesh)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundBody&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Body&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    mass: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    shape: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Box&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Vec3&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;)),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    position: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Vec3&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    material: groundMaterial&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  groundBody.quaternion.&lt;/span&gt;&lt;span&gt;setFromEuler&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;0.3&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.&lt;/span&gt;&lt;span&gt;addBody&lt;/span&gt;&lt;span&gt;(groundBody)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 立方体基础设置&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; cubeGeo&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;BoxGeometry&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; cubeShape&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Box&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Vec3&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; cubes&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; bodies&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; []&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; settings&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    { x: &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;, color: &lt;/span&gt;&lt;span&gt;0xff0000&lt;/span&gt;&lt;span&gt;, material: matLow },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    { x: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, color: &lt;/span&gt;&lt;span&gt;0xffff00&lt;/span&gt;&lt;span&gt;, material: matMid },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    { x: &lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;, color: &lt;/span&gt;&lt;span&gt;0x00ff00&lt;/span&gt;&lt;span&gt;, material: matHigh }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; i &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;; i &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;span&gt;; i&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;color&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;material&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; settings[i]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; mesh&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Mesh&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      cubeGeo,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;MeshStandardMaterial&lt;/span&gt;&lt;span&gt;({ color })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    mesh.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(x, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(mesh)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; body&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Body&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      mass: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      shape: cubeShape,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      position: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Vec3&lt;/span&gt;&lt;span&gt;(x, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      material&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    world.&lt;/span&gt;&lt;span&gt;addBody&lt;/span&gt;&lt;span&gt;(body)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    cubes.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(mesh)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    bodies.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(body)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 添加灯光&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; light&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;DirectionalLight&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0xffffff&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  light.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(light)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; clock&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Clock&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; timeStep&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 60&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  function&lt;/span&gt;&lt;span&gt; animate&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    requestAnimationFrame&lt;/span&gt;&lt;span&gt;(animate)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; delta&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; clock.&lt;/span&gt;&lt;span&gt;getDelta&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    world.&lt;/span&gt;&lt;span&gt;step&lt;/span&gt;&lt;span&gt;(timeStep, delta)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; i &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;; i &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; cubes.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;; i&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      cubes[i].position.&lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt;(bodies[i].position)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      cubes[i].quaternion.&lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt;(bodies[i].quaternion)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    groundMesh.quaternion.&lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt;(groundBody.quaternion)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    controls.&lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    renderer.&lt;/span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;(scene, camera)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  animate&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;canvas&lt;/span&gt;&lt;span&gt; ref&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;canvasRef&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;canvas&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7530105407846563890/img_0.png&quot; alt=&quot;2025-07-23T12_42_22.550Z-557681.gif&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;2025-07-23T12_42_22.550Z-557681.gif&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2&gt;观察结果&lt;a href=&quot;#观察结果&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;绿色立方体：&lt;code&gt;friction = 0&lt;/code&gt;，滑得最远&lt;/li&gt;
&lt;li&gt;黄色立方体：&lt;code&gt;friction = 0.3&lt;/code&gt;，滑行距离适中&lt;/li&gt;
&lt;li&gt;红色立方体：&lt;code&gt;friction = 1.0&lt;/code&gt;，几乎立即停止&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这就是摩擦系数对运动的控制作用。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;小结&lt;a href=&quot;#小结&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;使用 &lt;code&gt;Material&lt;/code&gt; 可以控制物体的物理属性&lt;/li&gt;
&lt;li&gt;必须用 &lt;code&gt;ContactMaterial&lt;/code&gt; 来定义两个材质之间的行为&lt;/li&gt;
&lt;li&gt;&lt;code&gt;friction&lt;/code&gt; 越大，滑动越慢甚至停止&lt;/li&gt;
&lt;li&gt;可以使用多组材质，设置多种不同摩擦情况&lt;/li&gt;
&lt;li&gt;倾斜地面是检验摩擦的最佳手段之一&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>category:掘金同步</category><category>category:前端</category><category>tag:前端</category><category>tag:JavaScript</category></item><item><title>02 - Cannon 引擎基础碰撞讲解</title><link>https://koharu.ac.ren/post/juejin/02_-_cannon_%E5%BC%95%E6%93%8E%E5%9F%BA%E7%A1%80%E7%A2%B0%E6%92%9E%E8%AE%B2%E8%A7%A3</link><guid isPermaLink="false">juejin/02_-_cannon_引擎基础碰撞讲解</guid><description>02  Cannon 引擎基础碰撞讲解  作者: 前端AC | 原文: https://juejin.cn/post/7530105395282952230  02  Cannon 引擎基础碰撞讲解  本节目标    理解 Cannon.js 中的碰撞原理    学习如何让两个刚体发生碰撞    配...</description><pubDate>Wed, 23 Jul 2025 16:19:59 GMT</pubDate><content:encoded>&lt;h1&gt;02 - Cannon 引擎基础碰撞讲解&lt;a href=&quot;#02---cannon-引擎基础碰撞讲解&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;作者: 前端AC | 原文: &lt;a href=&quot;https://juejin.cn/post/7530105395282952230&quot;&gt;https://juejin.cn/post/7530105395282952230&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;02 - Cannon 引擎基础碰撞讲解&lt;a href=&quot;#02---cannon-引擎基础碰撞讲解-1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;h2&gt;本节目标&lt;a href=&quot;#本节目标&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;理解 Cannon.js 中的碰撞原理&lt;/li&gt;
&lt;li&gt;学习如何让两个刚体发生碰撞&lt;/li&gt;
&lt;li&gt;配置质量、形状、位置使碰撞真实生效&lt;/li&gt;
&lt;li&gt;用 Vue3 示例观察碰撞过程&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;什么是碰撞？&lt;a href=&quot;#什么是碰撞&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;在 Cannon.js 中，&lt;strong&gt;碰撞（Collision）&lt;/strong&gt; 是指两个或多个刚体接触后发生的物理响应。要实现碰撞：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;两个物体都必须有 &lt;code&gt;shape&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;至少一个物体必须有质量（&lt;code&gt;mass &amp;gt; 0&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;物体必须靠近、发生交错&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;碰撞最小实现逻辑&lt;a href=&quot;#碰撞最小实现逻辑&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;我们用一个最基本的例子说明：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个球从上往下掉&lt;/li&gt;
&lt;li&gt;底下是一个静止的平面（地板）&lt;/li&gt;
&lt;li&gt;当球体接触地板后停止&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;示例：球落地碰撞+掉落&lt;a href=&quot;#示例球落地碰撞掉落&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;canvas&lt;/span&gt;&lt;span&gt; ref&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;canvasRef&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;canvas&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; setup&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { onMounted, ref } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;vue&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; THREE &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;three&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; CANNON &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;cannon-es&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { OrbitControls } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;three/examples/jsm/controls/OrbitControls&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; canvasRef&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; ref&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;onMounted&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 初始化场景&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; scene&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Scene&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; camera&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;PerspectiveCamera&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;75&lt;/span&gt;&lt;span&gt;, window.innerWidth &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; window.innerHeight, &lt;/span&gt;&lt;span&gt;0.1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  camera.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;15&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; renderer&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;WebGLRenderer&lt;/span&gt;&lt;span&gt;({ canvas: canvasRef.value })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  renderer.&lt;/span&gt;&lt;span&gt;setSize&lt;/span&gt;&lt;span&gt;(window.innerWidth, window.innerHeight)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 添加轨道控制器&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; controls&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; OrbitControls&lt;/span&gt;&lt;span&gt;(camera, renderer.domElement)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  controls.enableDamping &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 添加球体 Mesh&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; ballGeo&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;SphereGeometry&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; ballMat&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;MeshStandardMaterial&lt;/span&gt;&lt;span&gt;({ color: &lt;/span&gt;&lt;span&gt;0xff5555&lt;/span&gt;&lt;span&gt; })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; ballMesh&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Mesh&lt;/span&gt;&lt;span&gt;(ballGeo, ballMat)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(ballMesh)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 添加地面 Mesh&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundGeo&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;BoxGeometry&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundMat&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;MeshStandardMaterial&lt;/span&gt;&lt;span&gt;({ color: &lt;/span&gt;&lt;span&gt;0x88cc88&lt;/span&gt;&lt;span&gt; })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundMesh&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Mesh&lt;/span&gt;&lt;span&gt;(groundGeo, groundMat)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  groundMesh.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(groundMesh)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 添加灯光&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; light&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;DirectionalLight&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0xffffff&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  light.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(light)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 初始化物理世界&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; world&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;World&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.gravity.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;9.82&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 创建球体刚体&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; ballBody&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Body&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    mass: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// 会受到重力影响&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    shape: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Sphere&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    position: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Vec3&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.&lt;/span&gt;&lt;span&gt;addBody&lt;/span&gt;&lt;span&gt;(ballBody)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 创建地面刚体（静止）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundBody&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Body&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    type: &lt;/span&gt;&lt;span&gt;CANNON&lt;/span&gt;&lt;span&gt;.Body.&lt;/span&gt;&lt;span&gt;STATIC&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// 不会动&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    shape: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Box&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Vec3&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;)),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    position: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Vec3&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.&lt;/span&gt;&lt;span&gt;addBody&lt;/span&gt;&lt;span&gt;(groundBody)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 动画循环&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; clock&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Clock&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; timeStep&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 60&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  function&lt;/span&gt;&lt;span&gt; animate&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    requestAnimationFrame&lt;/span&gt;&lt;span&gt;(animate)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; delta&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; clock.&lt;/span&gt;&lt;span&gt;getDelta&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    world.&lt;/span&gt;&lt;span&gt;step&lt;/span&gt;&lt;span&gt;(timeStep, delta)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    // 同步位置&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ballMesh.position.&lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt;(ballBody.position)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ballMesh.quaternion.&lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt;(ballBody.quaternion)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    controls.&lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    renderer.&lt;/span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;(scene, camera)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  animate&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7530105395282952230/img_0.png&quot; alt=&quot;2025-07-23T12_14_56.568Z-506054.gif&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;2025-07-23T12_14_56.568Z-506054.gif&lt;/figcaption&gt;&lt;/figure&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; setup&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { onMounted, ref } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;vue&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; THREE &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;three&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; CANNON &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;cannon-es&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { OrbitControls } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;three/examples/jsm/controls/OrbitControls&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; canvasRef&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; ref&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;onMounted&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 初始化场景&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; scene&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Scene&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; camera&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;PerspectiveCamera&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;75&lt;/span&gt;&lt;span&gt;, window.innerWidth &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; window.innerHeight, &lt;/span&gt;&lt;span&gt;0.1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  camera.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;15&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; renderer&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;WebGLRenderer&lt;/span&gt;&lt;span&gt;({ canvas: canvasRef.value })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  renderer.&lt;/span&gt;&lt;span&gt;setSize&lt;/span&gt;&lt;span&gt;(window.innerWidth, window.innerHeight)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 添加轨道控制器&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; controls&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; OrbitControls&lt;/span&gt;&lt;span&gt;(camera, renderer.domElement)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  controls.enableDamping &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 添加球体 Mesh&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; ballGeo&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;SphereGeometry&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; ballMat&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;MeshStandardMaterial&lt;/span&gt;&lt;span&gt;({ color: &lt;/span&gt;&lt;span&gt;0xff5555&lt;/span&gt;&lt;span&gt; })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; ballMesh&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Mesh&lt;/span&gt;&lt;span&gt;(ballGeo, ballMat)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(ballMesh)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 添加倾斜地面 Mesh（略微倾斜）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundGeo&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;BoxGeometry&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundMat&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;MeshStandardMaterial&lt;/span&gt;&lt;span&gt;({ color: &lt;/span&gt;&lt;span&gt;0x88cc88&lt;/span&gt;&lt;span&gt; })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundMesh&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Mesh&lt;/span&gt;&lt;span&gt;(groundGeo, groundMat)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  groundMesh.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  groundMesh.rotation.z &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0.1&lt;/span&gt;&lt;span&gt; // 倾斜 0.1 弧度&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(groundMesh)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 添加灯光&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; light&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;DirectionalLight&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0xffffff&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  light.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(light)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 初始化物理世界&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; world&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;World&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.gravity.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;9.82&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 创建球体刚体&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; ballBody&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Body&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    mass: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    shape: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Sphere&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    position: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Vec3&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.&lt;/span&gt;&lt;span&gt;addBody&lt;/span&gt;&lt;span&gt;(ballBody)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 创建倾斜地面刚体&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; groundBody&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Body&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    type: &lt;/span&gt;&lt;span&gt;CANNON&lt;/span&gt;&lt;span&gt;.Body.&lt;/span&gt;&lt;span&gt;STATIC&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    shape: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Box&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Vec3&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;)),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    position: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Vec3&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  groundBody.quaternion.&lt;/span&gt;&lt;span&gt;setFromEuler&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.1&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;// Z 轴倾斜 0.1 弧度&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.&lt;/span&gt;&lt;span&gt;addBody&lt;/span&gt;&lt;span&gt;(groundBody)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 动画循环&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; clock&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Clock&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; timeStep&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 60&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  function&lt;/span&gt;&lt;span&gt; animate&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    requestAnimationFrame&lt;/span&gt;&lt;span&gt;(animate)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; delta&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; clock.&lt;/span&gt;&lt;span&gt;getDelta&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    world.&lt;/span&gt;&lt;span&gt;step&lt;/span&gt;&lt;span&gt;(timeStep, delta)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ballMesh.position.&lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt;(ballBody.position)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ballMesh.quaternion.&lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt;(ballBody.quaternion)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    groundMesh.quaternion.&lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt;(groundBody.quaternion)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    controls.&lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    renderer.&lt;/span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;(scene, camera)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  animate&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;canvas&lt;/span&gt;&lt;span&gt; ref&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;canvasRef&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;canvas&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7530105395282952230/img_1.png&quot; alt=&quot;2025-07-23T12_14_12.043Z-860045.gif&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;2025-07-23T12_14_12.043Z-860045.gif&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2&gt;核心知识点说明&lt;a href=&quot;#核心知识点说明&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;1. &lt;code&gt;mass&lt;/code&gt; 决定是否受重力影响&lt;a href=&quot;#1-mass-决定是否受重力影响&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mass = 0&lt;/code&gt;（或 &lt;code&gt;type: STATIC&lt;/code&gt;） 表示不受重力、不会移动&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mass &amp;gt; 0&lt;/code&gt; 表示刚体受力、能运动&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. &lt;code&gt;shape&lt;/code&gt; 决定碰撞体积&lt;a href=&quot;#2-shape-决定碰撞体积&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;用 &lt;code&gt;CANNON.Sphere(radius)&lt;/code&gt; 创建球体&lt;/li&gt;
&lt;li&gt;用 &lt;code&gt;CANNON.Box(new Vec3(x,y,z))&lt;/code&gt; 创建立方体（地面）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. &lt;code&gt;position&lt;/code&gt; 设置初始位置&lt;a href=&quot;#3-position-设置初始位置&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;球体在上方 &lt;code&gt;y=5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;地面在 &lt;code&gt;y = -0.5&lt;/code&gt;，高度为1（底面居中）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. &lt;code&gt;step()&lt;/code&gt; 每帧推进物理计算&lt;a href=&quot;#4-step-每帧推进物理计算&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;world.step()&lt;/code&gt; 计算物理&lt;/li&gt;
&lt;li&gt;然后把位置同步回 Three.js 的 Mesh 上&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;如何确认碰撞发生了？&lt;a href=&quot;#如何确认碰撞发生了&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;在这个例子中，我们还没有使用事件监听，但你可以通过观察：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;球体从空中落下&lt;/li&gt;
&lt;li&gt;触碰地面后停止下落&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;下一节将介绍如何监听碰撞事件，获取更详细的碰撞信息。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;小结&lt;a href=&quot;#小结&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;碰撞需要物体有 &lt;code&gt;shape&lt;/code&gt;，并至少一个有质量&lt;/li&gt;
&lt;li&gt;&lt;code&gt;STATIC&lt;/code&gt; 类型物体常用于地面、墙体等不可动物体&lt;/li&gt;
&lt;li&gt;每帧调用 &lt;code&gt;step()&lt;/code&gt; 推进物理，并同步位置&lt;/li&gt;
&lt;li&gt;碰撞响应是 Cannon.js 的核心机制之一&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;下一节：监听碰撞事件与获取碰撞信息&lt;/p&gt;</content:encoded><category>category:掘金同步</category><category>category:前端</category><category>tag:前端</category><category>tag:JavaScript</category></item><item><title>01 - Cannon.js 引擎的用途和基本使用</title><link>https://koharu.ac.ren/post/juejin/01_-_cannonjs_%E5%BC%95%E6%93%8E%E7%9A%84%E7%94%A8%E9%80%94%E5%92%8C%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8</link><guid isPermaLink="false">juejin/01_-_cannonjs_引擎的用途和基本使用</guid><description>01  Cannon.js 引擎的用途和基本使用  作者: 前端AC | 原文: https://juejin.cn/post/7530106154313400330  01  Cannon.js 引擎的用途和基本使用  前言 朱波想做游戏，前来学习这个cannon物理引擎，太好用了。  Canno...</description><pubDate>Wed, 23 Jul 2025 16:18:30 GMT</pubDate><content:encoded>&lt;h1&gt;01 - Cannon.js 引擎的用途和基本使用&lt;a href=&quot;#01---cannonjs-引擎的用途和基本使用&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;作者: 前端AC | 原文: &lt;a href=&quot;https://juejin.cn/post/7530106154313400330&quot;&gt;https://juejin.cn/post/7530106154313400330&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;01 - Cannon.js 引擎的用途和基本使用&lt;a href=&quot;#01---cannonjs-引擎的用途和基本使用-1&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;h1&gt;前言&lt;a href=&quot;#前言&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;朱波想做游戏，前来学习这个cannon物理引擎，太好用了。&lt;/p&gt;
&lt;h2&gt;Cannon.js 是什么？&lt;a href=&quot;#cannonjs-是什么&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Cannon.js 是一个轻量级的 JavaScript 物理引擎，适用于 Web 应用和游戏开发。它专注于刚体物理模拟，能高效地处理物体间的碰撞、力学反应、摩擦等物理效果。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;适用于 Three.js 等 WebGL 引擎配合使用，增强场景的真实物理行为。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;Cannon.js 的用途&lt;a href=&quot;#cannonjs-的用途&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;游戏开发中的物理碰撞检测与响应&lt;/li&gt;
&lt;li&gt;Web3D 场景中的物体重力、摩擦模拟&lt;/li&gt;
&lt;li&gt;拖拽物体、堆叠、刚体运动模拟&lt;/li&gt;
&lt;li&gt;教育和科学可视化中的物理实验&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;安装 Cannon.js&lt;a href=&quot;#安装-cannonjs&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;推荐使用 &lt;code&gt;cannon-es&lt;/code&gt;（是 Cannon.js 的现代 ES module 重构版，维护活跃）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; install&lt;/span&gt;&lt;span&gt; cannon-es&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你使用 Vue3 + Vite，安装完成后就可以像普通模块一样导入：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; CANNON &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;cannon-es&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Vue3 示例：球体因重力下落&lt;a href=&quot;#vue3-示例球体因重力下落&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;我们来展示一个最简物理模拟示例：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个球体在空中受到重力影响下落&lt;/li&gt;
&lt;li&gt;不设置地面，不发生碰撞，仅用于演示重力作用&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; setup&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// 引入必要模块&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { onMounted, ref } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;vue&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; THREE &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;three&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; CANNON &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;cannon-es&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// 创建 canvas 引用&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; canvasRef&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; ref&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;onMounted&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 初始化 Three.js 场景&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; scene&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Scene&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 创建相机并设置位置&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; camera&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;PerspectiveCamera&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;75&lt;/span&gt;&lt;span&gt;, window.innerWidth &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; window.innerHeight, &lt;/span&gt;&lt;span&gt;0.1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  camera.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;15&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 创建渲染器并绑定 canvas&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; renderer&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;WebGLRenderer&lt;/span&gt;&lt;span&gt;({ canvas: canvasRef.value })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  renderer.&lt;/span&gt;&lt;span&gt;setSize&lt;/span&gt;&lt;span&gt;(window.innerWidth, window.innerHeight)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 创建球体的几何体和材质，并生成 mesh&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; sphereGeometry&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;SphereGeometry&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; sphereMaterial&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;MeshStandardMaterial&lt;/span&gt;&lt;span&gt;({ color: &lt;/span&gt;&lt;span&gt;0x0077ff&lt;/span&gt;&lt;span&gt; })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; sphereMesh&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Mesh&lt;/span&gt;&lt;span&gt;(sphereGeometry, sphereMaterial)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(sphereMesh)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 添加半球光照，增强视觉效果&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; light&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;HemisphereLight&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0xffffff&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0x444444&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1.2&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  light.position.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  scene.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(light)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 初始化 Cannon.js 世界&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; world&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;World&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.gravity.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;9.82&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;// 设置重力加速度，单位 m/s²&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 创建一个球体刚体，初始位置在 y=10&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; ballBody&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Body&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    mass: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// 质量为 1，表示会受到重力影响&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    shape: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Sphere&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;// 与 Three.js 中的球体对应&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    position: &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; CANNON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Vec3&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  world.&lt;/span&gt;&lt;span&gt;addBody&lt;/span&gt;&lt;span&gt;(ballBody)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 创建时间步长与时钟&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; clock&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; THREE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Clock&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; step&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 60&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 动画更新函数&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  function&lt;/span&gt;&lt;span&gt; animate&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    requestAnimationFrame&lt;/span&gt;&lt;span&gt;(animate) &lt;/span&gt;&lt;span&gt;// 循环调用动画帧&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; delta&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; clock.&lt;/span&gt;&lt;span&gt;getDelta&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;// 获取自上一帧的时间差&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    world.&lt;/span&gt;&lt;span&gt;step&lt;/span&gt;&lt;span&gt;(step, delta) &lt;/span&gt;&lt;span&gt;// 推进物理世界一步&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    // 同步物理刚体位置到 Three.js mesh&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    sphereMesh.position.&lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt;(ballBody.position)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    // 渲染场景&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    renderer.&lt;/span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;(scene, camera)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  animate&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;// 启动动画循环&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;!-- 渲染用 canvas 元素 --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;canvas&lt;/span&gt;&lt;span&gt; ref&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;canvasRef&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;canvas&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;figure&gt;&lt;img src=&quot;/images/juejin/7530106154313400330/img_0.png&quot; alt=&quot;2025-07-23T10_15_23.407Z-25720.gif&quot; loading=&quot;lazy&quot; /&gt;&lt;figcaption&gt;2025-07-23T10_15_23.407Z-25720.gif&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;部分代码解释：&lt;/p&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;参数&lt;/th&gt;&lt;th&gt;含义&lt;/th&gt;&lt;th&gt;示例值&lt;/th&gt;&lt;th&gt;解释&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;75&lt;/code&gt;&lt;/td&gt;&lt;td&gt;视野角度（FOV）&lt;/td&gt;&lt;td&gt;&lt;code&gt;75&lt;/code&gt;&lt;/td&gt;&lt;td&gt;像人眼视角那样，角度越大视野越广，但会有畸变&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;window.innerWidth / window.innerHeight&lt;/code&gt;&lt;/td&gt;&lt;td&gt;长宽比&lt;/td&gt;&lt;td&gt;比如 &lt;code&gt;16:9&lt;/code&gt;&lt;/td&gt;&lt;td&gt;防止画面拉伸或压缩，使用窗口的实际比例&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;0.1&lt;/code&gt;&lt;/td&gt;&lt;td&gt;近裁剪面&lt;/td&gt;&lt;td&gt;&lt;code&gt;0.1&lt;/code&gt; 米&lt;/td&gt;&lt;td&gt;相机能“看到”的最近距离，太小会有深度精度问题&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;1000&lt;/code&gt;&lt;/td&gt;&lt;td&gt;远裁剪面&lt;/td&gt;&lt;td&gt;&lt;code&gt;1000&lt;/code&gt; 米&lt;/td&gt;&lt;td&gt;相机能“看到”的最远距离，超过这个距离的物体不会被渲染&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;




















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;行号&lt;/th&gt;&lt;th&gt;代码&lt;/th&gt;&lt;th&gt;解释&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;const renderer = new THREE.WebGLRenderer({ canvas: canvasRef.value });&lt;/code&gt;&lt;/td&gt;&lt;td&gt;创建一个 WebGL 渲染器（Renderer）实例，并将其渲染目标绑定到 Vue 模板中的 &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; 元素上。这样，Three.js 渲染结果会直接显示在页面上的这个 canvas 中。&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;renderer.setSize(window.innerWidth, window.innerHeight);&lt;/code&gt;&lt;/td&gt;&lt;td&gt;设置渲染器的宽度和高度为浏览器窗口的尺寸，使画布充满整个屏幕，避免图像变形或只显示一部分。&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;小结&lt;a href=&quot;#小结&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Cannon.js 用于模拟真实物理行为：重力、碰撞、摩擦等&lt;/li&gt;
&lt;li&gt;推荐使用 &lt;code&gt;cannon-es&lt;/code&gt; 模块，结合 Vue3 和 Three.js 渲染效果&lt;/li&gt;
&lt;li&gt;该示例展示了最基本的重力作用：球体自然下落&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>category:掘金同步</category><category>category:前端</category><category>tag:前端</category><category>tag:JavaScript</category></item><item><title>一款减轻前端图片命名工作量的图片转换器</title><link>https://koharu.ac.ren/post/juejin/%E4%B8%80%E6%AC%BE%E5%87%8F%E8%BD%BB%E5%89%8D%E7%AB%AF%E5%9B%BE%E7%89%87%E5%91%BD%E5%90%8D%E5%B7%A5%E4%BD%9C%E9%87%8F%E7%9A%84%E5%9B%BE%E7%89%87%E8%BD%AC%E6%8D%A2%E5%99%A8</link><guid isPermaLink="false">juejin/一款减轻前端图片命名工作量的图片转换器</guid><description>一款减轻前端图片命名工作量的图片转换器  作者: 前端AC | 原文: https://juejin.cn/post/7524608157575200787  在这个图片格式百花齐放的时代，你是否也遇到过这样的困扰：手机拍的HEIC格式照片无法在网页上显示？PNG图片太大影响网站加载速度？想要批量转...</description><pubDate>Wed, 09 Jul 2025 01:39:19 GMT</pubDate><content:encoded>&lt;h1&gt;一款减轻前端图片命名工作量的图片转换器&lt;a href=&quot;#一款减轻前端图片命名工作量的图片转换器&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;作者: 前端AC | 原文: &lt;a href=&quot;https://juejin.cn/post/7524608157575200787&quot;&gt;https://juejin.cn/post/7524608157575200787&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;在这个图片格式百花齐放的时代，你是否也遇到过这样的困扰：手机拍的HEIC格式照片无法在网页上显示？PNG图片太大影响网站加载速度？想要批量转换图片格式却找不到合适的工具？今天，我将分享如何用纯前端技术打造一款功能强大、用户友好的图片格式转换器。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;项目背景与需求分析&lt;a href=&quot;#项目背景与需求分析&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;h2&gt;痛点分析&lt;a href=&quot;#痛点分析&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;在日常工作和生活中，我们经常需要处理各种格式的图片：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;兼容性问题&lt;/strong&gt;：不同平台对图片格式的支持不同&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;文件大小&lt;/strong&gt;：PNG图片质量高但体积大，JPG压缩率高但不支持透明&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;批量处理&lt;/strong&gt;：需要转换大量图片时，逐个处理效率低下&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;隐私安全&lt;/strong&gt;：在线转换工具需要上传图片，存在隐私泄露风险&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;移动端体验&lt;/strong&gt;：手机上使用图片转换工具体验普遍较差&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;手动命名多个图片&lt;/strong&gt;：前端开发痛点，多个图片的命名增加命名工作量&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;解决方案&lt;a href=&quot;#解决方案&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;基于以上痛点，我决定开发一款：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;纯前端处理&lt;/strong&gt;的图片转换工具，保护用户隐私&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;支持批量转换&lt;/strong&gt;，提高工作效率&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;移动端优化&lt;/strong&gt;，特别是微信等内置浏览器&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;现代化UI设计&lt;/strong&gt;，提供优秀的用户体验&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;技术选型与架构设计&lt;a href=&quot;#技术选型与架构设计&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;h2&gt;为什么选择纯前端方案？&lt;a href=&quot;#为什么选择纯前端方案&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;隐私保护&lt;/strong&gt;：图片不离开用户设备，完全本地处理&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无服务器成本&lt;/strong&gt;：不需要后端服务器，降低维护成本&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;响应速度快&lt;/strong&gt;：无需网络传输，处理速度更快&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;部署简单&lt;/strong&gt;：静态文件，可部署到任何Web服务器&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;核心技术栈&lt;a href=&quot;#核心技术栈&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// 主要使用的Web API&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; techStack&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;图片处理&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;Canvas API + File API&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;文件操作&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;Blob API + URL API&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;移动端优化&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;Web Share API + Clipboard API&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;用户体验&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;CSS3 Animation + Intersection Observer&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;兼容性处理&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;Feature Detection + Polyfills&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;架构设计&lt;a href=&quot;#架构设计&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;采用&lt;strong&gt;单一职责原则&lt;/strong&gt;设计的类结构：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; ImageConverter&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    // 核心功能模块&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    constructor&lt;/span&gt;&lt;span&gt;()           &lt;/span&gt;&lt;span&gt;// 初始化配置&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    handleFiles&lt;/span&gt;&lt;span&gt;()          &lt;/span&gt;&lt;span&gt;// 文件处理模块&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    convertImages&lt;/span&gt;&lt;span&gt;()        &lt;/span&gt;&lt;span&gt;// 图片转换引擎&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    // 下载模块&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    downloadSingle&lt;/span&gt;&lt;span&gt;()       &lt;/span&gt;&lt;span&gt;// 单文件下载&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    downloadAll&lt;/span&gt;&lt;span&gt;()          &lt;/span&gt;&lt;span&gt;// 批量下载&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    mobileDownload&lt;/span&gt;&lt;span&gt;()       &lt;/span&gt;&lt;span&gt;// 移动端下载优化&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    // 用户体验模块&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    showNotification&lt;/span&gt;&lt;span&gt;()     &lt;/span&gt;&lt;span&gt;// 通知系统&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    createProgressBar&lt;/span&gt;&lt;span&gt;()    &lt;/span&gt;&lt;span&gt;// 进度显示&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    animateSection&lt;/span&gt;&lt;span&gt;()       &lt;/span&gt;&lt;span&gt;// 动画效果&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    // 环境检测模块&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    isWeChatBrowser&lt;/span&gt;&lt;span&gt;()      &lt;/span&gt;&lt;span&gt;// 微信环境检测&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    isMobileDevice&lt;/span&gt;&lt;span&gt;()       &lt;/span&gt;&lt;span&gt;// 移动设备检测&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    checkBrowserEnvironment&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;// 智能环境适配&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;核心功能实现详解&lt;a href=&quot;#核心功能实现详解&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;h2&gt;1. 图片格式转换引擎&lt;a href=&quot;#1-图片格式转换引擎&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;图片转换的核心是利用Canvas API：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;convertImage&lt;/span&gt;&lt;span&gt;(file, format, quality) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Promise&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;reject&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; canvas&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;createElement&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;canvas&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; ctx&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; canvas.&lt;/span&gt;&lt;span&gt;getContext&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;2d&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; img&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Image&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        img.&lt;/span&gt;&lt;span&gt;onload&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            canvas.width &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; img.width;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            canvas.height &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; img.height;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            //  关键优化：JPG格式添加白色背景&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            if&lt;/span&gt;&lt;span&gt; (format &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &apos;jpeg&apos;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                ctx.fillStyle &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;#FFFFFF&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                ctx.&lt;/span&gt;&lt;span&gt;fillRect&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, canvas.width, canvas.height);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            ctx.&lt;/span&gt;&lt;span&gt;drawImage&lt;/span&gt;&lt;span&gt;(img, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            //  转换为目标格式&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            canvas.&lt;/span&gt;&lt;span&gt;toBlob&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;blob&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                blob &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; resolve&lt;/span&gt;&lt;span&gt;(blob) &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; reject&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; Error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;转换失败&apos;&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            }, &lt;/span&gt;&lt;span&gt;`image/${&lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;}`&lt;/span&gt;&lt;span&gt;, quality);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        img.src &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; URL&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;createObjectURL&lt;/span&gt;&lt;span&gt;(file);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;技术亮点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;自动处理透明背景（PNG→JPG时添加白色背景）&lt;/li&gt;
&lt;li&gt;支持质量调节（0.1-1.0）&lt;/li&gt;
&lt;li&gt;异步处理，不阻塞UI线程&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;2. 移动端下载优化&lt;a href=&quot;#2-移动端下载优化&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;移动端下载是最大的技术挑战，特别是微信等内置浏览器：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;async &lt;/span&gt;&lt;span&gt;mobileDownload&lt;/span&gt;&lt;span&gt;(blob, fileName, url) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    //  智能环境检测&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;isInAppBrowser&lt;/span&gt;&lt;span&gt;()) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;handleInAppBrowserDownload&lt;/span&gt;&lt;span&gt;(blob, fileName, url);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    //  尝试现代Web Share API&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (navigator.share &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; navigator.canShare) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; file&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; File&lt;/span&gt;&lt;span&gt;([blob], fileName, { type: blob.type });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        if&lt;/span&gt;&lt;span&gt; (navigator.&lt;/span&gt;&lt;span&gt;canShare&lt;/span&gt;&lt;span&gt;({ files: [file] })) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            await&lt;/span&gt;&lt;span&gt; navigator.&lt;/span&gt;&lt;span&gt;share&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                files: [file],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                title: &lt;/span&gt;&lt;span&gt;&apos;保存转换后的图片&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    //  降级方案：传统下载&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fallbackDownload&lt;/span&gt;&lt;span&gt;(blob, fileName, url);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;创新点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;多层级降级策略&lt;/li&gt;
&lt;li&gt;环境自适应处理&lt;/li&gt;
&lt;li&gt;用户体验优先&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. 微信专属优化&lt;a href=&quot;#3-微信专属优化&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;针对微信环境的特殊处理：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;tryWeChatDirectSave&lt;/span&gt;&lt;span&gt;(blob, fileName, url) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    //  创建微信专用保存界面&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; modal&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;createWeChatSaveModal&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    //  转换为Base64便于微信处理&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; reader&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; FileReader&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    reader.&lt;/span&gt;&lt;span&gt;onload&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; base64Data&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; e.target.result;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        // ️ 显示可长按保存的图片&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        modal.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;img&apos;&lt;/span&gt;&lt;span&gt;).src &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; base64Data;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        //  添加长按反馈&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addTouchFeedback&lt;/span&gt;&lt;span&gt;(modal.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;img&apos;&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    reader.&lt;/span&gt;&lt;span&gt;readAsDataURL&lt;/span&gt;&lt;span&gt;(blob);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;用户体验优化&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;专门的微信保存界面&lt;/li&gt;
&lt;li&gt;详细的操作指导&lt;/li&gt;
&lt;li&gt;多种保存方案&lt;/li&gt;
&lt;li&gt;触觉反馈支持&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;UI/UX设计亮点&lt;a href=&quot;#uiux设计亮点&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;h2&gt;1. 现代化视觉设计&lt;a href=&quot;#1-现代化视觉设计&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/*  渐变背景 + 毛玻璃效果 */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    background&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;linear-gradient&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;135&lt;/span&gt;&lt;span&gt;deg&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;#667eea&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;#764ba2&lt;/span&gt;&lt;span&gt; 100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    background-attachment&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;fixed&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.container&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    background&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;rgba&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;255&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;255&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;255&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.95&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    backdrop-filter&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;blur&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;);  &lt;/span&gt;&lt;span&gt;/* 毛玻璃效果 */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    border-radius&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;25&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    box-shadow&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; 25&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt; 50&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt; rgba&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.15&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/*  动态光效 */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.container::before&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    content&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    background&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;linear-gradient&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;90&lt;/span&gt;&lt;span&gt;deg&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;#667eea&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;#764ba2&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;#667eea&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    background-size&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; 100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    animation&lt;/span&gt;&lt;span&gt;: shimmer &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt; ease-in-out&lt;/span&gt;&lt;span&gt; infinite&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2. 交互动画设计&lt;a href=&quot;#2-交互动画设计&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/*  上传区域悬停效果 */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.upload-area:hover&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    transform&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;translateY&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;-5&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    box-shadow&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; 15&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt; 35&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt; rgba&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;102&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;126&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;234&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.2&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/*  移动端触摸反馈 */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.upload-area:active&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    transform&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;scale&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0.98&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    background&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;linear-gradient&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;135&lt;/span&gt;&lt;span&gt;deg&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;#667eea&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;#764ba2&lt;/span&gt;&lt;span&gt; 100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/*  拖拽动画 */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.upload-area.dragover&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    animation&lt;/span&gt;&lt;span&gt;: pulse &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt; ease-in-out&lt;/span&gt;&lt;span&gt; infinite&lt;/span&gt;&lt;span&gt; alternate&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3. 响应式布局&lt;a href=&quot;#3-响应式布局&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/*  移动端优化 */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@media&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;max-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;768&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    .container&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        margin&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        border-radius&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;15&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    /*  触摸目标优化 */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    .convert-btn&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        min-height&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;56&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;  &lt;/span&gt;&lt;span&gt;/* 符合移动端最小触摸目标 */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        -webkit-tap-highlight-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;transparent&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    /*  防止页面缩放 */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    *&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        -webkit-touch-callout&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;none&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        -webkit-user-select&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;none&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;技术难点与解决方案&lt;a href=&quot;#技术难点与解决方案&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;h2&gt;1. 内存管理&lt;a href=&quot;#1-内存管理&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：大量图片处理可能导致内存泄漏&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决方案&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// 及时释放Blob URL&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;setTimeout&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    URL&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;revokeObjectURL&lt;/span&gt;&lt;span&gt;(url);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}, &lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// 清理Canvas引用&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;canvas.width &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; canvas.height &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;canvas &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2. 移动端兼容性&lt;a href=&quot;#2-移动端兼容性&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：不同移动浏览器对下载的支持差异很大&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决方案&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// 多层级降级策略&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; downloadStrategies&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;webShareAPI&apos;&lt;/span&gt;&lt;span&gt;,      &lt;/span&gt;&lt;span&gt;// 现代浏览器&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;directDownload&apos;&lt;/span&gt;&lt;span&gt;,   &lt;/span&gt;&lt;span&gt;// 传统下载&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;dataURLDownload&apos;&lt;/span&gt;&lt;span&gt;,  &lt;/span&gt;&lt;span&gt;// Data URL方案&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;iframeDownload&apos;&lt;/span&gt;&lt;span&gt;,   &lt;/span&gt;&lt;span&gt;// iframe方案&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;saveModal&apos;&lt;/span&gt;&lt;span&gt;         // 最终方案：显示保存界面&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. 文件格式检测&lt;a href=&quot;#3-文件格式检测&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：仅依靠文件扩展名不够可靠&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决方案&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// 多重验证&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; isValidImage&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    // 1. MIME类型检查&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;file.type.&lt;/span&gt;&lt;span&gt;startsWith&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;image/&apos;&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    // 2. 文件头检查（可选）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Promise&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; reader&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; FileReader&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        reader.&lt;/span&gt;&lt;span&gt;onload&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            const&lt;/span&gt;&lt;span&gt; arr&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Uint8Array&lt;/span&gt;&lt;span&gt;(e.target.result).&lt;/span&gt;&lt;span&gt;subarray&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            const&lt;/span&gt;&lt;span&gt; header&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; Array.&lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt;(arr).&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;b&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; b.&lt;/span&gt;&lt;span&gt;toString&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;16&lt;/span&gt;&lt;span&gt;)).&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            // 检查常见图片格式的文件头&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            resolve&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;isImageHeader&lt;/span&gt;&lt;span&gt;(header));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        reader.&lt;/span&gt;&lt;span&gt;readAsArrayBuffer&lt;/span&gt;&lt;span&gt;(file.&lt;/span&gt;&lt;span&gt;slice&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;性能优化策略&lt;a href=&quot;#性能优化策略&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;h2&gt;1. 异步处理&lt;a href=&quot;#1-异步处理&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;//  使用Web Workers处理大图片（可扩展）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; processLargeImage&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; async&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (file.size &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; 10&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; 1024&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; 1024&lt;/span&gt;&lt;span&gt;) { &lt;/span&gt;&lt;span&gt;// 10MB以上&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Promise&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            const&lt;/span&gt;&lt;span&gt; worker&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Worker&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;image-processor.js&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            worker.&lt;/span&gt;&lt;span&gt;postMessage&lt;/span&gt;&lt;span&gt;({ file, options });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            worker.&lt;/span&gt;&lt;span&gt;onmessage&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; resolve&lt;/span&gt;&lt;span&gt;(e.data);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;convertImage&lt;/span&gt;&lt;span&gt;(file);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2. 批量处理优化&lt;a href=&quot;#2-批量处理优化&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;//  分批处理，避免浏览器卡顿&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; batchProcess&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; async&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;files&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;batchSize&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; results&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; [];&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; i &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;; i &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; files.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;; i &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; batchSize) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; batch&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; files.&lt;/span&gt;&lt;span&gt;slice&lt;/span&gt;&lt;span&gt;(i, i &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; batchSize);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; batchResults&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; Promise&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            batch.&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;convertImage&lt;/span&gt;&lt;span&gt;(file))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        results.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;batchResults);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        // 更新进度&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;updateProgress&lt;/span&gt;&lt;span&gt;((i &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; batchSize) &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; files.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; 100&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        // 让出主线程&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        await&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Promise&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; setTimeout&lt;/span&gt;&lt;span&gt;(resolve, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; results;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;跨平台兼容性处理&lt;a href=&quot;#跨平台兼容性处理&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;h2&gt;浏览器特性检测&lt;a href=&quot;#浏览器特性检测&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; featureDetection&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    // 检测Canvas支持&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    canvas: (() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; canvas&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;createElement&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;canvas&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; !!&lt;/span&gt;&lt;span&gt;(canvas.getContext &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; canvas.&lt;/span&gt;&lt;span&gt;getContext&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;2d&apos;&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    })(),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    // 检测File API支持&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    fileAPI: &lt;/span&gt;&lt;span&gt;!!&lt;/span&gt;&lt;span&gt;(window.File &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; window.FileReader &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; window.FileList),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    // 检测Web Share API&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    webShare: &lt;/span&gt;&lt;span&gt;!!&lt;/span&gt;&lt;span&gt;(navigator.share &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; navigator.canShare),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    // 检测Clipboard API&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    clipboard: &lt;/span&gt;&lt;span&gt;!!&lt;/span&gt;&lt;span&gt;(navigator.clipboard &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; navigator.clipboard.write)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;优雅降级&lt;a href=&quot;#优雅降级&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;//  功能降级处理&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;featureDetection.webShare) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    // 使用传统下载方式&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fallbackDownload&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;featureDetection.clipboard) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    // 隐藏复制功能按钮&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    document.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;.copy-btn&apos;&lt;/span&gt;&lt;span&gt;).style.display &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;none&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;移动端体验优化&lt;a href=&quot;#移动端体验优化&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;h2&gt;1. 触摸交互优化&lt;a href=&quot;#1-触摸交互优化&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;//  防止双击缩放&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;document.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;touchstart&apos;&lt;/span&gt;&lt;span&gt;, (&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (e.touches.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        e.&lt;/span&gt;&lt;span&gt;preventDefault&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}, { passive: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//  触摸反馈&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; addTouchFeedback&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;element&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    element.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;touchstart&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        element.style.transform &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;scale(0.95)&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        navigator.vibrate &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; navigator.&lt;/span&gt;&lt;span&gt;vibrate&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;50&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    element.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;touchend&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        element.style.transform &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;scale(1)&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2. 视口优化&lt;a href=&quot;#2-视口优化&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;!--  完美的移动端视口设置 --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;viewport&quot;&lt;/span&gt;&lt;span&gt; content&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;apple-mobile-web-app-capable&quot;&lt;/span&gt;&lt;span&gt; content&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;yes&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;apple-mobile-web-app-status-bar-style&quot;&lt;/span&gt;&lt;span&gt; content&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;black-translucent&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;安全性考虑&lt;a href=&quot;#安全性考虑&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;h2&gt;1. 客户端安全&lt;a href=&quot;#1-客户端安全&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;//  文件类型严格验证&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; validateFile&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; allowedTypes&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&apos;image/png&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;image/jpeg&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;image/webp&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;image/bmp&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;image/gif&apos;&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; maxSize&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 50&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; 1024&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; 1024&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;// 50MB&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;allowedTypes.&lt;/span&gt;&lt;span&gt;includes&lt;/span&gt;&lt;span&gt;(file.type)) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        throw&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;不支持的文件类型&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (file.size &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; maxSize) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        throw&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;文件大小超出限制&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;//  防止XSS攻击&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; sanitizeFileName&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;fileName&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; fileName.&lt;/span&gt;&lt;span&gt;replace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;[&amp;lt;&amp;gt;:&quot;/&lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;span&gt;|?*]&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;g&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;_&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2. 隐私保护&lt;a href=&quot;#2-隐私保护&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;本地处理&lt;/strong&gt;：图片不上传到服务器&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;无数据收集&lt;/strong&gt;：不收集用户任何信息&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;内存清理&lt;/strong&gt;：及时释放图片数据&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;HTTPS部署&lt;/strong&gt;：建议使用HTTPS协议&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;部署与优化&lt;a href=&quot;#部署与优化&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h2&gt;1. 构建优化&lt;a href=&quot;#1-构建优化&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;//  资源压缩配置&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; buildConfig&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    minifyCSS: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    minifyJS: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    optimizeImages: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// 避免影响转换质量&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    gzip: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    cacheControl: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        static: &lt;/span&gt;&lt;span&gt;&apos;1y&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        html: &lt;/span&gt;&lt;span&gt;&apos;1h&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2. CDN部署&lt;a href=&quot;#2-cdn部署&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;!--  CDN加速 --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;preconnect&quot;&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;https://fonts.googleapis.com&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;dns-prefetch&quot;&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;//cdn.jsdelivr.net&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;!--  PWA支持 --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;manifest&quot;&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;/manifest.json&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;theme-color&quot;&lt;/span&gt;&lt;span&gt; content&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;#667eea&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://gitee.com/wac520/pic&quot;&gt;源码：Gitee仓库&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://129.204.12.129:9093/&quot;&gt;图片格式转换器&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;</content:encoded><category>category:掘金同步</category><category>category:前端</category><category>tag:前端</category><category>tag:JavaScript</category><category>tag:HTML</category></item><item><title>总结：一个双非大学生的两年，是怎么过来的？</title><link>https://koharu.ac.ren/post/juejin/%E6%80%BB%E7%BB%93_%E4%B8%80%E4%B8%AA%E5%8F%8C%E9%9D%9E%E5%A4%A7%E5%AD%A6%E7%94%9F%E7%9A%84%E4%B8%A4%E5%B9%B4_%E6%98%AF%E6%80%8E%E4%B9%88%E8%BF%87%E6%9D%A5%E7%9A%84_</link><guid isPermaLink="false">juejin/总结_一个双非大学生的两年_是怎么过来的_</guid><description>总结：一个双非大学生的两年，是怎么过来的？  作者: 前端AC | 原文: https://juejin.cn/post/7522418729440739370  前言 主包高考失利，来到一所双非读大数据，现大二的暑假，想总结一下这两年的大学经历，也是写下我这两年的前端学习经历吧。  初始前端 在进...</description><pubDate>Thu, 03 Jul 2025 07:31:44 GMT</pubDate><content:encoded>&lt;h1&gt;总结：一个双非大学生的两年，是怎么过来的？&lt;a href=&quot;#总结一个双非大学生的两年是怎么过来的&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;作者: 前端AC | 原文: &lt;a href=&quot;https://juejin.cn/post/7522418729440739370&quot;&gt;https://juejin.cn/post/7522418729440739370&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;前言&lt;a href=&quot;#前言&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;主包高考失利，来到一所双非读大数据，现大二的暑假，想总结一下这两年的大学经历，也是写下我这两年的前端学习经历吧。&lt;/p&gt;
&lt;h1&gt;初始前端&lt;a href=&quot;#初始前端&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;在进校园之前，我也是从我助班口中得知，学校有实验室，分为前后端嵌入式人工智能，我对计算机也是比较感兴趣，由于我助班学的也是前端，我也就开启的我的前端校园生活~刚开始也是学着入门口中熟知的前端三件套：Html+css+js，刚开始学前端我，也是保留着高中的记笔记习惯，笑死，也是开始背代码（现在真想抽死自己），还有拿本子记那些th，tr，表格表单这些，在图书馆抄代码，背代码，完全没有去自己手打，以为自己看完，抄下来，代码就是自己的了，可惜现实也是残酷，在实验室笔试的时候，也是有很多记不起来，依稀记得我不会的一个ui列表怎么去除前面的点，当时傻乎乎的写了一个display：none，笑死了。不过也是凭借主播的背诵技巧，也是拿到的笔试的第二，也是顺利的进入了二面，然后也是度过了大一的上半年，发现我大一的上半年，都是图书馆-教室-宿舍三点一线，发现出去玩的机会只有期末考完的一次，真想骂当时的自己怎么不多去走走玩玩，非要死卷绩点干什么。&lt;/p&gt;
&lt;h1&gt;小试前端&lt;a href=&quot;#小试前端&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;大一下的我，也是开始接触到vue框架，刚开始看的也是尚硅谷的vue视频，看完也是以为自己会了，项目也没去练，就敢敢的接下老师的一个小项目，结果师兄分配了一个搜索框的功能，我也能写两个星期，最后还是师兄远程帮我改的代码，那个时候开始，我发现我要自己手打代码，边看边理解，而不是纯看。后面参加了一个学校的比赛，从那次比赛开始，我也是亲自知道前端的魅力，是做一个大数据可视化，可视化这种是比较炫酷的，当时也是经过两个星期的努力学习，问ai，问师兄，也是不慌不忙的如期完成，拿到了二等奖，从这时候开始，我领悟到了代码是如何运作，一个框架是怎么搭的，也算一次成长，所以我想告诉师弟师妹们，不要等准备好了再去做，要边做边完善，中国人就是一直在等，等我学完，等我干嘛干嘛的，一直不敢去尝试，要一直试错，以后遇见一模一样的bug才会更高效的解决。正如我刚开始会的一个bug，就是按照package包的问题，当时npm i 也会报错，才发现忘记进入文件夹，忘记删除modules缓存，忘记删除package-lock.json…现在遇到一样的问题立马知道怎么解决了。后面也是实验室二面拿下面试A+第一的成绩，顺利进入实验室学习啦。但是，经过一学期的相处，老师开始分配项目，可惜遇到了一个人品不好的后端，写到后面代码删库跑路，也是个人才，朱波的被迫去学node，MySQL自己搭建了一个小的后台服务，后面也是开始在b站刷到了小王老师的视频，跟着练起来，进群讨论，也是认识了一些好网友。朱波的大一暑假生活前端暂缓一步。&lt;/p&gt;
&lt;h1&gt;新手前端&lt;a href=&quot;#新手前端&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;大二开学，就被老师叫去重构一个项目，也算是一个学校的机会，给一个乡村做一个宣传网站，当时我还是笨蛋，ai也没有那么发达，不过我脑子还是灵活，脑子也是开始想着怎么布局，用什么技术，当时就使用了vue+three+echarts+饿了么-ui来做，最后也是花了一个月搞定了，看起来还不错，不过当时还是一个新手，还没怎么做过前后端的项目，都是纯前端，所以我渴望有一个后端能给我接口，www。后面发现在这个实验室没有得到鉴赏，立马跳槽去别的老师的实验室当leadership，把我从这个实验室师兄教的知识，运用到我的团队中去，然后大二的上学期也是一个学习的阶段，然后学期末也是一如既往的写简历，当时好像是2.0版本，大一一个版本，说到简历，大一傻傻的把部门也加进去，然后去boss投，发现也是没人理，偶尔会有几个笔试刷kpi，面试几乎是0的，当时也是不懂，傻的一批。后面去请教了师兄们，也是修修补补了，写了三个项目进去，在大二寒假的一次偶然，约到了人生的第一场面试。&lt;/p&gt;
&lt;h1&gt;处女面&lt;a href=&quot;#处女面&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;面试约在四天后，当时也是既紧张又兴奋又害怕，兴奋是终于有人约我，害怕是我技术不牢，大多是问ai的，在跟老师沟通了之后，也是决定试一试，反正也是当锻炼了，然后就开启了我的面试之路：
1-csdn搜面经
2-搜vue3vue2的知识，比如常问的八股文：生命周期，组件通信，响应式原理，vue2Vu3对比…
3-牛客搜面经
4-默写，背诵
5-搜自我介绍怎么说
6-搜怎么反问面试官：薪资，公司业务，多久入职等
做了这些准备之后，也是出发，开始的时候，也没告诉我要笔试啊，当时给我整懵逼了，结果笔试结果也是不好，依稀记得有什么扁平化，性能优化，算法题等等，好像也是结果不乐观，基本很难做得出来，然后也是开始面试了，也是问的一些vue的八股，但是还是回答的不好，最后结果也是毫无音讯被刷kpi了哈哈，不过经过这一次之后，我也不在畏惧面试了，也知道面试的具体流程是什么样了。虽然没进，但是也是学到东西了哈哈，不过朱波也是重新努力，继续深耕。&lt;/p&gt;
&lt;h1&gt;一点突破&lt;a href=&quot;#一点突破&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;在大二下，我着急的跟导师要项目，为的就是大二暑期能够攒够项目去实习，所以导师也是分配了一个水产养殖相关的科普项目给我，我也是委以重任，担任着产品经理、UI、前端、算法的职位，我也是学习着公司的项目流程，从选用技术栈，分析项目需求，设计原型图，前后端接口文档，开发项目，服务器部署这一流程来，我很认真的对待这一次项目，所以我很认真的配置环境，配置路由，使用flex布局先布置好页面，但是最后漏了一个严重的错误，没有大屏适配，使用的px写死的位置，这个是我犯的一个大错。后续也是写好页面，自己训练YOLO模型，使用flask框架配置好api然后前端调用，后端那边接口也是正常使用，最后也是大功告成啦，后面也是拿这个项目打了很多比赛，也是获得很多奖项。在4月份初，我又学习如何写简历，把简历完善到6.0版本，也是开始继续我的投简历生涯，在四月份，我陆续收到了三家公司的面试，在第一家是在越秀，当时是我的第二次面试，也是有点小紧张，但是朱波也是准备了差不多80%，但是居然有一些居然忘记了，真的可恶啊，不过我也不喜欢这个公司（在旁边听到老板pua员工哈哈）不过面试官也是好好给我回答，我也是记起来最后自己总结成面经发在自己博客了。在第三家呢？由于有前两次面试的准备，我大概知道了小公司的面试套路，在面试过程中也是越来越自信了，后面也是面试官问到了我如何到岗，通勤时间，具体业务什么的，我就知道第三次稳了！不过在第三次结果出来之前，过两天我又去面试了一家教育公司，也是我现在正在实习的公司，面试官看到我的简历，没问什么八股，就是看到我的技术栈（three，动画）刚好符合他们公司的业务需求，然后我的成绩也是第一，奖项也很多，所以面试官问我要什么好问他的，我也是从公司业务，公司项目运行流程，技术栈问起…也是正常流程吧，后面就是两个hr来为问我的薪资要求，我肯定是越多越好啊，还问我会不会觉得他们公司的技术太普通了会不会接受不了哈哈，后面也是问了到岗时间等等，也是很愉快一次，毕竟一周至少到岗三天，对于一个大二有课的算是接纳了。最后也是拿到了第三家第四家的offer，最后结合通勤这个方面，我选择了第四家哈哈，毕竟第三家一周五天。&lt;/p&gt;
&lt;h1&gt;第一次实习体验&lt;a href=&quot;#第一次实习体验&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;人嘛，只有在入职和离职的时候是最开心的，所以在入职的前几天，也是熟悉一下公司代码，还有认识一下组长还有周围的同事，业务其实还好，不难，熟悉代码之后很快就能完成，公司也有免费午餐（食堂），也是不错的，其他三个实习生也是211的，第一天也是给我上院校压力了，就我一个双非，我也不好意思逼逼哈哈，只能默默工作然后摸鱼摸鱼。上班了才发现，厕所真的是摸鱼的好地方啊，只要进了公司一步，阳气就被吸光了一样，虽然一天没干什么累活，就是很累哈哈，每次下班头都很晕。实习了两个月之后，发现实习其实也一般哈哈，没那么可怕。所以说，只有自己经历过了，后面发现这些其实都是小事，人，过的开心就好。&lt;/p&gt;
&lt;h1&gt;总结&lt;a href=&quot;#总结&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;现在也是大二暑假了，也是在考虑大三是否考研，还是冲击大厂，不过最近投了大厂，也没约到机会，所以我也在学react，发现react不习惯哈哈，感觉还是vue好，不过没办法，还是要硬着头皮学下去，争取把项目完成。我的两年大学生活，好像就是这样子，时常感叹大学好累不如高中，但是看到自己获得荣誉之后，好像也是值得的，所以，加油吧孩子。做你自己！&lt;/p&gt;</content:encoded><category>category:掘金同步</category><category>category:前端</category><category>tag:前端</category><category>tag:面试</category></item><item><title>Webpack模板热更新实现</title><link>https://koharu.ac.ren/post/juejin/webpack%E6%A8%A1%E6%9D%BF%E7%83%AD%E6%9B%B4%E6%96%B0%E5%AE%9E%E7%8E%B0</link><guid isPermaLink="false">juejin/webpack模板热更新实现</guid><description>Webpack模板热更新实现  作者: 前端AC | 原文: https://juejin.cn/post/7517474788174348325  概述 本文档详细介绍了为  生成工具项目实现模板热更新功能的完整过程，包括技术选型、实现思路、遇到的问题及解决方案。  1. 项目背景分析  1.1 ...</description><pubDate>Fri, 20 Jun 2025 00:20:19 GMT</pubDate><content:encoded>&lt;h1&gt;Webpack模板热更新实现&lt;a href=&quot;#webpack模板热更新实现&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;作者: 前端AC | 原文: &lt;a href=&quot;https://juejin.cn/post/7517474788174348325&quot;&gt;https://juejin.cn/post/7517474788174348325&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;概述&lt;a href=&quot;#概述&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;本文档详细介绍了为  生成工具项目实现模板热更新功能的完整过程，包括技术选型、实现思路、遇到的问题及解决方案。&lt;/p&gt;
&lt;h2&gt;1. 项目背景分析&lt;a href=&quot;#1-项目背景分析&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;1.1 项目结构&lt;a href=&quot;#11-项目结构&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;Platform-Web-ExercisesGenerator/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── app/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── exercisesgenerator/     # 主应用（Vue.js）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   └── template/               # 模板源码（ES6）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│       ├── templateFun/        # 模板实现文件&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│       ├── common/            # 公共组件&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│       └── utils/             # 工具函数&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── public/template/           # 构建后的模板文件&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;└── script/                    # 构建脚本&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1.2 现有构建流程&lt;a href=&quot;#12-现有构建流程&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;原有的模板构建需要手动执行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;yarn&lt;/span&gt;&lt;span&gt; generateFile&lt;/span&gt;&lt;span&gt;    # 生成模板列表缓存&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;yarn&lt;/span&gt;&lt;span&gt; build:template&lt;/span&gt;&lt;span&gt;  # 构建模板&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2. 技术选型&lt;a href=&quot;#2-技术选型&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;2.1 核心依赖&lt;a href=&quot;#21-核心依赖&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;h4&gt;chokidar - 文件监听库&lt;a href=&quot;#chokidar---文件监听库&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&quot;chokidar&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;^3.5.3&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;选择理由：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;跨平台兼容性好（Windows/macOS/Linux）&lt;/li&gt;
&lt;li&gt;性能优秀，支持大量文件监听&lt;/li&gt;
&lt;li&gt;API 简洁，配置灵活&lt;/li&gt;
&lt;li&gt;支持忽略模式，避免监听不必要的文件&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;chalk - 终端彩色输出&lt;a href=&quot;#chalk---终端彩色输出&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; chalk&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;chalk&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(chalk.&lt;/span&gt;&lt;span&gt;green&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;✅ 构建成功&apos;&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(chalk.&lt;/span&gt;&lt;span&gt;red&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;❌ 构建失败&apos;&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;child_process - 子进程管理&lt;a href=&quot;#child_process---子进程管理&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;spawn&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;child_process&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.2 技术架构&lt;a href=&quot;#22-技术架构&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;文件变化 → chokidar监听 → 防抖处理 → 两阶段构建 → 结果反馈&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. 实现过程详解&lt;a href=&quot;#3-实现过程详解&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;3.1 创建监听脚本&lt;a href=&quot;#31-创建监听脚本&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;h4&gt;文件位置&lt;a href=&quot;#文件位置&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;script/bin/watch-template.js&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;核心实现&lt;a href=&quot;#核心实现&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const chokidar = require(&apos;chokidar&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const { spawn } = require(&apos;child_process&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const path = require(&apos;path&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const chalk = require(&apos;chalk&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// 监听目录配置&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const watchDir = path.resolve(__dirname, &apos;../../app/template&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const DEBOUNCE_DELAY = 1000; // 防抖延迟&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// 状态管理&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;let buildTimeout = null;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;let isBuilding = false;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.2 防抖机制实现&lt;a href=&quot;#32-防抖机制实现&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;为什么需要防抖？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;避免频繁触发构建（如保存时可能触发多次文件变化事件）&lt;/li&gt;
&lt;li&gt;提升性能，减少不必要的构建&lt;/li&gt;
&lt;li&gt;提供更好的用户体验&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; debouncedBuild&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (buildTimeout) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    clearTimeout&lt;/span&gt;&lt;span&gt;(buildTimeout);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  buildTimeout &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; setTimeout&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    buildTemplate&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }, &lt;/span&gt;&lt;span&gt;DEBOUNCE_DELAY&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.3 文件监听配置&lt;a href=&quot;#33-文件监听配置&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; watcher&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; chokidar.&lt;/span&gt;&lt;span&gt;watch&lt;/span&gt;&lt;span&gt;(watchDir, {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ignored: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;**/node_modules/**&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;**/.git/**&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;**/dist/**&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;**/build/**&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;**/.cache/**&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;**/templateFun/**&apos;&lt;/span&gt;&lt;span&gt; // 排除构建产物&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  persistent: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ignoreInitial: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;配置说明：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ignored&lt;/code&gt;: 排除不需要监听的目录&lt;/li&gt;
&lt;li&gt;&lt;code&gt;persistent&lt;/code&gt;: 保持监听进程运行&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ignoreInitial&lt;/code&gt;: 忽略初始扫描，只监听后续变化&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.4 两阶段构建实现&lt;a href=&quot;#34-两阶段构建实现&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;问题发现：&lt;/strong&gt;
直接执行 &lt;code&gt;yarn build:template&lt;/code&gt; 会因为缺少缓存文件而失败。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决方案：&lt;/strong&gt;
实现两阶段构建流程：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; buildTemplate&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 第一阶段：生成缓存文件&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; generateProcess&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; spawn&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;yarn&apos;&lt;/span&gt;&lt;span&gt;, [&lt;/span&gt;&lt;span&gt;&apos;generateFile&apos;&lt;/span&gt;&lt;span&gt;], {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    cwd: path.&lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt;(__dirname, &lt;/span&gt;&lt;span&gt;&apos;../..&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    stdio: &lt;/span&gt;&lt;span&gt;&apos;pipe&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    shell: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  generateProcess.&lt;/span&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;close&apos;&lt;/span&gt;&lt;span&gt;, (&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (code &lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(chalk.&lt;/span&gt;&lt;span&gt;red&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`❌ 生成文件失败! 退出码: ${&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;}`&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    // 第二阶段：构建模板&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; buildProcess&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; spawn&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;yarn&apos;&lt;/span&gt;&lt;span&gt;, [&lt;/span&gt;&lt;span&gt;&apos;build:template&apos;&lt;/span&gt;&lt;span&gt;], {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      cwd: path.&lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt;(__dirname, &lt;/span&gt;&lt;span&gt;&apos;../..&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      stdio: &lt;/span&gt;&lt;span&gt;&apos;inherit&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      shell: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    // 处理构建结果...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;4. 遇到的问题及解决方案&lt;a href=&quot;#4-遇到的问题及解决方案&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;4.1 工作目录路径错误&lt;a href=&quot;#41-工作目录路径错误&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;问题现象：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;npm ERR! enoent ENOENT: no such file or directory, open &apos;E:\ACwork\Allcode\package.json&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;原因分析：&lt;/strong&gt;
脚本位于 &lt;code&gt;script/bin/&lt;/code&gt; 目录，使用 &lt;code&gt;../../../&lt;/code&gt; 会指向错误的父目录。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决方案：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// 错误的路径&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;cwd&lt;/span&gt;&lt;span&gt;: path.&lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt;(__dirname, &lt;/span&gt;&lt;span&gt;&apos;../../..&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// 正确的路径&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;cwd&lt;/span&gt;&lt;span&gt;: path.&lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt;(__dirname, &lt;/span&gt;&lt;span&gt;&apos;../..&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.2 模板列表缓存文件缺失&lt;a href=&quot;#42-模板列表缓存文件缺失&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;问题现象：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;Error: Cannot resolve module &apos;../../app/template/.cache/templateList&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;原因分析：&lt;/strong&gt;
webpack 配置依赖 &lt;code&gt;templateList.js&lt;/code&gt; 文件，但该文件需要通过 &lt;code&gt;generateFile&lt;/code&gt; 命令生成。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决方案：&lt;/strong&gt;
在构建前自动执行文件生成：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// 先生成缓存文件&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;spawn&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;yarn&apos;&lt;/span&gt;&lt;span&gt;, [&lt;/span&gt;&lt;span&gt;&apos;generateFile&apos;&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// 再执行模板构建&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;spawn&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;yarn&apos;&lt;/span&gt;&lt;span&gt;, [&lt;/span&gt;&lt;span&gt;&apos;build:template&apos;&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.3 npm vs yarn 命令兼容性&lt;a href=&quot;#43-npm-vs-yarn-命令兼容性&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;问题：&lt;/strong&gt;
初始使用 &lt;code&gt;npm run build:template&lt;/code&gt;，在某些环境下可能出现兼容性问题。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决：&lt;/strong&gt;
统一使用项目配置的包管理器 &lt;code&gt;yarn&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;spawn&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;yarn&apos;&lt;/span&gt;&lt;span&gt;, [&lt;/span&gt;&lt;span&gt;&apos;build:template&apos;&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;5. 核心技术要点&lt;a href=&quot;#5-核心技术要点&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;5.1 子进程管理&lt;a href=&quot;#51-子进程管理&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; buildProcess&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; spawn&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;yarn&apos;&lt;/span&gt;&lt;span&gt;, [&lt;/span&gt;&lt;span&gt;&apos;build:template&apos;&lt;/span&gt;&lt;span&gt;], {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  cwd: path.&lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt;(__dirname, &lt;/span&gt;&lt;span&gt;&apos;../..&apos;&lt;/span&gt;&lt;span&gt;),  &lt;/span&gt;&lt;span&gt;// 工作目录&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  stdio: &lt;/span&gt;&lt;span&gt;&apos;inherit&apos;&lt;/span&gt;&lt;span&gt;,                       &lt;/span&gt;&lt;span&gt;// 继承父进程的输入输出&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  shell: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;                             // 使用系统shell&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;参数说明：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cwd&lt;/code&gt;: 指定命令执行的工作目录&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stdio&lt;/code&gt;: 控制子进程的输入输出流&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shell&lt;/code&gt;: 在shell中执行命令（Windows兼容性）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5.2 事件监听模式&lt;a href=&quot;#52-事件监听模式&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;watcher&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .&lt;/span&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;change&apos;&lt;/span&gt;&lt;span&gt;, (&lt;/span&gt;&lt;span&gt;filePath&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(chalk.&lt;/span&gt;&lt;span&gt;yellow&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;` 文件已修改: ${&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;relative&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;watchDir&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;filePath&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}`&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    debouncedBuild&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .&lt;/span&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;add&apos;&lt;/span&gt;&lt;span&gt;, (&lt;/span&gt;&lt;span&gt;filePath&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(chalk.&lt;/span&gt;&lt;span&gt;green&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`➕ 文件已添加: ${&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;relative&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;watchDir&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;filePath&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}`&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    debouncedBuild&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .&lt;/span&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;unlink&apos;&lt;/span&gt;&lt;span&gt;, (&lt;/span&gt;&lt;span&gt;filePath&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(chalk.&lt;/span&gt;&lt;span&gt;red&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`️ 文件已删除: ${&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;relative&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;watchDir&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;filePath&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}`&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    debouncedBuild&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.3 优雅退出处理&lt;a href=&quot;#53-优雅退出处理&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;process.&lt;/span&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;SIGINT&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(chalk.&lt;/span&gt;&lt;span&gt;cyan&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt; 正在关闭文件监听...&apos;&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  watcher.&lt;/span&gt;&lt;span&gt;close&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(chalk.&lt;/span&gt;&lt;span&gt;cyan&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;✅ 文件监听已关闭&apos;&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    process.&lt;/span&gt;&lt;span&gt;exit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;6. 配置文件修改&lt;a href=&quot;#6-配置文件修改&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;6.1 package.json 脚本添加&lt;a href=&quot;#61-packagejson-脚本添加&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;scripts&quot;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;watch:template&quot;: &quot;node script/bin/watch-template.js&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;devDependencies&quot;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;chokidar&quot;: &quot;^3.5.3&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.2 使用说明文档&lt;a href=&quot;#62-使用说明文档&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;创建了详细的使用说明文档 &lt;code&gt;TEMPLATE_HOT_RELOAD.md&lt;/code&gt;，包含：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;功能介绍&lt;/li&gt;
&lt;li&gt;安装步骤&lt;/li&gt;
&lt;li&gt;使用方法&lt;/li&gt;
&lt;li&gt;故障排除&lt;/li&gt;
&lt;li&gt;开发建议&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;7. 性能优化技巧&lt;a href=&quot;#7-性能优化技巧&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;7.1 文件过滤策略&lt;a href=&quot;#71-文件过滤策略&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;ignored&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &apos;**/node_modules/**&apos;&lt;/span&gt;&lt;span&gt;,    &lt;/span&gt;&lt;span&gt;// 排除依赖包&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &apos;**/.git/**&apos;&lt;/span&gt;&lt;span&gt;,           &lt;/span&gt;&lt;span&gt;// 排除版本控制&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &apos;**/dist/**&apos;&lt;/span&gt;&lt;span&gt;,           &lt;/span&gt;&lt;span&gt;// 排除构建产物&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &apos;**/build/**&apos;&lt;/span&gt;&lt;span&gt;,          &lt;/span&gt;&lt;span&gt;// 排除构建目录&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &apos;**/.cache/**&apos;&lt;/span&gt;&lt;span&gt;,         &lt;/span&gt;&lt;span&gt;// 排除缓存文件&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &apos;**/templateFun/**&apos;&lt;/span&gt;&lt;span&gt;     // 排除模板构建产物&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7.2 并发控制&lt;a href=&quot;#72-并发控制&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; isBuilding &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; buildTemplate&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (isBuilding) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(chalk.&lt;/span&gt;&lt;span&gt;yellow&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;构建正在进行中，跳过本次构建...&apos;&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  isBuilding &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 构建逻辑...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7.3 输出优化&lt;a href=&quot;#73-输出优化&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// 生成文件时使用 pipe 模式，减少输出干扰&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;stdio&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;pipe&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// 构建时使用 inherit 模式，显示详细进度&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;stdio&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;inherit&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;8. 扩展建议&lt;a href=&quot;#8-扩展建议&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;8.1 可配置化&lt;a href=&quot;#81-可配置化&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// 可以添加配置文件支持&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; config&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  watchDir: &lt;/span&gt;&lt;span&gt;&apos;./app/template&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  debounceDelay: &lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ignored: [&lt;/span&gt;&lt;span&gt;&apos;**/node_modules/**&apos;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  buildCommand: &lt;/span&gt;&lt;span&gt;&apos;yarn build:template&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8.2 增量构建&lt;a href=&quot;#82-增量构建&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// 可以根据变化的文件类型决定构建策略&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (filePath.&lt;/span&gt;&lt;span&gt;includes&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;templateFun/&apos;&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 只构建特定模板&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 全量构建&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8.3 通知集成&lt;a href=&quot;#83-通知集成&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// 可以集成系统通知&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; notifier&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;node-notifier&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;notifier.&lt;/span&gt;&lt;span&gt;notify&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  title: &lt;/span&gt;&lt;span&gt;&apos;模板构建&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  message: &lt;/span&gt;&lt;span&gt;&apos;构建完成！&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;9. 总结&lt;a href=&quot;#9-总结&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;通过实现模板热更新功能，我们：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;提升了开发效率&lt;/strong&gt;：无需手动执行构建命令&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;改善了开发体验&lt;/strong&gt;：实时反馈，彩色日志输出&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;增强了系统稳定性&lt;/strong&gt;：完善的错误处理和状态管理&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;保证了构建可靠性&lt;/strong&gt;：两阶段构建确保依赖完整&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这个实现展示了如何使用 Node.js 生态系统中的工具来解决实际开发问题，是一个很好的工程化实践案例。&lt;/p&gt;</content:encoded><category>category:掘金同步</category><category>category:Webpack</category><category>tag:Webpack</category></item></channel></rss>