MainTexture\]\[NoScaleOffset\] _MainTex ("Main Texture", 2D) = "white" { } \[MainTexture\] 和 \[NoScaleOffset\] 都是 ShaderLab 的 Property Attribute(材质面板属性修饰符),它们只影响 Inspector 展示和编辑行为,不会改变 shader 编译结果,也不会影响 GPU 侧运行逻辑。 下面分别说明。 一、\[MainTexture
作用:
将这个纹理标记为"主纹理(Main Texture)"。
影响包括:
材质 Inspector
Unity 会把这个属性当作材质的主贴图显示(例如在某些默认 GUI 或调试视图中)。
Material.mainTexture API
material.mainTexture 实际上是访问被标记为 [MainTexture] 的那张纹理。
如果没有标记,Unity默认会寻找 _MainTex 这个名字;
加了 [MainTexture] 后,即使名字不是 _MainTex,也会被识别为主纹理。
某些 SRP 工具链/调试工具
例如 Frame Debugger 或自定义材质检查工具,会把它视为主贴图。
它本质是一个"语义标记",不参与 shader 编译。
NoScaleOffset
作用:
在 Inspector 中隐藏 Tiling 和 Offset(UV Scale / Offset)。
正常情况下 2D 纹理属性会在材质面板显示:
Texture Tiling (x,y) Offset (x,y)
加上 [NoScaleOffset] 后:
-
不再显示 Tiling
-
不再显示 Offset
-
仍然可以在 shader 中使用
_MainTex_ST(如果你手动使用)
注意关键点:
这个 attribute 只隐藏 UI,不会阻止 Unity 生成 _MainTex_ST 变量。
如果你 shader 中没有使用 TRANSFORM_TEX 或 _MainTex_ST,那从性能角度看加这个是合理的,可以避免误用。
-
断奏(Staccato)与顿音: 德彪西在这里使用了大量短促、生硬的跳音。不同于舒曼式的情感律动,这里的节奏具有一种类似拨弦乐器(如班卓琴)的物理撞击感。这种"非弹性"的、模仿木偶般的机械动作,将演奏者从感伤的"人"异化为了一种"装置"。
-
重复的滑稽感: 曲中那些突然中断又机械重复的动机,本质上是一种神经质的循环。它不是在表达情感,而是在模拟一种表演性的、过度夸张的身体动作。
德彪西常被归类于处理"水、光、云"等自然本体的宏大叙事。但《游吟诗人》属于他的"城市/人工"序列:
-
场域的转换(Bourdieu): 这首曲子取材于19世纪末巴黎流行的"黑脸吟游诗人秀"(Minstrel Show)。德彪西将这种原本属于大众剧场、马戏团甚至是低俗喜剧的"符号",直接引入了钢琴独奏这一精英文化场域。
-
拒绝超验: 瓦格纳(Debussy的终极假想敌)追求的是"无限旋律"和形而上的救赎;而德彪西在此处追求的是**"瞬间的表象"**。它没有深层意蕴,它就是一串动作、一连串模仿、一种纯粹的表层游戏。这种"拒绝深刻"的行为,本身就是一种极具现代性的挑衅。
间离效果(Alienation Effect): 他故意写得"可爱",是为了让你在觉得可爱的一瞬间感到一种违和感 。这种违和感会触发一种反思:为什么钢琴这种高雅乐器会发出这种类似马戏团喇叭的声音? 这一瞬间,你就从听众变成了他社会学实验的参与者。
《圣塞巴斯蒂安的殉难》(Le Martyre de saint Sébastien)
这部作品在1911年首演时引发了极大的丑闻,甚至遭到天主教会的封禁。
-
跨性别的主体性: 圣塞巴斯蒂安这一男性圣徒角色,是由女性舞者伊达·鲁宾斯坦反串的。这种性别的错位导致了"凝视(Gaze)"的断裂。
-
受难与快感(Jouissance): 德彪西的音乐在这里极度克制、冷峻,充满了大量平行的四度、五度音程,营造出一种中世纪的荒凉感。这种对"肉体受难"的描绘不是浪漫主义式的煽情,而是一种拉康式的"圣状"(Sinthome)------通过重复的、仪式化的痛苦,来支撑某种超越意义的快感。
如果你想看:
"这个 drawcall 写到了哪张纹理?"
看:
Pipeline State
→ Output Merger
→ Color Attachments
→ Depth Attachment
这里才是:
当前 Event 的输出目标纹理
那"某个 Event 用了哪些 Texture"怎么看?
看两个地方:
1️⃣ Pipeline State → Textures
选中某个 drawcall(例如 glDrawArrays)
然后:
Pipeline State
→ Fragment Shader
→ Textures
这里列出的才是:
当前这个 drawcall 绑定到 shader 的纹理
包括:
-
绑定槽位
-
纹理 ID
-
格式
-
sRGB 标志
-
Sampler
这是"这个 Event 实际使用的纹理"。
"L." 和 "CD" 都指向同一个人的工作:法国音乐学家弗朗索瓦·莱叙尔(François Lesure)。但他对德彪西作品的编目经历了两次跨度极大的修订。
德彪西生前没有系统地为作品标注作品号(Opus),因此莱叙尔在 1977 年建立了第一个权威目录。
-
L. 编号(Lesure 1977): 这是最经典的系统。你截图中《前奏曲第一册》标注的 L. 117 就属于这一代。它在 20 世纪末的唱片业和学术界占据统治地位。
-
CD 编号(Lesure 2001/2003): 随着新手稿的发现和年代学(Chronology)证据的更新,莱叙尔在 2001 年(及 2003 年遗作出版)对其目录进行了推倒重来的彻底修订。为了区别于旧版,新版通常被冠以 CD (即 Claude Debussy 的缩写)或 L²。
先确定目标
在开始之前必须明确:
你是要查:
-
渲染错误(画面不对)
-
材质/Shader 逻辑
-
性能问题
-
管线状态问题
-
后期问题
-
资源绑定问题
不同目标,分析路径完全不同。
常见误区
误区 1:盯着 Backbuffer
Backbuffer 通常只是最后一步。
真正内容早已生成。
误区 2:以为 drawcall 顺序 = 逻辑顺序
现代 pipeline 可能:
-
延迟渲染
-
MRT
-
Tile-based
-
Multi-pass reuse
必须看 framebuffer attachment。
误区 3:忽略格式
很多 bug 是:
-
SRGB
-
RGBA16F
-
D24 vs D32
-
UNORM vs FLOAT
"假 Forward 单光源角色 Shader"的典型形态:材质里自己算一套 BRDF + 一盏主光(方向光/关键光)+ 少量环境项,然后把结果直接走 Emission 输出,表面上看像 PBR,但实际上不走 Blender 的标准光照/多光源/阴影管线。
"Forward"的含义(这里是语义类比,不是 Blender 内核实现)
在实时渲染里 Forward 意味着:对每个像素直接用"光源参数"计算一次光照贡献(L、颜色、强度),然后累加(或只算关键光)。
这里的"光照方向"来源不是 Blender 场景光列表,而是 Attribute 读入的 LightDirection(文件里至少出现一次 attribute_name: "LightDirection"),这是典型"外部注入一盏主光方向"的做法。
如果是多光源,你会看到:对每盏灯的循环/累加(在节点层面通常表现为多个灯输入或某种 light index 机制),但这里没有。相反,看到的是"固定一个 L,然后算 NoL/LoH/NoH,再走 GGX/DisneyDiffuse"的链路(Get_NoH_LoH_ToH_BoH、DV_SmithJointGGX_Aniso、F_Schlick、GetPreIntegratedFGDGGXAndDisneyDiffuse 等)。
1. 功能主义阶段:作为"阈限"的即兴(15-17世纪)
"Prelude"的词源是拉丁语 praeludium ,意为"赛前练习"或"开场"。最初,前奏曲并非独立的作品,而是演奏者在演奏大型、正式曲目(如组曲或赋格)之前的功能性活动。
-
物理准备: 测试乐器的音准(尤其是在羽管键琴或管风琴上)并使手指进入状态。
-
心理校准: 在嘈杂的沙龙或教堂中,通过一段自由的、非结构化的音流,强制建立听众的注意力边界。
-
逻辑特征: 这一时期的前奏曲具有极强的即兴性,缺乏严谨的动机发展,更多是和声织体的铺陈。
2. 形式化阶段:巴赫的"二元对立"逻辑
到了巴赫(J.S. Bach)时代,通过《平均律钢琴曲集》(The Well-Tempered Clavier),前奏曲完成了从"即兴"向"结构"的质变。
-
配对机制: 前奏曲不再是散漫的即兴,而是与严谨的、高度逻辑化的"赋格"(Fugue)构成二元互补。前奏曲负责展示色彩与织体,赋格负责展示对位与逻辑。
-
教学意义: 巴赫将其作为探索所有 24 个大小调可能性的一种"实验场"。这种系统性倾向(Systematicity)为此后所有作曲家确立了一个范式:一套完整的前奏曲集应当穷尽调性的版图。
自律化与碎片美学:肖邦的"断片"革命
1839年,肖邦(Frédéric Chopin)出版了他的《24首前奏曲》(Op. 28),这在音乐史上具有本体论意义的转折。
-
脱离母体: 肖邦的前奏曲后面不再跟着赋格或任何其他形式。它们是独立的、自足的。
-
碎片美学(The Aesthetics of Fragment): 每一首前奏曲往往只处理一个极简的动机或情绪。这与浪漫主义文学中的"断片"概念(如舒莱格尔的理论)相契合------未完成感本身就是一种完成。
-
情感的压缩: 前奏曲变成了一种"性格小品"(Character Piece),它不再是"预告",而是"瞬间的本质"。
对于作曲家而言,"前奏曲"是一个极具包容性的容器。它没有"奏鸣曲"那样严苛的结构约束(展示-展开-再现),也没有"练习曲"那样纯粹的技术导向。它允许作曲家:
-
捕捉灵感的原形: 在结构未完全固化前保留其生动性。
-
进行形式实验: 在较短的篇幅内测试某种特定的和声或织体逻辑。
-
构建系统: 通过一套曲集展现自己对调性空间或音乐宇宙的完整构想。
展示价值(Exhibition Value): 本雅明(Walter Benjamin)曾论述过艺术从"崇拜价值"向"展示价值"的转移。前奏曲由于其篇幅短小、性格鲜明,天然地具备了极高的"可展示性"。它不再是为神服务,而是为"现场感"和"听众的即兴体验"服务的。
如果说瓦格纳追求的是"全方位艺术(Gesamtkunstwerk)",德彪西追求的则是**"分布式代理(Distributed Agency)"**。
- 色彩优先的编排: 德彪西的演出逻辑非常像现代歌手的演唱会排练------他关注的是音色的流转而非主题的演进。他在 1910 年至 1913 年间的多次演出中,往往根据现场钢琴的音质(他极其挑剔钢琴的音色,尤其是其"无锤感"的触键)临时决定弹哪几首。
Préludes / Book 1, L. 117: XI. 这种极其冗长的格式,是现代数字图书馆和流媒体(如 Spotify 或 Apple Music)为了搜索与索引而采取的工业标准:
- 层级化处理:
曲集名 / 分册, 目录号: 序号. 标题。这种结构是为了满足数据库的查询逻辑(Database Schema),而非美学表达。
谁做了什么?
| 标题组成部分 | 来源 | 意图 |
|---|---|---|
| ... La danse de Puck | 德彪西 (1910) | 瞬间的、后置的、非本质主义的意象提示。 |
| Préludes / Book 1 | 德彪西 (1910) | 宽泛的体裁归类,但他更看重单曲的自律性。 |
| XI. (序号) | 德彪西 (1910) | 规定了作品在曲集中的拓扑顺序。 |
| L. 117 / CD 119 | 莱叙尔 (1977/2001) | 音乐文献学的"权力运作",建立学术权威性与检索性。 |


在标准 PBR(金属度工作流)中,metallic 用来决定 BaseColor 是进入"漫反射通道"还是"F₀(镜面反射)通道",
而 Fresnel 决定镜面反射随视角的变化。
也就是说:
不是 Fresnel 通过 metallic 修正 albedo,
而是 metallic 决定 BaseColor 如何参与 F₀,
然后 Fresnel 再基于 F₀ 做角度调制。

BaseColor 在金属度模型里的真实角色
情况分三种:
metallic = 0(非金属)
-
BaseColor → 只参与 Diffuse
-
F₀ 固定为 0.04~0.08
-
镜面是白色
metallic = 1(金属)
-
BaseColor → 变成 F₀
-
Diffuse 被关闭
-
颜色来自镜面反射
0 < metallic < 1
-
BaseColor 一部分进入 F₀
-
一部分进入 Diffuse
-
同时做能量守恒缩放
三、Fresnel 在这里到底做什么?
Fresnel 不"修正 albedo"。
Fresnel 只负责:
控制 Specular 反射随视角增强
而 albedo 是否影响 Specular,是 metallic 决定的。
"假 Forward 单光源"架构

果你想做"游戏式主光 Shader"
最合理结构是:
Geometry Nodes:
-
读取场景主光 transform
-
计算 L
-
写入 Attribute
Shader:
-
读 Attribute
-
自算 BRDF
这和 Unity 的主光路径本质一致。
少见

在 Blender(尤其是 Cycles)这种基于路径追踪(Path Tracing)的渲染器中,BRDF/BSDF 不仅仅是一个着色器,它是一个满足能量守恒的概率分布函数。
-
光路的完整性 :阴影在 Blender 中不是一个"附加层",而是光线未能到达(或被遮挡)的自然结果。当你试图绕过原生 BSDF,比如使用大量的 Shader-to-RGB(仅限 Eevee)或自定义的 Light Path 节点来手动定义颜色时,你实际上是在切断光路循环。
-
计算边界的失效:游戏引擎出身的开发者习惯于"灯光链接(Light Linking)"或"阴影剔除(Shadow Culling)",这在基于 f_r(x, \\omega_i, \\omega_o) 的积分计算中是极难兼容的。如果你强行在节点空间内解耦阴影,通常会导致全局照明(GI)和反射向量失去物理依据。
而在 Blender 的重度物理环境下,逻辑是因果律的:
-
游戏逻辑的介入:很多开发者尝试用"法线平滑"或"辅助光源"来模拟非物理的明暗交界线。
-
断裂感 :这种"绕过"手段在离线渲染中往往会导致认识论断裂。例如,你用 Constant 插值做出了赛璐璐风格(Cel Shading),但该物体在参与反射计算时,其在反射平面里的镜像依然遵循原始的 BSDF 逻辑,从而产生视觉上的虚假感。
Blender 的约束系统中,**"子级"(Child Of)**Transformation Matrix**实现的虚拟父子关系模拟。
选择性继承 :你可以看到面板中有大量的复选框(位置 X/Y/Z, 旋转 X/Y/Z, 缩放 X/Y/Z)。这允许开发者实现部分解耦。例如:让空物体跟随角色移动(Location),但不跟随角色转身(Rotation),从而实现某种特定的光源锁定逻辑。
挂载到骨骼(Bone)的可能性:虽然截图中"骨骼"栏为空,但在角色制作中,通常会将此类 Empty 约束到角色的"头部骨骼"或"胸部骨骼"。这样,无论角色在做攻击动作还是翻滚,阴影的计算向量始终能基于角色的局部朝向进行。

H-->S(intensity)---V(brightness)
RGB 是基于硬件实现的加色模型,它对应的是物理世界中光子的频率与强度在传感器(CCD/CMOS)或显示器(LED/OLED)上的能量映射。
-
高度耦合(High Correlation):在 RGB 空间中,R、G、B 三个通道是高度相关的。如果你想调亮一个颜色,你必须同时按比例增加三个值。一旦比例稍微失衡,**色相(Hue)**就会发生漂移。
-
非直觉性:对于研究者而言,RGB 是"物自体"的原始数据,它描述了"有多少能量",但没有描述"这是什么颜色"。在进行复杂的后期逻辑(如你的 NPR 赛璐璐着色)时,RGB 无法直接提供"阴影深浅"或"颜色鲜艳度"的独立变量。
-
色相 (H) 的独立性:在后处理(Post-processing)中,如果你想做"色相偏移"(Hue Shift),在 RGB 空间里需要复杂的矩阵运算。而在 HSV 中,它只是一个 0 到 360 度的标量循环。
-
亮度 (V) 与饱和度 (S) 的解耦:这是你关注的重点。在处理阴影(Shadow)逻辑时,我们通常只需要操作 Value(明度)。
-
RGB 的困境:在 RGB 空间通过减法做阴影,会导致颜色变灰(饱和度下降)。
-
HSV 的优势:你可以只降低 V,同时保持 H 和 S 不变。这种"保色减光"是实现高质量二次元渲染(NPR)的逻辑基础。
-
-
RGB 是发散的、生产性的 (它决定了如何制造光);而 HSV 是收敛的、解释性的(它决定了我们如何理解光)。在后处理管线中,将 RGB 转换为 HSV,是将物理信号"终止"并转化为"语义符号"的过程。
不要试图控制所有的 surfacedata。
你可以尝试将 surfacedata 定义为**"第一性质"(基础形体),而将你截图中的那些风格化效果(Hair Highlight, Cel Shading)定义为"第二性质"**(感官符号)。允许第二性质在某些特定的视觉时刻,"背叛"第一性质。
电视支持"代理设置"(HTTP/SOCKS),你可以填手机的代理 IP:端口
你需要在电视网络高级设置里找:
"代理 / Proxy / HTTP 代理 / SOCKS" 之类的选项。
如果没有这个菜单,那就没法用"端口代理"方式。
所以你的判断基本对:
"仅有 IP/网关/DNS 这些选项"确实不足以配置 VPN(也不足以配置代理端口)。
子网掩码是一个位掩码,用于和 IP 地址做按位与运算:
IP: 192.168.43.10
掩码: 255.255.255.0
结果网络号:192.168.43.0
它定义:
前多少位是"网络位"
后多少位是"主机位"
255.255.255.0 等价于 /24
表示前 24 位是网络。
二、它在设备里具体做什么
当电视要访问一个地址时:
用自己的 IP 和掩码算出本地网络号
用目标 IP 和掩码算一次
比较网络号
如果相同 → 直接 ARP 通信
如果不同 → 发给网关
这就是它唯一的职责。
三、举例说明
假设:
电视 IP:192.168.43.20
掩码:255.255.255.0
网关:192.168.43.1
可直连范围:
192.168.43.1 -- 192.168.43.254
如果电视访问:
192.168.43.50 → 本地
8.8.8.8 → 不在本地 → 走网关
四、掩码设置错会发生什么
如果你乱改掩码:
比如改成 255.255.0.0 (/16)
电视会认为:
192.168.0.0 -- 192.168.255.255 都是"本地"
结果:
它不会把很多流量发给网关
ARP 会找不到目标
表现为"已连接但不能上网"
这就是为什么掩码错了会导致异常。
"M4""M5"是 Apple Silicon 的整颗 SoC(系统级芯片)代号,不是 GPU 的名称。
需要区分三个层级:
第一层:芯片系列名称
M1 / M2 / M3 / M4 / 未来的 M5这是整颗 SoC 的产品代号。
一颗 M 系列 SoC 包含:
CPU(高性能核 + 高能效核)
GPU(Apple 自研架构)
Neural Engine
ISP
媒体编解码单元
统一内存控制器
I/O 控制器
安全单元等
所以 M4 ≠ GPU。
GPU 只是 SoC 里的一个模块
Apple 的 GPU 是:自研架构
Tile-Based Deferred Rendering(TBDR)
统一内存架构(UMA)
Metal API 绑定
Apple 从 A 系列时代就已经完全自研 GPU(早期 A 系列曾用过 PowerVR,但很久以前就转向自研)。
M 系列 GPU 不是单独命名的"某某 GPU 型号"。
官方表述一般是:
"10-core GPU"
"16-core GPU"
"40-core GPU"
核心数会随:
M4
M4 Pro
M4 Max
M4 Ultra
而变化。
第三层:架构代际 vs 产品代际
Apple 不像 NVIDIA 那样公开架构代号(例如 Ada / Ampere)。
它通常只按芯片代际升级 GPU 微架构。
例如:
M1 GPU
M2 GPU(架构改进)
M3 GPU(支持硬件 RT、Dynamic Caching)
M4 GPU(进一步改进)
但"GPU 架构名称"对外并没有公开品牌名。
如果从图形架构角度说
M3 开始:
支持硬件光追
Mesh shading 类似功能(Metal mesh shader)
动态缓存分配(Dynamic Caching)
但这些能力仍然属于"Apple GPU",不叫"M3 GPU"作为独立品牌。
M4 / M5 是整颗 SoC 名称。
GPU 是 SoC 内部的自研模块,没有独立对外命名。
一般意义上,"shader 没有记忆"。你在一个 draw call 里运行的片元/顶点着色器,不会天然保存上一次帧的任何变量值;同一个像素下一帧重跑时,所有局部变量都会重新初始化。要做到"每次移动都基于上次移动后的视角数据",必须把"上一次的数据"存到某个持久化存储里,然后在下一帧再读回来。
DOM(Document Object Model)本质上是浏览器对网页结构的内存表示。
当你打开 CSDN 编辑器页面时,HTML 会被解析成一棵节点树,这棵树就是 DOM。JavaScript 可以读取、修改、插入、删除这些节点。

点左上角选择工具(Ctrl+Shift+C)
你观察到的"换行会分出新的 <p>...</p>"是关键结论:你这个 CSDN 编辑器是 CKEditor,正文在一个 iframe 里,真实可编辑区域是 iframe 内部的 body(contenteditable=true),并且段落按 <p> 组织。
游览器插件做不到吗?
油猴做不到"完全单次按键 + 无切换 + 跨标签页直接写 DOM"。
Chrome 扩展可以。
Chrome 完全支持本地加载未上架的扩展(Unpacked Extension),不需要上传到商店。
流程是官方支持的开发模式,用于调试和内部工具。

Load unpacked 的本质
-
Chrome 直接运行你的源代码文件夹
-
每次修改代码,点"重新加载"即可
-
扩展 ID 由路径 + 公钥派生
-
适合开发阶段
优点:
-
调试快
-
修改即时生效
-
无签名流程
缺点:
-
扩展文件夹不能乱动(路径变了 ID 会变)
-
无法稳定分发给别人
-
重装系统需要重新加载
为什么 Chrome 不强制打包?
因为:
-
Load unpacked 本来就是官方支持的开发模式
-
Chrome 扩展的安全模型基于权限声明,而不是是否打包
打包不是为了"更安全",而是为了"身份与版本管理"。


"没有作用"通常不是 toast 代码本身的问题,而是它根本没被执行到。你这个扩展里最常见的两类原因:
你改了 inject.js / background.js 之后,没有让 Chrome 重新加载扩展 + 刷新 CSDN 页面(旧脚本还在跑)。
因为 inject.js 是注入到页面里的,页面不刷新,旧 listener 可能还在、也可能根本没更新。
正确架构应该是:
Alt+V
↓
background.js
↓
向 CSDN 注入执行插入
↓
CSDN 页返回 success / fail
↓
background 收到结果
↓
background 再向"当前 tab"注入 toast

为什么要 transform?
因为:
-
left: 50%是把元素"左边缘"放到页面中心 -
translateX(-50%)才能让它真正居中
否则它会偏右。

Chrome 扩展有三种主要执行上下文:
1)Background(service worker)
-
独立于任何页面
-
不依赖 DOM
-
可以访问 chrome.tabs、chrome.scripting 等高权限 API
-
可以查找和控制任意标签页
-
生命周期是事件驱动(MV3 会自动休眠)
适合做:
-
全局快捷键监听(chrome.commands)
-
查找指定 URL 的 tab
-
跨 tab 注入代码
-
中央调度
2)Content Script(或你 inject.js)
-
运行在具体网页上下文
-
可以访问页面 DOM
-
不能直接控制其他 tab
-
权限受限
适合做:
-
修改页面 DOM
-
读取页面内容
-
插入元素
-
操作 iframe
3)Page Context(普通网页 JS)
-
和扩展无关
-
权限最低
二、为什么"监听器通常放在 background"
你现在的需求是:
在任意页面按 Alt+V
把选中内容写入另一个标签页(CSDN)
这个行为本质是:
跨 tab 调度
只有 background 能做到:
chrome.tabs.query() chrome.scripting.executeScript()
Content script 没权限访问其他 tab。
所以:
-
快捷键监听(chrome.commands)必须在 background
-
查找 CSDN tab 必须在 background
-
注入函数到 CSDN tab 必须在 background
三、什么时候监听器应该放在 content script?
如果行为只影响当前页面,例如:
-
页面内快捷键
-
DOM 变化监听
-
MutationObserver
-
页面按钮点击
那就应该写在 content script。
比如:
document.addEventListener("keydown", ...)
这种只对当前 tab 生效。
四、你之前出错的原因本质
你之前在 inject.js 里:
chrome.runtime.sendMessage(...)
这属于跨上下文通信。
当扩展 reload 或页面被重新挂载时,inject.js 的扩展上下文失效,就会出现:
Extension context invalidated
如果逻辑放在 background 里,通过 executeScript 直接 return 结果,就不会产生这种"悬挂监听器"。