<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="https://mokurin000.github.io/feed_style.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <tabi:metadata xmlns:tabi="https://github.com/welpo/tabi">
        <tabi:base_url>https:&#x2F;&#x2F;mokurin000.github.io</tabi:base_url>
        <tabi:separator>
            •
        </tabi:separator>
        <tabi:about_feeds>This is a web feed, also known as an Atom feed. Subscribe by copying the URL from the address bar into your newsreader. Visit About Feeds to learn more and get started. It&#x27;s free.</tabi:about_feeds>
        <tabi:visit_the_site>Visit website</tabi:visit_the_site>
        <tabi:recent_posts>Recent posts</tabi:recent_posts>
        <tabi:last_updated_on>Updated on $DATE</tabi:last_updated_on>
        <tabi:default_theme></tabi:default_theme>
        <tabi:post_listing_date>date</tabi:post_listing_date>
        <tabi:current_section>userscript</tabi:current_section>
    </tabi:metadata><title>mokurin000's blog - userscript</title>
        <subtitle>geek blog</subtitle>
    <link href="https://mokurin000.github.io/tags/userscript/atom.xml" rel="self" type="application/atom+xml"/>
    <link href="https://mokurin000.github.io/tags/userscript/" rel="alternate" type="text/html"/>
    <generator uri="https://www.getzola.org/">Zola</generator><updated>2026-06-04T17:30:00+08:00</updated><id>https://mokurin000.github.io/tags/userscript/atom.xml</id><entry xml:lang="en">
        <title>用 Tauri 注入 UserScript</title>
        <published>2026-06-04T17:30:00+08:00</published>
        <updated>2026-06-04T17:30:00+08:00</updated>
        <author>
            <name>mokurin000</name>
        </author>
        <link rel="alternate" href="https://mokurin000.github.io/blog/tauri-userscript-injection/" type="text/html"/>
        <id>https://mokurin000.github.io/blog/tauri-userscript-injection/</id>
        
            <content type="html">&lt;blockquote&gt;
&lt;p&gt;注意：该方式注入的脚本具备接近页面最高权限的执行能力。
如若涉及远程加载，必须实现签名、证书检查等完整性保护机制，否则可导致页面被完全接管。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;在使用 Tauri 构建桌面 WebView 应用时，笔者偶然发现
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;tauri&#x2F;latest&#x2F;tauri&#x2F;struct.Builder.html?utm_source=chatgpt.com#method.append_invoke_initialization_script&quot;&gt;&lt;code&gt;Builder::append_invoke_initialization_script&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; 这个 API。&lt;&#x2F;p&gt;
&lt;p&gt;它允许在 WebView 初始化阶段注入 JavaScript，在页面业务代码执行之前运行。这使得将 UserScript 直接内置进应用成为可能。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zhu-ru-ji-zhi&quot;&gt;注入机制&lt;&#x2F;h2&gt;
&lt;p&gt;Tauri 提供的注入方式如下：&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;tauri&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Builder&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;default&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; Example script&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; or: include_str!(&amp;quot;script.js&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;    .&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;append_invoke_initialization_script&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt;r#&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        &#x2F;&#x2F; modify window properties directly&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        window.__APP__ = true;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        &#x2F;&#x2F; Overriding `fetch()`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        window.fetch = () =&amp;gt; {};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        &#x2F;&#x2F; inject custom stylesheets on the document, e.g.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        window.addEventListener(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;            &amp;quot;DOMContentLoaded&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;            () =&amp;gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        );&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;quot;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;    .&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;run&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;tauri&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function z-macro z-rust&quot;&gt;generate_context!&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;    .&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;error while running tauri application&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这个阶段发生在 WebView 初始化早期，比
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;zh-CN&#x2F;docs&#x2F;Web&#x2F;API&#x2F;Document&#x2F;DOMContentLoaded_event&quot;&gt;DOMContentLoaded&lt;&#x2F;a&gt;
更靠前，因此可以影响页面最初的运行环境。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;userscript-jian-rong-xing-de-chu-li-fang-shi&quot;&gt;UserScript 兼容性的处理方式&lt;&#x2F;h2&gt;
&lt;p&gt;UserScript 通常运行在 TamperMonkey &#x2F; GreaseMonkey 环境中，依赖 &lt;code&gt;GM_*&lt;&#x2F;code&gt; API 或 &lt;code&gt;unsafeWindow&lt;&#x2F;code&gt; 等扩展能力。而在普通 WebView 环境中，这些对象不存在。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-constant z-js&quot;&gt; unsafeWindow&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;#39;undefined&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; === typeof&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; GM_info&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ?&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; window&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; :&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-readwrite&quot;&gt; unsafeWindow&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;在必要时，我们可以手动提供兼容的对象，供 UserScript 使用。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-yong-jia-zhi&quot;&gt;使用价值&lt;&#x2F;h2&gt;
&lt;p&gt;这种方式本质上是在把 UserScript 作为应用内置能力使用。&lt;&#x2F;p&gt;
&lt;p&gt;相比传统 UserScript 流程，它减少了外部依赖：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;不需要用户安装油猴类扩展&lt;&#x2F;li&gt;
&lt;li&gt;不需要依赖 GreasyFork &#x2F; OpenUserJS &#x2F; jsDelivr 等脚本托管站&lt;&#x2F;li&gt;
&lt;li&gt;在网络受限环境下更稳定&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;在分发层面，它更接近：&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;“带脚本能力的桌面 Web 应用”&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;an-quan-bian-jie&quot;&gt;安全边界&lt;&#x2F;h2&gt;
&lt;p&gt;该模式的关键风险在于：注入脚本等同于页面最高权限执行环境。&lt;&#x2F;p&gt;
&lt;p&gt;需要注意：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;只加载可信脚本或本地内置脚本&lt;&#x2F;li&gt;
&lt;li&gt;远程脚本必须具备签名或完整性校验机制&lt;&#x2F;li&gt;
&lt;li&gt;避免将其作为普通前端逻辑入口&lt;&#x2F;li&gt;
&lt;li&gt;必要时进行能力拆分或隔离执行环境&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;zong-jie&quot;&gt;总结&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;tauri&#x2F;latest&#x2F;tauri&#x2F;struct.Builder.html?utm_source=chatgpt.com#method.append_invoke_initialization_script&quot;&gt;append_invoke_initialization_script&lt;&#x2F;a&gt; 提供了一个非常直接的能力：在 WebView 初始化阶段注入 JavaScript。&lt;&#x2F;p&gt;
&lt;p&gt;借助这一机制，可以将 UserScript 作为应用内部逻辑层进行封装，实现：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;桌面应用内置脚本能力&lt;&#x2F;li&gt;
&lt;li&gt;与 UserScript 生态兼容的脚本复用&lt;&#x2F;li&gt;
&lt;li&gt;减少对外部脚本托管与浏览器扩展的依赖&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;这是一种将“浏览器扩展式能力”迁移到桌面应用分发体系中的实现路径。&lt;&#x2F;p&gt;
&lt;p&gt;示例项目：&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mokurin000&#x2F;fknc-calculator&#x2F;&quot;&gt;fknc-calculator&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        <summary type="html">一篇由付费游戏价格计算器引发的思考</summary>
        </entry>
</feed>
