## 设置位置 菜单栏 -> 设置 -> 外观设置 -> 主题 ## 定位点 1. F12打开调试器 2. 进入Elements标签 3. 变更设置,发现`body`标签的`class`属性发生变化 4. 变化:深色添加`dark`类,浅色则移除 5. 完整class: `class="with-vscode-font-patch --with-theming dark"` 可以将这三个`class`作为定位点入手处理 ## 定位具体代码 使用`--with-theming` 在解压后的`core.wxvpkg`中搜索 `grep -lr '\-\-with\-theming' .` 得到两个位置: 1. (exports.ThemingEnabledClass = "--with-theming"); 2. t.body.classList.add("--with-theming"), 第二个大概率是目标了; 正好第二个后面紧接着`this.themeChange()`,好了,看命名可以确定是这个了 ```javascript this.setup().finally(() => { var e, t; document.body.classList.add("with-vscode-font-patch"), null === (e = this.ourDocumentRef) || void 0 === e || e.body.classList.add("with-vscode-font-patch"), null === (t = this.ourDocumentRef) || void 0 === t || t.body.classList.add("--with-theming"), this.themeChange(); }); ``` ## 分析实现 `themeChange` 函数: ```javascript themeChange(e) { var t, n, i, s, o; "dark" === (null !== (n = null === (t = (e = e || this.props).settings.appearance) || void 0 === t ? void 0 : t.theme) && void 0 !== n ? n : "dark") ? null === (i = this.ourDocumentRef) || void 0 === i || i.body.classList.add("dark") : null === (s = this.ourDocumentRef) || void 0 === s || s.body.classList.remove("dark"), null === (o = this.ourDocumentRef) || void 0 === o || o.body.classList.add("with-vscode-font-patch"); } ``` 命名处理: ```javascript themeChange(config) { var appearance, theme, docRef1, docRef2, docRef3; console.warn('themeChange', config); "dark" === (null !== (theme = null === (appearance = (config = config || this.props).settings.appearance) || void 0 === appearance ? void 0 : appearance.theme) && void 0 !== theme ? theme : "dark") ? null === (docRef1 = this.ourDocumentRef) || void 0 === docRef1 || docRef1.body.classList.add("dark") : null === (docRef2 = this.ourDocumentRef) || void 0 === docRef2 || docRef2.body.classList.remove("dark"), null === (docRef3 = this.ourDocumentRef) || void 0 === docRef3 || docRef3.body.classList.add("with-vscode-font-patch"); } ``` 审查代码,得出大致逻辑: 1. 有传入配置使用配置;没有传入,则使用当前配置 2. 获取主题配置 3. 有主题配置则直接使用,否则默认`dark` 4. 主题配置是`dark`添加`dark`类,否则移除 很明显,`theme`的值只有`dark`和`非dark`的区别(根据`console.warn`的结果看,另一个值是`white`,当然这不重要); 此处没有跟随系统的判断,应该在别处。 另外,根据`console.warn`可以知道跟随系统也会触发`themeChange`。 好的,我们卡住了,需要另辟蹊径。 ## 另辟蹊径 简述: 通过字符串定位触发位置 1. 搜索`应用(除调试器外)的主题设置`,即`grep -lr '应用(除调试器外)的主题设置' .` 2. 获取对应标志`SETTING_THEME_DESC` 3. 搜索`SETTING_THEME_DESC`,即`grep -lr 'SETTING_THEME_DESC' .` 4. 三个结果,两个翻译,确定了 关键代码: ```javascript t.createElement( "div", { className: "uiv2-padding" }, t.createElement( "label", { className: "uiv2-radio", onClick: this.handleAutoDetectThemeClick, }, t.createElement("i", { className: r ? "uiv2-radio-icon-o" : "uiv2-radio-icon", }), t.createElement( "span", { className: "uiv2-radio-text" }, o.config.FOLLOW_SYSTEM ) ) ) ``` 可以看到处理“跟随系统”的函数是`handleAutoDetectThemeClick` 函数内执行`this.props.updateIDESetting("appearance", "autoDetectTheme", !0);` 通过搜索`autoDetectTheme`,找到一个跟主题控制相关的实现 ```javascript constructor() { (this.onDidOSThemeChange = () => { const o = s.OSThemeController.shared.getCurrentTheme(); if (o === s.OSTheme.Unknown) return; const n = o === s.OSTheme.Dark ? "dark" : "white"; this._enabledThemeAutoDetect && e.dispatch(t.updateIDESetting("appearance", "theme", n)), this._enabledDevtoolsThemeAutoDetect && e.dispatch( t.updateIDESetting("appearance", "devtoolsTheme", n) ); }), (this._enabledThemeAutoDetect = !1), (this._enabledDevtoolsThemeAutoDetect = !1), this.registerListeners(); } ``` 好了,去看看`OSThemeController`就知道怎么回事了