<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Angular的坑与最佳实践 on 雪狼的书斋</title>
    <link>/angular/angular%E7%9A%84%E5%9D%91%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/</link>
    <description>Recent content in Angular的坑与最佳实践 on 雪狼的书斋</description>
    <generator>Hugo</generator>
    <language>zh-hans</language>
    <atom:link href="/angular/angular%E7%9A%84%E5%9D%91%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>1.Angular避坑指南：这些雷区，我替你先踩了！</title>
      <link>/angular/angular%E7%9A%84%E5%9D%91%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/010-angular%E9%81%BF%E5%9D%91%E6%8C%87%E5%8D%97%E8%BF%99%E4%BA%9B%E9%9B%B7%E5%8C%BA%E6%88%91%E6%9B%BF%E4%BD%A0%E5%85%88%E8%B8%A9%E4%BA%86/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/angular/angular%E7%9A%84%E5%9D%91%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/010-angular%E9%81%BF%E5%9D%91%E6%8C%87%E5%8D%97%E8%BF%99%E4%BA%9B%E9%9B%B7%E5%8C%BA%E6%88%91%E6%9B%BF%E4%BD%A0%E5%85%88%E8%B8%A9%E4%BA%86/</guid>
      <description>&lt;h2 id=&#34;any编程世界里的万金油还是定时炸弹&#34;&gt;&lt;code&gt;any&lt;/code&gt;：编程世界里的「万金油」还是「定时炸弹」？&lt;a class=&#34;anchor&#34; href=&#34;#any%e7%bc%96%e7%a8%8b%e4%b8%96%e7%95%8c%e9%87%8c%e7%9a%84%e4%b8%87%e9%87%91%e6%b2%b9%e8%bf%98%e6%98%af%e5%ae%9a%e6%97%b6%e7%82%b8%e5%bc%b9&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;挖坑现场&lt;/strong&gt;：产品经理火急火燎地跑来：「这个接口联调怎么报错？下午就要上线了！」 你一看，后端返回的数据结构跟文档里说好的不一样，TypeScript 编译器正在疯狂抗议。你眉头一皱，计上心来，大手一挥，给那个接收数据的变量安上了一个 &lt;code&gt;any&lt;/code&gt; 类型。编译器立刻闭嘴，世界清静了，你长舒一口气，提交代码，发布上线。事了拂衣去，深藏功与名。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;核爆瞬间&lt;/strong&gt;：一个月后，用户反馈某个页面在特定情况下会白屏。你查了半天，发现在某个不起眼的角落，一个 &lt;code&gt;user.profile.avatar&lt;/code&gt; 的调用抛出了 &lt;code&gt;TypeError: Cannot read property &#39;profile&#39; of undefined&lt;/code&gt; 的错误。你百思不得其解，&lt;code&gt;user&lt;/code&gt; 怎么可能没有 &lt;code&gt;profile&lt;/code&gt; 呢？&lt;/p&gt;&#xA;&lt;p&gt;经过数小时的艰苦排查，你终于定位到，当初那个被你 &lt;code&gt;any&lt;/code&gt; 掉的接口，在某种条件下返回的 &lt;code&gt;user&lt;/code&gt; 对象，真的就只是一个 &lt;code&gt;{ id: 1, name: &#39;Guest&#39; }&lt;/code&gt;，根本没有 &lt;code&gt;profile&lt;/code&gt; 字段。而 &lt;code&gt;any&lt;/code&gt; 这位「损友」，当时为你摆平了编译器，却也为你埋下了一颗不知何时会爆炸的定时炸弹。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;./angular_pits_images/any_as_bandaid.jpg&#34; alt=&#34;文生图：一个程序员得意洋洋地把一个写着“any”的巨大创可贴，贴在一个正在喷涌红色警告代码的火山上。火山下面，岩浆正在悄悄流向一座城市（生产环境）。风格：讽刺漫画。&#34; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;血泪教训&lt;/strong&gt;：&lt;strong&gt;&lt;code&gt;any&lt;/code&gt; 是与魔鬼的交易，你用一时的便利，换来的是未来的灾难。&lt;/strong&gt; 它废掉了 TypeScript 的武功，让你回到了裸奔的 JavaScript 时代。如果实在无法确定类型，请使用 &lt;code&gt;unknown&lt;/code&gt;，它会强制你在使用前进行类型检查，这才是安全之道。&lt;/p&gt;&#xA;&lt;h2 id=&#34;你的-subscribe-有超度机制吗警惕那些有去无回的订阅僵尸&#34;&gt;你的 &lt;code&gt;subscribe&lt;/code&gt; 有「超度」机制吗？警惕那些有去无回的「订阅僵尸」！&lt;a class=&#34;anchor&#34; href=&#34;#%e4%bd%a0%e7%9a%84-subscribe-%e6%9c%89%e8%b6%85%e5%ba%a6%e6%9c%ba%e5%88%b6%e5%90%97%e8%ad%a6%e6%83%95%e9%82%a3%e4%ba%9b%e6%9c%89%e5%8e%bb%e6%97%a0%e5%9b%9e%e7%9a%84%e8%ae%a2%e9%98%85%e5%83%b5%e5%b0%b8&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;挖坑现场&lt;/strong&gt;：你在一个组件的 &lt;code&gt;ngOnInit&lt;/code&gt; 里，订阅了一个来自服务的数据流，用来实时更新页面信息。&lt;code&gt;this.dataService.getRealTimeData().subscribe(data =&amp;gt; this.info = data);&lt;/code&gt; 一行代码，如此优雅，完美运行。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;核爆瞬间&lt;/strong&gt;：用户在这个页面和其它页面之间反复横跳了几次后，你发现浏览器开始变得卡顿，风扇狂转。打开控制台的性能监视器，内存占用像坐了火箭一样往上涨。你惊恐地发现，之前 &lt;code&gt;subscribe&lt;/code&gt; 里的那个 &lt;code&gt;console.log&lt;/code&gt;，现在每次会打印出 10 次、20 次、50 次……&lt;/p&gt;&#xA;&lt;p&gt;你亲手制造了一支「订阅僵尸」大军！每当组件被创建时，一个新的订阅就被建立；但当组件被销毁（&lt;code&gt;ngOnDestroy&lt;/code&gt;）时，你忘了「超度」这些亡魂（&lt;code&gt;unsubscribe&lt;/code&gt;）。它们留在了内存里，继续接收着数据，造成了经典的内存泄漏。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;./angular_pits_images/zombie_subscription.jpg&#34; alt=&#34;文生图：一个墓地，每个墓碑上都写着“Component”，墓碑前有一个“僵尸”的手（代表未取消的订阅）破土而出，手上还抓着一条数据流。远处的地平线上，内存使用率的曲线像心电图一样疯狂跳动。风格：哥特式、略带恐怖的卡通。&#34; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;血泪教训&lt;/strong&gt;：&lt;strong&gt;每一个 &lt;code&gt;subscribe&lt;/code&gt;，都必须有一个对应的 &lt;code&gt;unsubscribe&lt;/code&gt;。&lt;/strong&gt; 这是响应式编程的铁律。当然，我们有更优雅的「超度」方式：&lt;/p&gt;</description>
    </item>
    <item>
      <title>2.Angular性能优化：避开这些“坑”，你的应用才能飞起来！</title>
      <link>/angular/angular%E7%9A%84%E5%9D%91%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/020-angular%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E9%81%BF%E5%BC%80%E8%BF%99%E4%BA%9B%E5%9D%91%E4%BD%A0%E7%9A%84%E5%BA%94%E7%94%A8%E6%89%8D%E8%83%BD%E9%A3%9E%E8%B5%B7%E6%9D%A5/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/angular/angular%E7%9A%84%E5%9D%91%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/020-angular%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E9%81%BF%E5%BC%80%E8%BF%99%E4%BA%9B%E5%9D%91%E4%BD%A0%E7%9A%84%E5%BA%94%E7%94%A8%E6%89%8D%E8%83%BD%E9%A3%9E%E8%B5%B7%E6%9D%A5/</guid>
      <description>&lt;h2 id=&#34;默认变更检测是效率助手还是焦虑过度的驾驶员&#34;&gt;默认变更检测：是效率助手，还是「焦虑过度」的驾驶员？&lt;a class=&#34;anchor&#34; href=&#34;#%e9%bb%98%e8%ae%a4%e5%8f%98%e6%9b%b4%e6%a3%80%e6%b5%8b%e6%98%af%e6%95%88%e7%8e%87%e5%8a%a9%e6%89%8b%e8%bf%98%e6%98%af%e7%84%a6%e8%99%91%e8%bf%87%e5%ba%a6%e7%9a%84%e9%a9%be%e9%a9%b6%e5%91%98&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;挖坑现场&lt;/strong&gt;：你的应用刚起步，组件不多，一切都如丝般顺滑。你自然不会去关心「变更检测策略」这种「高级」玩意儿。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;坠坑表现&lt;/strong&gt;：随着应用日益复杂，组件数量上百，页面上还有一些实时数据在跳动。你渐渐发现，哪怕只是在输入框里打个字，整个应用都会感到一丝丝的「粘滞感」。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;病因分析&lt;/strong&gt;：这是 Angular 默认的、基于 Zone.js 的变更检测策略（&lt;code&gt;CheckAlways&lt;/code&gt;）的「锅」。它就像一个「焦虑过度」的新手司机，路上任何一点风吹草动（任何异步事件），都会让他把车里所有的仪表盘、后视镜、指示灯全部重新检查一遍。当「仪表盘」（组件）成百上千时，这一遍检查下来，时间就耗费在路上了。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;逃生路线&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;传统解法：&lt;code&gt;OnPush&lt;/code&gt; 策略&lt;/strong&gt;。将组件的变更检测策略设为 &lt;code&gt;OnPush&lt;/code&gt;，并配合不可变数据和 &lt;code&gt;async&lt;/code&gt; 管道使用。这能极大地减少不必要的检查，是 Zone.js 时代最核心的性能优化手段。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;现代解法：拥抱 Signals&lt;/strong&gt;。在新的 Signal-based 组件中，这个「天坑」被从根本上填平了。Signal 的更新是细粒度的，它会直接通知依赖它的视图进行更新，完全绕开了「从上到下」的全局检查。可以说，&lt;strong&gt;Signals 是比 &lt;code&gt;OnPush&lt;/code&gt; 更彻底、更自然的性能优化方案&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;99的情况下你都不需要-ngdocheck警惕这颗大力出奇迹的恐慌按钮&#34;&gt;99%的情况下，你都不需要 &lt;code&gt;ngDoCheck&lt;/code&gt;？警惕这颗「大力出奇迹」的恐慌按钮！&lt;a class=&#34;anchor&#34; href=&#34;#99%e7%9a%84%e6%83%85%e5%86%b5%e4%b8%8b%e4%bd%a0%e9%83%bd%e4%b8%8d%e9%9c%80%e8%a6%81-ngdocheck%e8%ad%a6%e6%83%95%e8%bf%99%e9%a2%97%e5%a4%a7%e5%8a%9b%e5%87%ba%e5%a5%87%e8%bf%b9%e7%9a%84%e6%81%90%e6%85%8c%e6%8c%89%e9%92%ae&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;挖坑现场&lt;/strong&gt;：你用了 &lt;code&gt;OnPush&lt;/code&gt;，但发现当传入对象（&lt;code&gt;@Input()&lt;/code&gt;）的内部属性变化时，视图不更新了。情急之下，你翻到了 &lt;code&gt;ngDoCheck&lt;/code&gt; 这个生命周期钩子，在里面手动比对新旧值的差异，然后手动触发更新。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;坠坑表现&lt;/strong&gt;：&lt;code&gt;ngDoCheck&lt;/code&gt; 里的代码被执行的频率高到令人发指！它在&lt;strong&gt;每一次&lt;/strong&gt;变更检测周期中都会被调用，无论你的组件输入是否真的变化。如果你在里面放了复杂的深比对逻辑，其性能损耗甚至比默认策略还要恐怖！&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;逃生路线&lt;/strong&gt;：&lt;strong&gt;99% 的情况下，你都不需要 &lt;code&gt;ngDoCheck&lt;/code&gt;。&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;在 &lt;code&gt;OnPush&lt;/code&gt; 的世界里，正确的做法是&lt;strong&gt;使用不可变数据&lt;/strong&gt;。当数据变化时，创建一个新的对象引用。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;在现代的 Signal-based 组件中，这个问题很大程度上自然消失了。因为你可以直接更新嵌套在 &lt;code&gt;signal&lt;/code&gt; 中的状态，而 &lt;code&gt;computed&lt;/code&gt; 信号能够智能地只在真正依赖的值变化时才重新计算，无需手动比对。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;你的-for-循环有身份证吗别让脸盲的健忘症保安拖慢你的列表&#34;&gt;你的 &lt;code&gt;@for&lt;/code&gt; 循环有「身份证」吗？别让「脸盲」的健忘症保安拖慢你的列表！&lt;a class=&#34;anchor&#34; href=&#34;#%e4%bd%a0%e7%9a%84-for-%e5%be%aa%e7%8e%af%e6%9c%89%e8%ba%ab%e4%bb%bd%e8%af%81%e5%90%97%e5%88%ab%e8%ae%a9%e8%84%b8%e7%9b%b2%e7%9a%84%e5%81%a5%e5%bf%98%e7%97%87%e4%bf%9d%e5%ae%89%e6%8b%96%e6%85%a2%e4%bd%a0%e7%9a%84%e5%88%97%e8%a1%a8&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;挖坑现场&lt;/strong&gt;：在 &lt;code&gt;*ngFor&lt;/code&gt; 时代，&lt;code&gt;trackBy&lt;/code&gt; 是一个「建议」使用的优化项，很多开发者会忘记它。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;坠坑表现&lt;/strong&gt;：当一个长列表的数据发生更新时（哪怕只是顺序变化），整个列表的 DOM 元素被全部销毁，再全部重建，引发剧烈的性能抖动。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;病因分析&lt;/strong&gt;：没有 &lt;code&gt;track&lt;/code&gt;，Angular 就像一个「脸盲症」晚期的健忘保安。他不认识任何一个「老朋友」，只好把所有人都赶出去，再挨个重新检查、放行。这种 DOM 的大规模销毁和重建，是性能的巨大杀手。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;./angular_perf_images/trackby_bouncer.jpg&#34; alt=&#34;文生图：一个夜店门口，一位“脸盲”的保安（无track）正在粗暴地把所有客人（DOM元素）都推出去，让他们重新排队。而另一边，一位戴着眼镜、拿着名单的“精明”保安（有track），正高效地让客人凭ID入场。风格：对比鲜明的卡通漫画。&#34; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;逃生路线&lt;/strong&gt;：&lt;strong&gt;拥抱新的 &lt;code&gt;@for&lt;/code&gt; 语法，它「强制」你变好！&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;在 Angular v17+ 的新版内置控制流中，&lt;code&gt;@for&lt;/code&gt; 循环&lt;strong&gt;强制要求&lt;/strong&gt;你必须提供一个 &lt;code&gt;track&lt;/code&gt; 表达式。&lt;/p&gt;</description>
    </item>
    <item>
      <title>3.TypeScript在Angular中的“坑”与“药方”：类型安全，你值得拥有</title>
      <link>/angular/angular%E7%9A%84%E5%9D%91%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/030-typescript%E5%9C%A8angular%E4%B8%AD%E7%9A%84%E5%9D%91%E4%B8%8E%E8%8D%AF%E6%96%B9%E7%B1%BB%E5%9E%8B%E5%AE%89%E5%85%A8%E4%BD%A0%E5%80%BC%E5%BE%97%E6%8B%A5%E6%9C%89/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/angular/angular%E7%9A%84%E5%9D%91%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/030-typescript%E5%9C%A8angular%E4%B8%AD%E7%9A%84%E5%9D%91%E4%B8%8E%E8%8D%AF%E6%96%B9%E7%B1%BB%E5%9E%8B%E5%AE%89%E5%85%A8%E4%BD%A0%E5%80%BC%E5%BE%97%E6%8B%A5%E6%9C%89/</guid>
      <description>&lt;p&gt;如果说 Angular 是一座设计精良的「天空之城」，那么 TypeScript 就是这座城市的「基石」与「律法」。它为 Angular 注入了秩序、稳定和可预测性，是其工程化「道」的完美体现。可以说，TypeScript 和 Angular 是一对不折不扣的「灵魂伴侣」。&lt;/p&gt;&#xA;&lt;p&gt;但是，再恩爱的伴侣，也难免有闹矛盾、有误解的时候。很多开发者在使用 TS 和 Angular 的组合时，常常因为一些错误的「相处模式」，非但没有享受到类型安全带来的幸福，反而感觉处处受限，甚至埋下了更深的「坑」。&lt;/p&gt;&#xA;&lt;p&gt;今天，雪狼就来当一回「金牌调解员」，为你剖析这对「伴侣」间的常见矛盾，并开出「和睦相处」的药方。&lt;/p&gt;&#xA;&lt;h2 id=&#34;你的模板值得信任吗解密类型系统在-html-中的灯下黑&#34;&gt;你的模板，值得信任吗？解密类型系统在 HTML 中的「灯下黑」！&lt;a class=&#34;anchor&#34; href=&#34;#%e4%bd%a0%e7%9a%84%e6%a8%a1%e6%9d%bf%e5%80%bc%e5%be%97%e4%bf%a1%e4%bb%bb%e5%90%97%e8%a7%a3%e5%af%86%e7%b1%bb%e5%9e%8b%e7%b3%bb%e7%bb%9f%e5%9c%a8-html-%e4%b8%ad%e7%9a%84%e7%81%af%e4%b8%8b%e9%bb%91&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;症状&lt;/strong&gt;：你在 &lt;code&gt;.ts&lt;/code&gt; 文件里如履薄冰，为每个变量都一丝不苟地标上了类型，享受着编译器的保驾护航。但一转身，你在 &lt;code&gt;.html&lt;/code&gt; 模板里写下了 &lt;code&gt;{{ user.profil.avatr }}&lt;/code&gt; （一连串的拼写错误），TypeScript 编译器却沉默不语，像一个不负责任的伴侣，眼睁睁看着你犯错。直到运行时，浏览器控制台用鲜红的 &lt;code&gt;TypeError&lt;/code&gt; 来嘲笑你。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;病根&lt;/strong&gt;：默认情况下，TypeScript 的「管辖范围」仅限于 &lt;code&gt;.ts&lt;/code&gt; 文件。HTML 模板是一块「法外之地」，类型系统鞭长莫及。这种「灯下黑」式的类型残缺，是对「类型安全」承诺的巨大背叛。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;./angular_ts_pits_images/strict_templates_shield.jpg&#34; alt=&#34;文生图：一分为二的画面。左边，一个程序员在写.ts文件，身前有一个坚固的TypeScript盾牌，抵挡着飞来的Bug箭矢。右边，他在写.html文件，身前空无一物，一支Bug箭矢正中靶心。风格：对比鲜明的卡通插画。&#34; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;药方&lt;/strong&gt;：&lt;strong&gt;开启「完全严格模式」！&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;打开你的 &lt;code&gt;tsconfig.json&lt;/code&gt;，在 &lt;code&gt;angularCompilerOptions&lt;/code&gt; 里，毅然决然地把 &lt;code&gt;strictTemplates&lt;/code&gt; 设置为 &lt;code&gt;true&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;angularCompilerOptions&amp;#34;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;strict&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;strictTemplates&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这剂「猛药」下去，你的模板将立刻被纳入类型系统的「天眼」之下。&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;code&gt;{{ user.profil }}&lt;/code&gt;？编译器会告诉你：&lt;code&gt;User&lt;/code&gt; 类型上没有 &lt;code&gt;profil&lt;/code&gt; 属性，你是不是想写 &lt;code&gt;profile&lt;/code&gt;？&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;code&gt;&amp;lt;app-avatar [url]=&amp;quot;user.id&amp;quot;&amp;gt;&amp;lt;/app-avatar&amp;gt;&lt;/code&gt;？编译器会告诉你：&lt;code&gt;url&lt;/code&gt; 属性需要的是 &lt;code&gt;string&lt;/code&gt; 类型，你给的 &lt;code&gt;id&lt;/code&gt; 是 &lt;code&gt;number&lt;/code&gt; 类型，不行！&lt;/p&gt;</description>
    </item>
    <item>
      <title>4.解密Angular变更检测：如何避免“死循环”与性能陷阱</title>
      <link>/angular/angular%E7%9A%84%E5%9D%91%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/040-%E8%A7%A3%E5%AF%86angular%E5%8F%98%E6%9B%B4%E6%A3%80%E6%B5%8B%E5%A6%82%E4%BD%95%E9%81%BF%E5%85%8D%E6%AD%BB%E5%BE%AA%E7%8E%AF%E4%B8%8E%E6%80%A7%E8%83%BD%E9%99%B7%E9%98%B1/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/angular/angular%E7%9A%84%E5%9D%91%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/040-%E8%A7%A3%E5%AF%86angular%E5%8F%98%E6%9B%B4%E6%A3%80%E6%B5%8B%E5%A6%82%E4%BD%95%E9%81%BF%E5%85%8D%E6%AD%BB%E5%BE%AA%E7%8E%AF%E4%B8%8E%E6%80%A7%E8%83%BD%E9%99%B7%E9%98%B1/</guid>
      <description>&lt;p&gt;&lt;strong&gt;开篇提醒&lt;/strong&gt;：本文将要深入探讨的 &lt;code&gt;ExpressionChangedAfterItHasBeenCheckedError&lt;/code&gt;，是 Angular 在 Zone.js 时代一个著名且重要的错误。在基于 Signals 的新一代组件中，由于其响应式模型的根本性变革，这类问题已在很大程度上被规避。然而，理解这个经典错误背后的原理，对于我们深刻领悟 Angular 的「单向数据流」原则，以及响应式设计的演进，仍具有不可替代的价值。它是一块理解 Angular 历史与未来的「活化石」。&lt;/p&gt;&#xA;&lt;p&gt;变更检测，是 Angular 框架的核心魔法。但任何强大的魔法，一旦失控，都可能反噬自身。在 Angular 的世界里，最臭名昭著的诅咒，莫过于那串让无数开发者彻夜难眠的 &lt;code&gt;ExpressionChangedAfterItHasBeenCheckedError&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;p&gt;今天，雪狼就来扮演一回「捉鬼大师」，带你解密这个「诅咒」背后的真相，并教你如何画符念咒，让它永不缠身。&lt;/p&gt;&#xA;&lt;h2 id=&#34;变更检测的头号梦魇expressionchangedafterithasbeencheckederror究竟是-bug-还是保护机制&#34;&gt;变更检测的头号梦魇：&lt;code&gt;ExpressionChangedAfterItHasBeenCheckedError&lt;/code&gt;，究竟是 Bug 还是保护机制？&lt;a class=&#34;anchor&#34; href=&#34;#%e5%8f%98%e6%9b%b4%e6%a3%80%e6%b5%8b%e7%9a%84%e5%a4%b4%e5%8f%b7%e6%a2%a6%e9%ad%87expressionchangedafterithasbeencheckederror%e7%a9%b6%e7%ab%9f%e6%98%af-bug-%e8%bf%98%e6%98%af%e4%bf%9d%e6%8a%a4%e6%9c%ba%e5%88%b6&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;这个错误，可以说是每一位传统 Angular 开发者的「成人礼」。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;闹鬼现场&lt;/strong&gt;：你有一个父组件和一个子组件。在子组件的某个生命周期钩子（如 &lt;code&gt;ngOnInit&lt;/code&gt;）中，你通过 &lt;code&gt;@Output&lt;/code&gt; 发射了一个事件，而父组件监听到后，立即改变了一个通过 &lt;code&gt;@Input&lt;/code&gt; 传给子组件的状态。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;诅咒降临&lt;/strong&gt;：刷新页面，控制台「啪」的一下，红得扎眼。&lt;code&gt;ExpressionChangedAfterItHasBeenCheckedError&lt;/code&gt;，虽迟但到。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;大师解密&lt;/strong&gt;：首先，你要明白：&lt;strong&gt;这不是 Bug，这是一个保护机制！&lt;/strong&gt; 是 Angular 的「护法神兽」在咆哮，警告你已经破坏了宇宙的基本法则 —— &lt;strong&gt;单向数据流！&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;变更检测的「圣旨」是：变更永远要&lt;strong&gt;从上到下&lt;/strong&gt;单向传递。让我们回顾一下「案发」过程：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Angular 开始本轮变更检测，它先检查了&lt;strong&gt;父组件&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;它顺着组件树往下，开始检查&lt;strong&gt;子组件&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;在检查子组件的过程中，子组件发射事件，「穿越」回去修改了&lt;strong&gt;父组件&lt;/strong&gt;的状态。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;本轮检测结束。但在开发模式下，Angular 会立刻再进行&lt;strong&gt;第二轮&lt;/strong&gt;检查，以确保状态稳定。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;在第二轮检查中，Angular 再次检查父组件，发现它的状态竟然跟第一轮检查结束时不一样了！&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;神兽咆哮&lt;/strong&gt;：Angular 怒了。「岂有此理！一轮检查下来，状态竟然还不稳定，这可能导致无限循环！」 于是，它果断抛出错误，阻止了这场潜在的灾难。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;&lt;strong&gt;驱魔符咒&lt;/strong&gt;：问题的核心是「时机」。你需要把你的修改「延迟」到下一趟变更检测周期中。&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;下策：&lt;code&gt;setTimeout(() =&amp;gt; ...)&lt;/code&gt;&lt;/strong&gt;。利用事件循环，将代码调度到下一个「宏任务」中。能解决问题，但不够优雅。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;上策：选择正确的生命周期钩子&lt;/strong&gt;。在 &lt;code&gt;ngAfterViewInit&lt;/code&gt; 中发出的事件，通常能更好地规避这个问题，因为此时视图的初始检查已经完成。为了绝对保险，即使在 &lt;code&gt;ngAfterViewInit&lt;/code&gt; 中，使用 &lt;code&gt;setTimeout&lt;/code&gt; 也是最稳妥的方式。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;模板里的无限循环机为什么不要在模板绑定中返回新的集合引用&#34;&gt;模板里的「无限循环机」：为什么不要在模板绑定中返回新的集合引用？&lt;a class=&#34;anchor&#34; href=&#34;#%e6%a8%a1%e6%9d%bf%e9%87%8c%e7%9a%84%e6%97%a0%e9%99%90%e5%be%aa%e7%8e%af%e6%9c%ba%e4%b8%ba%e4%bb%80%e4%b9%88%e4%b8%8d%e8%a6%81%e5%9c%a8%e6%a8%a1%e6%9d%bf%e7%bb%91%e5%ae%9a%e4%b8%ad%e8%bf%94%e5%9b%9e%e6%96%b0%e7%9a%84%e9%9b%86%e5%90%88%e5%bc%95%e7%94%a8&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;挖坑现场&lt;/strong&gt;：你在组件类里定义了一个 getter，它每次都会返回一个&lt;strong&gt;新&lt;/strong&gt;的数组或对象实例。&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;get filteredUsers() { return this.users.filter(u =&amp;gt; u.active); }&lt;/code&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>5.Angular组件通信：选择恐惧症患者的“最佳实践”指南</title>
      <link>/angular/angular%E7%9A%84%E5%9D%91%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/050-angular%E7%BB%84%E4%BB%B6%E9%80%9A%E4%BF%A1%E9%80%89%E6%8B%A9%E6%81%90%E6%83%A7%E7%97%87%E6%82%A3%E8%80%85%E7%9A%84%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5%E6%8C%87%E5%8D%97/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/angular/angular%E7%9A%84%E5%9D%91%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/050-angular%E7%BB%84%E4%BB%B6%E9%80%9A%E4%BF%A1%E9%80%89%E6%8B%A9%E6%81%90%E6%83%A7%E7%97%87%E6%82%A3%E8%80%85%E7%9A%84%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5%E6%8C%87%E5%8D%97/</guid>
      <description>&lt;p&gt;对于很多 Angular 开发者来说，组件之间如何优雅、高效地通信，是一个足以引发「选择恐惧症」的难题。选错了通信方式，轻则代码混乱，重则架构腐败。&lt;/p&gt;&#xA;&lt;p&gt;别怕，今天「社交达人」雪狼就为你梳理一下现代 Angular 世界的「社交礼仪」，让你面对任何场景，都能选出最得体的通信方式。&lt;/p&gt;&#xA;&lt;h2 id=&#34;父组件如何向子组件叮嘱&#34;&gt;父组件如何向子组件「叮嘱」？&lt;a class=&#34;anchor&#34; href=&#34;#%e7%88%b6%e7%bb%84%e4%bb%b6%e5%a6%82%e4%bd%95%e5%90%91%e5%ad%90%e7%bb%84%e4%bb%b6%e5%8f%ae%e5%98%b1&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;这是最常见、最直接的通信方式。父组件要给子组件传递信息或任务。&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;社交工具&lt;/strong&gt;：&lt;code&gt;@Input()&lt;/code&gt; 装饰器&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;社交隐喻&lt;/strong&gt;：长辈对晚辈的「叮嘱」。清晰、直接，单向传递。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;使用姿势&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;@Component&lt;/span&gt;({&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;standalone&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;selector&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;app-child&amp;#39;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;template&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`&amp;lt;p&amp;gt;{{ message() }}&amp;lt;/p&amp;gt;`&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ChildComponent&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;message&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;input&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;string&lt;/span&gt;&amp;gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;app-child&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;message&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;]=&amp;#34;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;好好学习&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;，&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;天天向上&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;！&amp;#39;&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;app-child&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;优先使用 &lt;code&gt;input()&lt;/code&gt; 函数&lt;/strong&gt;：在 Angular v17.1+ 中，推荐使用新的 &lt;code&gt;input()&lt;/code&gt; 函数来定义输入属性。它返回一个只读的 &lt;code&gt;Signal&lt;/code&gt;，能更好地与 Angular 的新响应式模型集成，并且可以轻松地创建派生状态（&lt;code&gt;computed&lt;/code&gt;）。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;拥抱不可变性&lt;/strong&gt;：当传递的是对象或数组时，为了配合传统的 &lt;code&gt;OnPush&lt;/code&gt; 变更检测策略，请在父组件中通过创建&lt;strong&gt;新引用&lt;/strong&gt;的方式来变更数据。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;子组件如何向父组件呐喊报告&#34;&gt;子组件如何向父组件「呐喊」报告？&lt;a class=&#34;anchor&#34; href=&#34;#%e5%ad%90%e7%bb%84%e4%bb%b6%e5%a6%82%e4%bd%95%e5%90%91%e7%88%b6%e7%bb%84%e4%bb%b6%e5%91%90%e5%96%8a%e6%8a%a5%e5%91%8a&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;子组件发生了某件事，需要通知它的「监护人」父组件。&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;社交工具&lt;/strong&gt;：&lt;code&gt;@Output()&lt;/code&gt; 装饰器 + &lt;code&gt;EventEmitter&lt;/code&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;社交隐喻&lt;/strong&gt;：孩子的「呐喊」或「举手报告」。孩子只负责喊出「我做完作业了！」，但他不应该关心妈妈听到后是会奖励冰淇淋还是让他去弹钢琴。这种方式保证了子组件的独立和可复用性。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;使用姿势&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;@Component&lt;/span&gt;({ ... })&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ChildComponent&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;@Output&lt;/span&gt;() &lt;span style=&#34;color:#a6e22e&#34;&gt;taskCompleted&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;EventEmitter&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;string&lt;/span&gt;&amp;gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;onComplete() {&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;taskCompleted&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;emit&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;数学作业&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;app-child&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;taskCompleted&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;)=&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;onChildTaskCompleted&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;($&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;event&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;)&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;app-child&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：&lt;/p&gt;</description>
    </item>
    <item>
      <title>6.RxJS的“坑”与“美”：掌握这些姿势，告别内存泄漏！</title>
      <link>/angular/angular%E7%9A%84%E5%9D%91%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/060-rxjs%E7%9A%84%E5%9D%91%E4%B8%8E%E7%BE%8E%E6%8E%8C%E6%8F%A1%E8%BF%99%E4%BA%9B%E5%A7%BF%E5%8A%BF%E5%91%8A%E5%88%AB%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/angular/angular%E7%9A%84%E5%9D%91%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/060-rxjs%E7%9A%84%E5%9D%91%E4%B8%8E%E7%BE%8E%E6%8E%8C%E6%8F%A1%E8%BF%99%E4%BA%9B%E5%A7%BF%E5%8A%BF%E5%91%8A%E5%88%AB%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F/</guid>
      <description>&lt;p&gt;这种又爱又恨的感觉，我懂。很多开发者对 RxJS 望而生畏，视之为洪水猛兽。但今天，雪狼想对你说，RxJS 的「坑」，往往源于我们试图用「骑毛驴」的命令式思维，去驾驭这匹「宝马」。一旦你掌握了它响应式的「马语」，你将发现一片前所未有的新天地。&lt;/p&gt;&#xA;&lt;h2 id=&#34;你的订阅安息了吗警惕-rxjs-的僵尸订阅&#34;&gt;你的订阅「安息」了吗？警惕 RxJS 的「僵尸订阅」！&lt;a class=&#34;anchor&#34; href=&#34;#%e4%bd%a0%e7%9a%84%e8%ae%a2%e9%98%85%e5%ae%89%e6%81%af%e4%ba%86%e5%90%97%e8%ad%a6%e6%83%95-rxjs-%e7%9a%84%e5%83%b5%e5%b0%b8%e8%ae%a2%e9%98%85&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;挖坑现场&lt;/strong&gt;：你在组件的 &lt;code&gt;ngOnInit&lt;/code&gt; 中订阅了一个每秒更新一次的定时器流，用来显示时间。页面正常工作，你很满意。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;坠坑表现&lt;/strong&gt;：你跳转到别的页面，再回来，再跳转……你以为组件销毁了，一切就结束了。但如果你打开内存快照，会发现那些本该「安息」的组件实例，还像「僵尸」一样盘踞在内存里。它们体内的订阅还在，定时器还在不知疲倦地运行，直到耗尽你所有的内存。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;病因分析&lt;/strong&gt;：这是 RxJS 中最经典，也是最致命的坑：&lt;strong&gt;忘记退订&lt;/strong&gt;。&lt;code&gt;subscribe&lt;/code&gt; 就像打开了一个水龙头，如果你不亲手关上（&lt;code&gt;unsubscribe&lt;/code&gt;），它就会流到天荒地老。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;绝美「姿势」一：拥抱 &lt;code&gt;async&lt;/code&gt; 管道或 &lt;code&gt;toSignal&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;&lt;code&gt;async&lt;/code&gt; 管道&lt;/strong&gt;: 在模板中，用 &lt;code&gt;@if (time$ | async; as time)&lt;/code&gt; 来订阅流。Angular 会在组件销毁时自动替你关闭水龙头。这是经典而可靠的「自动阀门」。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;&lt;code&gt;toSignal&lt;/code&gt; 函数&lt;/strong&gt;: 这是更现代的「姿势」。在组件类中，直接将 &lt;code&gt;Observable&lt;/code&gt; 转换为 &lt;code&gt;Signal&lt;/code&gt;：&lt;code&gt;time = toSignal(this.time$)&lt;/code&gt;。&lt;code&gt;toSignal&lt;/code&gt; 同样会自动处理订阅的生命周期，并且让你能以更简单的方式在模板中使用 (&lt;code&gt;{{ time() }}&lt;/code&gt;)。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;绝美「姿势」二：&lt;code&gt;takeUntil&lt;/code&gt; —— 「You are terminated!」宣告终结的艺术&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;如果必须手动订阅，那就用最 RxJS 的方式来结束它。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;SomeCLass&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;destroy$&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Subject&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;void&lt;/span&gt;&amp;gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;ngOnInit() {&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;time$&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;pipe&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;takeUntil&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;destroy$&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ).&lt;span style=&#34;color:#a6e22e&#34;&gt;subscribe&lt;/span&gt;(...);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;ngOnDestroy() {&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;destroy$&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;destroy$&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;complete&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个「姿势」的美在于，它将「取消订阅」这个命令式的行为，转化成了一个声明式的流操作。代码的意图变成了：「从 &lt;code&gt;time$&lt;/code&gt; 流中取值，&lt;strong&gt;直到&lt;/strong&gt; &lt;code&gt;destroy$&lt;/code&gt; 流发出了信号为止。」&lt;/p&gt;</description>
    </item>
    <item>
      <title>7.Angular模块化：如何避免“巨石应用”的尴尬</title>
      <link>/angular/angular%E7%9A%84%E5%9D%91%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/070-angular%E6%A8%A1%E5%9D%97%E5%8C%96%E5%A6%82%E4%BD%95%E9%81%BF%E5%85%8D%E5%B7%A8%E7%9F%B3%E5%BA%94%E7%94%A8%E7%9A%84%E5%B0%B4%E5%B0%AC/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/angular/angular%E7%9A%84%E5%9D%91%E4%B8%8E%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/070-angular%E6%A8%A1%E5%9D%97%E5%8C%96%E5%A6%82%E4%BD%95%E9%81%BF%E5%85%8D%E5%B7%A8%E7%9F%B3%E5%BA%94%E7%94%A8%E7%9A%84%E5%B0%B4%E5%B0%AC/</guid>
      <description>&lt;h2 id=&#34;独立组件时代的新魔鬼你的上帝组件还好吗&#34;&gt;独立组件时代的新魔鬼：你的「上帝组件」还好吗？&lt;a class=&#34;anchor&#34; href=&#34;#%e7%8b%ac%e7%ab%8b%e7%bb%84%e4%bb%b6%e6%97%b6%e4%bb%a3%e7%9a%84%e6%96%b0%e9%ad%94%e9%ac%bc%e4%bd%a0%e7%9a%84%e4%b8%8a%e5%b8%9d%e7%bb%84%e4%bb%b6%e8%bf%98%e5%a5%bd%e5%90%97&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;挖坑现场&lt;/strong&gt;：在 &lt;code&gt;NgModule&lt;/code&gt; 时代，我们有臃肿的「上帝模块」。而在独立组件时代，我们有了新的魔鬼 —— 「&lt;strong&gt;上帝组件&lt;/strong&gt;」 。这是一个独立的「智能组件」，通常是某个页面的入口。它的 &lt;code&gt;imports&lt;/code&gt; 数组长达50行，导入了应用中所有能想到的共享组件、指令和管道。它的 &lt;code&gt;.ts&lt;/code&gt; 文件里有上千行代码，注入了十几个服务。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;尴尬后果&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;这个组件成了一个微缩版的「巨石」，它与应用的每一个角落都产生了耦合。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;它几乎无法被复用，也极难进行单元测试。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;任何一个被它导入的小组件发生破坏性变更，都可能导致这个「上帝组件」崩溃。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;解脱之道&lt;/strong&gt;：&lt;strong&gt;坚持「关注点分离原则」&lt;/strong&gt;。一个组件，应该只做一件事（只有一个关注点），并把它做好。将你的「上帝组件」，毫不留情地拆分成更小、更专注的子组件。让每个组件都成为一个只关注自己职责的「专家」，而不是一个什么都懂一点的「万金油」。&lt;/p&gt;&#xA;&lt;h2 id=&#34;你的libs是分类书架还是大杂烩垃圾场&#34;&gt;你的&lt;code&gt;libs&lt;/code&gt;是「分类书架」，还是「大杂烩垃圾场」？&lt;a class=&#34;anchor&#34; href=&#34;#%e4%bd%a0%e7%9a%84libs%e6%98%af%e5%88%86%e7%b1%bb%e4%b9%a6%e6%9e%b6%e8%bf%98%e6%98%af%e5%a4%a7%e6%9d%82%e7%83%a9%e5%9e%83%e5%9c%be%e5%9c%ba&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;挖坑现场&lt;/strong&gt;：你学习了 Monorepo 的最佳实践，开始使用 &lt;code&gt;libs&lt;/code&gt; 文件夹来组织可复用代码，这是一个巨大的进步。但你只是简单地创建了一个 &lt;code&gt;libs/shared&lt;/code&gt;，然后把所有组件、服务、工具函数，一股脑地全扔了进去。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;尴尬后果&lt;/strong&gt;：你只是换了一种形式，重新制造了一个旧时代的「大杂烩 &lt;code&gt;SharedModule&lt;/code&gt;」。&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;逻辑边界模糊&lt;/strong&gt;：项目的领域划分不清晰，&lt;code&gt;shared&lt;/code&gt; 库最终会变得难以理解和维护。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;不必要的依赖&lt;/strong&gt;：&lt;code&gt;products&lt;/code&gt; 功能可能只需要 &lt;code&gt;shared&lt;/code&gt; 里的一个 &lt;code&gt;PricePipe&lt;/code&gt;，但现在它间接依赖了 &lt;code&gt;shared&lt;/code&gt; 里的所有东西。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;解脱之道&lt;/strong&gt;：&lt;strong&gt;用「领域驱动」和「类型驱动」的思维，来规划你的 &lt;code&gt;libs&lt;/code&gt;！&lt;/strong&gt; 这才是现代 Angular 模块化的精髓。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;./angular_modularity_images/libs_structure.jpg&#34; alt=&#34;文生图：一个清晰的文件夹树状图，顶层是libs，下面分出products, shared等子目录，每个子目录下又分出feature, ui, data-access等文件夹，像一个整齐的分类书架。风格：干净、现代的信息图表。&#34; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;一个健康的 &lt;code&gt;libs&lt;/code&gt; 结构应该像这样：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;libs/&#xA;├── products/&#xA;│   ├── feature/  # 包含路由、入口组件等「智能」部分&#xA;│   ├── ui/       # 可复用的、与产品相关的「木偶」组件 (如 ProductCard)&#xA;│   └── data-access/ # 负责获取产品数据的服务和状态管理&#xA;├── orders/&#xA;│   ├── feature/&#xA;│   └── data-access/&#xA;└── shared/&#xA;    ├── ui/       # 全局真正可复用的 UI 组件 (如 Button, Avatar)&#xA;    ├── pipes/    # 全局可复用的管道&#xA;    └── utils/    # 全局可复用的工具函数&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;通过这种方式，你为你的应用建立了清晰的、有层次的「功能地图」和「依赖边界」。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
