<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>阿涛の小破站</title><description>记录折腾日常和技术分享</description><link>https://blog.emohe.cn/</link><language>zh_CN</language><item><title>WebView2应用资源占用优化指南</title><link>https://blog.emohe.cn/posts/webview2%E4%BC%98%E5%8C%96/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/webview2%E4%BC%98%E5%8C%96/</guid><pubDate>Mon, 10 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;基于WebView2的框架开发的应用一般内存占用都很高，默认的初始化界面的运行时内存占用都有100多MB，加上几个页面和功能交互后，很容易就超过200多MB。&lt;/p&gt;
&lt;p&gt;最近精读了各框架的性能优化文档，并且参照了很多最佳实践，在实现一个真正可用的应用后，内存占用依然可以降低到100MB以内，现在整理成优化经验分享给大家。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;通用经验文档，面向 Windows 上基于 WebView2 的&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tauri 2&lt;/strong&gt;、&lt;strong&gt;Wails v3&lt;/strong&gt; 以及内嵌 Chromium 的 &lt;strong&gt;Electron&lt;/strong&gt; 最新版本，&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;汇总内存占用与交互流畅度的优化要点、框架差异和避坑清单。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;1. 内核认知：WebView2 / Chromium 进程模型&lt;/h2&gt;
&lt;h3&gt;1.1 进程组成&lt;/h3&gt;
&lt;p&gt;每个 WebView2 / Chromium 实例不是单进程，而是一个进程组：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;browser（主浏览器进程）&lt;/strong&gt;：每套“环境”一份，负责导航、网络、会话管理；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;renderer（渲染进程）&lt;/strong&gt;：每个页面/iframe 一份，承载 DOM、JS heap、CSS；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GPU 进程&lt;/strong&gt;：硬件加速渲染；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;utility（网络服务、存储、音频等）&lt;/strong&gt; 若干。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;进程数量与内存随页面复杂度、窗口数量增长，这是“轻量 WebView 应用”内存仍
偏高、以及多窗口应用内存成倍增长的根本原因。&lt;/p&gt;
&lt;h3&gt;1.2 环境、数据目录与 profile&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;环境（Environment） = 独立 User Data Folder + 一致的环境参数&lt;/strong&gt;。同一环境内
的多个 WebView 控件共享同一进程组；不同环境各自拥有独立的 browser 进程树。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多 profile&lt;/strong&gt;：同一环境内可建多个 profile，各自隔离 cookie、storage、
IndexedDB、缓存等站点数据，但共享运行时进程（WebView2 multi-profile 支持）。
这是“既要隔离数据、又要省内存”时优先于独立环境的手段。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;渲染进程保留机制&lt;/strong&gt;：Chromium 可能保留已关闭窗口的渲染进程以便复用，因此
共享环境下“关窗”不代表内存立即回落。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;独立环境的进程树退出&lt;/strong&gt;：环境内最后一个 WebView 销毁后，browser 进程退出
（&lt;code&gt;BrowserProcessExited&lt;/code&gt; 事件），其全部子进程随之释放，内存可回收。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1.3 内存量级参考&lt;/h3&gt;
&lt;p&gt;简单应用的 WebView 页面本身约 10–20MB（Wails 官方架构文档参考值）；实际占用
还需加上每套环境的 browser 进程、GPU 进程与页面复杂度带来的渲染进程开销，
第三方站点页面往往数倍于此。具体数值应以本机任务管理器/进程树测量为准。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. 优化总则&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;先测后优&lt;/strong&gt;：用任务管理器 / 浏览器任务管理器 / DevTools Memory 定位内存
大头，不要凭感觉优化。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;内存与流畅度是权衡&lt;/strong&gt;：禁用 GPU 省内存但可能让动画/滚动变卡；后台节流
省 CPU 但会延迟后台任务。每项开关按实际页面场景取舍。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;常驻窗口共享环境，临时窗口用完即毁&lt;/strong&gt;：常驻 UI 复用进程省内存；一次性
窗口（认证、弹窗）用独立环境 + 立即销毁，换取确定性回收。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;生产环境不依赖实验性 browser flags&lt;/strong&gt;：微软官方明确警告这些 flags 可能
随时移除或改变行为。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;3. 通用优化清单（三框架均适用）&lt;/h2&gt;
&lt;h3&gt;3.1 窗口与环境策略&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;共享环境（框架默认），不要为每个窗口创建独立环境&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;一次性窗口（认证 / 弹窗）&lt;/td&gt;
&lt;td&gt;独立 User Data Folder（独立环境）+ 销毁窗口，进程树整体退出&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;仅需隔离站点数据&lt;/td&gt;
&lt;td&gt;同一环境内用独立 profile，共享运行时（比独立环境省内存）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;启动页 / 简单对话框&lt;/td&gt;
&lt;td&gt;不要用 WebView2，用原生 UI，或延迟到真正需要时再创建窗口&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;p&gt;注意事项：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不同环境 / 不同 profile 之间 cookie、localStorage 不互通。临时窗口里产生的
会话凭据需显式持久化，再交给主逻辑使用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;浏览器参数不同的 WebView 必须使用不同的数据目录&lt;/strong&gt;（wry 官方警告）；同一
环境的全部窗口应保持参数一致。&lt;/li&gt;
&lt;li&gt;覆盖 &lt;code&gt;additional_browser_args&lt;/code&gt; 时必须显式保留框架默认参数，否则默认值被
整体替换（详见 §4 避坑）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.2 启动参数（内存向）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--disable-gpu&lt;/code&gt;：禁用 GPU 进程。&lt;strong&gt;微软官方仅建议用于排障&lt;/strong&gt;；简单静态 UI
（表单、设置页）可接受，含动画 / 视频 / 复杂滚动的页面必须保留硬件加速。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--disable-features=msEdgeAutofill,msEdgeShopping,msEdgeWallet&lt;/code&gt;：禁用 Edge
附加服务，减少常驻资源。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--js-flags=--scavenger_max_new_space_capacity_mb=8&lt;/code&gt;：限制 V8 新生代堆上限，
降低 JS 引擎内存占用（微软官方 flags 文档示例值）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--disk-cache-size=N&lt;/code&gt;：限制磁盘缓存上限（字节）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;实验性（微软官方 flags 文档，生产谨慎）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;msWebView2CancelInitialNavigation&lt;/code&gt;：取消初始导航，加快启动；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;msWebView2CodeCache&lt;/code&gt;：为虚拟主机映射/拦截的资源启用字节码缓存，加速重复加载；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;msFloatyMode=false&lt;/code&gt;：关闭“浏览器保留”实验（WebView 不支持该保留实验）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;反例：&lt;code&gt;--disable-background-timer-throttling&lt;/code&gt; 会&lt;strong&gt;增加&lt;/strong&gt;后台 CPU/内存消耗，
默认不要使用。&lt;/p&gt;
&lt;h3&gt;3.3 生命周期与回收&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;WebView2 提供 &lt;code&gt;MemoryUsageTargetLevel = Low&lt;/code&gt;（应用非活动时降低 WebView 内存
目标，丢弃缓存、换页到磁盘）与 &lt;code&gt;TrySuspendAsync()&lt;/code&gt; / &lt;code&gt;Resume()&lt;/code&gt;（挂起渲染
进程）；恢复时自动回到 &lt;code&gt;Normal&lt;/code&gt;（微软官方 API 文档）。&lt;/li&gt;
&lt;li&gt;Electron 的 &lt;code&gt;backgroundThrottling&lt;/code&gt; 默认开启：后台自动节流动画与定时器，
不要为“看起来流畅”随意关闭。&lt;/li&gt;
&lt;li&gt;长期运行的页面会自然累积状态，&lt;strong&gt;定期刷新 WebView 可回到干净基线&lt;/strong&gt;
（微软官方建议）。&lt;/li&gt;
&lt;li&gt;删除 User Data Folder 必须等 &lt;code&gt;BrowserProcessExited&lt;/code&gt;，否则删除失败或残留
旧配置（微软官方进程事件文档）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.4 前端与交互（流畅度）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;动画优先用 CSS；高频事件防抖/节流；低优任务用 &lt;code&gt;requestIdleCallback&lt;/code&gt;；
长任务交给 Web Worker（Electron 官方性能文档建议）。&lt;/li&gt;
&lt;li&gt;避免 layout thrashing（一帧内交替读写 DOM 造成多次 reflow）；长列表虚拟化；
图片/组件懒加载；代码分割减小初始载荷。&lt;/li&gt;
&lt;li&gt;减少桥接/IPC 频率：批量、合并消息、推送代替轮询；避免大对象在桥接层往返。&lt;/li&gt;
&lt;li&gt;事件监听随组件卸载清理，防止 DOM/闭包泄漏。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.5 后端 / 主进程&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;不阻塞 UI 线程：磁盘 I/O、CPU 密集任务全部异步或进 worker。&lt;/li&gt;
&lt;li&gt;事件推送要合并/节流。教训：Wails 旧版用 &lt;code&gt;eval&lt;/code&gt; 直推事件，高频大载荷下造成
内存泄漏，v3 已改为拉取模型（见 §5）。&lt;/li&gt;
&lt;li&gt;大数据分页或流式返回，不要把整包数据塞给渲染层。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;4. 框架落地：Tauri 2&lt;/h2&gt;
&lt;h3&gt;4.1 配置（tauri.conf.json）&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;app&quot;: {
    &quot;windows&quot;: [
      {
        &quot;label&quot;: &quot;main&quot;,
        &quot;additionalBrowserArgs&quot;: &quot;--disable-gpu --disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection,msEdgeAutofill,msEdgeShopping,msEdgeWallet&quot;
      }
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;backgroundThrottling&lt;/code&gt; 支持三档：&lt;code&gt;&quot;suspend&quot;&lt;/code&gt;（默认，不在窗口中的 WebView
完全挂起任务）、&lt;code&gt;&quot;throttle&quot;&lt;/code&gt;（限制处理但不完全挂起）、&lt;code&gt;&quot;disabled&quot;&lt;/code&gt;（禁用
后台节流）——配置参考页面已列出。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;覆盖 browser args 必须保留 wry 默认值&lt;/strong&gt;：wry 默认传
&lt;code&gt;--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection&lt;/code&gt;，并在启用
autoplay / 设置代理时附带 &lt;code&gt;--autoplay-policy&lt;/code&gt; / &lt;code&gt;--proxy-server&lt;/code&gt;（wry 官方
文档警告：不保留则默认行为被替换）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4.2 Rust API&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;WebviewWindowBuilder::additional_browser_args()&lt;/code&gt;：每窗口浏览器参数；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WebviewWindowBuilder::data_directory()&lt;/code&gt;：覆盖该窗口的 WebView2 数据目录，
传不同路径即得到独立环境（独立进程树）；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WebviewWindowBuilder::with_profile_name()&lt;/code&gt;（wry 层）：同一环境内隔离站点
数据但共享运行时；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;window.destroy()&lt;/code&gt;：立即销毁（跳过关闭事件拦截），优于 &lt;code&gt;close()&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Windows 上创建 WebView2 窗口应在 async 命令中执行&lt;/strong&gt;，同步路径会死锁。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4.3 一次性窗口推荐模式&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;临时窗口使用独立数据目录，获得独立环境；&lt;/li&gt;
&lt;li&gt;每次打开前按业务需要清空该目录：销毁窗口后旧会话数据会残留在 profile 中，
复用时可能导致状态误判；&lt;/li&gt;
&lt;li&gt;需要保留的会话凭据先显式持久化（注意平台凭据存储存在单条长度上限，
Windows Credential Manager 对 UTF-16 编码条目限制约 2560 字符，大体积凭据
可能超限，需评估存储方案）；&lt;/li&gt;
&lt;li&gt;完成后 &lt;code&gt;destroy()&lt;/code&gt; 窗口：独立环境中最后一个 WebView 销毁后整个进程树退出，
内存随之释放。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;5. 框架落地：Wails v3&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;状态：Wails v3.0.0-beta.0 于 2026-08-01 发布，桌面端 API 已稳定，但仍是
预发布版本；Wails v2 仍是当前稳定版。本文以 v3 官方文档为准。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;5.1 浏览器参数是应用级的&lt;/h3&gt;
&lt;p&gt;Windows 上 WebView2 按 user data path 共享单一浏览器环境，因此浏览器参数放在
应用级，而非每窗口：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;app := application.New(application.Options{
    Windows: application.WindowsOptions{
        EnabledFeatures:       nil, // WebView2 feature flags 启用列表
        DisabledFeatures:      nil, // WebView2 feature flags 禁用列表
        AdditionalBrowserArgs: &quot;--disable-gpu --disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection,msEdgeAutofill,msEdgeShopping,msEdgeWallet&quot;,
    },
})
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;三个选项作用于共享的 WebView2 环境，所有窗口统一生效（v3.0.0-alpha.66 起从
每窗口选项移到应用级，原因即“共享单一环境”）；&lt;/li&gt;
&lt;li&gt;历史默认 User Data Folder 位于 &lt;code&gt;%APPDATA%\&amp;lt;二进制名&amp;gt;&lt;/code&gt;（v2 提供
&lt;code&gt;WebviewUserDataPath&lt;/code&gt; 自定义，v3 是否保留以官方文档为准）；&lt;/li&gt;
&lt;li&gt;禁用 GPU 直接通过 &lt;code&gt;AdditionalBrowserArgs&lt;/code&gt; 传 &lt;code&gt;--disable-gpu&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5.2 桥接与事件&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;避免大数据传输：传 ID、按需取详情；用事件推送代替前端轮询；&lt;/li&gt;
&lt;li&gt;大文件流式处理，不要整体载入内存；&lt;/li&gt;
&lt;li&gt;清理监听器：&lt;code&gt;app.On&lt;/code&gt; 返回的 &lt;code&gt;unsubscribe&lt;/code&gt; 函数在不用时调用；&lt;/li&gt;
&lt;li&gt;控制事件推送频率与单次载荷，防止高频大载荷撑爆 WebView 内存（v3 已改
拉取模型，仍需注意载荷大小）。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;6. 框架落地：Electron&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：Electron &lt;strong&gt;不是 WebView2&lt;/strong&gt;，它内嵌自己的 Chromium。两者内核同源，
渲染进程 / GPU 进程 / 后台节流等经验大部分通用；差异在进程生命周期归属：
Electron 的浏览器进程随应用生命周期，WebView2 的进程树由系统运行时按环境
管理。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;6.1 必须项&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// ready 之前调用
app.disableHardwareAcceleration() // 或 app.commandLine.appendSwitch(&apos;disable-gpu&apos;)

// 单实例：防止多实例各起一套进程组
if (!app.requestSingleInstanceLock()) {
  app.quit()
}

app.on(&apos;window-all-closed&apos;, () =&amp;gt; {
  if (process.platform !== &apos;darwin&apos;) app.quit()
})
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.2 关键 API&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;webPreferences.backgroundThrottling&lt;/code&gt;：默认 &lt;code&gt;true&lt;/code&gt;，后台节流动画与定时器
（同时影响 Page Visibility API）。&lt;strong&gt;不要随意关&lt;/strong&gt;，否则后台窗口持续满帧，
耗电且占内存；仅对确实需要后台保持帧率的窗口关闭。&lt;/li&gt;
&lt;li&gt;关窗用 &lt;code&gt;win.destroy()&lt;/code&gt;：跳过 &lt;code&gt;beforeunload&lt;/code&gt; 拦截，立即释放窗口资源；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;session.clearCache()&lt;/code&gt;：清理磁盘缓存；&lt;/li&gt;
&lt;li&gt;渲染进程复用：现代 Electron 默认启用，避免加载非 context-aware 的原生模块
破坏复用。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6.3 官方性能文档要点&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;精简依赖：评估每个模块的依赖树与加载成本，避免“服务器向”重型模块；&lt;/li&gt;
&lt;li&gt;延迟加载：不要在启动时一口气 require/执行所有代码；&lt;/li&gt;
&lt;li&gt;不阻塞主进程：异步 I/O、worker threads，避免同步 IPC 与 &lt;code&gt;@electron/remote&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;不阻塞渲染进程：&lt;code&gt;requestIdleCallback&lt;/code&gt; + Web Workers；&lt;/li&gt;
&lt;li&gt;不用 polyfill：目标 Chromium 版本已支持的能力直接用；&lt;/li&gt;
&lt;li&gt;资源本地化：字体、图片等不常变资源打包进应用，减少网络等待；&lt;/li&gt;
&lt;li&gt;用 DevTools Performance/Memory 反复测量后再优化。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;7. 避坑清单&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;共享环境关窗内存不回落&lt;/strong&gt;：Chromium 会保留渲染进程复用，关窗不等于释放。
一次性窗口用独立环境 + &lt;code&gt;destroy()&lt;/code&gt;，让 &lt;code&gt;BrowserProcessExited&lt;/code&gt; 触发。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;覆盖 browser args 丢失框架默认&lt;/strong&gt;：wry 默认的
&lt;code&gt;msWebOOUI/msPdfOOUI/msSmartScreenProtection&lt;/code&gt; 禁用项必须显式保留，否则被
重新启用，内存与后台活动上升。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不同参数共用数据目录&lt;/strong&gt;：wry 官方明确要求浏览器参数不同的 WebView 使用
不同数据目录，否则环境配置冲突。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;临时窗口 profile 残留旧会话&lt;/strong&gt;：销毁窗口后旧会话数据仍留在 profile 中，
复用时可能导致状态误判或串号；按业务需要每次清空或使用独立 profile。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;平台凭据存储有单条上限&lt;/strong&gt;：Windows Credential Manager 对 UTF-16 条目限制
约 2560 字符，大体积会话凭据可能超限；需评估文件存储等替代方案。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Windows 同步路径创建 WebView2 死锁&lt;/strong&gt;（Tauri）：窗口创建必须在 async
命令中执行。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;简单页面禁 GPU 可接受，复杂页面禁 GPU 卡顿&lt;/strong&gt;：滚动、动画、视频、WebGL
页面禁用硬件加速会明显掉帧，按页面复杂度决定。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;乱关后台节流&lt;/strong&gt;：&lt;code&gt;disable-background-timer-throttling&lt;/code&gt;（WebView2）与
&lt;code&gt;backgroundThrottling: false&lt;/code&gt;（Electron）会让后台持续消耗 CPU/电量。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;频繁 IPC / 大对象传输&lt;/strong&gt;：桥接层高频小消息或大对象往返会导致卡顿与内存
膨胀；批量、合并、分页、流式处理。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;生产依赖实验性 flags&lt;/strong&gt;：微软官方明确警告可能随时移除/改变行为；如需
使用，记录版本并在升级后回归。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;清 User Data Folder 不等待 &lt;code&gt;BrowserProcessExited&lt;/code&gt;&lt;/strong&gt;：删除会失败或残留
旧配置，导致更换环境配置/升级运行时失败。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;8. 验证与调优工具&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Windows 任务管理器&lt;/strong&gt;：按进程树查看 WebView2 运行时进程
（&lt;code&gt;msedgewebview2.exe&lt;/code&gt;）的 browser / renderer / GPU 各自内存；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;浏览器任务管理器 / &lt;code&gt;edge://inspect&lt;/code&gt;&lt;/strong&gt;：逐进程内存与 CPU；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DevTools Memory&lt;/strong&gt;：JS heap snapshot、DOM 节点数、事件监听器数量；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DevTools Performance&lt;/strong&gt;：帧率、长任务、布局抖动定位卡顿根因；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ETW + WPR/WPA&lt;/strong&gt;（微软官方 &lt;code&gt;WebView2.wprp&lt;/code&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;9. 决策速查表&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;要，配合 destroy 实现确定性回收&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;仅需隔离站点数据？&lt;/td&gt;
&lt;td&gt;同环境内用独立 profile，共享运行时&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;简单静态 UI 要不要 GPU？&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;生产能用实验性 flags 吗？&lt;/td&gt;
&lt;td&gt;谨慎，记录版本并回归；能不用就不用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;多实例怎么防？&lt;/td&gt;
&lt;td&gt;单实例锁（Electron）/ 框架自带单实例能力&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;10. 参考来源&lt;/h2&gt;
&lt;p&gt;注意：在使用以上优化指南之前，需要参考下方最新文档，避免时效差异&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;WebView2 性能最佳实践（微软官方）：
https://learn.microsoft.com/microsoft-edge/webview2/concepts/performance&lt;/li&gt;
&lt;li&gt;WebView2 进程相关事件（BrowserProcessExited / 清理 UDF 时机）：
https://learn.microsoft.com/microsoft-edge/webview2/concepts/process-related-events&lt;/li&gt;
&lt;li&gt;WebView2 浏览器 flags（实验性开关清单）：
https://learn.microsoft.com/microsoft-edge/webview2/concepts/webview-features-flags&lt;/li&gt;
&lt;li&gt;WebView2 MemoryUsageTargetLevel（微软官方 API）：
https://learn.microsoft.com/dotnet/api/microsoft.web.webview2.core.corewebview2.memoryusagetargetlevel&lt;/li&gt;
&lt;li&gt;WebView2 多 profile 支持：
https://learn.microsoft.com/microsoft-edge/webview2/concepts/multi-profile-support&lt;/li&gt;
&lt;li&gt;Wails v3 Application API（Windows 浏览器参数）：
https://v3.wails.io/reference/application/&lt;/li&gt;
&lt;li&gt;Wails v3 架构（进程与内存区域说明）：
https://v3.wails.io/concepts/architecture/&lt;/li&gt;
&lt;li&gt;Wails v3 Beta 发布说明：
https://github.com/wailsapp/wails/releases/tag/v3.0.0-beta.0&lt;/li&gt;
&lt;li&gt;Wails 性能优化清单：
https://v3.wails.io/guides/performance/&lt;/li&gt;
&lt;li&gt;Electron 官方性能文档：
https://www.electronjs.org/docs/latest/tutorial/performance&lt;/li&gt;
&lt;li&gt;Electron app API（单实例锁 / 禁用硬件加速）：
https://www.electronjs.org/docs/latest/api/app&lt;/li&gt;
&lt;li&gt;Tauri 2 配置参考（additionalBrowserArgs / backgroundThrottling）：
https://v2.tauri.app/reference/config/&lt;/li&gt;
&lt;li&gt;wry WebViewBuilderExtWindows（默认浏览器参数与数据目录警告）：
https://docs.rs/wry/latest/x86_64-pc-windows-msvc/wry/trait.WebViewBuilderExtWindows.html&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Docker使用教程和镜像加速</title><link>https://blog.emohe.cn/posts/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B%E5%92%8C%E9%95%9C%E5%83%8F%E5%8A%A0%E9%80%9F/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B%E5%92%8C%E9%95%9C%E5%83%8F%E5%8A%A0%E9%80%9F/</guid><pubDate>Tue, 14 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Docker Hub 镜像加速&lt;/h1&gt;
&lt;p&gt;国内拉取镜像有时会遇到困难，对于学习或者开发者来说很难受，此时可以配置镜像加速。&lt;/p&gt;
&lt;h3&gt;安装Docker&lt;/h3&gt;
&lt;p&gt;官方安装脚本：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;curl -fsSL https://get.docker.com | sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;国内一键安装脚本&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bash &amp;lt;(curl -sSL https://gitee.com/SuperManito/LinuxMirrors/raw/main/DockerInstallation.sh)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 手动离线安装Docker &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;h4&gt;下载 Docker:&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://download.docker.com/linux/static/stable/x86_64/&quot;&gt;官方文件下载地址——下载后上传到root目录&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/static/stable/x86_64/&quot;&gt;清华大学下载地址&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tar xzvf docker-26.1.3.tgz     # 替换版本号
sudo mv docker/* /usr/local/bin/
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;创建 Docker 服务文件&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;sudo vim /etc/systemd/system/docker.service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;添加以下内容&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Unit]
Description=Docker Application Container Engine
After=network-online.target firewalld.service
Wants=network-online.target

[Service]
Type=notify
ExecStart=/usr/local/bin/dockerd
ExecReload=/bin/kill -s HUP $MAINPID
Restart=always
RestartSec=2
StartLimitBurst=3
StartLimitInterval=60s
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
Delegate=yes
KillMode=process

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;启动并启用 Docker 服务&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;sudo chmod +x /usr/local/bin/dockerd
sudo systemctl daemon-reload
sudo systemctl start docker
sudo systemctl enable docker
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;查看版本&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;docker -v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt; 手动离线安装Docker-compose &amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;国内环境手动安装Docker-compose&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/docker/compose/releases&quot;&gt;点这里手动下载文件&lt;/a&gt; 上传到服务器的&lt;code&gt;/usr/local/bin&lt;/code&gt;目录&lt;/p&gt;
&lt;p&gt;重命名为docker-compose&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo cp docker-compose-linux-x86_64 /usr/local/bin/docker-compose
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;增加执行权限&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;chmod +x /usr/local/bin/docker-compose
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;验证安装&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker-compose --version
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意：
由于是以二进制文件安装的&lt;code&gt;docker-compose&lt;/code&gt;，所以运行命令有所变化，运行示例&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker-compose up -d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;区别在于中间的&lt;code&gt;-&lt;/code&gt;，官方安装脚本是以插件形式安装的&lt;code&gt;docker-compose&lt;/code&gt;，所以中间不需要&lt;code&gt;-&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;h2&gt;配置加速地址&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Ubuntu 16.04+、Debian 8+、CentOS 7+&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;创建或修改 &lt;code&gt;/etc/docker/daemon.json&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo mkdir -p /etc/docker
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;sudo tee /etc/docker/daemon.json &amp;lt;&amp;lt;EOF
{
    &quot;registry-mirrors&quot;: [
        &quot;https://docker.1panel.live&quot;
    ]
}
EOF
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl daemon-reload
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl restart docker
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;提示：如果不方便重启Docker服务，也可以不用设置全局加速地址，拉取镜像时增加加速地址即可，示例：&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;docker pull docker.1panel.live/library/mysql:5.7
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;说明：&lt;code&gt;library&lt;/code&gt;是一个特殊的命名空间，它代表的是官方镜像。如果是某个用户的镜像就把&lt;code&gt;library&lt;/code&gt;替换为镜像的用户名。&lt;/p&gt;
&lt;h3&gt;Docker Desktop 配置&lt;/h3&gt;
&lt;p&gt;对于&lt;code&gt;Windows&lt;/code&gt;系统的&lt;code&gt;Docker Desktop&lt;/code&gt;用户，点击右上角&lt;code&gt;设置&lt;/code&gt;，找到&lt;code&gt;Docker Engine&lt;/code&gt;然后修改配置，修改后的示例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;builder&quot;: {
    &quot;gc&quot;: {
      &quot;defaultKeepStorage&quot;: &quot;20GB&quot;,
      &quot;enabled&quot;: true
    }
  },
  &quot;experimental&quot;: false,
  &quot;registry-mirrors&quot;: [
    &quot;https://docker.1panel.live&quot;
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后点击右下角的&lt;code&gt;Apply &amp;amp; restart&lt;/code&gt;保存并重启即可。&lt;/p&gt;
&lt;h3&gt;检查加速是否生效&lt;/h3&gt;
&lt;p&gt;查看docker系统信息 &lt;code&gt;docker info&lt;/code&gt;，如果从结果中看到了你配置的加速地址，说明配置成功。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Registry Mirrors:
 [...]
 https://docker.1panel.live
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;使用代理拉取镜像&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;注意：使用了加速源就别使用这个方法了&lt;/li&gt;
&lt;li&gt;此方法支持&lt;code&gt;login&lt;/code&gt;和&lt;code&gt;push&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;创建配置文件&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;sudo mkdir -p /etc/systemd/system/docker.service.d
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;sudo vim /etc/systemd/system/docker.service.d/http-proxy.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;在文件中添加代理&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;[Service]
Environment=&quot;HTTP_PROXY=http://127.0.0.1:1080&quot;
Environment=&quot;HTTPS_PROXY=http://127.0.0.1:1080&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;重启Docker&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl daemon-reload
sudo systemctl restart docker
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;查看环境变量&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl show --property=Environment docker
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;本地流量转发到服务器&lt;/h4&gt;
&lt;p&gt;使用SSH反向转发把本地的10808端口的流量转发给远程服务器1080端口&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh -R 1080:127.0.0.1:10808 root@服务器地址 -N
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;-N&lt;/code&gt; 代表仅连接但不打开对话框&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;备用方法：打包镜像到本地&lt;/h2&gt;
&lt;p&gt;1：压缩保存镜像到本地&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker save 镜像名 &amp;gt; 镜像名.tar
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2：手动上传到另一个服务器&lt;/p&gt;
&lt;p&gt;3：另一个服务器解压镜像&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker load &amp;lt; 镜像名.tar
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4：查看镜像&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker images
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Docker Hub 镜像测速&lt;/h2&gt;
&lt;p&gt;拉取镜像时，可使用 &lt;code&gt;time&lt;/code&gt; 统计所花费的总时间。测速前记得移除本地的镜像。&lt;/p&gt;
&lt;p&gt;例如：&lt;code&gt;time docker pull node:latest&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;修改最大并发数加快镜像下载速度&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;/etc/docker/daemon.json&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;registry-mirrors&quot;: [
    &quot;https://docker.1panel.live&quot;
  ],
  &quot;max-concurrent-downloads&quot;: 10,
  &quot;max-concurrent-uploads&quot;: 10,
  &quot;max-download-attempts&quot;: 5,
  &quot;default-ulimits&quot;: {
    &quot;nofile&quot;: {
      &quot;Hard&quot;: 64000,
      &quot;Name&quot;: &quot;nofile&quot;,
      &quot;Soft&quot;: 64000
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl daemon-reload
sudo systemctl restart docker
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;为Docker启用IPV6&lt;/h2&gt;
&lt;p&gt;创建或修改&lt;code&gt;/etc/docker/daemon.json&lt;/code&gt;文件&lt;/p&gt;
&lt;p&gt;增加如下配置：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;ipv6&quot;: true,
  &quot;fixed-cidr-v6&quot;: &quot;fd00:1::/64&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后配置一下流量路由&lt;/p&gt;
&lt;p&gt;重启：&lt;code&gt;sudo systemctl restart docker&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;卸载Docker&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl stop docker
sudo apt-get purge docker-ce docker-ce-cli containerd.io
sudo rm -rf /etc/docker /var/lib/docker
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Docker最新稳定加速源列表&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;th&gt;加速类型&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://1panel.cn/docs/user_manual/containers/setting/&quot;&gt;1panel&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://docker.1panel.live&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;仅限CN&lt;/td&gt;
&lt;td&gt;Docker Hub&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://github.com/DaoCloud/public-image-mirror&quot;&gt;DaoCloud&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://docker.m.daocloud.io&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;白名单和限流&lt;/td&gt;
&lt;td&gt;Docker Hub&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://console.huaweicloud.com/swr/#/swr/dashboard&quot;&gt;华为云&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://***.mirror.swr.myhuaweicloud.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;需登录分配&lt;/td&gt;
&lt;td&gt;Docker Hub&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://cloud.tencent.com/document/product/1207/45596&quot;&gt;腾讯云&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://mirror.ccs.tencentyun.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;仅限腾讯云机器&lt;/td&gt;
&lt;td&gt;Docker Hub&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://doc.nju.edu.cn/books/e1654&quot;&gt;南京大学&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://ghcr.nju.edu.cn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ghcr加速&lt;/td&gt;
&lt;td&gt;ghcr&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://doc.nju.edu.cn/books/e1654&quot;&gt;南京大学&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://k8s.nju.edu.cn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;k8s加速&lt;/td&gt;
&lt;td&gt;k8s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;Docker常用命令:&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;/td&gt;
&lt;td&gt;&lt;code&gt;docker build -t 镜像名 .&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;先&lt;code&gt;docker login&lt;/code&gt;登录docker hub&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;推送镜像&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker push 用户名/镜像名&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;需先标记镜像 &lt;code&gt;docker tag 53321f173e 用户名/镜像名&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;查看容器&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker ps&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-a&lt;/code&gt;查看包括已停止的容器&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;容器资源占用&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker stats&lt;/code&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;docker inspect&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;挂载看&lt;code&gt;Mounts&lt;/code&gt;网络看&lt;code&gt;Networks&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;进入容器内部&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker exec -it 容器名 sh&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;结尾使用&lt;code&gt;/bash&lt;/code&gt;也可以&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;创建容器网络&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker network create my-network&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;my-network&lt;/code&gt;为网络名称&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;容器加入网络&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker network connect my-network 容器名&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;替换容器名或ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;宿主机网络&lt;/td&gt;
&lt;td&gt;&lt;code&gt;network_mode: host&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker-compose&lt;/code&gt;使用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;宿主机网络&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--network host&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker run&lt;/code&gt;使用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;查看网络&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker network inspect my-network&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;查看&lt;code&gt;my-network&lt;/code&gt;网络中的容器&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;停止容器&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker stop&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker stop 容器名或ID&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;启动容器&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker start&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker start 容器名或ID&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;重启容器&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker restart&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker restart 容器名或ID&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;删除容器&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker rm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker rm 容器名或ID&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;查看镜像&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker images&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker images 镜像名或ID&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;删除镜像&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker rmi -f&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker rmi -f 镜像名或ID&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;清除资源&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker system prune&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;清除所有未使用资源&lt;code&gt;容器 网络 镜像 缓存&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;删除所有镜像&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker rmi -f $(docker images -aq)&lt;/code&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;docker container prune -f&lt;/code&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;docker stop $(docker ps -aq)&lt;/code&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;docker compose down&lt;/code&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;docker compose up -d --force-recreate&lt;/code&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;docker cp wordpress:/app/data.yaml /home&lt;/code&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;docker cp /home/data.yaml wordpress:/app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;从宿主机复制到容器&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</content:encoded></item><item><title>使用Rust开发的Winui3应用</title><link>https://blog.emohe.cn/posts/winiso/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/winiso/</guid><pubDate>Thu, 09 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;微软官方的windows-rs库最近已经支持了winui3，可以直接使用winui3的控件了。地址：&lt;a href=&quot;https://github.com/microsoft/windows-rs/blob/master/docs/crates/windows-reactor.md&quot;&gt;windows-reactor&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;之前想要使用winui3，最低需要 net 6.0 以上来开发，但是net运行时在win系统中没有内置，也就是说想要避免环境差异，不仅需要打包&lt;code&gt;Windows App SDK&lt;/code&gt;到二进制文件中，还需要把 net 运行时也一起打包进去，避免用户自己下载运行时。&lt;/p&gt;
&lt;p&gt;现在微软官方在Rust中支持了winui3，就可以少打包一个运行时了，因为Rust是没有运行时的编译型语言，编译后的包体积也非常小。&lt;/p&gt;
&lt;h2&gt;小试牛刀&lt;/h2&gt;
&lt;p&gt;目前 windows-reactor 库还没发布到 crates.io，所以需要先通过Git引入依赖。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;windows-reactor = { git = &quot;https://github.com/microsoft/windows-rs&quot; }

windows-reactor-setup = { git = &quot;https://github.com/microsoft/windows-rs&quot; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;为了试试水，我开发一个用于获取微软 Windows 10/11 官方 ISO 临时下载链接的小工具。&lt;/p&gt;
&lt;p&gt;因为微软官方没有提供 ISO 镜像下载直链，所以我开发了一个GUI小工具可以直接提取直链。&lt;/p&gt;
&lt;h3&gt;下载地址&lt;/h3&gt;
&lt;p&gt;代码开源，欢迎大佬们下载体验：&lt;a href=&quot;https://cnb.cool/wanfeng789/winiso/-/releases&quot;&gt;winiso.msi下载地址&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;界面预览&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;png/win-iso.png&quot; alt=&quot;winiso&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;功能介绍&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;获取官方链接：&lt;/strong&gt; 选择版本、语言、架构，一键向微软 API 请求 ISO 临时下载地址&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多种组合：&lt;/strong&gt; Windows 10 / 11，简体中文 / English，x64 / arm64&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;复制链接：&lt;/strong&gt; 获取后可一键复制到剪贴板，用浏览器或其他工具下载&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多线程下载：&lt;/strong&gt; 内置 4 / 8 / 16 连接并行下载，显示进度与速度&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;
</content:encoded></item><item><title>安卓SDK封装笔记</title><link>https://blog.emohe.cn/posts/%E5%AE%89%E5%8D%93sdk%E5%B0%81%E8%A3%85/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/%E5%AE%89%E5%8D%93sdk%E5%B0%81%E8%A3%85/</guid><pubDate>Thu, 09 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;安卓SDK封装笔记&lt;/h1&gt;
&lt;h3&gt;Go 项目生成 Android SDK (AAR) 并在 Kotlin 调用&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;安装 gomobile&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;go install golang.org/x/mobile/cmd/gomobile@latest
go install golang.org/x/mobile/cmd/gobind@latest
gomobile init
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;安装安卓 NDK r29以上的版本
https://developer.android.com/ndk/downloads?hl=zh-cn&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;会自动满足16kb对齐的要求&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;生成 AAR&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;gomobile bind -v -target=android -androidapi 24 -trimpath -ldflags=&quot;-s -w -buildid=&quot; -o mylib.aar ./
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;封装安卓SDK大致思路&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;GO 语言中导出函数的公开接口需要首字母为大写&lt;/li&gt;
&lt;li&gt;一般只需暴露 Go 项目的启动、停止、重启、日志、生命周期等函数接口即可&lt;/li&gt;
&lt;li&gt;gobind 将 Go 名称直接转换为 Java 方法名，会遵循 Java 命名规范，也就是首字母是小写，调用方需要注意&lt;/li&gt;
&lt;li&gt;默认会生成一个&lt;code&gt;多种架构的通用 AAR&lt;/code&gt;，里面会包含所有 Go 支持的多种架构，只需在业务端打包时拆分ABI即可&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Rust 使用 uniffi 将项目打包为 Android AAR&lt;/h3&gt;
&lt;h4&gt;大致思路&lt;/h4&gt;
&lt;p&gt;接口声明 → 工具生成绑定 → 标准 NDK 编译 → 打包，避免手写 JNI 胶水代码。&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;和 Go &lt;code&gt;gomobile bind&lt;/code&gt; 思路完全一致&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;步骤&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Rust 侧暴露接口&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用 &lt;code&gt;#[uniffi::export]&lt;/code&gt; 宏标注要公开的函数/类型&lt;/li&gt;
&lt;li&gt;或编写一个 UDL 文件描述接口&lt;/li&gt;
&lt;li&gt;在 &lt;code&gt;Cargo.toml&lt;/code&gt; 中设置 &lt;code&gt;crate-type = [&quot;cdylib&quot;]&lt;/code&gt; 并添加 &lt;code&gt;uniffi&lt;/code&gt; 依赖&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;生成 Kotlin 绑定&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用 &lt;code&gt;uniffi-bindgen&lt;/code&gt; 命令行工具&lt;/li&gt;
&lt;li&gt;根据上一步的接口定义自动生成 Kotlin 调用代码&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;编译原生 .so&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用 &lt;code&gt;cargo-ndk&lt;/code&gt; 编译出 &lt;code&gt;arm64-v8a&lt;/code&gt; 等架构的 &lt;code&gt;.so&lt;/code&gt; 动态库&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;在 Android 项目中集成&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将生成的 Kotlin 文件和 &lt;code&gt;.so&lt;/code&gt; 文件放入 Android 项目目录&lt;/li&gt;
&lt;li&gt;或通过 Gradle 插件实现自动化&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;C 和 C++ 相关文档&lt;/h3&gt;
&lt;p&gt;https://android-docs.cn/studio/projects/add-native-code&lt;/p&gt;
</content:encoded></item><item><title>我的常用开发环境安装文档</title><link>https://blog.emohe.cn/posts/%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83%E5%AE%89%E8%A3%85/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83%E5%AE%89%E8%A3%85/</guid><pubDate>Tue, 07 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;PowerShell&lt;/h1&gt;
&lt;p&gt;解除脚本限制，执行完后重启PowerShell窗口生效。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 解除限制（当前用户）
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Force

# 完全解除限制
Set-ExecutionPolicy Unrestricted -Scope CurrentUser -Force
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;一行命令添加目录到系统环境变量（PATH）&lt;/h3&gt;
&lt;p&gt;添加某个目录到系统PATH（以flutter为例），需要管理员权限，执行完需要重新打开PowerShell&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$dir = &quot;D:\flutter\bin&quot;; [Environment]::SetEnvironmentVariable(&quot;Path&quot;, $env:Path + &quot;;$dir&quot;, &quot;Machine&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看当前系统PATH&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$env:Path
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Rust环境安装&lt;/h1&gt;
&lt;h3&gt;下载 Visual Studio 生成工具&lt;/h3&gt;
&lt;p&gt;下载地址：https://aka.ms/vs/stable/vs_BuildTools.exe&lt;/p&gt;
&lt;p&gt;下载后在&lt;code&gt;使用C++的桌面开发&lt;/code&gt;菜单里勾选如下组件即可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;适用于x64/x86 的MSVC 生成工具

Windows 11 SDK

用于Windows的C++ CMake 工具
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;安装Rust开发环境&lt;/h3&gt;
&lt;p&gt;下载地址：https://rust-lang.org/zh-CN/tools/install/&lt;/p&gt;
&lt;p&gt;下载后双击安装，一路回车即可。&lt;/p&gt;
&lt;h3&gt;常用命令&lt;/h3&gt;
&lt;p&gt;初始化&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 创建一个二进制项目（可执行程序）
cargo new mydemo

# 创建一个库项目（Library）
cargo new --lib mylibrary

# 当前目录初始化项目
cargo init
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;编译与运行&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cargo run                     # 编译并运行（最常用）
cargo run --release           # Release 模式运行（更快）
cargo build                   # 只编译
cargo build --release         # Release 模式编译
cargo check                   # 快速检查错误（推荐开发时使用，速度快）
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;依赖管理&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cargo add serde               # 添加依赖（最新推荐方式）
cargo add serde --features derive
cargo remove serde            # 移除依赖
cargo update                  # 更新依赖到最新版本
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;清理与发布&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cargo clean                   # 清理 target 目录
cargo clippy                  # 运行 Clippy 代码检查
cargo fmt                     # 代码格式化
cargo publish                 # 发布到 crates.io
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;项目里配置加速&lt;/h3&gt;
&lt;p&gt;项目根目录添加&lt;code&gt;.cargo\config.toml&lt;/code&gt;配置字节跳动的Rust镜像&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[source.crates-io]
replace-with = &apos;rsproxy-sparse&apos;
[source.rsproxy]
registry = &quot;https://rsproxy.cn/crates.io-index&quot;
[source.rsproxy-sparse]
registry = &quot;sparse+https://rsproxy.cn/index/&quot;
[registries.rsproxy]
index = &quot;https://rsproxy.cn/crates.io-index&quot;
[net]
git-fetch-with-cli = true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;git-fetch-with-cli = true&lt;/code&gt;代表使用系统的Git工具&lt;/p&gt;
&lt;h1&gt;安卓开发环境&lt;/h1&gt;
&lt;p&gt;安装IDE下载地址：https://developer.android.com/studio&lt;/p&gt;
&lt;p&gt;AI android-cli：https://developer.android.com/tools/agents/android-cli/download.md.txt&lt;/p&gt;
&lt;p&gt;中文汉化包：https://github.com/sollyu/AndroidStudioChineseLanguagePack&lt;/p&gt;
&lt;h3&gt;汉化&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;安装汉化包&lt;br /&gt;
&lt;code&gt;右上角设置 ⚙️&lt;/code&gt; → &lt;code&gt;Plugins…&lt;/code&gt; → &lt;code&gt;Plugins页面的设置 ⚙️&lt;/code&gt; → &lt;code&gt;Install Plugin from Disk…&lt;/code&gt; → 选择汉化包 → OK → 重启 IDE&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;切换语言和地区&lt;br /&gt;
&lt;code&gt;右上角设置 ⚙️&lt;/code&gt; → &lt;code&gt;Settings…&lt;/code&gt; → &lt;code&gt;Appearance &amp;amp; Behavior&lt;/code&gt; → &lt;code&gt;System Settings&lt;/code&gt; → &lt;code&gt;Language and Region&lt;/code&gt; → 语言选&lt;code&gt;简体中文&lt;/code&gt;、地区选&lt;code&gt;China Mainland&lt;/code&gt;→ OK → 重启 IDE&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Gradle国内镜像配置&lt;/h3&gt;
&lt;p&gt;在项目根目录下的&lt;code&gt;settings.gradle&lt;/code&gt;（或&lt;code&gt;settings.gradle.kts&lt;/code&gt;）中添加：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gradlepluginManagement {
    repositories {
        maven { url &apos;https://mirrors.aliyun.com/gradle-plugin&apos; }
        maven { url &apos;https://mirrors.cloud.tencent.com/gradle-plugin&apos; }
        google()
        mavenCentral()
    }
}

dependencyResolutionManagement {
    repositories {
        maven { url &apos;https://mirrors.aliyun.com/maven&apos; }
        maven { url &apos;https://mirrors.cloud.tencent.com/maven&apos; }
        google()
        mavenCentral()
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;配置代理&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;项目根目录/gradle.properties&lt;/code&gt; 追加：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;systemProp.http.proxyHost=127.0.0.1
systemProp.http.proxyPort=10808
systemProp.https.proxyHost=127.0.0.1
systemProp.https.proxyPort=10808
# 不走代理的地址
systemProp.http.nonProxyHosts=*.aliyun.com|*.tencent.com
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&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;清 IDE 缓存&lt;/td&gt;
&lt;td&gt;File → Invalidate Caches （使缓存失效...）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;清 Gradle 依赖缓存&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~/.gradle/caches/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;清 Gradle 分发&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~/.gradle/wrapper/dists/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;清构建产物&lt;/td&gt;
&lt;td&gt;&lt;code&gt;./gradlew clean&lt;/code&gt; 或删除 &lt;code&gt;app/build&lt;/code&gt; 目录&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;gradlew&lt;/code&gt;命令临时变量设置&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$env:JAVA_HOME = &quot;IDE安装目录\jbr&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Gradle Wrapper下载加速&lt;/h3&gt;
&lt;p&gt;打开项目中&lt;code&gt;gradle/wrapper/gradle-wrapper.properties&lt;/code&gt;修改：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 阿里云
distributionUrl=https\://mirrors.aliyun.com/gradle/distributions/gradle-9.5.0-all.zip
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;常用命令&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;./gradlew tasks                    # 查看所有可用任务
./gradlew build                    # 完整构建项目
./gradlew clean                    # 清理项目依赖

./gradlew assembleDebug            # 构建 Debug APK
./gradlew assembleRelease          # 构建 Release APK
./gradlew bundleRelease            # 构建 AAB（Google Play）

./gradlew installDebug             # 构建并安装 Debug 版到设备
./gradlew installRelease           # 安装 Release 版

./gradlew sync                     # 同步 Gradle 项目
./gradlew dependencies             # 查看完整依赖树
./gradlew app:dependencies         # 查看 app 模块依赖

./gradlew test                     # 运行单元测试
./gradlew lint                     # 运行代码检查
./gradlew connectedAndroidTest     # 运行设备上的测试

./gradlew clean assembleDebug      # 清理后构建 Debug
./gradlew build --parallel         # 并行构建（更快）
./gradlew wrapper --gradle-version 8.10   # 更新 Gradle Wrapper
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Flutter环境安装&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://atomgit.com/CPF-Flutter/flutter_flutter&quot;&gt;鸿蒙Flutter SDK&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;安装安卓 IDE&lt;/h3&gt;
&lt;p&gt;Flutter常用于移动端开发，所以建议先安装安卓IDE，具体如何安装见上文，如果需要打包PC端则需要对应环境，例如C++的桌面开发环境&lt;/p&gt;
&lt;h3&gt;安装Flutter SDK&lt;/h3&gt;
&lt;p&gt;Flutter SDK下载页面：https://docs.flutter.cn/install/manual&lt;/p&gt;
&lt;p&gt;下载后按照页面步骤 将Flutter添加到PATH，也就是环境变量&lt;/p&gt;
&lt;h3&gt;安装安卓 SDK&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;png/android-sdk.png&quot; alt=&quot;安卓SDK安装&quot; /&gt;&lt;/p&gt;
&lt;p&gt;运行环境检查&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;flutter doctor -v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后在安卓IDE里安装插件：https://docs.flutter.cn/tools/android-studio#setup&lt;/p&gt;
&lt;h3&gt;初始化项目&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;flutter create --platforms=android,ios,linux,macos,windows \
  --org com.example \
  --project-name myapp \
  myapp
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Flutter 常用命令&lt;/h4&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;flutter doctor -v&lt;/td&gt;
&lt;td&gt;检查 Flutter 开发环境&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flutter doctor --android-licenses&lt;/td&gt;
&lt;td&gt;接受 Android SDK 许可证&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flutter config --enable-windows-desktop&lt;/td&gt;
&lt;td&gt;启用 Windows 桌面支持&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flutter devices&lt;/td&gt;
&lt;td&gt;查看已连接设备&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flutter emulators&lt;/td&gt;
&lt;td&gt;查看模拟器列表&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flutter create myapp&lt;/td&gt;
&lt;td&gt;创建 Flutter 项目&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flutter create .&lt;/td&gt;
&lt;td&gt;为当前项目补充缺失平台目录&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flutter pub get&lt;/td&gt;
&lt;td&gt;下载项目依赖&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flutter pub upgrade&lt;/td&gt;
&lt;td&gt;升级依赖&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flutter pub outdated&lt;/td&gt;
&lt;td&gt;检查可升级依赖&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flutter clean&lt;/td&gt;
&lt;td&gt;清理构建缓存&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flutter run&lt;/td&gt;
&lt;td&gt;运行项目&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flutter run -d chrome&lt;/td&gt;
&lt;td&gt;在 Chrome 中运行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flutter run -d windows&lt;/td&gt;
&lt;td&gt;在 Windows 桌面运行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flutter run -d android&lt;/td&gt;
&lt;td&gt;在 Android 设备运行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flutter run --release&lt;/td&gt;
&lt;td&gt;Release 模式运行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flutter build apk&lt;/td&gt;
&lt;td&gt;构建 APK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flutter build appbundle&lt;/td&gt;
&lt;td&gt;构建 AAB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flutter build windows&lt;/td&gt;
&lt;td&gt;构建 Windows 程序&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flutter build apk --release&lt;/td&gt;
&lt;td&gt;构建 Release APK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flutter build apk --split-per-abi&lt;/td&gt;
&lt;td&gt;按 ABI 拆分 APK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flutter test&lt;/td&gt;
&lt;td&gt;运行单元测试&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flutter analyze&lt;/td&gt;
&lt;td&gt;静态代码检查&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flutter format .&lt;/td&gt;
&lt;td&gt;格式化代码&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flutter --version&lt;/td&gt;
&lt;td&gt;查看版本&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1&gt;Go 开发环境&lt;/h1&gt;
&lt;p&gt;下载地址：https://go.dev/doc/install&lt;/p&gt;
&lt;h3&gt;Go 国内镜像与代理配置（PowerShell 命令）&lt;/h3&gt;
&lt;p&gt;国内镜像&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;go env -w GOPROXY=&quot;https://goproxy.cn,direct&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;恢复官方镜像&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;go env -w GOPROXY=&quot;https://proxy.golang.org,direct&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Go代理&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;go env -w HTTP_PROXY=&quot;http://127.0.0.1:10808&quot;
go env -w HTTPS_PROXY=&quot;http://127.0.0.1:10808&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;移除代理&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;go env -w HTTP_PROXY=&quot;&quot;
go env -w HTTPS_PROXY=&quot;&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看全局配置&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;go env
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;常用命令&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;go mod init myproject                         # 初始化模块
go mod tidy                                   # 清理和更新依赖（最常用）

go run .                                      # 运行当前项目
go build                                      # 编译当前项目
go build -o app.exe                           # 指定输出文件名

go test -v                                    # 运行测试
go fmt ./...                                  # 格式化代码
go vet                                        # 静态检查
go mod tidy                                   # 整理依赖

go version                                    # 查看版本
go env                                        # 查看环境变量
go clean -cache                               # 清理缓存
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;质量检查&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
golangci-lint run
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;go install honnef.co/go/tools/cmd/staticcheck@latest
staticcheck ./...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;交叉编译&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 交叉编译 Windows 64位
$env:GOOS=&quot;windows&quot;; $env:GOARCH=&quot;amd64&quot;; $env:CGO_ENABLED=&quot;0&quot;; go build -ldflags=&quot;-s -w&quot; -o myapp.exe

# 交叉编译 Linux 64位
$env:GOOS=&quot;linux&quot;; $env:GOARCH=&quot;amd64&quot;; $env:CGO_ENABLED=&quot;0&quot;; go build -ldflags=&quot;-s -w&quot; -o myapp

# 交叉编译 Android ARM64
$env:GOOS=&quot;android&quot;; $env:GOARCH=&quot;arm64&quot;; $env:CGO_ENABLED=&quot;0&quot;; go build -ldflags=&quot;-s -w&quot; -o myapp
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags=&quot;-s -w&quot; -o myapp
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;前端开发环境&lt;/h1&gt;
&lt;p&gt;nodejs下载地址：https://nodejs.org/zh-cn&lt;/p&gt;
&lt;h3&gt;国内镜像&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 设置淘宝镜像
npm config set registry https://registry.npmmirror.com

# 恢复官方镜像
npm config set registry https://registry.npmjs.org
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;代理&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 设置代理
npm config set proxy http://127.0.0.1:10808
npm config set https-proxy http://127.0.0.1:10808

# 取消代理
npm config delete proxy
npm config delete https-proxy
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;常用命令&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;axios&lt;/code&gt;是包名&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm i                              # 安装依赖
npm install axios                  # 安装依赖（生产环境）
npm install axios --save-dev       # 安装开发依赖
npm install axios@latest           # 安装最新版本

npm uninstall axios                  # 卸载包
npm cache clean --force              # 清理缓存

npm start                            # 运行 start 脚本
npm run dev                          # 运行 dev 脚本
npm run build                        # 运行 build 脚本

npm list                             # 查看已安装依赖
npm outdated                         # 检查可更新依赖
npm view express version             # 查看某个包的最新版本

npm install -g pnpm                  # 全局安装工具（pnpm包管理器）
npm update -g                        # 更新全局包
npm -v                               # 查看 npm 版本
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&lt;code&gt;C#&lt;/code&gt;开发环境&lt;/h1&gt;
&lt;h3&gt;&lt;code&gt;.NET&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;https://dotnet.microsoft.com/zh-cn/download/dotnet&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;.NET Framework 4.8&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;https://dotnet.microsoft.com/zh-cn/download/dotnet-framework/net48&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 恢复项目依赖，下载 NuGet 包并生成依赖缓存
dotnet restore

# 安装代码格式化
dotnet tool install -g dotnet-format

# 执行代码格式化
dotnet format

# 编译项目并执行静态代码分析
dotnet build -warnaserror -p:AnalysisLevel=latest

# 运行单元测试，确保代码逻辑正确
dotnet test

# 热重载运行
dotnet watch run

# 生成可部署的生产版本，放在 publish 目录
dotnet publish -c Release
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Flutter跨端应用的架构设计</title><link>https://blog.emohe.cn/posts/flutter%E8%B7%A8%E7%AB%AF%E5%BA%94%E7%94%A8%E7%9A%84%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/flutter%E8%B7%A8%E7%AB%AF%E5%BA%94%E7%94%A8%E7%9A%84%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/</guid><pubDate>Wed, 17 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;Flutter最吸引我的地方就是多平台能力和跨端能力，并且他也是移动端应用比较主流的框架。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一套代码编译多端应用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Android、iOS、Windows、macOS、Linux，甚至 Web，都可以使用同一套 Flutter 代码构建应用。如果全部都使用纯Dart语言实现的话，会非常方便和效率。&lt;/p&gt;
&lt;p&gt;但随着项目复杂度不断提高，我发现这些场景不太够用，对于系统强关联和性能要求高的应用，纯Dart实现就不太合适，所以就需要配合其他语言来处理。&lt;/p&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;文件系统，本地服务，后台任务，蓝牙通信，USB 设备，系统通知，系统权限，等等...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这些能力都带有明显的平台特征，不同平台的生命周期，权限模型，系统能力，等等也都不同。&lt;/p&gt;
&lt;p&gt;还有一些特殊需求场景：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;大规模并发，高性能网络转发，AI 推理，密码学计算，压缩算法，数据库引擎，等等...
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;架构理解&lt;/h2&gt;
&lt;p&gt;其实很多跨端框架都是UI层面和业务层面的，系统层面和核心能力需要抽象出来，然后封装一个统一接口供业务层面调用。&lt;/p&gt;
&lt;h2&gt;大致思路：&lt;/h2&gt;
&lt;p&gt;Flutter负责UI和业务逻辑，接口层负责平台差异，跨平台语言负责真正的后端运行。&lt;/p&gt;
&lt;p&gt;对于需要调用系统能力的后端，我觉得最好是使用跨平台能力优秀的编译型语言，例如&lt;code&gt;go，rust，C，C++&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;以下以go语言为后端示例，其他语言思路也差不多。&lt;/p&gt;
&lt;h2&gt;流程如下：&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;App 启动
↓
初始化依赖注入 / 配置 / 存储
↓
注册当前平台的 RuntimeService
↓
加载 Flutter UI
↓
用户操作 UI
↓
ViewModel 处理状态与交互
↓
UseCase / Repository 执行业务逻辑
↓
调用统一 RuntimeService 接口
↓
Platform Adapter 适配当前平台
↓
调用原生内核
   Android → AAR / .so
   Windows → exe
   Linux → binary
   macOS → binary / framework
   iOS → XCFramework / NetworkExtension
↓
内核回传状态 / 日志 / 错误 / 流量统计
↓
RuntimeService 统一转换为 Dart Model / Stream
↓
ViewModel 更新状态
↓
UI 自动刷新
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;最简结构:&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;lib/
├── main.dart
├── ui/              # 页面、组件、ViewModel
├── domain/          # Model、UseCase、抽象接口
├── data/            # Repository、Service实现
└── platform/        # Android/Windows/Linux/macOS/iOS 适配器
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;后端封装统一接口&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Android / iOS： Go 内核 SDK 化，做成 AAR / XCFramework 

Windows / macOS / Linux： Go 内核 daemon 化，本地 Socket IPC调用
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后Flutter层调用统一接口。&lt;/p&gt;
&lt;p&gt;这里更建议桌面端优先使用 &lt;code&gt;Unix Socket / Named Pipe&lt;/code&gt; 进行IPC互相通信，而非 &lt;code&gt;127.0.0.1 TCP&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;因为无需开放端口、性能更高、安全性更好，冲突也能更少，也更符合本地进程通信场景。&lt;/p&gt;
&lt;p&gt;当然需要对接公网后端服务的话除外。&lt;/p&gt;
&lt;h3&gt;后端封装接口 - 代码结构示例&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;core/
├─ runtime/        # 纯 Go 核心逻辑：启动、停止、reload、状态、日志
├─ api/            # 统一接口定义
├─ mobile/         # Android/iOS 导出层：AAR / XCFramework
├─ daemon/         # Windows/macOS/Linux 守护进程入口
└─ ipc/            # 桌面 IPC：Named Pipe / Unix Socket
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>给博客补上面向AI的入口</title><link>https://blog.emohe.cn/posts/%E7%BB%99%E5%8D%9A%E5%AE%A2%E8%A1%A5%E4%B8%8A%E9%9D%A2%E5%90%91ai%E7%9A%84%E5%85%A5%E5%8F%A3/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/%E7%BB%99%E5%8D%9A%E5%AE%A2%E8%A1%A5%E4%B8%8A%E9%9D%A2%E5%90%91ai%E7%9A%84%E5%85%A5%E5%8F%A3/</guid><pubDate>Mon, 18 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;给博客补上 llms.txt&lt;/h2&gt;
&lt;p&gt;最近给博客补了一点面向大语言模型的入口，也就是 &lt;a href=&quot;https://llmstxt.org/&quot;&gt;&lt;code&gt;llms.txt&lt;/code&gt;&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;llms.txt 是一个为大型语言模型（LLM）设计的网站文件标准，目的是让 AI 在需要时能快速、准确地理解并利用网站内容。用一份简洁的 Markdown 文件告诉模型：这个网站是什么、哪些页面比较重要、哪些内容适合优先阅读。按照规范，它通常放在站点根路径 &lt;code&gt;/llms.txt&lt;/code&gt;，内容尽量清晰、克制，不需要把整个网站都塞进去。&lt;/p&gt;
&lt;p&gt;这次我给博客补了两类入口：站点根目录的 &lt;code&gt;/llms.txt&lt;/code&gt;，以及每篇文章对应的 Markdown 原文地址，比如 &lt;code&gt;/posts/docker.md&lt;/code&gt;。这样正常读者还是看原来的博客页面，而模型、阅读工具或者其他自动化程序，可以直接读取更干净的文本内容。&lt;/p&gt;
&lt;p&gt;一开始直接引入astro现成库 &lt;a href=&quot;https://www.npmjs.com/package/@nuasite/llm-enhancements&quot;&gt;&lt;code&gt;@nuasite/llm-enhancements&lt;/code&gt;&lt;/a&gt;。但是过程中发现和我当前这个 Astro 项目不太合拍：它对 Vite、TypeScript 的版本要求过高，还会因为可选依赖解析导致本地开发服务启动失败。&lt;/p&gt;
&lt;p&gt;为了不强行升级整套 Astro 生态，最后还是改成自己写一个很小的 Astro integration。开发环境里通过中间件提供 &lt;code&gt;/llms.txt&lt;/code&gt; 和 &lt;code&gt;/posts/*.md&lt;/code&gt;；正式构建时，再把这些文件写进最终产物。内容直接来自 &lt;code&gt;src/content/posts&lt;/code&gt;，所以不用手动维护，也不用把生成文件提前塞进 &lt;code&gt;public&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;文章页也顺手加了两个小入口：复制 MD 链接，以及打开 MD 版本。位置放在文章元信息下面，不太显眼，但需要的时候能很快找到。&lt;/p&gt;
&lt;p&gt;中间还遇到一个小坑：博客用了Swup做页面切换，而 &lt;code&gt;.md&lt;/code&gt; 是纯文本资源，不是普通 HTML 页面。如果让 Swup 接管这个链接，返回时页面状态可能会乱，样式也会像丢了一样。最后给 MD 链接加了 &lt;code&gt;data-no-swup&lt;/code&gt;，让浏览器原生打开这个资源，问题就解决了。&lt;/p&gt;
</content:encoded></item><item><title>Go语言版本的轻量级龙虾</title><link>https://blog.emohe.cn/posts/%E9%BE%99%E8%99%BEai/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/%E9%BE%99%E8%99%BEai/</guid><pubDate>Sun, 17 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;原版龙虾 openclow 实在太臃肿了，根本没办法在资源受限的设备上跑，最近我又想部署个AI助手在我的路由器上跑一些定时任务，搜索了多个版本的龙虾，最后决定使用 PicoClaw。&lt;/p&gt;
&lt;p&gt;PicoClaw 是一款用 Go 语言编写的超轻量个人AI助手。只需部署一个二进制文件，就能在任何设备上运行，包括树莓派或者路由器这些资源受限的设备。
初始内存占用不到&lt;code&gt;10MB&lt;/code&gt;，长期运行内存稳定在30MB以内。包体积也不到50MB，秒级启动。并且对标原版龙虾的能力，而且中文生态友好。&lt;/p&gt;
&lt;h2&gt;Windows部署&lt;/h2&gt;
&lt;p&gt;windows下载：https://github.com/sipeed/picoclaw/releases/latest/download/picoclaw_Windows_x86_64.zip&lt;/p&gt;
&lt;p&gt;解压后双击运行&lt;code&gt;picoclaw-launcher&lt;/code&gt;会自动打开浏览器，然后通过管理面板配置。&lt;/p&gt;
&lt;h2&gt;Docker部署&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;docker-compose.yml&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  picoclaw:
    image: sipeed/picoclaw:launcher
    container_name: picoclaw
    restart: always
    ports:
      - &quot;18800:18800&quot;
      - &quot;18790:18790&quot;
    volumes:
      - ./data:/root/.picoclaw
    environment:
      TZ: Asia/Shanghai
      PICOCLAW_GATEWAY_HOST: 0.0.0.0
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Linux 宿主机部署（systemd）&lt;/h2&gt;
&lt;h4&gt;1. 安装&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;mkdir -p /opt/picoclaw
cd /opt/picoclaw

wget https://github.com/sipeed/picoclaw/releases/latest/download/picoclaw_Linux_x86_64.tar.gz

tar -xzf picoclaw_Linux_x86_64.tar.gz

chmod +x picoclaw-launcher
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 创建 systemd 服务&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;nano /etc/systemd/system/picoclaw.service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;写入：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Unit]
Description=PicoClaw Launcher
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
WorkingDirectory=/opt/picoclaw
ExecStart=/opt/picoclaw/picoclaw-launcher -public

Restart=always
RestartSec=3

Environment=TZ=Asia/Shanghai

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. 启动服务&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;systemctl daemon-reload

systemctl enable --now picoclaw
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4. 查看状态&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;systemctl status picoclaw
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;5. 查看日志&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;journalctl -u picoclaw -f
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;6. 重启服务&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;systemctl restart picoclaw
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;7. 停止服务&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;systemctl stop picoclaw
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;8. WebUI 地址&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;http://你的服务器IP:18800
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;9. 数据目录&lt;/h4&gt;
&lt;p&gt;默认保存：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/root/.picoclaw
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;里面包含：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;配置&lt;/li&gt;
&lt;li&gt;Provider&lt;/li&gt;
&lt;li&gt;Workspace&lt;/li&gt;
&lt;li&gt;对话&lt;/li&gt;
&lt;li&gt;Channel&lt;/li&gt;
&lt;li&gt;Cron&lt;/li&gt;
&lt;li&gt;Agent 数据&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>最新PXE网络安装系统教程</title><link>https://blog.emohe.cn/posts/pxe/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/pxe/</guid><pubDate>Mon, 27 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;简介&lt;/h2&gt;
&lt;p&gt;PXE是一套让电脑从网卡启动而无需从硬盘或U盘启动的标准协议，支持多种系统，并且非常适合批量安装系统的场景&lt;/p&gt;
&lt;p&gt;本文中用到的 &lt;code&gt;netboot.xyz&lt;/code&gt; iPXE固件是一个开源项目，是基于iPXE实现的增强版网络装系统的启动工具，内置了很多功能。&lt;/p&gt;
&lt;h2&gt;Linux系统网络重装系统&lt;/h2&gt;
&lt;h4&gt;查看系统信息并备份&lt;/h4&gt;
&lt;p&gt;出意外时可用于手动配置&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 查看系统盘
df -h /

# 查看默认网关
ip r

# 查看默认网络接口
ip a
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;下载 netboot.xyz 启动镜像（iPXE引导器）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;wget https://boot.netboot.xyz/ipxe/netboot.xyz.img
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;将镜像DD写入系统盘（记得替换你实际的系统盘，会覆盖原系统，请确保重要数据已备份）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;dd if=netboot.xyz.img of=/dev/vda bs=4M
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;强制将缓存写入磁盘，确保数据落盘完成&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;sync
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;重启服务器&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;reboot
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;进入启动菜单，选择网络安装&lt;/h4&gt;
&lt;p&gt;云服务器厂商可以在管理后台通过VNC界面操作&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Linux Network Installs (64-bit)
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;如果重装成功，结束后会自动进入新系统，需要注意的是新系统一般没有开启root用户的ssh权限，需要用普通用户ssh连接，然后切换为root用户后再修改。&lt;code&gt;Alpine&lt;/code&gt;系统默认是安装在内存中的，进入系统后需要执行&lt;code&gt;setup-alpine&lt;/code&gt;命令安装到硬盘。&lt;/p&gt;
&lt;p&gt;如果安装失败的话可能是硬盘分区有问题或者其他设置不对，可以返回到&lt;code&gt;netboot.xyz 启动菜单&lt;/code&gt;，重新选择网络安装再装一遍即可。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;国内镜像站&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;netboot.xyz&lt;/code&gt;项目下载系统镜像是在各大镜像官网下载的，国内服务器下载会很慢，可以加载自己的ipxe脚本，然后脚本里使用国内镜像源。&lt;/p&gt;
&lt;p&gt;可以在第6步中，选择&lt;code&gt;IPXE shell&lt;/code&gt;加载自己的脚本，命令示例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;chain https://example.com/embed.ipxe
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Windows系统重装为Linux&lt;/h2&gt;
&lt;h4&gt;下载 netboot.xyz 启动镜像&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;C&lt;/code&gt;盘里创建一个&lt;code&gt;boot&lt;/code&gt;目录&lt;/p&gt;
&lt;p&gt;下载&lt;code&gt;https://boot.netboot.xyz/ipxe/netboot.xyz.img&lt;/code&gt;放到&lt;code&gt;boot&lt;/code&gt;目录&lt;/p&gt;
&lt;h4&gt;以管理员权限执行命令添加启动项&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;bcdedit /copy {current} /d &quot;Linux Install&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行完成后会显示一个GUID，类似于&lt;code&gt;{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;确认GUID后，逐条复制粘贴执行，记得替换GUID：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bcdedit /set {你的GUID} path \boot\netboot.xyz.img

bcdedit /set {你的GUID} loadoptions &quot;initrd=\boot\netboot.xyz.img&quot;

bcdedit /set {你的GUID} inirootpath \boot\netboot.xyz.img

bcdedit /timeout 60
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;验证配置&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;bcdedit /enum {你的GUID}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;确认输出中至少包含：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;path                    \boot\netboot.xyz.img
description              Linux Install
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;手搓PXE服务&lt;/h2&gt;
&lt;h4&gt;安装dnsmasq&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;apt update
apt install -y dnsmasq
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;创建和编辑 &lt;code&gt;/etc/dnsmasq.d/pxe.conf&lt;/code&gt; 配置ProxyDHCP和TFTP服务，要和PXE客户端（要装系统的设备）在同一局域网。&lt;/p&gt;
&lt;p&gt;此配置使用&lt;code&gt;ProxyDHCP&lt;/code&gt;模式，不会跟其他&lt;code&gt;DHCP&lt;/code&gt;服务冲突，只会响应和下发PXE请求&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# ProxyDHCP + TFTP
# 关闭 DNS
port=0

# 日志
log-dhcp
log-facility=/var/log/dnsmasq.log

# 改1：你的网段
dhcp-range=192.168.1.0,proxy

# 改2：本机 IP
dhcp-option=option:tftp-server,192.168.1.10

# ==================== TFTP （放IPXE固件）====================
enable-tftp
tftp-root=/etc/tftp

# ==================== PXE 引导 ====================
pxe-service=x86PC,       &quot;PXE BIOS&quot;,             netboot.xyz-undionly.kpxe
pxe-service=X86-64_EFI,  &quot;PXE UEFI x64&quot;,         netboot.xyz-snponly.efi
pxe-service=ARM64_EFI,   &quot;PXE UEFI ARM64&quot;,       netboot.xyz-arm64-snponly.efi
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;管理命令&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;# 启动服务
sudo systemctl start dnsmasq

# 设置开机自启
sudo systemctl enable dnsmasq

# 查看运行状态
sudo systemctl status dnsmasq

# 查看实时日志
sudo tail -f /var/log/dnsmasq.log

# 重启服务
sudo systemctl restart dnsmasq
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;netboot.xyz的IPXE固件下载地址&lt;/h4&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;strong&gt;Legacy (PCBIOS)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;netboot.xyz-undionly.kpxe&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;https://boot.netboot.xyz/ipxe/netboot.xyz-undionly.kpxe&lt;/td&gt;
&lt;td&gt;传统 BIOS DHCP 引导文件，通用网卡驱动&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UEFI x86_64&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;netboot.xyz-snponly.efi&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;https://boot.netboot.xyz/ipxe/netboot.xyz-snponly.efi&lt;/td&gt;
&lt;td&gt;UEFI 引导文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UEFI ARM64&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;netboot.xyz-arm64-snponly.efi&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;https://boot.netboot.xyz/ipxe/netboot.xyz-arm64-snponly.efi&lt;/td&gt;
&lt;td&gt;ARM64 UEFI 引导文件&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;netboot.xyz本地钩子&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;netboot.xyz&lt;/code&gt;官方固件启动后会自动调用&lt;code&gt;local-vars.ipxe&lt;/code&gt;脚本，如果存在的话。&lt;/p&gt;
&lt;p&gt;判断脚本是否存在：
他会自动扫描你tftp目录里的&lt;code&gt;local-vars.ipxe&lt;/code&gt;脚本，你需要提前创建并放到tftp目录。如果没有的话他会走自己菜单的逻辑。并且是根据&lt;code&gt;DHCP&lt;/code&gt;响应里的&lt;code&gt;server&lt;/code&gt;信息找到你的tftp的IP地址。&lt;/p&gt;
&lt;h4&gt;编译自己的IPXE固件&lt;/h4&gt;
&lt;p&gt;当然你也可以从 &lt;a href=&quot;https://github.com/ipxe/ipxe&quot;&gt;IPXE官方项目地址&lt;/a&gt; 自行编译，这样你就可以将自己的ipxe启动脚本内置进去。&lt;/p&gt;
&lt;h4&gt;ipxe脚本示例&lt;/h4&gt;
&lt;p&gt;ipxe脚本是核心步骤，从这里定义去哪里下载镜像，其他资源，和如何启动。&lt;/p&gt;
&lt;p&gt;可根据自己情况修改，本地镜像文件则需要你启动一个HTTP服务，然后将镜像文件放进去，供客户端下载，适合离线环境。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!ipxe
# ==================== iPXE 网络安装菜单 ====================
# 包含 Debian 12 安装 + Windows PE 安装

# 镜像文件托管地址（本地和高校镜像站二选一）
set server_ip http://192.168.1.10
set debian_mirror http://mirrors.cernet.edu.cn

# ==================== 菜单界面 ====================
:start
menu iPXE Network Install Menu
item debian    Debian 12 Automated Install
item winpe     Windows PE Install (Load boot.wim)
item shell     Enter iPXE Shell
item reboot    Reboot
choose --default debian --timeout 10000 option

goto ${option}

# ==================== Debian 12 安装 ====================
:debian
echo Starting Debian 12 automated installation...

kernel ${debian_mirror}/debian/dists/bookworm/main/installer-amd64/current/images/netboot/debian-installer/amd64/linux
initrd ${debian_mirror}/debian/dists/bookworm/main/installer-amd64/current/images/netboot/debian-installer/amd64/initrd.gz

imgargs linux initrd=initrd.gz auto=true priority=critical
boot

goto start

# ==================== Windows PE 安装 ====================
:winpe
echo Loading Windows PE...
kernel ${server_ip}/winpe/wimboot
initrd ${server_ip}/winpe/sources/boot.wim boot.wim

boot

goto start

# ==================== 进入 iPXE 命令行 ====================
:shell
echo Entering iPXE Shell...
shell

goto start

# ==================== 重启 ====================
:reboot
echo Rebooting...
reboot
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后在IPXE环境里下载并运行脚本&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dhcp

chain http://example.com/embed.ipxe
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;建议使用HTTP协议，因为某些旧版本没有内置HTTPS模块，并且固件里的根证书也不齐全。&lt;/p&gt;
&lt;h3&gt;PXE 客户端启动链路&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;开机
  │
  ▼
DHCP (路由器)
  │ 分配 IP/网关/DNS
  ▼
ProxyDHCP (dnsmasq)
  │ 附加 TFTP 地址 + 引导文件名
  ▼
TFTP (dnsmasq)
  │ 下载 iPXE 固件
  ▼
iPXE 环境启动
  │
  ▼
HTTP 加载 menu.ipxe 脚本（需要手动加载，除非自己编译固件内置进去）
  │
  ▼
menu.ipxe 菜单（定义安装选项和逻辑）
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;客户端机器装Windows&lt;/h3&gt;
&lt;p&gt;对于安装Windows系统，可以使用 https://github.com/ipxe/wimboot 来直接加载 Windows 安装程序。&lt;/p&gt;
&lt;p&gt;微软官方的&lt;code&gt;Windows ISO&lt;/code&gt;镜像文件中，解压后&lt;code&gt;sources&lt;/code&gt;目录下有一个核心文件&lt;code&gt;boot.wim&lt;/code&gt;。这个&lt;code&gt;boot.wim&lt;/code&gt;本质上就是一个微型的 &lt;code&gt;Windows PE&lt;/code&gt;环境，可以用来启动并运行Windows安装程序。&lt;/p&gt;
&lt;h3&gt;编译自己的IPXE固件&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;安装依赖&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;sudo apt update
sudo apt install -y build-essential liblzma-dev mtools mkisofs syslinux gcc-aarch64-linux-gnu wget tar git
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;下载源码&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/ipxe/ipxe.git
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;进入&lt;code&gt;src&lt;/code&gt;目录&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;编辑&lt;code&gt;config/general.h&lt;/code&gt;文件（可选）
这个文件定义了固件的功能，常用的功能默认已经启用了，有些高级功能被注释了，如果需要启用就取消注释，一般默认的就够用了。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;创建内置脚本&lt;code&gt;embed.ipxe&lt;/code&gt;放到当前目录&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;编译命令&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;三种BIOS架构&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Legacy BIOS
make bin/undionly.kpxe EMBED=embed.ipxe

# UEFI x86_64
make bin-x86_64-efi/ipxe.efi EMBED=embed.ipxe

# UEFI ARM64
make CROSS_COMPILE=aarch64-linux-gnu- bin-arm64-efi/ipxe.efi EMBED=embed.ipxe
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;编译后的产物在&lt;code&gt;bin&lt;/code&gt;目录&lt;/p&gt;
</content:encoded></item><item><title>给你的电脑增加一个自定义右键菜单</title><link>https://blog.emohe.cn/posts/%E8%87%AA%E5%AE%9A%E4%B9%89%E5%8F%B3%E9%94%AE%E8%8F%9C%E5%8D%95/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/%E8%87%AA%E5%AE%9A%E4%B9%89%E5%8F%B3%E9%94%AE%E8%8F%9C%E5%8D%95/</guid><pubDate>Thu, 05 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Windows右键菜单&lt;/h2&gt;
&lt;p&gt;有时候写一些简单的html网页，当必须使用HTTP服务加载的时候，都需要手动启动一下命令，感觉步骤很繁琐 效率也很低，都是一些重复性动作。
需要在当前目录下打开终端，然后执行&lt;code&gt;python -m http.server 8080&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;所以我就想着直接右键菜单就能在当前目录自动执行这个命令就好了。&lt;/p&gt;
&lt;p&gt;目前最简单的方式就是注册表实现右键菜单，所以我就直接写了个注册表注入脚本，并且在启动HTTP服务的时候自动打开浏览器，代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\Directory\Background\shell\HttpServer]
@=&quot;启动HTTP服务 (9090)&quot;
&quot;Icon&quot;=&quot;imageres.dll,-5302&quot;
&quot;Position&quot;=&quot;Top&quot;

[HKEY_CLASSES_ROOT\Directory\Background\shell\HttpServer\command]
@=&quot;cmd.exe /s /k pushd \&quot;%V\&quot; &amp;amp;&amp;amp; (start /b cmd /c \&quot;timeout /t 1 /nobreak &amp;gt;nul &amp;amp;&amp;amp; start http://127.0.0.1:9090\&quot;) &amp;amp;&amp;amp; python -m http.server 9090&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将代码保存为&lt;code&gt;.reg&lt;/code&gt;后缀的注册表导入脚本，例如&lt;code&gt;install.reg&lt;/code&gt;，然后双击执行。需要注意的是要以&lt;code&gt;UTF-16 LE&lt;/code&gt;编码保存此文件，不然中文会乱码。&lt;/p&gt;
&lt;p&gt;右键菜单效果图：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;png/youjiancaidan.webp&quot; alt=&quot;右键菜单&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;代码解析&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心机制&lt;/strong&gt;：向 &lt;code&gt;HKEY_CLASSES_ROOT\Directory\Background\shell&lt;/code&gt; 写入键值。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;作用域&lt;/strong&gt;：&lt;code&gt;Directory\Background&lt;/code&gt; 代表在文件夹空白处点击右键。如果是点在文件夹图标上，则需要写到 &lt;code&gt;Directory\shell&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;命令解析&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pushd &quot;%V&quot;&lt;/code&gt;: 将 CMD 工作目录切换到当前文件夹（&lt;code&gt;%V&lt;/code&gt; 是变量）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;start http://127.0.0.1:9090&lt;/code&gt;：调用默认浏览器打开此地址。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;python -m http.server 9090&lt;/code&gt;: Python自带的启动静态服务器快捷命令，我电脑基本必装Python。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优化体验&lt;/strong&gt;：使用异步执行 优化了一下使用体验。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;进阶&lt;/h3&gt;
&lt;p&gt;此时你会发现这个右键菜单在windows 11系统里需要点击&lt;code&gt;显示更多选项&lt;/code&gt;才能看到。&lt;/p&gt;
&lt;p&gt;这是因为 Windows 11 重构了右键菜单，传统的注册表方式&lt;code&gt;IContextMenu&lt;/code&gt;默认被放入二级菜单。&lt;/p&gt;
&lt;p&gt;想要直接显示在一级菜单里，只有实现了&lt;code&gt;IExplorerCommand&lt;/code&gt;接口并具有&lt;code&gt;Package Identity (包身份)&lt;/code&gt;的应用，才有资格进入一级菜单。&lt;/p&gt;
&lt;h3&gt;那么想要进入一级菜单，需要做什么？&lt;/h3&gt;
&lt;p&gt;如果想要在一级菜单直接显示，单纯改注册表已经做不到了，必须升级技术栈，大致三个步骤：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;语言首选 C++&lt;/strong&gt;：写一个 DLL，实现 COM 接口&lt;code&gt;IExplorerCommand&lt;/code&gt;。比改注册表要复杂得多，需要处理引用计数、接口查询等底层逻辑。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;打包 (Sparse Package)&lt;/strong&gt;：必须给程序一个&lt;code&gt;身份证&lt;/code&gt; (AppxManifest.xml)。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;签名 (Identity)&lt;/strong&gt;：必须要有数字签名才行。自己用的话，可以生成一个自签名证书并信任它。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;当然我这种简单的小众需求还是使用注册表方式最合适，要是写一个DLL就有点小题大做了。&lt;/p&gt;
</content:encoded></item><item><title>OpenWrt固件编译</title><link>https://blog.emohe.cn/posts/openwrt%E5%9B%BA%E4%BB%B6%E7%BC%96%E8%AF%91/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/openwrt%E5%9B%BA%E4%BB%B6%E7%BC%96%E8%AF%91/</guid><pubDate>Wed, 19 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;介绍&lt;/h1&gt;
&lt;p&gt;OpenWrt 是一个 基于 Linux 的路由器操作系统，专门为嵌入式设备（路由器、NAS、小型网关设备等）设计。&lt;/p&gt;
&lt;p&gt;本篇教程基于&lt;code&gt;immortalwrt&lt;/code&gt;，immortalwrt 是基于 openwrt 开发的优化版本，是专门针对中国大陆环境优化的版本。国内用户基本都使用这个版本。&lt;/p&gt;
&lt;p&gt;项目地址：https://github.com/immortalwrt/immortalwrt&lt;/p&gt;
&lt;h1&gt;immortalwrt 在线构建&lt;/h1&gt;
&lt;p&gt;官方提供的网页在线构建叫&lt;code&gt;ImageBuilder&lt;/code&gt;，不是编译源码，而是借助官方的服务器资源，使用预编译好的SDK，快速选择包然后生成固件。优点是构建很快，缺点是自定义程度不是很高，但是常用的配置都可以自定义，例如提前安装插件，配置网口信息和防火墙什么的。&lt;/p&gt;
&lt;p&gt;官方在线编译地址：https://firmware-selector.immortalwrt.org&lt;/p&gt;
&lt;h2&gt;必备软件&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;openssh-sftp-server curl luci-theme-argon luci-i18n-homeproxy-zh-cn luci-i18n-ttyd-zh-cn luci-i18n-diskman-zh-cn luci-i18n-filemanager-zh-cn luci-i18n-package-manager-zh-cn luci-i18n-firewall-zh-cn
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当你安装一个插件包时，他会自动下载所需的软件和依赖，所以只需添加对应插件的汉化包即可。&lt;/p&gt;
&lt;p&gt;ImmortalWrt软件包查询（注意替换实际的版本和设备架构）：&lt;/p&gt;
&lt;p&gt;带luci：https://downloads.immortalwrt.org/releases/25.12.0/packages/x86_64/luci/index.json&lt;/p&gt;
&lt;p&gt;不带luci：https://downloads.immortalwrt.org/releases/25.12.0/packages/x86_64/packages/index.json&lt;/p&gt;
&lt;h2&gt;初始化构建脚本&lt;/h2&gt;
&lt;p&gt;要修改的地方请去掉注释&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/sh
exec &amp;gt;/tmp/setup.log 2&amp;gt;&amp;amp;1

###########################################################
#                  自 定 义 配 置 区 域
###########################################################

### 系统后台密码（为空则不修改）
root_password=&quot;admin&quot;

### LAN 的 IPv4 地址（也是后台地址，例如 192.168.2.1）
lan_ip_address=&quot;192.168.2.1&quot;

### LAN 的子网掩码（例如 255.255.255.0）
# lan_netmask=&quot;255.255.255.0&quot;

### LAN 的 IPv4 网关（可为空）
# lan_gateway=&quot;192.168.1.1&quot;

### LAN 的 DNS（多个 DNS 可空格分隔，如 &quot;8.8.8.8 1.1.1.1&quot;）
# lan_dns=&quot;8.8.8.8 223.5.5.5&quot;

### DHCP 是否开启（1=开启，0=关闭）
# lan_dhcp_enable=&quot;1&quot;

### DHCP 起始地址
# lan_dhcp_start=&quot;100&quot;

### DHCP 地址池数量
# lan_dhcp_limit=&quot;150&quot;

### DHCP 租约时间
# lan_dhcp_leasetime=&quot;12h&quot;

### WiFi 名称 SSID（为空则不修改）
# wlan_name=&quot;ImmortalWrt&quot;

### WiFi 密码（≥ 8 位才生效）
# wlan_password=&quot;12345678&quot;

### PPPoE 宽带账号（为空则跳过）
# pppoe_username=&quot;&quot;

### PPPoE 宽带密码
# pppoe_password=&quot;&quot;

###########################################################

# ------------ root 密码 ------------
if [ -n &quot;$root_password&quot; ]; then
  (echo &quot;$root_password&quot;; sleep 1; echo &quot;$root_password&quot;) | passwd &amp;gt;/dev/null
fi

# ------------ LAN 基础配置 ------------
if [ -n &quot;$lan_ip_address&quot; ]; then
  uci set network.lan.ipaddr=&quot;$lan_ip_address&quot;
fi

if [ -n &quot;$lan_netmask&quot; ]; then
  uci set network.lan.netmask=&quot;$lan_netmask&quot;
fi

if [ -n &quot;$lan_gateway&quot; ]; then
  uci set network.lan.gateway=&quot;$lan_gateway&quot;
fi

# DNS
if [ -n &quot;$lan_dns&quot; ]; then
  uci delete network.lan.dns 2&amp;gt;/dev/null
  for d in $lan_dns; do
    uci add_list network.lan.dns=&quot;$d&quot;
  done
fi

uci commit network

# ------------ DHCP 设置 ------------
if [ -n &quot;$lan_dhcp_enable&quot; ]; then
  uci set dhcp.lan.ignore=$([ &quot;$lan_dhcp_enable&quot; = &quot;1&quot; ] &amp;amp;&amp;amp; echo 0 || echo 1)
fi

[ -n &quot;$lan_dhcp_start&quot; ] &amp;amp;&amp;amp; uci set dhcp.lan.start=&quot;$lan_dhcp_start&quot;
[ -n &quot;$lan_dhcp_limit&quot; ] &amp;amp;&amp;amp; uci set dhcp.lan.limit=&quot;$lan_dhcp_limit&quot;
[ -n &quot;$lan_dhcp_leasetime&quot; ] &amp;amp;&amp;amp; uci set dhcp.lan.leasetime=&quot;$lan_dhcp_leasetime&quot;

uci commit dhcp

# ------------ WIFI 配置 ------------
if [ -n &quot;$wlan_name&quot; ] &amp;amp;&amp;amp; [ -n &quot;$wlan_password&quot; ] &amp;amp;&amp;amp; [ ${#wlan_password} -ge 8 ]; then
  uci set wireless.@wifi-device[0].disabled=&apos;0&apos;
  uci set wireless.@wifi-iface[0].disabled=&apos;0&apos;
  uci set wireless.@wifi-iface[0].encryption=&apos;psk2&apos;
  uci set wireless.@wifi-iface[0].ssid=&quot;$wlan_name&quot;
  uci set wireless.@wifi-iface[0].key=&quot;$wlan_password&quot;
  uci commit wireless
fi

# ------------ PPPoE 宽带拨号 ------------
if [ -n &quot;$pppoe_username&quot; ] &amp;amp;&amp;amp; [ -n &quot;$pppoe_password&quot; ]; then
  uci set network.wan.proto=pppoe
  uci set network.wan.username=&quot;$pppoe_username&quot;
  uci set network.wan.password=&quot;$pppoe_password&quot;
  uci commit network
fi

echo &quot;All done!&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;构建完成后下载对应的固件包刷入系统即可。&lt;/p&gt;
&lt;h1&gt;immortalwrt 基于源码编译&lt;/h1&gt;
&lt;p&gt;使用干净的&lt;code&gt;Debian 12&lt;/code&gt;或者&lt;code&gt;ubuntu 22.04&lt;/code&gt;系统，国外网络通畅，有50G以上的空闲存储空间。&lt;/p&gt;
&lt;h3&gt;1：安装依赖&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;sudo apt update -y
sudo apt full-upgrade -y
sudo apt install -y \
  ack antlr3 asciidoc autoconf automake autopoint binutils bison build-essential \
  bzip2 ccache clang cmake cpio curl device-tree-compiler ecj fastjar flex gawk gettext \
  gcc-multilib g++-multilib git gperf haveged help2man intltool libc6-dev-i386 libelf-dev \
  libglib2.0-dev libgmp-dev libltdl-dev libmpc-dev libmpfr-dev libncurses-dev libpython3-dev \
  libreadline-dev libssl-dev libtool libyaml-dev lld llvm lrzsz genisoimage \
  ninja-build p7zip-full patch pkgconf python3 python3-pip python3-ply python3-docutils \
  python3-pyelftools qemu-utils re2c rsync scons squashfs-tools subversion swig texinfo \
  uglifyjs unzip wget nano xmlto xxd zlib1g-dev upx zstd
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2：下载对应tags的源码（以&lt;code&gt;v24.10.4&lt;/code&gt;为例）&lt;/h3&gt;
&lt;p&gt;后续步骤建议切换为普通用户，不要用root用户编译。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone -b v24.10.4 --single-branch --filter=blob:none https://github.com/immortalwrt/immortalwrt
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3：进入项目目录&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;cd immortalwrt
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4：获取最新软件包清单&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;./scripts/feeds update -a
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5：安装软件包符号链接&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;./scripts/feeds install -a
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6：配置固件信息&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;make menuconfig
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这一步很重要，前三个选项分别是&lt;code&gt;目标系统&lt;/code&gt; - &lt;code&gt;子架构&lt;/code&gt; - &lt;code&gt;目标机型&lt;/code&gt;。根据你的实际设备来选。&lt;/p&gt;
&lt;p&gt;然后第四个选项&lt;code&gt;Target Images&lt;/code&gt;是修改固件配置的，其中&lt;code&gt;Root filesystem partition size&lt;/code&gt;是&lt;code&gt;根文件系统分区大小&lt;/code&gt;建议修改，根据你设备的存储空间来选择，这里的值就是最大存储空间。&lt;/p&gt;
&lt;p&gt;其他配置都是可选的，其中&lt;code&gt;luci&lt;/code&gt;选项中可以预安装一些插件包。&lt;/p&gt;
&lt;h3&gt;7：修改LAN口IP地址等等信息&lt;/h3&gt;
&lt;p&gt;可以在这里查看默认值：https://github.com/immortalwrt/immortalwrt/blob/master/package/base-files/files/bin/config_generate&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nano package/base-files/files/bin/config_generate
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8：编译固件&lt;/h3&gt;
&lt;p&gt;单线程编译&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;make -j1 V=s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或者多线程编译&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;make -j$(nproc --ignore=1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;编译出来的固件在&lt;code&gt;bin&lt;/code&gt;目录下&lt;/p&gt;
&lt;h3&gt;9：重新编译说明&lt;/h3&gt;
&lt;p&gt;如果需要重新编译 则执行&lt;code&gt;make distclean&lt;/code&gt;清理一些残留和工具链等等，然后再从第四步重新开始。&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;VirtualBox虚拟机运行immortalwrt&lt;/h1&gt;
&lt;p&gt;1：选择&lt;code&gt;x86/64&lt;/code&gt;型号，编译后，下载&lt;code&gt;COMBINED (EXT4)&lt;/code&gt;格式的镜像，并解压到下载目录&lt;/p&gt;
&lt;p&gt;2：在文件目录下打开 PowerShell&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将镜像转换成VDI格式&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;amp; &quot;C:\Program Files\Oracle\VirtualBox\VBoxManage.exe&quot; convertfromraw `
    &quot;immortalwrt-24.10.4-f726c678216d-x86-64-generic-ext4-combined.img&quot; `
    &quot;immortalwrt.vdi&quot; `
    --format VDI
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3: 系统内修改网络配置的命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vi /etc/config/network
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;/etc/init.d/network restart
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4：浏览器进入&lt;code&gt;LAN&lt;/code&gt;口IP地址，密码是脚本中设置的密码。&lt;/p&gt;
&lt;h1&gt;X86主机设备将镜像写入到硬盘&lt;/h1&gt;
&lt;p&gt;先将镜像烧录到U盘，然后使用U盘启动进入到openwrt系统，然后使用DD命令写入镜像，就可以拔掉U盘运行openwrt系统了。&lt;/p&gt;
&lt;h4&gt;查看U盘和目标硬盘&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;lsblk -f
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;例如 U 盘是 &lt;code&gt;/dev/sdb&lt;/code&gt;  硬盘是 &lt;code&gt;/dev/sda&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;如果目标硬盘有被挂载则需要卸载，例如之前装了系统之类的&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;umount /dev/sda1
umount /dev/sda2
umount /dev/sda3
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;检查是否卸载成功&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;mount | grep sda
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;如果返回为空就代表卸载成功了&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;写入镜像（注意替换实际的U盘和硬盘）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;dd if=/dev/sdb of=/dev/sda bs=4M conv=fsync
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;如果没报错，并且看到了两行数字就代表成功了&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;强制刷新缓存&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;sync
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;命令解释：&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;if=/dev/sdb&lt;/code&gt; ：输入源，U 盘设备&lt;/p&gt;
&lt;p&gt;&lt;code&gt;of=/dev/sda&lt;/code&gt; ：输出目标，硬盘/闪存设备&lt;/p&gt;
&lt;p&gt;&lt;code&gt;bs=4M&lt;/code&gt; ：每次写入 4M，提高速度&lt;/p&gt;
&lt;p&gt;&lt;code&gt;conv=fsync&lt;/code&gt; ：写完后刷新缓存，确保数据落盘&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sync&lt;/code&gt; ：再次确保所有数据写入完成&lt;/p&gt;
</content:encoded></item><item><title>香橙派开发板折腾日记</title><link>https://blog.emohe.cn/posts/%E9%A6%99%E6%A9%99%E6%B4%BE%E5%BC%80%E5%8F%91%E6%9D%BF%E6%8A%98%E8%85%BE%E6%97%A5%E8%AE%B0/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/%E9%A6%99%E6%A9%99%E6%B4%BE%E5%BC%80%E5%8F%91%E6%9D%BF%E6%8A%98%E8%85%BE%E6%97%A5%E8%AE%B0/</guid><pubDate>Sat, 06 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Orange Pi Zero3 香橙派开发板折腾日记&lt;/p&gt;
&lt;p&gt;主包最近从拼夕夕入手了Orange Pi Zero3开发板，4G内存，价格是199人民币。&lt;/p&gt;
&lt;p&gt;到货后就迫不及待的折腾了起来。&lt;/p&gt;
&lt;h3&gt;首先需要准备这些东西来安装系统&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;TF内存卡一个（16G以上）&lt;/li&gt;
&lt;li&gt;TF读卡器一个&lt;/li&gt;
&lt;li&gt;路由器用的网线一根&lt;/li&gt;
&lt;li&gt;&lt;code&gt;5V/2A&lt;/code&gt;USB充电头&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Type C&lt;/code&gt;口数据线&lt;/li&gt;
&lt;li&gt;写盘软件&lt;a href=&quot;https://etcher.balena.io/&quot;&gt;balenaEtcher&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;注意不要和SD卡搞混了，SD卡是大的，TF卡是小的&lt;/p&gt;
&lt;h3&gt;写入系统&lt;/h3&gt;
&lt;p&gt;首先去&lt;a href=&quot;http://www.orangepi.cn/&quot;&gt;香橙派官网&lt;/a&gt;，找到服务与下载，找到对应的型号，我这里是&lt;code&gt;Orange Pi Zero3&lt;/code&gt;，然后点击进去找到&lt;code&gt;官方镜像&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;点击进入官方提供的百度网盘地址，下载你想要的镜像，我这里下载的是&lt;code&gt;Debian 12&lt;/code&gt;系统，全称是&lt;code&gt;Orangepizero3_1.0.4_debian_bookworm_server_linux6.1.31.7z&lt;/code&gt;其中bookworm代表是12的版本，serve代表是服务器版本，不带桌面的那种。&lt;/p&gt;
&lt;p&gt;下载后用读卡器把TF卡插到电脑上，使用&lt;a href=&quot;https://etcher.balena.io/&quot;&gt;balenaEtcher&lt;/a&gt;将img格式的系统镜像烧录进去，完事后将TF卡插到香橙派开发板上。&lt;/p&gt;
&lt;p&gt;然后使用&lt;code&gt;5V/2A&lt;/code&gt;的USB充电头和&lt;code&gt;Type C&lt;/code&gt;数据线给香橙派接入电源。接入电源后不要拔掉，他会自动引导并安装系统，等一会后就可以用网线将香橙派连上路由器。连上路由器是为了方便远程连接。因为没有接外设的线。&lt;/p&gt;
&lt;h3&gt;使用SSH远程连接系统&lt;/h3&gt;
&lt;p&gt;现在就可以进入路由器后台找到香橙派的局域网IP地址，使用此IP地址进行SSH连接。&lt;/p&gt;
&lt;p&gt;Debian和Ubuntu系统的默认SSH用户名是&lt;code&gt;orangepi&lt;/code&gt;  密码是&lt;code&gt;orangepi&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;png/pai1.webp&quot; alt=&quot;1&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;设置系统&lt;/h3&gt;
&lt;p&gt;通过SSH进入系统后，首先&lt;code&gt;sudo -i&lt;/code&gt;切换为root权限，然后使用&lt;code&gt;passwd&lt;/code&gt;命令设置一个root密码，请务必设置一个复杂的密码。&lt;/p&gt;
&lt;p&gt;设置完密码后使用&lt;code&gt;apt update&lt;/code&gt;更新一下源，然后安装一下常用的包&lt;code&gt;apt install -y curl wget git zip tar lsof vim sudo&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;禁用默认用户登录&lt;/h3&gt;
&lt;p&gt;我宽带是有公网的，为了安全起见，防止SSH被爆破，建议禁用默认用户远程登录，以Debian或者Ubuntu系统为例&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 编辑SSH配置文件
nano /etc/ssh/sshd_config

# 文件末尾添加以下内容
DenyUsers orangepi

# 重启SSH服务
sudo systemctl restart ssh
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;连接WIFI&lt;/h3&gt;
&lt;p&gt;这个板子是支持wifi的，进入系统后，可以连上wifi，就不用插网线了。&lt;/p&gt;
&lt;p&gt;使用&lt;code&gt;nmcli dev wifi&lt;/code&gt;命令搜索附件的WIFI&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;png/pai2.webp&quot; alt=&quot;1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后使用如下命令连接WIFI&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nmcli device wifi connect &quot;Wi-Fi名称&quot; password &quot;Wi-Fi密码&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后使用&lt;code&gt;nmcli device status&lt;/code&gt;查看连接状态，如果输出的结果中有你连接的Wi-Fi名称，就代表连接成功。&lt;/p&gt;
&lt;p&gt;然后就可以使用&lt;code&gt;ip a&lt;/code&gt;来查看IP地址了，一般&lt;code&gt;end0&lt;/code&gt;是有线网卡，&lt;code&gt;wlan0&lt;/code&gt;是无线网卡，其他局域网地址可以通过这些IP来互联，或者SSH连接。当然也可以在路由器里查看分配的IP。&lt;/p&gt;
&lt;p&gt;然后后就可以跑各种服务了&lt;/p&gt;
</content:encoded></item><item><title>支付宝收款码前端动态渲染</title><link>https://blog.emohe.cn/posts/%E6%94%AF%E4%BB%98%E5%AE%9D%E6%94%B6%E6%AC%BE%E7%A0%81%E5%89%8D%E7%AB%AF%E5%8A%A8%E6%80%81%E6%B8%B2%E6%9F%93/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/%E6%94%AF%E4%BB%98%E5%AE%9D%E6%94%B6%E6%AC%BE%E7%A0%81%E5%89%8D%E7%AB%AF%E5%8A%A8%E6%80%81%E6%B8%B2%E6%9F%93/</guid><pubDate>Wed, 14 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;支付宝收款码使用浏览器动态渲染&lt;/h1&gt;
&lt;p&gt;无需自己上传收款码图片，用户输入金额后前端自动生成对应金额的转账二维码，个人账户原生支持，只需到官网获取账户ID即可调用。&lt;/p&gt;
&lt;h3&gt;技术解析&lt;/h3&gt;
&lt;p&gt;原理是前端调用&lt;code&gt;alipays://&lt;/code&gt;支付宝私有协议，是支付宝&lt;code&gt;Alipay&lt;/code&gt;客户端使用的私有 URL 协议，也叫&lt;code&gt;URL Scheme&lt;/code&gt;，用于通过网页、App 或其他外部应用调用支付宝客户端，执行特定动作，比如打开某个页面、发起支付、跳转小程序等。&lt;/p&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;alipays://platformapi/startapp?appId=20000123&amp;amp;actionType=scan&amp;amp;biz_data={&quot;s&quot;:&quot;money&quot;,&quot;u&quot;:&quot;2088xxxxxx&quot;,&quot;a&quot;:&quot;10&quot;,&quot;m&quot;:&quot;打赏支持&quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后使用&lt;code&gt;qrcode&lt;/code&gt;将私有协议链接转换为二维码就行了。&lt;/p&gt;
&lt;p&gt;参数解析：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;appId=20000123&lt;/code&gt;：支付宝内置的&quot;转账到个人账户&quot;功能编号&lt;/p&gt;
&lt;p&gt;&lt;code&gt;actionType=scan&lt;/code&gt;：指定操作为扫码支付&lt;/p&gt;
&lt;p&gt;&lt;code&gt;biz_data&lt;/code&gt;：核心业务参数（JSON格式，需URL编码）：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;s: &quot;money&quot;&lt;/code&gt;：固定值，表示转账业务&lt;/p&gt;
&lt;p&gt;&lt;code&gt;u: &quot;2088xxxxxx&quot;&lt;/code&gt;：收款方支付宝UID（用户唯一标识）&lt;/p&gt;
&lt;p&gt;&lt;code&gt;a: &quot;10&quot;&lt;/code&gt;：初始转账金额（单位：元）&lt;/p&gt;
&lt;p&gt;&lt;code&gt;m: &quot;打赏支持&quot;&lt;/code&gt;：转账备注（显示在支付宝账单）&lt;/p&gt;
</content:encoded></item><item><title>前端性能优化技术汇总</title><link>https://blog.emohe.cn/posts/%E5%89%8D%E7%AB%AF%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E6%B1%87%E6%80%BB/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/%E5%89%8D%E7%AB%AF%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E6%B1%87%E6%80%BB/</guid><pubDate>Tue, 06 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;前端性能优化技术合集与汇总（含示例）&lt;/h1&gt;
&lt;h2&gt;1. 资源加载优化&lt;/h2&gt;
&lt;h3&gt;异步加载（Asynchronous Loading）&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：不阻塞页面渲染，后台加载资源，加载完成后执行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：使用 &lt;code&gt;async&lt;/code&gt;、&lt;code&gt;defer&lt;/code&gt; 属性异步加载 JS 脚本。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- async: 下载完成后立即执行，可能中断HTML解析，执行顺序不定 --&amp;gt;
&amp;lt;script async src=&quot;analytics.js&quot;&amp;gt;&amp;lt;/script&amp;gt;

&amp;lt;!-- defer: HTML解析完成后，DOMContentLoaded事件前按顺序执行 --&amp;gt;
&amp;lt;script defer src=&quot;main-bundle.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;懒加载（Lazy Loading）&lt;/h3&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;pre&gt;&lt;code&gt;&amp;lt;!-- 使用 loading=&quot;lazy&quot; 属性（浏览器原生支持） --&amp;gt;
&amp;lt;img src=&quot;placeholder.jpg&quot; data-src=&quot;real-image.jpg&quot; loading=&quot;lazy&quot; alt=&quot;Lazy loaded image&quot;&amp;gt;
&amp;lt;iframe src=&quot;placeholder.html&quot; data-src=&quot;real-content.html&quot; loading=&quot;lazy&quot;&amp;gt;&amp;lt;/iframe&amp;gt;

&amp;lt;!-- 使用 Intersection Observer API 实现自定义懒加载 --&amp;gt;
&amp;lt;script&amp;gt;
  const images = document.querySelectorAll(&apos;img[data-src]&apos;);
  const observer = new IntersectionObserver((entries, observer) =&amp;gt; {
    entries.forEach(entry =&amp;gt; {
      if (entry.isIntersecting) {
        const img = entry.target;
        img.src = img.dataset.src;
        img.removeAttribute(&apos;data-src&apos;);
        observer.unobserve(img);
      }
    });
  });
  images.forEach(img =&amp;gt; observer.observe(img));
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;预加载（Preload）&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：页面加载时提前加载未来可能需要的资源。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：使用 &lt;code&gt;&amp;lt;link rel=&quot;preload&quot; href=&quot;...&quot; as=&quot;...&quot;&amp;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;&amp;lt;!-- 预加载关键CSS文件 --&amp;gt;
&amp;lt;link rel=&quot;preload&quot; href=&quot;critical.css&quot; as=&quot;style&quot;&amp;gt;

&amp;lt;!-- 预加载将在稍后使用的JS文件 --&amp;gt;
&amp;lt;link rel=&quot;preload&quot; href=&quot;late-loaded-script.js&quot; as=&quot;script&quot;&amp;gt;

&amp;lt;!-- 预加载字体文件 --&amp;gt;
&amp;lt;link rel=&quot;preload&quot; href=&quot;font.woff2&quot; as=&quot;font&quot; type=&quot;font/woff2&quot; crossorigin&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;空闲时间调度（Idle Time Scheduling）&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：使用 &lt;code&gt;requestIdleCallback&lt;/code&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;pre&gt;&lt;code&gt;requestIdleCallback((deadline) =&amp;gt; {
  // 如果有剩余时间，或者任务必须执行
  if (deadline.timeRemaining() &amp;gt; 0 || deadline.didTimeout) {
    // 执行低优先级任务，例如发送统计数据
    sendAnalyticsData();
  }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2. 资源压缩与合并&lt;/h2&gt;
&lt;h3&gt;图片压缩（Image Optimization）&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：压缩图片文件，减少图片体积，使用高效格式（如 WebP、AVIF）。&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;strong&gt;工具&lt;/strong&gt;: 使用在线工具如 TinyPNG、Squoosh.app 或构建工具插件（如 &lt;code&gt;imagemin-webpack-plugin&lt;/code&gt;）进行自动化压缩。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;格式选择&lt;/strong&gt;: 使用 &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; 标签提供多种格式供浏览器选择。&lt;pre&gt;&lt;code&gt;&amp;lt;picture&amp;gt;
  &amp;lt;source srcset=&quot;image.avif&quot; type=&quot;image/avif&quot;&amp;gt;
  &amp;lt;source srcset=&quot;image.webp&quot; type=&quot;image/webp&quot;&amp;gt;
  &amp;lt;img src=&quot;image.jpg&quot; alt=&quot;Optimized image&quot;&amp;gt;
&amp;lt;/picture&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;JS / CSS 压缩（Minification）&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：去除代码中的空格、注释等，减少文件体积，提升加载速度。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：生产环境中的所有 JavaScript 和 CSS 文件。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;构建工具&lt;/strong&gt;: 使用 Webpack、Rollup、Parcel 等构建工具，并配置相应的插件（如 &lt;code&gt;TerserWebpackPlugin&lt;/code&gt; 用于 JS，&lt;code&gt;CssMinimizerWebpackPlugin&lt;/code&gt; 用于 CSS）在生产构建时自动压缩代码。&lt;pre&gt;&lt;code&gt;// webpack.config.js (示例)
const TerserPlugin = require(&apos;terser-webpack-plugin&apos;);
const CssMinimizerPlugin = require(&apos;css-minimizer-webpack-plugin&apos;);

module.exports = {
  // ...
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin(),
      new CssMinimizerPlugin(),
    ],
  },
  // ...
};
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;CSS 和 JS 合并（Concatenation）&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：合并多个文件，减少 HTTP 请求次数（在 HTTP/1.1 环境下尤其重要，HTTP/2 中重要性降低但仍有益）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：将多个小的 CSS 或 JS 文件合并成一个或少数几个较大的文件。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;构建工具&lt;/strong&gt;: Webpack、Rollup 等现代构建工具通常在打包过程中自动处理模块合并。确保配置合理，避免生成过大的单一文件，可以结合代码分割使用。&lt;pre&gt;&lt;code&gt;// main.js (示例入口文件)
import &apos;./moduleA.js&apos;;
import &apos;./moduleB.js&apos;;
import &apos;./styles.css&apos;;
import &apos;./more-styles.css&apos;;

// 构建工具会将这些导入合并到输出的 bundle 文件中
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. 代码优化&lt;/h2&gt;
&lt;h3&gt;代码分割（Code Splitting）&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：按需加载 JS 代码，减小首屏加载体积，常见于单页应用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：使用动态 &lt;code&gt;import()&lt;/code&gt; 和路由懒加载，按需加载模块。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 使用动态 import() 按需加载模块
document.getElementById(&apos;loadButton&apos;).addEventListener(&apos;click&apos;, () =&amp;gt; {
  import(&apos;./heavy-module.js&apos;)
    .then(module =&amp;gt; {
      module.doSomething();
    })
    .catch(err =&amp;gt; {
      console.error(&apos;Failed to load module:&apos;, err);
    });
});

// 路由懒加载 (以 React Router 为例)
import React, { Suspense, lazy } from &apos;react&apos;;
import { BrowserRouter as Router, Route, Switch } from &apos;react-router-dom&apos;;

const HomePage = lazy(() =&amp;gt; import(&apos;./routes/HomePage&apos;));
const AboutPage = lazy(() =&amp;gt; import(&apos;./routes/AboutPage&apos;));

const App = () =&amp;gt; (
  &amp;lt;Router&amp;gt;
    &amp;lt;Suspense fallback={&amp;lt;div&amp;gt;Loading...&amp;lt;/div&amp;gt;}&amp;gt;
      &amp;lt;Switch&amp;gt;
        &amp;lt;Route exact path=&quot;/&quot; component={HomePage} /&amp;gt;
        &amp;lt;Route path=&quot;/about&quot; component={AboutPage} /&amp;gt;
      &amp;lt;/Switch&amp;gt;
    &amp;lt;/Suspense&amp;gt;
  &amp;lt;/Router&amp;gt;
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Tree Shaking&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：删除未使用的代码（dead code elimination），减小打包体积，优化代码执行效率。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：适用于 Webpack、Rollup 等支持 Tree Shaking 的工具，需要使用 ES6 模块语法（&lt;code&gt;import&lt;/code&gt;/&lt;code&gt;export&lt;/code&gt;）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;构建工具配置&lt;/strong&gt;: 在 Webpack 中，将 &lt;code&gt;mode&lt;/code&gt; 设置为 &lt;code&gt;production&lt;/code&gt; 会自动启用 Tree Shaking。确保你的代码使用 ES6 模块，并且没有副作用（side effects）阻止 Tree Shaking。&lt;pre&gt;&lt;code&gt;// webpack.config.js
module.exports = {
  mode: &apos;production&apos;, // 启用包括 Tree Shaking 在内的多项优化
  // ...
};
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;标记副作用&lt;/strong&gt;: 如果某个模块有副作用（例如，全局样式注入、polyfill），可以在 &lt;code&gt;package.json&lt;/code&gt; 中标记。&lt;pre&gt;&lt;code&gt;// package.json
{
  &quot;name&quot;: &quot;my-library&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;sideEffects&quot;: false // 默认无副作用，利于 Tree Shaking
  // 或者指定有副作用的文件
  // &quot;sideEffects&quot;: [&quot;./src/polyfill.js&quot;, &quot;*.css&quot;]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Font Subsetting&lt;/h3&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;strong&gt;工具&lt;/strong&gt;: 使用 &lt;code&gt;font-spider&lt;/code&gt; (国人开发，适合中文字体)、&lt;code&gt;glyphhanger&lt;/code&gt; 或在线字体子集化工具，根据项目实际使用的文字生成精简后的字体文件。&lt;pre&gt;&lt;code&gt;# 使用 font-spider (示例)
# 1. 在 CSS 中正常引用字体
# @font-face {
#   font-family: &apos;MyFont&apos;;
#   src: url(&apos;../fonts/MyFont.ttf&apos;);
# }
# body {
#   font-family: &apos;MyFont&apos;;
# }
# 2. 运行命令扫描 HTML 文件并生成子集字体
font-spider ./index.html
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;4. 浏览器渲染优化&lt;/h2&gt;
&lt;h3&gt;减少重排与重绘（Reflow &amp;amp; Repaint）&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：避免频繁修改布局（如 &lt;code&gt;width&lt;/code&gt;、&lt;code&gt;height&lt;/code&gt;、&lt;code&gt;top&lt;/code&gt;、&lt;code&gt;left&lt;/code&gt;），减少重排（Reflow/Layout）。优化 DOM 操作，避免页面卡顿和性能瓶颈。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：涉及 DOM 元素尺寸、位置、内容变化的 JavaScript 操作。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 不好的做法：每次循环都读取 offsetHeight，可能触发重排
for (let i = 0; i &amp;lt; elements.length; i++) {
  elements[i].style.width = elements[i].offsetHeight + &apos;px&apos;; // 读取触发重排
}

// 改进：先读后写，分离读写操作
const heights = [];
for (let i = 0; i &amp;lt; elements.length; i++) {
  heights[i] = elements[i].offsetHeight; // 批量读取
}
for (let i = 0; i &amp;lt; elements.length; i++) {
  elements[i].style.width = heights[i] + &apos;px&apos;; // 批量写入
}

// 使用 CSS transform 代替 top/left 实现位移动画，避免重排
// 不推荐
// element.style.left = x + &apos;px&apos;;
// element.style.top = y + &apos;px&apos;;

// 推荐
element.style.transform = `translate(${x}px, ${y}px)`;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;合并 DOM 操作&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：将多次 DOM 操作合并成一次，减少重排和重绘的频率。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：需要向 DOM 中插入多个元素时。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const list = document.getElementById(&apos;myList&apos;);
const items = [&apos;Apple&apos;, &apos;Banana&apos;, &apos;Cherry&apos;];

// 不好的做法：每次循环都操作 DOM
// items.forEach(itemText =&amp;gt; {
//   const li = document.createElement(&apos;li&apos;);
//   li.textContent = itemText;
//   list.appendChild(li); // 每次 appendChild 都可能触发重排/重绘
// });

// 推荐：使用 DocumentFragment
const fragment = document.createDocumentFragment();
items.forEach(itemText =&amp;gt; {
  const li = document.createElement(&apos;li&apos;);
  li.textContent = itemText;
  fragment.appendChild(li); // 操作 Fragment 不会触发重排/重绘
});
list.appendChild(fragment); // 最后一次性插入 DOM
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;硬件加速（Transform &amp;amp; Opacity）&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：使用 &lt;code&gt;transform&lt;/code&gt; 和 &lt;code&gt;opacity&lt;/code&gt; 代替布局属性（如 &lt;code&gt;top&lt;/code&gt;、&lt;code&gt;left&lt;/code&gt;）进行动画，利用 GPU 加速，提高动画流畅度。&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;pre&gt;&lt;code&gt;.animated-element {
  /* 不推荐：使用 top/left 可能导致重排，动画可能卡顿 */
  /* position: absolute; */
  /* left: 0; */
  /* transition: left 0.3s ease; */

  /* 推荐：使用 transform 利用 GPU 加速 */
  transform: translateX(0);
  transition: transform 0.3s ease, opacity 0.3s ease;
  opacity: 1;
}

.animated-element.move {
  /* transform: translateX(100px); */
  transform: translate3d(100px, 0, 0); /* 强制开启 GPU 加速 */
}

.animated-element.fade-out {
  opacity: 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;will-change 提前通知浏览器&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：使用 &lt;code&gt;will-change&lt;/code&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;pre&gt;&lt;code&gt;.element-about-to-animate {
  /* 告知浏览器 transform 和 opacity 属性即将发生变化 */
  will-change: transform, opacity;
}

.element-about-to-animate:hover {
  transform: scale(1.1);
  opacity: 0.8;
}

/* 注意：不要滥用 will-change，仅在确实需要时添加，并在变化结束后移除 */
/* 可以通过 JavaScript 在动画开始前添加，结束后移除 */
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;5. 网络优化&lt;/h2&gt;
&lt;h3&gt;HTTP/2 / HTTP/3 协议&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：启用 HTTP/2 或 HTTP/3，利用其多路复用、头部压缩、服务器推送（HTTP/2）、基于 UDP 的 QUIC（HTTP/3）等特性，提高并行传输效率，减少请求延迟。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：Web 服务器配置。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;服务器配置&lt;/strong&gt;: 在 Nginx, Apache 或其他 Web 服务器上启用 HTTP/2 或 HTTP/3 支持（通常需要 HTTPS）。&lt;pre&gt;&lt;code&gt;# Nginx 配置启用 HTTP/2 (示例)
server {
    listen 443 ssl http2;
    # ... 其他 SSL 配置 ...
}
# Nginx 配置启用 HTTP/3 (需要较新版本和额外模块)
server {
    listen 443 quic reuseport;
    listen 443 ssl http2;
    # ... 其他 SSL 和 HTTP/3 配置 ...
    add_header Alt-Svc &apos;h3=&quot;:443&quot;; ma=86400&apos;; # 告知浏览器支持 HTTP/3
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;验证&lt;/strong&gt;: 使用浏览器开发者工具的网络(Network)面板查看协议版本。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Content Delivery Network (CDN)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：使用 CDN 将静态资源（如图片、CSS、JS）缓存到全球各地的边缘节点，用户从最近的节点获取资源，减少网络延迟，提高加载速度。&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;strong&gt;服务商&lt;/strong&gt;: 选择 CDN 服务商（如 Cloudflare, Akamai, AWS CloudFront, 阿里云 CDN, 腾讯云 CDN 等）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;配置&lt;/strong&gt;: 将静态资源的 URL 指向 CDN 提供的域名。&lt;pre&gt;&lt;code&gt;&amp;lt;!-- 原始路径 --&amp;gt;
&amp;lt;!-- &amp;lt;script src=&quot;/assets/app.js&quot;&amp;gt;&amp;lt;/script&amp;gt; --&amp;gt;
&amp;lt;!-- &amp;lt;img src=&quot;/images/logo.png&quot;&amp;gt; --&amp;gt;

&amp;lt;!-- 使用 CDN 路径 --&amp;gt;
&amp;lt;script src=&quot;https://cdn.example.com/assets/app.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;img src=&quot;https://cdn.example.com/images/logo.png&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;缓存优化（Caching）&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：使用 HTTP 缓存头（如 &lt;code&gt;Cache-Control&lt;/code&gt;、&lt;code&gt;ETag&lt;/code&gt;、&lt;code&gt;Last-Modified&lt;/code&gt;）优化资源缓存，让浏览器可以复用已下载的资源，减少重复请求。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：所有可以通过 HTTP 请求的资源。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;服务器配置&lt;/strong&gt;: 配置 Web 服务器发送合适的缓存头。&lt;pre&gt;&lt;code&gt;# Nginx 配置示例
location ~* \.(?:css|js)$ {
    # 强缓存：缓存 1 年
    add_header Cache-Control &quot;public, max-age=31536000, immutable&quot;;
    access_log off;
}

location ~* \.(?:jpg|jpeg|png|gif|ico|webp|avif)$ {
    # 强缓存：缓存 1 个月
    add_header Cache-Control &quot;public, max-age=2592000&quot;;
    access_log off;
}

location = /index.html {
    # 协商缓存：每次验证
    add_header Cache-Control &quot;no-cache&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ETag&lt;/strong&gt;: 服务器为资源生成唯一标识，浏览器下次请求时通过 &lt;code&gt;If-None-Match&lt;/code&gt; 发送此标识，服务器比对后若无变化则返回 304 Not Modified。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Last-Modified&lt;/strong&gt;: 服务器告知资源最后修改时间，浏览器下次通过 &lt;code&gt;If-Modified-Since&lt;/code&gt; 发送此时间，服务器比对后若无更新则返回 304。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;DNS 预解析 / 预连接（DNS Prefetch / Preconnect）&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：提前解析域名或建立到目标源（域名+端口）的 TCP 连接（甚至 TLS 握手），减少后续资源请求时的延迟。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：页面中引用了来自其他域名的资源（如 CDN、API、第三方脚本）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- DNS 预解析：提前解析域名 --&amp;gt;
&amp;lt;link rel=&quot;dns-prefetch&quot; href=&quot;//fonts.googleapis.com&quot;&amp;gt;
&amp;lt;link rel=&quot;dns-prefetch&quot; href=&quot;//api.example.com&quot;&amp;gt;

&amp;lt;!-- 预连接：提前完成 DNS 解析 + TCP 握手 + TLS 协商 --&amp;gt;
&amp;lt;link rel=&quot;preconnect&quot; href=&quot;https://fonts.gstatic.com&quot; crossorigin&amp;gt;
&amp;lt;link rel=&quot;preconnect&quot; href=&quot;https://api.example.com&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;6. 服务端优化&lt;/h2&gt;
&lt;h3&gt;Server-Side Rendering (SSR) / Static Site Generation (SSG)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：使用 SSR（服务器端渲染）或 SSG（静态站点生成）在服务器端生成完整的 HTML 页面或预渲染的静态 HTML 文件，直接发送给浏览器，加快首屏显示速度，并有利于 SEO。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：内容型网站、需要良好 SEO 的应用、追求极致首屏性能的场景。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;框架&lt;/strong&gt;: 使用支持 SSR/SSG 的前端框架，如 Next.js (React), Nuxt.js (Vue), SvelteKit (Svelte), Gatsby (React, SSG), Astro 等。&lt;pre&gt;&lt;code&gt;// Next.js 页面示例 (SSR)
export async function getServerSideProps(context) {
  const res = await fetch(`https://api.example.com/data`);
  const data = await res.json();
  return { props: { data } }; // 数据将作为 props 传递给页面组件
}
function Page({ data }) {
  // 渲染页面...
}
export default Page;

// Next.js 页面示例 (SSG)
export async function getStaticProps() {
  const res = await fetch(`https://api.example.com/data`);
  const data = await res.json();
  return { props: { data } };
}
// ... Page 组件同上 ...
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;API 优化&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：缩短 API 接口的响应时间，使用合适的数据格式（如 JSON），采用有效的缓存策略（如 Redis 缓存查询结果），减少 API 请求处理的延迟。&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;strong&gt;后端&lt;/strong&gt;: 优化数据库查询（添加索引、慢查询分析）、使用缓存层（Redis, Memcached）、优化业务逻辑、使用异步处理非核心任务。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据格式&lt;/strong&gt;: 确保 API 返回精简的 JSON 数据，避免冗余字段。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;网络&lt;/strong&gt;: 减少 API 服务器与客户端之间的网络延迟（如使用 CDN 加速 API）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;GraphQL 优化&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：采用 GraphQL 代替传统 RESTful API，允许客户端精确指定需要获取的数据结构，避免了 REST 中常见的 over-fetching（获取过多数据）和 under-fetching（需要多次请求才能获取足够数据）的问题。&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;pre&gt;&lt;code&gt;# GraphQL 查询示例：只请求需要的字段
query GetUserProfile {
  user(id: &quot;123&quot;) {
    id
    name
    email
    posts(last: 5) { # 只获取最近 5 篇文章
      title
      createdAt
    }
  }
}

# 相比之下，REST 可能需要多个请求或返回大量不需要的数据
# GET /users/123
# GET /users/123/posts?limit=5
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;7. 前端缓存与持久化&lt;/h2&gt;
&lt;h3&gt;LocalStorage / SessionStorage&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：使用 Web Storage API (LocalStorage, SessionStorage) 在浏览器端存储少量键值对数据。LocalStorage 数据持久存在，除非手动清除；SessionStorage 数据在会话结束后（通常是浏览器标签页关闭时）清除。&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;pre&gt;&lt;code&gt;// LocalStorage: 持久存储
localStorage.setItem(&apos;theme&apos;, &apos;dark&apos;);
const currentTheme = localStorage.getItem(&apos;theme&apos;);
localStorage.removeItem(&apos;theme&apos;);
localStorage.clear(); // 清除所有

// SessionStorage: 会话级存储
sessionStorage.setItem(&apos;sessionId&apos;, &apos;abc123xyz&apos;);
const sessionId = sessionStorage.getItem(&apos;sessionId&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;: Web Storage 容量有限（通常 5-10MB），且是同步操作，可能阻塞主线程，不适合存储大量数据。&lt;/p&gt;
&lt;h3&gt;IndexedDB&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：一个在浏览器中存储大量结构化数据（包括文件/Blob）的底层 API。它创建了一个事务型数据库，支持索引，可以进行高性能搜索。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：离线数据存储、大型数据集缓存、PWA（Progressive Web Apps）的数据存储。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 打开（或创建）数据库
const request = indexedDB.open(&apos;myDatabase&apos;, 1);

request.onupgradeneeded = event =&amp;gt; {
  const db = event.target.result;
  // 创建一个对象存储空间（类似表）
  const objectStore = db.createObjectStore(&apos;customers&apos;, { keyPath: &apos;id&apos; });
  // 创建索引
  objectStore.createIndex(&apos;name&apos;, &apos;name&apos;, { unique: false });
};

request.onsuccess = event =&amp;gt; {
  const db = event.target.result;
  // 使用事务进行数据操作
  const transaction = db.transaction([&apos;customers&apos;], &apos;readwrite&apos;);
  const objectStore = transaction.objectStore(&apos;customers&apos;);

  // 添加数据
  const addRequest = objectStore.add({ id: &apos;1&apos;, name: &apos;Alice&apos;, email: &apos;alice@example.com&apos; });
  addRequest.onsuccess = () =&amp;gt; console.log(&apos;Data added&apos;);

  // 获取数据
  const getRequest = objectStore.get(&apos;1&apos;);
  getRequest.onsuccess = () =&amp;gt; console.log(&apos;Retrieved data:&apos;, getRequest.result);
};

request.onerror = event =&amp;gt; {
  console.error(&apos;Database error:&apos;, event.target.errorCode);
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;: IndexedDB API 较为复杂，通常建议使用封装库（如 Dexie.js, idb）。&lt;/p&gt;
&lt;h3&gt;缓存 API (Cache API)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：通常与 Service Worker 结合使用，提供一个用于存储和检索网络请求及其相应响应的系统。它允许你缓存资源，以便在离线时或为了提高性能而使用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：PWA 的离线缓存、静态资源缓存、API 响应缓存。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 在 Service Worker 中使用 Cache API
const CACHE_NAME = &apos;my-site-cache-v1&apos;;
const urlsToCache = [
  &apos;/&apos;,
  &apos;/styles/main.css&apos;,
  &apos;/script/main.js&apos;
];

self.addEventListener(&apos;install&apos;, event =&amp;gt; {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache =&amp;gt; {
        console.log(&apos;Opened cache&apos;);
        return cache.addAll(urlsToCache); // 将资源添加到缓存
      })
  );
});

self.addEventListener(&apos;fetch&apos;, event =&amp;gt; {
  event.respondWith(
    caches.match(event.request) // 尝试从缓存中查找匹配的请求
      .then(response =&amp;gt; {
        // 如果缓存中有匹配的响应，则返回它
        if (response) {
          return response;
        }
        // 否则，从网络获取
        return fetch(event.request);
      })
  );
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;8. 响应式设计与适配&lt;/h2&gt;
&lt;h3&gt;媒体查询（Media Queries）&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：使用 CSS 媒体查询根据设备的特性（如视口宽度、高度、方向、分辨率等）应用不同的样式规则。&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;pre&gt;&lt;code&gt;/* 默认样式 (移动优先) */
.container {
  width: 95%;
  margin: 0 auto;
}

/* 平板设备 */
@media (min-width: 768px) {
  .container {
    width: 90%;
  }
  .sidebar {
    display: block; /* 在平板上显示侧边栏 */
  }
}

/* 桌面设备 */
@media (min-width: 1024px) {
  .container {
    width: 80%;
    max-width: 1200px;
  }
}

/* 加载不同背景图 */
body {
  background-image: url(&apos;background-small.jpg&apos;);
}
@media (min-width: 768px) {
  body {
    background-image: url(&apos;background-large.jpg&apos;);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;图片适配（Responsive Images）&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：使用 HTML 的 &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; 元素或 &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; 元素的 &lt;code&gt;srcset&lt;/code&gt; 和 &lt;code&gt;sizes&lt;/code&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;pre&gt;&lt;code&gt;&amp;lt;!-- 使用 srcset 和 sizes --&amp;gt;
&amp;lt;img srcset=&quot;image-320w.jpg 320w,
             image-480w.jpg 480w,
             image-800w.jpg 800w&quot;
     sizes=&quot;(max-width: 320px) 280px,
            (max-width: 480px) 440px,
            800px&quot;
     src=&quot;image-800w.jpg&quot; alt=&quot;Responsive image&quot;&amp;gt;

&amp;lt;!-- 使用 &amp;lt;picture&amp;gt; 元素提供不同格式或裁剪 --&amp;gt;
&amp;lt;picture&amp;gt;
  &amp;lt;source media=&quot;(min-width: 650px)&quot; srcset=&quot;image-large.webp&quot; type=&quot;image/webp&quot;&amp;gt;
  &amp;lt;source media=&quot;(min-width: 465px)&quot; srcset=&quot;image-medium.webp&quot; type=&quot;image/webp&quot;&amp;gt;
  &amp;lt;source srcset=&quot;image-small.webp&quot; type=&quot;image/webp&quot;&amp;gt;
  &amp;lt;!-- Fallback for browsers that don&apos;t support WebP or &amp;lt;picture&amp;gt; --&amp;gt;
  &amp;lt;source media=&quot;(min-width: 650px)&quot; srcset=&quot;image-large.jpg&quot;&amp;gt;
  &amp;lt;source media=&quot;(min-width: 465px)&quot; srcset=&quot;image-medium.jpg&quot;&amp;gt;
  &amp;lt;img src=&quot;image-small.jpg&quot; alt=&quot;Art directed image&quot;&amp;gt;
&amp;lt;/picture&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;9. 智能化和延迟加载&lt;/h2&gt;
&lt;h3&gt;智能加载（Smart Loading）&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：根据用户的行为、网络状况（如使用 Network Information API）、设备性能或数据节省偏好，智能地决定加载哪些内容或何种质量的内容。&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;pre&gt;&lt;code&gt;// 检查网络状况决定加载资源质量 (概念示例)
if (&apos;connection&apos; in navigator) {
  const connection = navigator.connection;
  if (connection.saveData === true) {
    // 用户启用了数据节省模式，加载低质量资源
    loadLowQualityResources();
  } else if (connection.effectiveType === &apos;4g&apos;) {
    // 网络良好，加载高质量资源
    loadHighQualityResources();
  } else {
    // 网络一般或未知，加载标准资源
    loadStandardResources();
  }
} else {
  // 不支持 Network Information API，加载标准资源
  loadStandardResources();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;按需加载（On-demand Loading）&lt;/h3&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;pre&gt;&lt;code&gt;// 点击按钮加载更多评论
const loadMoreButton = document.getElementById(&apos;loadMoreComments&apos;);
let currentPage = 1;

loadMoreButton.addEventListener(&apos;click&apos;, () =&amp;gt; {
  currentPage++;
  fetch(`/api/comments?page=${currentPage}`)
    .then(response =&amp;gt; response.json())
    .then(comments =&amp;gt; {
      renderComments(comments);
      if (comments.length === 0) {
        loadMoreButton.style.display = &apos;none&apos;; // 没有更多评论了
      }
    });
});

// 结合 Intersection Observer 实现无限滚动加载
const sentinel = document.getElementById(&apos;infinite-scroll-sentinel&apos;);
const observer = new IntersectionObserver(entries =&amp;gt; {
  if (entries[0].isIntersecting) {
    loadMoreContent();
  }
});
observer.observe(sentinel);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;10. 字体优化&lt;/h2&gt;
&lt;h3&gt;字体懒加载&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：仅在需要显示相应文本时才开始加载字体文件，或者使用 &lt;code&gt;font-display&lt;/code&gt; CSS 属性控制字体加载过程中的文本显示行为，避免字体加载阻塞页面渲染或导致文本闪烁（FOIT/FOUT）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：优化使用了自定义 Web 字体的页面加载体验。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@font-face {
  font-family: &apos;MyCustomFont&apos;;
  src: url(&apos;myfont.woff2&apos;) format(&apos;woff2&apos;);
  /* font-display 控制字体加载行为 */
  /* swap: 先显示后备字体，字体加载完后替换。可能导致 FOUT (Flash of Unstyled Text) */
  font-display: swap;

  /* block: 短暂阻塞（约3秒），期间不显示文本。若超时则显示后备字体，加载完后替换。*/
  /* font-display: block; */

  /* fallback: 极短阻塞（约100ms），期间不显示文本。若超时则显示后备字体，后续不再替换。*/
  /* font-display: fallback; */

  /* optional: 极短阻塞（约100ms），期间不显示文本。若超时则显示后备字体，字体仍在后台下载，但仅在下次导航时使用。*/
  /* font-display: optional; */
}

body {
  font-family: &apos;MyCustomFont&apos;, sans-serif; /* 指定后备字体 */
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;JS 懒加载&lt;/strong&gt;: 可以使用 JavaScript 监听特定元素进入视口或其他条件，然后动态添加包含 &lt;code&gt;@font-face&lt;/code&gt; 规则的 CSS 或直接加载字体。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;合并字体文件&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：如果页面使用了同一字体家族的多个字重（如 Regular, Bold, Italic），并且这些字重分别在不同的字体文件中，可以将它们合并成一个或少数几个文件（如果格式支持，如 WOFF2 可变字体），或者只加载必要的字重和字符集，以减少 HTTP 请求次数。&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;strong&gt;策略&lt;/strong&gt;: 评估是否所有字重都是必需的。如果可能，使用可变字体（Variable Fonts），一个文件包含所有变体。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;工具&lt;/strong&gt;: 使用字体编辑工具（如 FontForge）或专门的服务来合并字体文件或创建子集。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CSS&lt;/strong&gt;: 确保 &lt;code&gt;@font-face&lt;/code&gt; 规则正确指向合并后的文件或只引用需要的字重。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;11. WebP 和 AVIF 格式&lt;/h2&gt;
&lt;h3&gt;WebP / AVIF 图片格式&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：使用现代图片格式 WebP 或 AVIF 替代传统的 JPG、PNG、GIF。它们通常能在相同视觉质量下提供更小的文件体积（WebP 比 JPG 小约 25-35%，AVIF 比 JPG 小约 50%），支持有损和无损压缩、透明度以及动画。&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;pre&gt;&lt;code&gt;&amp;lt;!-- 使用 &amp;lt;picture&amp;gt; 元素提供多种格式 --&amp;gt;
&amp;lt;picture&amp;gt;
  &amp;lt;!-- 优先尝试 AVIF --&amp;gt;
  &amp;lt;source srcset=&quot;image.avif&quot; type=&quot;image/avif&quot;&amp;gt;
  &amp;lt;!-- 其次尝试 WebP --&amp;gt;
  &amp;lt;source srcset=&quot;image.webp&quot; type=&quot;image/webp&quot;&amp;gt;
  &amp;lt;!-- 最后回退到 JPG --&amp;gt;
  &amp;lt;img src=&quot;image.jpg&quot; alt=&quot;Modern image format example&quot;&amp;gt;
&amp;lt;/picture&amp;gt;

&amp;lt;!-- 在 CSS 中使用 --&amp;gt;
&amp;lt;style&amp;gt;
.hero {
  /* 浏览器会自动选择它支持的第一个格式 */
  background-image: url(&apos;hero.avif&apos;), url(&apos;hero.webp&apos;), url(&apos;hero.jpg&apos;);
}
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;转换工具&lt;/strong&gt;: 使用 Squoosh.app、cwebp/avifenc 命令行工具或构建插件（如 &lt;code&gt;imagemin-webp&lt;/code&gt;）将现有图片转换为 WebP/AVIF。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;12. JavaScript 性能优化&lt;/h2&gt;
&lt;h3&gt;减少 JavaScript 阻塞&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：避免长时间运行的 JavaScript 任务阻塞主线程，导致页面无响应。通过异步加载（&lt;code&gt;async&lt;/code&gt;/&lt;code&gt;defer&lt;/code&gt;）、延迟执行非关键脚本、将耗时任务移至 Web Worker 或使用 &lt;code&gt;requestIdleCallback&lt;/code&gt; 来实现。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：优化页面加载和交互过程中的 JavaScript 执行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;async/defer&lt;/strong&gt;: 见 1.1 异步加载示例。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;延迟执行&lt;/strong&gt;: 对于非首屏必需的 JS（如聊天插件、广告脚本），可以在页面主要内容加载完成后再加载和执行。&lt;pre&gt;&lt;code&gt;window.addEventListener(&apos;load&apos;, () =&amp;gt; {
  const script = document.createElement(&apos;script&apos;);
  script.src = &apos;non-critical-script.js&apos;;
  document.body.appendChild(script);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Web Worker&lt;/strong&gt;: 见 12.3 Web Worker 示例。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;requestIdleCallback&lt;/strong&gt;: 见 1.4 空闲时间调度示例。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;优化垃圾回收（GC）&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：编写更有效的代码以减少内存泄漏和不必要的内存分配，从而降低垃圾回收（GC）的频率和持续时间，避免 GC 暂停导致页面卡顿。&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;strong&gt;避免全局变量&lt;/strong&gt;: 全局变量不易被回收。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;及时移除事件监听器&lt;/strong&gt;: 在元素移除或组件卸载时，使用 &lt;code&gt;removeEventListener&lt;/code&gt; 清除不再需要的监听器。&lt;pre&gt;&lt;code&gt;function handleClick() { /* ... */ }
element.addEventListener(&apos;click&apos;, handleClick);
// ... 当不再需要时 ...
element.removeEventListener(&apos;click&apos;, handleClick);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;管理闭包&lt;/strong&gt;: 注意闭包可能意外地持有不再需要的变量引用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用 WeakMap / WeakSet&lt;/strong&gt;: 对于对象键或集合成员，如果希望它们在没有其他引用的情况下能被 GC 回收，可以使用 WeakMap 或 WeakSet。&lt;pre&gt;&lt;code&gt;const metadata = new WeakMap(); // 使用 WeakMap 存储对象元数据
let obj = {};
metadata.set(obj, { info: &apos;some data&apos; });
// 当 obj 不再被引用时，WeakMap 中的条目也会被自动移除，不会阻止 obj 被回收
obj = null; // 假设这是 obj 的最后一个引用
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Web Worker&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：将计算密集型或长时间运行的 JavaScript 任务放到后台线程（Web Worker）中执行，避免阻塞主线程，保持 UI 的响应性。&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;pre&gt;&lt;code&gt;// main.js (主线程)
const worker = new Worker(&apos;worker.js&apos;);

// 向 Worker 发送数据
worker.postMessage({ data: [1, 2, 3, 4, 5] });

// 接收来自 Worker 的消息
worker.onmessage = event =&amp;gt; {
  console.log(&apos;Result from worker:&apos;, event.data.result);
};

// 处理错误
worker.onerror = error =&amp;gt; {
  console.error(&apos;Worker error:&apos;, error);
};

// worker.js (后台线程)
self.onmessage = event =&amp;gt; {
  console.log(&apos;Data received in worker:&apos;, event.data.data);
  // 执行耗时计算
  const result = event.data.data.reduce((sum, val) =&amp;gt; sum + val * val, 0);

  // 将结果发送回主线程
  self.postMessage({ result: result });
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;13. Web API 性能优化&lt;/h2&gt;
&lt;h3&gt;Intersection Observer API&lt;/h3&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;pre&gt;&lt;code&gt;const targetElement = document.getElementById(&apos;observeMe&apos;);

const callback = (entries, observer) =&amp;gt; {
  entries.forEach(entry =&amp;gt; {
    if (entry.isIntersecting) {
      // 元素进入视口
      console.log(&apos;Element is visible!&apos;);
      targetElement.classList.add(&apos;visible&apos;);
      // 如果只需要触发一次，可以停止观察
      // observer.unobserve(targetElement);
    } else {
      // 元素离开视口
      console.log(&apos;Element is hidden!&apos;);
      targetElement.classList.remove(&apos;visible&apos;);
    }
  });
};

const options = {
  root: null, // 相对于视口
  rootMargin: &apos;0px&apos;,
  threshold: 0.5 // 元素 50% 可见时触发回调
};

const observer = new IntersectionObserver(callback, options);
observer.observe(targetElement);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Performance API&lt;/h3&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;pre&gt;&lt;code&gt;// 测量代码块执行时间
performance.mark(&apos;start-calculation&apos;);
// ... 执行一些耗时操作 ...
calculateFibonacci(40);
performance.mark(&apos;end-calculation&apos;);
performance.measure(&apos;calculation-duration&apos;, &apos;start-calculation&apos;, &apos;end-calculation&apos;);

// 获取所有测量结果
const measures = performance.getEntriesByType(&apos;measure&apos;);
measures.forEach(measure =&amp;gt; {
  console.log(`${measure.name}: ${measure.duration}ms`);
});

// 获取资源加载性能数据
const resources = performance.getEntriesByType(&apos;resource&apos;);
resources.forEach(resource =&amp;gt; {
  console.log(`Resource ${resource.name} loaded in ${resource.duration}ms`);
});

// 获取导航性能数据
const navigation = performance.getEntriesByType(&apos;navigation&apos;)[0];
console.log(`DOM content loaded in ${navigation.domContentLoadedEventEnd}ms`);
console.log(`Page loaded in ${navigation.loadEventEnd}ms`);

// 清除标记和测量
performance.clearMarks();
performance.clearMeasures();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Service Worker&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;描述&lt;/strong&gt;：一个运行在浏览器后台的脚本，独立于网页，可以拦截和处理网络请求、推送通知、后台同步等。是构建 PWA 的核心技术之一。&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;pre&gt;&lt;code&gt;// main.js (注册 Service Worker)
if (&apos;serviceWorker&apos; in navigator) {
  window.addEventListener(&apos;load&apos;, () =&amp;gt; {
    navigator.serviceWorker.register(&apos;/sw.js&apos;)
      .then(registration =&amp;gt; {
        console.log(&apos;ServiceWorker registration successful with scope: &apos;, registration.scope);
      })
      .catch(error =&amp;gt; {
        console.log(&apos;ServiceWorker registration failed: &apos;, error);
      });
  });
}

// sw.js (Service Worker 脚本 - 缓存优先策略示例)
const CACHE_NAME = &apos;my-app-cache-v1&apos;;

self.addEventListener(&apos;install&apos;, event =&amp;gt; {
  // 安装时缓存核心资源
  event.waitUntil(
    caches.open(CACHE_NAME).then(cache =&amp;gt; {
      return cache.addAll([
        &apos;/&apos;,
        &apos;/index.html&apos;,
        &apos;/styles.css&apos;,
        &apos;/app.js&apos;
      ]);
    })
  );
});

self.addEventListener(&apos;fetch&apos;, event =&amp;gt; {
  event.respondWith(
    caches.match(event.request)
      .then(response =&amp;gt; {
        // 缓存命中，返回缓存的响应
        if (response) {
          return response;
        }
        // 缓存未命中，从网络获取
        return fetch(event.request).then(networkResponse =&amp;gt; {
          // 可选：将新的响应添加到缓存
          // caches.open(CACHE_NAME).then(cache =&amp;gt; cache.put(event.request, networkResponse.clone()));
          return networkResponse;
        });
      })
      .catch(() =&amp;gt; {
        // 网络和缓存都失败时，可以返回一个备用的离线页面
        // return caches.match(&apos;/offline.html&apos;);
      })
  );
});
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>DOH和ECH配置文档</title><link>https://blog.emohe.cn/posts/ech/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/ech/</guid><pubDate>Fri, 14 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;前言&lt;/h3&gt;
&lt;p&gt;DoH（DNS over HTTPS）是一种安全协议，它通过 HTTPS（TLS 加密）传输 DNS 解析请求，防止 DNS 查询被窃听、篡改或屏蔽。&lt;/p&gt;
&lt;p&gt;正常情况下，当你访问一个 &lt;code&gt;HTTPS&lt;/code&gt; 网站时，需要先向 &lt;code&gt;DNS&lt;/code&gt; 发送查询该域名 &lt;code&gt;IP&lt;/code&gt; 地址的请求。由于传统 &lt;code&gt;DNS&lt;/code&gt; 查询是明文传输的，ISP 或其他网络中间人可以看到你查询了哪个域名。虽然 &lt;code&gt;HTTPS&lt;/code&gt; 内容是加密的，无法看到具体内容，但可以得知你在访问什么网站。&lt;/p&gt;
&lt;p&gt;另外，访问 &lt;code&gt;HTTPS&lt;/code&gt; 网站需要进行 &lt;code&gt;TLS&lt;/code&gt; 握手，握手时会携带域名信息，也就是 &lt;code&gt;SNI&lt;/code&gt;（Server Name Indication）。&lt;code&gt;ISP&lt;/code&gt; 同样能看到这个信息。&lt;/p&gt;
&lt;p&gt;此时，中间人可以劫持并篡改 &lt;code&gt;DNS&lt;/code&gt; 查询返回的 &lt;code&gt;IP&lt;/code&gt; 地址。&lt;/p&gt;
&lt;p&gt;当我们使用加密 &lt;code&gt;DNS&lt;/code&gt; 后，可以隐藏 &lt;code&gt;DNS&lt;/code&gt; 查询的域名信息，但由于 &lt;code&gt;TLS&lt;/code&gt; 握手仍会携带 &lt;code&gt;SNI&lt;/code&gt; 信息，中间人依然能够监控。为了进一步保护隐私，网站可以配置 &lt;code&gt;ECH&lt;/code&gt;（Encrypted Client Hello）来加密 &lt;code&gt;SNI&lt;/code&gt; 信息。&lt;/p&gt;
&lt;p&gt;目前 &lt;code&gt;Cloudflare&lt;/code&gt; 已支持 &lt;code&gt;ECH&lt;/code&gt;，使用其 &lt;code&gt;CDN&lt;/code&gt; 服务的网站会默认开启此功能。&lt;code&gt;ECH&lt;/code&gt; 会加密真实的 &lt;code&gt;SNI&lt;/code&gt; 信息，只有支持 &lt;code&gt;ECH&lt;/code&gt; 的服务器才能解密，从而实现对真实访问目标的隐藏。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ECH&lt;/code&gt; 配合 &lt;code&gt;DoH&lt;/code&gt; 加密 DNS 可以提供更完整的隐私保护。&lt;/p&gt;
&lt;h3&gt;享受ECH加密的一些注意事项&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;服务端域名开启了ECH&lt;/li&gt;
&lt;li&gt;服务端域名支持TLS 1.3&lt;/li&gt;
&lt;li&gt;域名托管商支持HTTPS解析&lt;/li&gt;
&lt;li&gt;客户端使用国际主流浏览器&lt;/li&gt;
&lt;li&gt;客户端不要用代理&lt;/li&gt;
&lt;li&gt;客户端使用加密DNS&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;Windows 11&lt;/code&gt; 配置加密 DNS&lt;/h3&gt;
&lt;p&gt;由于网络环境限制，国外的加密 &lt;code&gt;DNS&lt;/code&gt; 服务可能无法直接访问。本文教大家配置阿里云的加密 &lt;code&gt;DNS&lt;/code&gt; 服务。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;系统设置方法：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;打开 &lt;code&gt;设置&lt;/code&gt; &amp;gt; &lt;code&gt;网络和 Internet&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;找到当前网络连接的 &lt;code&gt;属性&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;在 &lt;code&gt;DNS 服务器分配&lt;/code&gt; 中选择 &lt;code&gt;手动&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;首选 DNS 填入 &lt;code&gt;223.5.5.5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DNS over HTTPS&lt;/code&gt; 选择 &lt;code&gt;手动模板&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;模板地址填入 &lt;code&gt;https://dns.alidns.com/dns-query&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;控制面板配置方法：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;打开控制面板，选择 &lt;code&gt;查看网络状态和任务&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;点击你的网卡连接&lt;/li&gt;
&lt;li&gt;选择 &lt;code&gt;属性&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;双击 &lt;code&gt;Internet 协议版本 4 (TCP/IPv4)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;点击 &lt;code&gt;属性&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;首选 DNS 填入 &lt;code&gt;223.5.5.5&lt;/code&gt;，备选 DNS 填入 &lt;code&gt;8.8.8.8&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;点击 &lt;code&gt;确定&lt;/code&gt; 保存设置&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;配置完成后，你的 DNS 查询将通过加密通道进行，提升上网安全性和隐私保护。&lt;/p&gt;
&lt;h3&gt;验证是否开启&lt;/h3&gt;
&lt;p&gt;访问域名 https://cdnjs.com/cdn-cgi/trace 查看&lt;code&gt;sni&lt;/code&gt;参数&lt;/p&gt;
&lt;p&gt;或者也可以访问https://tls-ech.dev&lt;/p&gt;
&lt;h3&gt;服务端域名配置ECH教程&lt;/h3&gt;
&lt;p&gt;由于Cloudflare CDN国内访问速度慢，所以我介绍一下不使用CDN的教程&lt;/p&gt;
&lt;p&gt;我使用caddy较多，所以用caddy做演示，其他同理&lt;/p&gt;
&lt;p&gt;&lt;code&gt;caddy&lt;/code&gt;需要使用&lt;code&gt;2.10.0&lt;/code&gt;以上的版本。&lt;/p&gt;
&lt;p&gt;要使用&lt;code&gt;caddy&lt;/code&gt;自动配置&lt;code&gt;ech&lt;/code&gt;则需要编译&lt;code&gt;cloudflare dns&lt;/code&gt;插件，用于caddy自动添加DNS记录，官方编译的包默认不带此插件。&lt;/p&gt;
&lt;h3&gt;编译&lt;code&gt;caddy&lt;/code&gt;并加入&lt;code&gt;cloudflare DNS&lt;/code&gt;插件的支持&lt;/h3&gt;
&lt;p&gt;1：安装&lt;code&gt;go&lt;/code&gt;环境&lt;/p&gt;
&lt;p&gt;2：下载&lt;code&gt;xcaddy&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3：编译带&lt;code&gt;cloudflare dns&lt;/code&gt;插件的&lt;code&gt;caddy&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;xcaddy build --with github.com/caddy-dns/cloudflare
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时你的当前目录会生成一个已经包含了&lt;code&gt;cloudflare dns&lt;/code&gt;插件的&lt;code&gt;caddy&lt;/code&gt;二进制文件&lt;/p&gt;
&lt;h3&gt;以&lt;code&gt;debug&lt;/code&gt;模式测试运行&lt;/h3&gt;
&lt;p&gt;创建&lt;code&gt;/etc/caddy/Caddyfile&lt;/code&gt;文件，并写入以下文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    debug
    dns cloudflare xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    ech ech.example.com

    log {
        output stdout
        format console
        level DEBUG
    }
}

test.example.com {
    root * /tmp
    file_server browse
    encode zstd gzip
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;配置说明&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;dns cloudflare&lt;/code&gt;这个配置填CF的&lt;code&gt;区域 DNS API 令牌&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ech ech.example.com&lt;/code&gt;替换你的任意二级域名，并且解析到你的IP，用于发布ECH公钥，或者第三方提供的ECH配置服务域名也行。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;test.example.com&lt;/code&gt;替换你的域名，并且解析到你的IP，不要开小黄云。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;root * /tmp&lt;/code&gt;这个配置代表以网页浏览文件的形式，浏览你服务器的&lt;code&gt;/tmp&lt;/code&gt;目录，这里只是测试为了方便起服务。&lt;/p&gt;
&lt;h3&gt;启动&lt;code&gt;caddy&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;sudo ./caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不出意外的话就启动成功了，然后可以访问你的域名看下是否能成功访问&lt;/p&gt;
&lt;p&gt;并且&lt;code&gt;/root/.local/share/caddy&lt;/code&gt;目录会生成一个锁文件，如果需要重新部署则需要删除这个目录&lt;/p&gt;
&lt;p&gt;CF里也会自动添加一个类型为&lt;code&gt;HTTPS&lt;/code&gt;的解析记录&lt;/p&gt;
&lt;h3&gt;检查&lt;code&gt;ech&lt;/code&gt;是否配置成功&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;dig test.example.com TYPE65
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行这个命令后，在输出的信息里可以看到有如下类似的信息就代表启用成功&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ech=AEr+DEBUGAgACBDEMOnixVAfd/ASODJKADKSODAPSkZIwAMADQABAAIASDASDADHw9lY2guZXhhbXBsZS5jb20AAA==
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;生产环境&lt;/h3&gt;
&lt;p&gt;移动二进制文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mv ~/caddy /usr/bin/caddy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;创建系统服务配置&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;touch /etc/systemd/system/caddy.service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;写入配置&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Unit]
Description=Caddy Web Server
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Wants=network-online.target

[Service]
User=root
Group=root
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile
TimeoutStopSec=5s
LimitNOFILE=1048576
LimitNPROC=512
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;管理命令&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 重新加载 systemd 配置
sudo systemctl daemon-reexec
sudo systemctl daemon-reload

# 启动 Caddy
sudo systemctl start caddy

# 设置开机启动
sudo systemctl enable caddy

# 查看运行状态
sudo systemctl status caddy
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;生产环境反代配置示例&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;{
	dns cloudflare xxxxxxxxxxxxxxxxxxxxx
	ech ech.example.com
}

test.example.com {
	reverse_proxy localhost:8080
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重新部署记得删除：&lt;code&gt;/root/.local/share/caddy&lt;/code&gt;锁文件，和CF里的&lt;code&gt;HTTPS&lt;/code&gt;解析记录&lt;/p&gt;
</content:encoded></item><item><title>tailscale异地组网教程</title><link>https://blog.emohe.cn/posts/tailscale/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/tailscale/</guid><pubDate>Tue, 11 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://tailscale.com/&quot;&gt;Tailscale&lt;/a&gt; 是一种新型组网工具.
它能帮助我们把安装了&lt;code&gt;Tailscale&lt;/code&gt;服务的机器，都放到同一个局域网内，实现内网互联。
即公司或者家里的 PC 机器连到同一网络，包括云服务器等等都能放到同一个局域网。
并且能够自动发现点对点直连，如果无法直连则会通过他的公共中继服务器来转发，他的公共网络我实测大概有&lt;code&gt;30M&lt;/code&gt;带宽，并且永久免费。
当然也可以自建中继服务器，本文有介绍。部署和使用过程都非常简单方便。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;安卓，ios，windows，mac 直接官网下载即可&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Docker部署&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;docker run -d \
  --name tailscale \
  --privileged \
  --network host \
  --restart always \
  -v $(pwd)/tailscale:/var/lib/tailscale \
  tailscale/tailscale:latest \
  tailscaled --state=/var/lib/tailscale/tailscaled.state
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;进入容器：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker exec -it tailscale sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;生成登录链接：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tailscale up
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;（可选）使用密钥连接：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tailscale up --auth-key=&amp;lt;你的AuthKey&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看中继网络&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tailscale netcheck
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看是否点对点直连&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tailscale status
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果看到&lt;code&gt;relay &quot;tok&quot;&lt;/code&gt;则说明使用了公共中继连接，&lt;code&gt;tok&lt;/code&gt;代表日本公共节点&lt;/p&gt;
&lt;h3&gt;指定密钥运行&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;docker-compose.yml&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  tailscale:
    image: tailscale/tailscale:latest
    container_name: tailscale
    privileged: true
    network_mode: host
    restart: always
    volumes:
      - ./tailscale:/var/lib/tailscale
    command:
      - tailscaled
      - --state=/var/lib/tailscale/tailscaled.state
      - --tun=tailscale0
      - --auth-key=你的AuthKey
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;宿主机部署&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 安装
sudo apt install tailscale -y

# 登录
sudo tailscale up

# 查看运行状态
sudo systemctl status tailscaled

# 开机自启
sudo systemctl enable tailscaled

# 停止运行
sudo systemctl stop tailscaled

# 重启
sudo systemctl restart tailscaled

# 查看日志
sudo journalctl -u tailscaled -f
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;自建&lt;code&gt;derper&lt;/code&gt;中继服务器&lt;/h3&gt;
&lt;p&gt;自动申请证书&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  derper:
    image: fredliang/derper:latest
    container_name: derper
    restart: unless-stopped
    environment:
      - DERP_DOMAIN=derper.your-domain.com  # 替换为您的域名
      - DERP_CERT_MODE=letsencrypt
      - DERP_STUN=true
      - DERP_HTTP_PORT=80
      - DERP_ADDR=:443
      - DERP_VERIFY_CLIENTS=true  # 本地 tailscaled 实例验证客户端，只允许自己使用
    ports:
      - &quot;80:80&quot;
      - &quot;443:443&quot;
      - &quot;3478:3478/udp&quot;
    volumes:
      - ./certs:/app/certs # 证书持久化存储
      - /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;手动配置证书&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  derper:
    image: fredliang/derper:latest
    container_name: derper
    restart: unless-stopped
    environment:
      - DERP_DOMAIN=derper.your-domain.com  # 替换为您的域名
      - DERP_CERT_MODE=manual
      - DERP_CERT_DIR=/app/certs
      - DERP_STUN=true
      - DERP_HTTP_PORT=80
      - DERP_ADDR=:443
      - DERP_VERIFY_CLIENTS=true  # 通过本地 tailscaled 实例验证客户端
    ports:
      - &quot;80:80&quot;
      - &quot;443:443&quot;
      - &quot;3478:3478/udp&quot;
    volumes:
      - ./certs:/app/certs # 手动挂载证书
      - /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://login.tailscale.com/admin/acls/file&quot;&gt;修改Tailscale后台配置文件&lt;/a&gt; 添加如下内容&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;        &quot;derpMap&quot;: {
                &quot;OmitDefaultRegions&quot;: true,
                // OmitDefaultRegions 忽略官方的中继节点
                &quot;Regions&quot;: {
                        // 这里的 901 从 900 开始随便取数字
                        &quot;901&quot;: {
                                // RegionID 和上面的相等
                                &quot;RegionID&quot;: 901,
                                // RegionCode 名称
                                &quot;RegionCode&quot;: &quot;Vultr-SG&quot;,
                                &quot;Nodes&quot;: [
                                        {
                                                // Name 保持 1不动
                                                &quot;Name&quot;:     &quot;1&quot;,
                                                // 这个也和 RegionID 一样
                                                &quot;RegionID&quot;: 901,
                                                // 域名
                                                &quot;HostName&quot;: &quot;&amp;lt;你的域名&amp;gt;&quot;,
                                                // 端口号
                                                &quot;DERPPort&quot;: 443,
                                        },
                                ],
                        },
                },
        },
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Docker变量&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;DERP_DOMAIN&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;derper 服务器主机名&lt;/td&gt;
&lt;td&gt;your-hostname.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DERP_CERT_DIR&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;存储证书的目录 (如果地址端口是 :443)&lt;/td&gt;
&lt;td&gt;/app/certs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DERP_CERT_MODE&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;获取证书的模式。可选项: manual, letsencrypt&lt;/td&gt;
&lt;td&gt;letsencrypt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DERP_ADDR&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;服务器监听地址&lt;/td&gt;
&lt;td&gt;:443&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DERP_STUN&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;是否同时运行 STUN 服务器&lt;/td&gt;
&lt;td&gt;true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DERP_HTTP_PORT&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;提供 HTTP 服务的端口。设置为 -1 禁用&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DERP_VERIFY_CLIENTS&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;是否通过本地 tailscaled 实例验证此 DERP 服务器的客户端&lt;/td&gt;
&lt;td&gt;false&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;子网路由&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;192.168.3.0/24&lt;/code&gt;根据自己需求更改，添加子网路由后，&lt;code&gt;192.168.3.0/24&lt;/code&gt;网段的所有设备都能直接访问tailscale中的其他设备，无需安装tailscale&lt;/p&gt;
&lt;p&gt;开启子网路由：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tailscale up --advertise-routes=192.168.3.0/24 --accept-routes --reset
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;开启后，在&lt;code&gt;tailscale&lt;/code&gt;后台找到你路由器设备，点击右侧菜单(三个点)，选择&lt;code&gt;Edit route settings...&lt;/code&gt;，勾选&lt;code&gt;192.168.3.0/24&lt;/code&gt;，点击&lt;code&gt;Save&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;然后输入以下命令查看是否存在你广播的IP段&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tailscale status --json | grep advertisedRoutes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;能看到你广播的网段就说明成功了，但是这个检查方式不准确，最好还是手动ping测试&lt;/p&gt;
&lt;h3&gt;需要注意的坑&lt;/h3&gt;
&lt;p&gt;子网路由功能依赖&lt;code&gt;iptables&lt;/code&gt;，immortalwrt默认防火墙不是这个，需要安装&lt;code&gt;iptables&lt;/code&gt;，immortalwrt默认不会让 LAN 网段流量被转发到&lt;code&gt;tailscale0&lt;/code&gt;，需要手动加。&lt;/p&gt;
&lt;p&gt;需要启用 IP 转发，请确保已经启用。&lt;/p&gt;
</content:encoded></item><item><title>SSH防护最简单的两种方案</title><link>https://blog.emohe.cn/posts/ssh%E7%AB%AF%E5%8F%A3%E6%95%B2%E9%97%A8/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/ssh%E7%AB%AF%E5%8F%A3%E6%95%B2%E9%97%A8/</guid><pubDate>Thu, 27 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;前言&lt;/h3&gt;
&lt;p&gt;互联网上有黑客无时无刻都在扫端口，我也看到过很多网友的惨痛经历，服务器的SSH密码被黑客爆破后进入服务器捣乱或者植入后门，所以网络安全要牢记心中。本篇文章为大家分享防止SSH被爆破的两种方案，并且最简单方便。&lt;/p&gt;
&lt;h3&gt;方案一：关闭密码登录改为使用私钥登录&lt;/h3&gt;
&lt;p&gt;简介：通过关闭SSH密码登录，改用公钥认证，只有拥有对应私钥的用户才能登录。
服务器存储用户的公钥，客户端使用私钥进行身份验证，非对称加密确保安全性，彻底杜绝密码暴力破解。&lt;/p&gt;
&lt;p&gt;1：生成&lt;code&gt;ED25519&lt;/code&gt;类型的 SSH 密钥对&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh-keygen -t ed25519
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;一路回车即可，生成的&lt;code&gt;id_ed25519&lt;/code&gt;文件为私钥，使用这个私钥文件连接服务器，&lt;code&gt;id_ed25519.pub&lt;/code&gt;文件为公钥。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;2：然后进入存储SSH密钥的目录，并配置公钥&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd /root/.ssh
cat id_ed25519.pub &amp;gt;&amp;gt; authorized_keys
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3：修改SSH配置文件&lt;/p&gt;
&lt;p&gt;文件路径在&lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo vim /etc/ssh/sshd_config
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4：找到对应的配置然后修改，这里仅展示需要修改的地方&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 修改SSH服务端口
Port 2222

# 启用公钥认证
PubkeyAuthentication yes

# 指定存储公钥的文件位置(增加此项)
AuthorizedKeysFile .ssh/authorized_keys

# 禁止使用空密码登录
PermitEmptyPasswords no

# 禁止使用密码认证登录
PasswordAuthentication no
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;5：重启SSH服务&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl restart ssh
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;方案二：端口敲门&lt;/h3&gt;
&lt;p&gt;:::note[注意]
请先看完教程再配置，配置后记得先测试一下，免得把自己关在门外了。
:::&lt;/p&gt;
&lt;p&gt;简介：端口敲门通过关闭SSH端口，服务器监听预设的端口序列，当客户端按正确顺序&lt;code&gt;敲门&lt;/code&gt;后，防火墙规则动态开放SSH端口，仅允许敲门成功的IP访问，阻止未授权访问。&lt;/p&gt;
&lt;p&gt;1：安装&lt;code&gt;iptables&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;大部分常见的Linux发行版已经默认安装了，无需额外安装，并且你安装了Docker后也是肯定安装了&lt;code&gt;iptables&lt;/code&gt;。可以运行&lt;code&gt;iptables --version&lt;/code&gt;查看版本。如果没有安装，可以执行这个命令安装：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt update &amp;amp;&amp;amp; sudo apt install iptables -y
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2：安装端口敲门程序&lt;code&gt;knockd&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt update &amp;amp;&amp;amp; sudo apt install knockd -y
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3：配置&lt;code&gt;knockd&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;knockd&lt;/code&gt;的默认配置文件路径在&lt;code&gt;/etc/knockd.conf&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[options]
    UseSyslog
    logfile = /var/log/knockd.log

# 开启SSH访问 - 只允许敲门的IP访问
[openSSH]
    sequence    = 5003,5001
    seq_timeout = 15
    start_command = /sbin/iptables -C INPUT -s %IP% -p tcp --dport 22 -j ACCEPT || /sbin/iptables -I INPUT 1 -s %IP% -p tcp --dport 22 -j ACCEPT
    tcpflags    = syn
    cmd_timeout = 10

# 关闭所有SSH访问 - 阻止所有IP
[closeSSH]
    sequence    = 7001
    seq_timeout = 15
    start_command = /sbin/iptables -C INPUT -p tcp --dport 22 -j DROP || /sbin/iptables -I INPUT 1 -p tcp --dport 22 -j DROP &amp;amp;&amp;amp; /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT 2&amp;gt;/dev/null
    tcpflags    = syn
    cmd_timeout = 10
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4：启动&lt;code&gt;knockd&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl enable knockd
sudo systemctl start knockd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;5：关门&lt;/p&gt;
&lt;p&gt;网页访问&lt;code&gt;7001&lt;/code&gt;端口关闭SSH服务，阻止所有IP访问SSH&lt;/p&gt;
&lt;p&gt;6：敲门&lt;/p&gt;
&lt;p&gt;按顺序网页访问&lt;code&gt;5003&lt;/code&gt;和&lt;code&gt;5001&lt;/code&gt;端口，需要15秒内完成动作，敲门后会自动放行敲门的IP允许连接SSH。&lt;/p&gt;
&lt;p&gt;Liunx可以使用wget等命令敲门&lt;/p&gt;
&lt;h4&gt;（可选）关闭端口敲门&lt;/h4&gt;
&lt;p&gt;停止并禁用&lt;code&gt;knockd&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl stop knockd
sudo systemctl disable knockd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;清除防火墙所有的自定义规则&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo iptables -F
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;端口敲门不仅限于SSH端口，还能实现更多玩法。&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Docker部署DeepSeek-R1本地大模型</title><link>https://blog.emohe.cn/posts/deepseek-r1/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/deepseek-r1/</guid><pubDate>Sat, 08 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;前言&lt;/h3&gt;
&lt;p&gt;使用&lt;code&gt;docker&lt;/code&gt;部署&lt;code&gt;OpenUI + Ollama&lt;/code&gt; + &lt;code&gt;DeepSeek-R1:7B&lt;/code&gt;，通过浏览器访问&lt;code&gt;OpenUI&lt;/code&gt;进行交互。部署完成后可以离线使用&lt;code&gt;DeepSeek-R1&lt;/code&gt;大模型。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Ollama&lt;/code&gt;的&lt;code&gt;DeepSeek-R1&lt;/code&gt;大模型官网：https://ollama.com/library/deepseek-r1:7b&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;1.5B：CPU最低4核，内存8GB+，若GPU加速可选4GB+显存，适合低资源设备部署等场景。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;7B：CPU 8核以上，内存16GB+，硬盘8GB+，显卡推荐8GB+显存，可用于本地开发测试等场景。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;8B：硬件需求与7B相近略高，适合需更高精度的轻量级任务。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;14B：CPU 12核以上，内存32GB+，硬盘15GB+，显卡16GB+显存，可用于企业级复杂任务等场景。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;32B：CPU 16核以上，内存64GB+，硬盘30GB+，显卡24GB+显存，适合高精度专业领域任务等场景。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;70B：CPU 32核以上，内存128GB+，硬盘70GB+，显卡需多卡并行，适合科研机构等进行高复杂度生成任务等场景。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;部署教程&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;open-webui&lt;/code&gt;镜像大小：8 GB&lt;/p&gt;
&lt;p&gt;&lt;code&gt;docker-compose.yml&lt;/code&gt;配置示例&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  open-webui:
    image: ghcr.io/open-webui/open-webui:ollama
    container_name: open-webui
    ports:
      - &quot;3000:8080&quot;
    volumes:
      - ./ollama:/root/.ollama
      - ./open-webui:/app/backend/data
    restart: always
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;国内网络环境可以使用南京大学 ghcr 镜像加速&lt;code&gt;ghcr.nju.edu.cn&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;容器内安装&lt;code&gt;DeepSeek-R1 7b&lt;/code&gt;模型：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker exec -it open-webui sh
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;ollama run deepseek-r1:7b
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;部署完成后浏览器访问&lt;code&gt;3000&lt;/code&gt;端口使用web面板进行对话，如果需要域名访问反代一下即可。&lt;/p&gt;
&lt;hr /&gt;
&lt;h4&gt;如果有GPU服务器可以使用如下命令&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;docker run -d -p 3000:8080 --gpus=all \
  -v ./ollama:/root/.ollama \
  -v ./open-webui:/app/backend/data \
  --name open-webui \
  --restart always \
  ghcr.io/open-webui/open-webui:ollama
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;电脑本地安装ollama&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;官方地址：https://ollama.com&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;下载对应的系统版本&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;安装完成后打开电脑终端&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;查看版本&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;ollama --version
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;下载并运行 deepseek-r1:7b 模型&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;ollama run deepseek-r1:7b
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;可以在终端对话使用&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;也可以部署&lt;code&gt;open-webui&lt;/code&gt;访问并使用本地&lt;code&gt;ollama&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;docker run -d -p 3000:8080 \
  --add-host=host.docker.internal:host-gateway \
  -e OLLAMA_API_URL=http://host.docker.internal:11434 \
  -v ./open-webui:/app/backend/data \
  --name open-webui \
  --restart always \
  ghcr.io/open-webui/open-webui:main
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h4&gt;ollama api 调用示例 POST 请求&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;curl http://localhost:11434/api/generate -d &apos;{
  &quot;model&quot;: &quot;deepseek-r1:7b&quot;,
  &quot;prompt&quot;: &quot;你是什么模型?&quot;
}&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;API 会返回生成的结果，通常是 JSON 格式&lt;/p&gt;
</content:encoded></item><item><title>一生真的太短了</title><link>https://blog.emohe.cn/posts/%E4%B8%80%E7%94%9F%E7%9C%9F%E7%9A%84%E5%A4%AA%E7%9F%AD%E4%BA%86/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/%E4%B8%80%E7%94%9F%E7%9C%9F%E7%9A%84%E5%A4%AA%E7%9F%AD%E4%BA%86/</guid><pubDate>Wed, 29 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&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;p&gt;时间过得真快。&lt;/p&gt;
&lt;p&gt;此刻我执笔之时是 2025 年 1 月 29 日，农历新年的清晨。凌晨四点，被鞭炮声惊醒，我才意识到又过年了。便再也睡不着了，躺在床上，任由思绪在黑暗中不受控制的游走。&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;/p&gt;
&lt;p&gt;昨天，家人随口提起隔壁村一位老人去世了。
他们说，冬天一到，老人往往更容易扛不住，说走就走。&lt;/p&gt;
&lt;p&gt;这句话在我心里反复回响。
人一旦死去，就再也见不到了。永远。&lt;/p&gt;
&lt;p&gt;从这一刻开始，死亡的恐惧毫无征兆地袭来。&lt;/p&gt;
&lt;p&gt;我开始不停地想：死了之后，什么都没有了。连感受&quot;黑&quot;的能力都没有了。甚至连&quot;世界&quot;本身，也似乎是依附于&quot;我&quot;的存在而存在的——如果我消失了，这个世界于我而言，也将彻底消散。&lt;/p&gt;
&lt;p&gt;想到这里，我的心跳骤然加快，呼吸变得困难。&lt;/p&gt;
&lt;p&gt;生不带来，死不带去。
一切仿佛都失去了意义。&lt;/p&gt;
&lt;p&gt;人生终将落幕。若干年后，我会死去；那些记忆中曾经认识我的人，也终将死去；直到关于我的所有痕迹彻底消散，仿佛我从未存在过一样。此时此刻正在思考的这个&quot;我&quot;，也将不复存在。&lt;/p&gt;
&lt;p&gt;我好不甘...&lt;/p&gt;
&lt;p&gt;这一生如此凄凉，甚至连&quot;平庸&quot;都谈不上，这种无可回避的结局，让人感到彻骨的绝望。&lt;/p&gt;
&lt;p&gt;地球终会毁灭，人类终将消亡，连宇宙本身也拥有尽头。
当宇宙走向死亡——&lt;a href=&quot;https://www.baike.com/wikiid/7257034255048556556&quot;&gt;热寂&lt;/a&gt;降临，一切物质与能量都会归于沉寂，世界陷入无尽的黑暗、虚无与孤独。没有任何波动，什么都不剩。&lt;/p&gt;
&lt;p&gt;那种荒凉，深入骨髓。&lt;/p&gt;
&lt;p&gt;有时候，我会刻意的去感受&quot;死亡&quot;的边缘。
在夜晚极度困倦的时候，我努力感受从意识模糊到彻底沉睡的那一瞬间，去尝试意识消散的感觉。可越接近，我就越害怕。我不知道明天和意外哪一个会先到来，害怕自己一旦睡着，就再也无法醒来。&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;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>给IP申请SSL证书并开启HTTPS访问</title><link>https://blog.emohe.cn/posts/ip-ssl/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/ip-ssl/</guid><pubDate>Fri, 17 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;前言&lt;/h3&gt;
&lt;p&gt;IP证书是一种特殊的SSL证书，它直接绑定到服务器的公网IP地址上，当用户通过这个IP地址访问时，也能享受到如同访问经过域名验证的HTTPS网站一样的加密安全连接。这样一来，即便是没有注册域名或仅通过IP地址访问的服务，也能确保数据传输过程中的安全性，防止信息被窃取或篡改，同时也提供了对服务器身份的验证，增加了用户对服务提供者的信任度。&lt;/p&gt;
&lt;p&gt;本教程以&lt;code&gt;caddy&lt;/code&gt;作为演示，如何安装&lt;code&gt;caddy&lt;/code&gt;请参考本博客的教程，使用&lt;code&gt;nginx&lt;/code&gt;的话思路也一样。&lt;/p&gt;
&lt;h3&gt;1. 打开免费申请IP SSL证书的网站&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;https://zerossl.com/&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;1：打开后首页就直接输入你要申请的IP，点击&lt;code&gt;Next Step&lt;/code&gt;进入下一步。&lt;/p&gt;
&lt;p&gt;2：输入注册邮箱和密码（邮箱可以随便填，不会验证真实性）&lt;/p&gt;
&lt;p&gt;3：免费版IP SSL证书有效期是3个月。需要在第二项里手动选择下。否则是默认的1年付费版。&lt;/p&gt;
&lt;h3&gt;2. 验证IP所有权&lt;/h3&gt;
&lt;p&gt;以下配置中的&lt;code&gt;123.123.123.123&lt;/code&gt;为示例&lt;code&gt;IP地址&lt;/code&gt;，请根据自己情况替换。&lt;/p&gt;
&lt;p&gt;1：下载&lt;code&gt;Auth&lt;/code&gt;验证文件&lt;/p&gt;
&lt;p&gt;2：在网站目录里创建&lt;code&gt;/.well-known/pki-validation/&lt;/code&gt;目录，以&lt;code&gt;caddy&lt;/code&gt;为例&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir -p /var/www/.well-known/pki-validation
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;caddy&lt;/code&gt;配置示例&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://123.123.123.123 {
    root * /var/www
    encode zstd gzip
    file_server
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3：将刚刚下载的验证文件上传到&lt;code&gt;/var/www/.well-known/pki-validation/&lt;/code&gt;目录里&lt;/p&gt;
&lt;p&gt;4：启动&lt;code&gt;caddy&lt;/code&gt;，访问你的公网IP，如果能看到验证文件，则代表成功&lt;/p&gt;
&lt;h3&gt;3. 生成SSL证书&lt;/h3&gt;
&lt;p&gt;然后&lt;code&gt;zerossl&lt;/code&gt;网站里点击验证，验证成功后下载&lt;code&gt;zip&lt;/code&gt;证书压缩包，上传到服务器&lt;code&gt;/etc/caddy&lt;/code&gt;目录，并使用&lt;code&gt;unzip&lt;/code&gt;命令解压&lt;/p&gt;
&lt;h3&gt;4. 合并证书文件&lt;/h3&gt;
&lt;p&gt;在&lt;code&gt;/etc/caddy&lt;/code&gt;目录下执行如下命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd /etc/caddy
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;cat certificate.crt ca_bundle.crt &amp;gt; fullchain.crt
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5. 启动站点&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;caddy&lt;/code&gt;配置示例&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://123.123.123.123 {
    root * /var/www
    encode zstd gzip
    file_server

    tls /etc/caddy/fullchain.crt /etc/caddy/private.key
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;/var/www&lt;/code&gt;为站点目录，&lt;code&gt;123.123.123.123&lt;/code&gt;为示例&lt;code&gt;IP地址&lt;/code&gt;，请根据自己情况替换。&lt;/p&gt;
&lt;p&gt;使用&lt;code&gt;nginx&lt;/code&gt;的话思路也一样。&lt;/p&gt;
</content:encoded></item><item><title>Docker部署Poste.io邮局</title><link>https://blog.emohe.cn/posts/docker%E9%83%A8%E7%BD%B2poste%E9%82%AE%E5%B1%80/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/docker%E9%83%A8%E7%BD%B2poste%E9%82%AE%E5%B1%80/</guid><pubDate>Tue, 14 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;前言&lt;/h3&gt;
&lt;p&gt;很多企业都有自建邮局的需求，本篇介绍如何使用Docker部署开源的&lt;code&gt;Poste.io&lt;/code&gt;邮局系统。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;需要占用的端口&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;80 - HTTP（用于 web 界面和未加密的邮件服务）
443 - HTTPS（用于加密的 web 界面和邮件服务）
25 - SMTP（用于发送邮件）
587 - SMTP（用于加密的邮件发送）
993 - IMAP（用于加密的 IMAP 邮件接收）
995 - POP3（用于加密的 POP3 邮件接收）
110 - POP3（用于未加密的 POP3 邮件接收）
143 - IMAP（用于未加密的 IMAP 邮件接收）
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1：检查25端口是否能通&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;apt update &amp;amp;&amp;amp; apt install telnet -y
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;telnet smtp.gmail.com 25
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;如果显示下面的信息就代表是通的&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Connected to smtp.gmail.com.
Escape character is &apos;^]&apos;.
220 smtp.gmail.com ESMTP d2e1a72fcca58-71dd9d6e713sm17347b3a.23 - gsmtp
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2：域名解析（以cloudflare为例）&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;th&gt;内容&lt;/th&gt;
&lt;th&gt;代理状态&lt;/th&gt;
&lt;th&gt;TTL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;mail&lt;/td&gt;
&lt;td&gt;服务器IP&lt;/td&gt;
&lt;td&gt;仅 DNS&lt;/td&gt;
&lt;td&gt;自动&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CNAME&lt;/td&gt;
&lt;td&gt;imap&lt;/td&gt;
&lt;td&gt;mail.example.com&lt;/td&gt;
&lt;td&gt;仅 DNS&lt;/td&gt;
&lt;td&gt;自动&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CNAME&lt;/td&gt;
&lt;td&gt;pop&lt;/td&gt;
&lt;td&gt;mail.example.com&lt;/td&gt;
&lt;td&gt;仅 DNS&lt;/td&gt;
&lt;td&gt;自动&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CNAME&lt;/td&gt;
&lt;td&gt;smtp&lt;/td&gt;
&lt;td&gt;mail.example.com&lt;/td&gt;
&lt;td&gt;仅 DNS&lt;/td&gt;
&lt;td&gt;自动&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MX&lt;/td&gt;
&lt;td&gt;example.com&lt;/td&gt;
&lt;td&gt;mail.example.com&lt;/td&gt;
&lt;td&gt;仅 DNS&lt;/td&gt;
&lt;td&gt;自动&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TXT&lt;/td&gt;
&lt;td&gt;example.com&lt;/td&gt;
&lt;td&gt;v=spf1 mx ~all&lt;/td&gt;
&lt;td&gt;仅 DNS&lt;/td&gt;
&lt;td&gt;自动&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TXT&lt;/td&gt;
&lt;td&gt;s20241002362._domainkey&lt;/td&gt;
&lt;td&gt;k=rsa; p=MIIBIjA.............xXX&lt;/td&gt;
&lt;td&gt;仅 DNS&lt;/td&gt;
&lt;td&gt;自动&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;最后一条&lt;code&gt;TXT&lt;/code&gt;记录需要部署完成后进面板查看名称和内容。&lt;/p&gt;
&lt;p&gt;替换&lt;code&gt;example.com&lt;/code&gt;为你的域名&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;3：&lt;code&gt;docker-compose.yaml&lt;/code&gt;启动&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;services:
  mailer:
    image: analogic/poste.io
    container_name: mailer
    restart: always
    hostname: mail.example.com  # 容器内主机名替换你的域名
    network_mode: host
    environment:
      - TZ=Asia/Shanghai  # 时区设置
      - DISABLE_CLAMAV=TRUE  # 禁用 ClamAV
      - DISABLE_RSPAMD=FALSE  # 启用 Rspamd
      - DISABLE_ROUNDCUBE=FALSE  # 启用 Roundcube
    volumes:
      - ./mailer:/data
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4：进入面板&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;mail.example.com&lt;/code&gt;使用这个域名进入管理面板&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;第一行默认，第二行设置管理员邮箱，例如：&lt;code&gt;admin@example.com&lt;/code&gt;，第三行输入管理员密码，smtp密码也是这个&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;左侧选择&lt;code&gt;系统设置&lt;/code&gt;，然后上面找到TLS证书，然后申请证书。通用名默认，替代名称里把&lt;code&gt;smtp&lt;/code&gt;这些域名也填入进去，然后申请，查看下面的日志，申请完成后保存即可。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;然后左侧选择&lt;code&gt;虚拟域名&lt;/code&gt;，然后点击域名，找到&lt;code&gt;DKlM key&lt;/code&gt;，然后点击钥匙按钮激活，然后域名解析&lt;code&gt;TXT&lt;/code&gt;类型的验证。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;然后用管理员邮箱登录，测试发邮件即可。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;poste.io批量创建邮箱脚本&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/bash

# Poste.io 容器ID或名称
CONTAINER_ID=&quot;mailer&quot;

# 邮箱的@后缀，也就是根域名
DOMAIN=&quot;example.com&quot;

# 所有邮箱账户的统一密码
PASSWORD=&quot;admin123456&quot;

# 如果域名不存在，则创建域名
echo &quot;创建域名 $DOMAIN (如果不存在)&quot;
docker exec $CONTAINER_ID poste domain:create $DOMAIN

# 创建邮箱账户
for i in {100..200}
do
    EMAIL=&quot;$i@$DOMAIN&quot;
    echo &quot;正在创建邮箱: $EMAIL&quot;
    docker exec $CONTAINER_ID poste email:create $EMAIL $PASSWORD
    
    # 检查上一个命令的退出状态
    if [ $? -eq 0 ]; then
        echo &quot;成功创建邮箱 $EMAIL&quot;
    else
        echo &quot;创建邮箱 $EMAIL 失败&quot;
    fi
    
    # 可选：添加小延迟以避免对服务器造成过大压力
    sleep 1
done

echo &quot;邮箱创建过程完成。&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
</content:encoded></item><item><title>亚马逊云CDN配置教程</title><link>https://blog.emohe.cn/posts/%E4%BA%9A%E9%A9%AC%E9%80%8A%E4%BA%91cdn%E9%85%8D%E7%BD%AE%E6%95%99%E7%A8%8B/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/%E4%BA%9A%E9%A9%AC%E9%80%8A%E4%BA%91cdn%E9%85%8D%E7%BD%AE%E6%95%99%E7%A8%8B/</guid><pubDate>Mon, 13 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;前言&lt;/h3&gt;
&lt;p&gt;众所周知，海外有一些免费的CDN，而且还是大厂，其中最强大的就是&lt;code&gt;cloudflare&lt;/code&gt;，这个配置最简单，只需开启小黄云即可，但是&lt;code&gt;cloudflare&lt;/code&gt;的很多IP国内访问速度不佳，所以本篇介绍一下亚马逊云CDN的配置方法，亚马逊云的IP相对CF的速度稍微好点，需要注意的是，亚马逊云CDN的免费额度是每个月1000G流量，并且只是下载的流量是免费，上传是收费的（不过一般的网站很少用到上传流量），注意别被反薅了，注册亚马逊云需要验证外币卡，注册方法网上有很多，就不介绍了。&lt;/p&gt;
&lt;h3&gt;配置&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;本教程是基于不改动源站的配置下使用亚马逊云CDN，默认源站已经开启TLS&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;1：源域直接填源站域名，协议选&lt;code&gt;HTTPS&lt;/code&gt;，最小源选&lt;code&gt;TLSv1&lt;/code&gt;，&lt;/p&gt;
&lt;p&gt;2：行为里的压缩建议选&lt;code&gt;NO&lt;/code&gt;，查看器协议策略选&lt;code&gt;Redirect HTTP to HTTPS&lt;/code&gt;，缓存键和源请求要选&lt;code&gt;旧版缓存设置&lt;/code&gt;，也就是第二个。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这里是我踩过的一个坑，必须选&lt;code&gt;旧版缓存&lt;/code&gt;，不然访问站点会报错502&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;3：&lt;code&gt;WAF&lt;/code&gt;防火墙选&lt;code&gt;不启用&lt;/code&gt;，因为这个是付费的。&lt;/p&gt;
&lt;p&gt;4：&lt;code&gt;支持的 HTTP 版本&lt;/code&gt;建议全部选中，然后点击&lt;code&gt;创建分配&lt;/code&gt;，等待部署完成，会给你分配一个域名，访问这个域名查看是否正常。&lt;/p&gt;
&lt;h4&gt;自定义域名&lt;/h4&gt;
&lt;p&gt;1：进入到&lt;code&gt;常规&lt;/code&gt;的&lt;code&gt;设置&lt;/code&gt;里的&lt;code&gt;编辑&lt;/code&gt;，添加&lt;code&gt;备用域名&lt;/code&gt;  &amp;gt; 也就是给用户访问的域名，然后&lt;code&gt;申请证书&lt;/code&gt;，然后在申请证书的页面里直接点下一步，输入一个域名，验证方法就用默认的&lt;code&gt;DNS 验证&lt;/code&gt;，然后点请求。&lt;/p&gt;
&lt;p&gt;2：这里以&lt;code&gt;cloudflare&lt;/code&gt;为例，在域名里添加一个&lt;code&gt;CNAME&lt;/code&gt;，名称和值直接复制亚马逊云里的即可，等待一会亚马逊云里会显示验证成功，需要耐心等待。&lt;/p&gt;
&lt;p&gt;3：证书验证完成后，记得回到上一个页面，选中刚刚颁发的域名，然后点保存，保存成功后等待部署完成。&lt;/p&gt;
&lt;p&gt;4：部署完成后，在cloudflare里把刚刚的那个用于验证域名的&lt;code&gt;CNAME&lt;/code&gt;修改一下，&lt;code&gt;名称&lt;/code&gt;就填你刚刚申请证书的二级域名，&lt;code&gt;目标&lt;/code&gt;填亚马逊云给你分配的域名即可。&lt;/p&gt;
&lt;p&gt;5：如果无法访问可能是aws正在部署中，多等待一会即可。&lt;/p&gt;
</content:encoded></item><item><title>Docker部署哪吒监控</title><link>https://blog.emohe.cn/posts/docker%E9%83%A8%E7%BD%B2%E5%93%AA%E5%90%92%E7%9B%91%E6%8E%A7/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/docker%E9%83%A8%E7%BD%B2%E5%93%AA%E5%90%92%E7%9B%91%E6%8E%A7/</guid><pubDate>Fri, 10 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;简介&lt;/h3&gt;
&lt;p&gt;哪吒监控是一款开源的轻量化的服务器监控和运维工具，提供实时性能监控与告警通知。&lt;/p&gt;
&lt;p&gt;1：安装Docker参考我博客的教程&lt;/p&gt;
&lt;p&gt;2：创建&lt;code&gt;docker-compose.yml&lt;/code&gt;文件写入如下配置&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  dashboard:
    image: ghcr.io/nezhahq/nezha
    restart: always
    ports:
      - &quot;127.0.0.1:8008:8008&quot;
    volumes:
      - ./data:/dashboard/data
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;监听本地防止被扫端口&lt;/p&gt;
&lt;p&gt;然后&lt;code&gt;docker compose up -d&lt;/code&gt;启动&lt;/p&gt;
&lt;p&gt;3：我选择使用&lt;code&gt;caddy&lt;/code&gt;作为web服务，简单方便，安装命令如下&lt;/p&gt;
&lt;p&gt;使用apt包一键安装caddy：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt install -yq debian-keyring debian-archive-keyring apt-transport-https curl &amp;amp;&amp;amp; curl -1sLf &apos;https://dl.cloudsmith.io/public/caddy/stable/gpg.key&apos; | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg &amp;amp;&amp;amp; curl -1sLf &apos;https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt&apos; | sudo tee /etc/apt/sources.list.d/caddy-stable.list &amp;amp;&amp;amp; sudo apt update -q &amp;amp;&amp;amp; sudo apt install -yq caddy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4：写入&lt;code&gt;Caddyfile&lt;/code&gt;反代配置&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;example.com {
    reverse_proxy /proto.NezhaService/* h2c://127.0.0.1:8008
    
    reverse_proxy /* 127.0.0.1:8008
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意替换你的域名，caddy会自动配置证书，启动caddy后就部署完成了。&lt;/p&gt;
&lt;p&gt;5：哪吒面板配置&lt;/p&gt;
&lt;p&gt;后台路径&lt;code&gt;/dashboard&lt;/code&gt;默认用户名密码&lt;code&gt;admin&lt;/code&gt; &lt;code&gt;admin&lt;/code&gt;，进入后台后首先修改用户名和密码，必须修改强密码，因为如果被爆破后你的小鸡将会被恶意者完全掌控。&lt;/p&gt;
&lt;p&gt;然后到设置里配置面板后台Agent对接地址，示例：&lt;code&gt;example.com:443&lt;/code&gt;，并选中&lt;code&gt;使用 TLS 连接&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;如果你要套cf cdn，则域名设置里的网络需要开启&lt;code&gt;grpc&lt;/code&gt;选项，并且SSL加密模式改成完全。&lt;/p&gt;
&lt;p&gt;6：国内网络环境说明&lt;/p&gt;
&lt;p&gt;哪吒官方镜像托管在github，国内网络拉取很慢，可以使用南京大学的镜像加速，&lt;code&gt;docker-compose.yml&lt;/code&gt;示例&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  dashboard:
    image: ghcr.nju.edu.cn/nezhahq/nezha
    restart: always
    ports:
      - &quot;127.0.0.1:8008:8008&quot;
    volumes:
      - ./data:/dashboard/data
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;国内环境安装Docker也可以参考我博客的教程&lt;/p&gt;
&lt;p&gt;国内小鸡安装Agent请替换哪吒官方的码云地址，将复制出来的Agent安装脚本替换如下地址即可，其他参数不变&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://gitee.com/naibahq/scripts/raw/main/agent/install.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;卸载v1版本的Agent方法&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;v1版本的Agent多次安装不会覆盖，而是会出现多个重复监控的情况，所以我提供一下卸载干净的方法&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 停止并禁用
sudo systemctl stop nezha-agent.service
sudo systemctl disable nezha-agent.service

# 删除服务文件
rm -f /etc/systemd/system/nezha-a*.service
rm -f /lib/systemd/system/nezha-a*.service

# 删除相关文件夹
sudo rm -rf /opt/nezha

# 重新加载 systemd
sudo systemctl daemon-reload

# 验证服务是否有残留
systemctl list-units --type=service | grep nezha
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>windows重装系统教程</title><link>https://blog.emohe.cn/posts/windows%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E6%95%99%E7%A8%8B/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/windows%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E6%95%99%E7%A8%8B/</guid><pubDate>Thu, 09 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;windows重装系统之U盘引导&lt;/h3&gt;
&lt;p&gt;微软官网系统镜像下载：https://www.microsoft.com/zh-cn/software-download/windows11&lt;/p&gt;
&lt;p&gt;1：下载&lt;code&gt;Windows 11 安装媒体&lt;/code&gt;并执行程序写入到U盘&lt;/p&gt;
&lt;p&gt;2：开机重复按&lt;code&gt;F2&lt;/code&gt;或者&lt;code&gt;F12&lt;/code&gt;或者&lt;code&gt;DELETE&lt;/code&gt;键 进入BIOS界面&lt;/p&gt;
&lt;p&gt;3：U盘引导，&lt;code&gt;UEFI&lt;/code&gt; ——&amp;gt; 对应&lt;code&gt;GPT&lt;/code&gt;磁盘分区格式&lt;/p&gt;
&lt;p&gt;4：将U盘改为第一启动项，然后保存并重启&lt;/p&gt;
&lt;p&gt;5：第一个分区默认是C盘，建议分区300G：填&lt;code&gt;307200&lt;/code&gt;MB。剩下的全给第二个分区，记得给盘符命名。&lt;/p&gt;
&lt;p&gt;6：进入系统后，右键打开显示设置——&amp;gt;个性化——&amp;gt;主题——&amp;gt;桌面图标设置——&amp;gt;勾选想要的桌面图标&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;重装系统前先关闭&lt;code&gt;Bitlocker&lt;/code&gt;磁盘加密，现在的大部分电脑都是&lt;code&gt;UEFI&lt;/code&gt;引导，和&lt;code&gt;GPT&lt;/code&gt;磁盘分区格式。如果无法进入安装系统的界面，需要关闭安全启动&lt;code&gt;Secure Boot&lt;/code&gt;选项。比较旧的电脑或者主板是&lt;code&gt;Legacy BIOS&lt;/code&gt;引导，和&lt;code&gt;MBR&lt;/code&gt;磁盘分区格式。Linux 系统默认的磁盘分区格式主要是&lt;code&gt;ext4&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h3&gt;windows重装系统之本地重装&lt;/h3&gt;
&lt;p&gt;1：微软官网下载&lt;code&gt;Windows 11 磁盘映像 (ISO)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;2：解压下载的&lt;code&gt;iso&lt;/code&gt;文件，点击&lt;code&gt;5etup&lt;/code&gt;按提示重装即可&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;系统内给磁盘分区&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;打开磁盘管理并压缩C盘&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Win&lt;/code&gt; + &lt;code&gt;X&lt;/code&gt; 快捷键 → 选择“磁盘管理”&lt;/li&gt;
&lt;li&gt;右键点击 &lt;strong&gt;(C:)&lt;/strong&gt; 分区 → 选择“压缩卷”&lt;/li&gt;
&lt;li&gt;输入要分出的空间大小（单位：MB）→ 点击“压缩”&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;创建新分区&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;右键新出现的 &lt;strong&gt;未分配&lt;/strong&gt; 空间 → 选择“新建简单卷”&lt;/li&gt;
&lt;li&gt;按照向导完成&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;安装界面绕过硬件检查&lt;/h3&gt;
&lt;p&gt;报错信息：这台电脑不符合安装此版本的 Windows 所需的最低系统要求。&lt;/p&gt;
&lt;p&gt;1：在安装界面按下&lt;code&gt;Shift+F10&lt;/code&gt;组合键调出管理员命令窗口&lt;/p&gt;
&lt;p&gt;2：输入&lt;code&gt;regedit&lt;/code&gt;按回车键调出注册表&lt;/p&gt;
&lt;p&gt;3：依次展开&lt;code&gt;HKEY_LOCAL_MACHINE \ SYSTEM \ Setup&lt;/code&gt;目录，右键点击&lt;code&gt;Setup&lt;/code&gt;选择新建&lt;code&gt;项&lt;/code&gt;，命名为&lt;code&gt;LabConfig&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;4：选择&lt;code&gt;LabConfig&lt;/code&gt;右键–新建–DWORD（32 位）值，需要创建5个，分别命名为：&lt;code&gt;BypassCPUCheck&lt;/code&gt;、&lt;code&gt;BypassRAMCheck&lt;/code&gt;、&lt;code&gt;BypassSecureBootCheck&lt;/code&gt;、&lt;code&gt;BypassStorageCheck&lt;/code&gt;、&lt;code&gt;BypassTPMCheck&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;5：将这些十进制的值都改成&lt;code&gt;1&lt;/code&gt;确定后即可绕过硬件检测，继续安装&lt;/p&gt;
&lt;h3&gt;安装界面绕过联网&lt;/h3&gt;
&lt;p&gt;1：打开命令窗口：&lt;code&gt;Shift + F10&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;2：修改注册表跳过联网&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;reg add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\OOBE /v BypassNRO /t REG_DWORD /d 1 /f

shutdown /r /t 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行完成后会自动重启，然后就可以直接创建本地账户，而无需使用联网账户。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;或者直接通过命令创建本地账户来跳过联网&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;net user 你的用户名 /add

net localgroup Administrators 你的用户名 /add

cd OOBE

msoobe &amp;amp;&amp;amp; shutdown -r
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行完成后会自动重启，然后就可以直接登录本地账户了&lt;/p&gt;
&lt;h3&gt;磁盘分区格式&lt;/h3&gt;
&lt;p&gt;如果磁盘分区格式不对，可以按&lt;code&gt;Shift + F10&lt;/code&gt;，打开命令窗口，更改磁盘存储格式：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;diskpart                     # 进入磁盘管理
list disk                    # 列出所有磁盘
select disk 0                # 选择要操作的磁盘，0 是磁盘编号
clean                        # 清除磁盘上的所有分区和数据
convert gpt                  # 将磁盘转换为 GPT 分区格式
exit                         # 退出
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;完成以上步骤后，硬盘已被格式化并转换为 &lt;strong&gt;UEFI&lt;/strong&gt; 的 &lt;strong&gt;GPT&lt;/strong&gt; 分区格式。然后刷新可以继续进行操作系统安装。&lt;/p&gt;
&lt;p&gt;驱动器0，即表示第一块物理磁盘，该类型索引从0开始&lt;/p&gt;
&lt;p&gt;分区1、2、3编号，即第一个分区……依次排列，分区从1开始索引编号。&lt;/p&gt;
&lt;h3&gt;备注：&lt;/h3&gt;
&lt;p&gt;1：&lt;code&gt;win+R&lt;/code&gt;运行&lt;code&gt;msinfo32&lt;/code&gt;查看BIOS引导模式，或者&lt;code&gt;cmd&lt;/code&gt;命令运行&lt;code&gt;bcdedit&lt;/code&gt;查看引导模式，如果是&lt;code&gt;\WINDOWS\system32\winload.efi&lt;/code&gt;则是&lt;code&gt;UEFI&lt;/code&gt;引导&lt;/p&gt;
&lt;p&gt;2：重装系统时有的机器需要进入&lt;code&gt;UEFI BIOS&lt;/code&gt;固件设置，关闭安全启动&lt;code&gt;Secure Boot&lt;/code&gt;选项。&lt;/p&gt;
&lt;p&gt;3：如果启动时有多个系统，可以&lt;code&gt;win+R&lt;/code&gt;运行&lt;code&gt;msconfig&lt;/code&gt;查看&lt;code&gt;引导&lt;/code&gt;选项卡，删掉不需要的。&lt;/p&gt;
&lt;p&gt;4：&lt;code&gt;可靠性历史&lt;/code&gt;可以查看当前系统的错误日志，&lt;code&gt;事件查看器&lt;/code&gt;也有更详细的日志&lt;/p&gt;
&lt;p&gt;5：更多问题可以问AI：https://chat.deepseek.com&lt;/p&gt;
&lt;h3&gt;系统修复命令：&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 内存诊断
mdsched.exe

# 检查并修复磁盘文件系统错误和坏道
chkdsk /f /r C:

# 扫描并修复损坏的系统文件
sfc /scannow

# 修复 Windows 系统映像及更新组件问题
dism /online /cleanup-image /restorehealth

# 修复启动引导(MBR)(UEFI)和(BCD)启动配置
bootrec /fixmbr
bootrec /fixboot
bootrec /rebuildbcd

# 重置网络设置
netsh int ip reset
netsh winsock reset

# 扫描远程计算机上的磁盘错误
chkdsk /scan

# 重置计算机，恢复出厂设置
systemreset

# 配置系统启动选项和服务
msconfig

# 修复驱动器错误
powershell -Command &quot;Repair-Volume -DriveLetter C&quot;

# 仅验证系统文件的完整性，不修复
sfc /verifyonly

# 显示系统的详细配置信息
systeminfo
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;系统激活代码&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;PowerShell&lt;/code&gt;脚本命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;irm https://get.activated.win | iex
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行命令可以看到激活选项。选择 &lt;code&gt;[1] HWID&lt;/code&gt; 用于 &lt;code&gt;Windows&lt;/code&gt; 激活。选择 &lt;code&gt;[2] Ohook&lt;/code&gt; 用于 &lt;code&gt;Office&lt;/code&gt; 激活。&lt;/p&gt;
&lt;p&gt;旧命令：&lt;code&gt;irm https://massgrave.dev/get | iex&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/massgravel/Microsoft-Activation-Scripts&quot;&gt;开源地址&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;新机常用软件安装&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;chorme浏览器：https://www.google.com/chrome/&lt;/li&gt;
&lt;li&gt;7z解压工具：https://www.7-zip.org/&lt;/li&gt;
&lt;li&gt;驱动总裁：https://www.sysceo.com/dc&lt;/li&gt;
&lt;li&gt;图吧工具箱：https://www.tbtool.cn/&lt;/li&gt;
&lt;li&gt;致美化：https://zhutix.com/&lt;/li&gt;
&lt;li&gt;蓝屏分析工具：https://learn.microsoft.com/zh-cn/windows-hardware/drivers/debugger/&lt;/li&gt;
&lt;li&gt;Windows官方系统工具箱：https://learn.microsoft.com/zh-cn/windows/powertoys/&lt;/li&gt;
&lt;li&gt;文件搜索：https://www.listary.net/&lt;/li&gt;
&lt;li&gt;文件备份和同步：https://freefilesync.org/&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;国内BT下载&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;BT下载：https://www.qbittorrent.org&lt;/li&gt;
&lt;li&gt;比特彗星：https://www.bitcomet.com/cn&lt;/li&gt;
&lt;li&gt;Windows镜像：https://next.itellyou.cn/&lt;/li&gt;
&lt;li&gt;Windows镜像：https://www.hellowindows.cn/&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;系统安装工具&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;微PE：https://www.wepe.com.cn/ PE系统&lt;/li&gt;
&lt;li&gt;https://www.ventoy.net/ 支持多系统&lt;/li&gt;
&lt;li&gt;https://rufus.ie/zh/ 支持嵌入式烧录&lt;/li&gt;
&lt;li&gt;https://etcher.balena.io/  跨平台嵌入式烧录工具&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;官方Windows启动盘文件结构&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;/
├── boot/              &amp;lt;- 启动文件
├── efi/               &amp;lt;- UEFI 启动文件（适用于 UEFI 模式）
├── sources/           &amp;lt;- 安装源文件
│   ├── install.wim    &amp;lt;- 安装镜像文件
│   ├── boot.wim       &amp;lt;- 启动映像
├── autorun.inf        &amp;lt;- 自动运行配置文件
└── setup.exe          &amp;lt;- 安装程序
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以在启动盘中创建&lt;code&gt;driver&lt;/code&gt;文件夹，驱动压缩包放至&lt;code&gt;driver&lt;/code&gt;目录下，并将其解压，然后安装系统界面可以加载驱动程序，最好是将驱动解压缩并按硬件类别放置在子文件夹中。这样就可以实现离线安装网卡驱动了。&lt;/p&gt;
</content:encoded></item><item><title>Git命令笔记</title><link>https://blog.emohe.cn/posts/git%E7%AC%94%E8%AE%B0/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/git%E7%AC%94%E8%AE%B0/</guid><pubDate>Wed, 08 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;切换分支&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# 基于当前分支创建dev新分支，并切换过去
git switch -c dev

# 把本地dev推送到远程，会自动在远程创建dev分支并建立跟踪关系
git push -u origin dev
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;重新上传代码&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# 删除 Git 管理（可选）
rm -rf .git

# 初始化 Git
git init

# 创建并切换分支（可忽略）
git checkout -b main

# 添加所有文件
git add .

# 提交所有文件
git commit -m &quot;add&quot;

# 关联仓库
git remote add origin https://github.com/your-username/your-repo.git

# 强制推送
git push --force origin
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;项目添加子模块&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;ddns-go&lt;/code&gt; 为例，项目根目录执行。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git submodule add https://github.com/jeessy2/ddns-go.git
git commit -m &quot;添加子模块&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后提交并推送即可&lt;/p&gt;
&lt;h4&gt;更新所有子模块&lt;/h4&gt;
&lt;p&gt;项目根目录执行&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git submodule update --remote --merge
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后正常提交&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git add .
git commit -m &quot;更新子模块&quot;
git push
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;删除子模块&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;git submodule deinit -f ddns-go
git rm -f ddns-go
rm -rf .git/modules/ddns-go
rm -rf ddns-go
git commit -m &quot;删除子模块&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;锁定子模块到某个提交&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;cd ddns-go
git checkout &amp;lt;commit-hash&amp;gt;
cd ..
git add ddns-go
git commit -m &quot;锁定子模块版本&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;回滚&lt;/h2&gt;
&lt;p&gt;执行回滚操作后无法恢复&lt;/p&gt;
&lt;p&gt;回滚到上一次提交&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;命令结尾的数字代表前几次&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;git reset --hard HEAD~1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;强制推送&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git push origin --force
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或者回滚到指定提交&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git reset --hard 6038e0a
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;配置代理&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git config --global http.proxy http://127.0.0.1:10808
git config --global https.proxy http://127.0.0.1:10808
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;配置提交信息&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git config --global user.name &quot;user123&quot;
git config --global user.email &quot;user123@example.com&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果需要更改登陆凭据，则需要在系统&lt;code&gt;凭据管理器&lt;/code&gt;中移除旧凭据&lt;/p&gt;
&lt;h3&gt;查看配置&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git config --global --list
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>数据库学习笔记</title><link>https://blog.emohe.cn/posts/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</guid><pubDate>Wed, 08 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;MySQL和MariaDB常用命令&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;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;strong&gt;MySQL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;更新软件包列表&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt update&lt;/code&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;安装 MySQL 服务器&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt install mysql-server&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;安装 MySQL 服务器&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;启动 MySQL 服务&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl start mysql&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;启动 MySQL 服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;检查 MySQL 服务状态&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl status mysql&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;检查 MySQL 服务的运行状态&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;code&gt;sudo mysql_secure_installation&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;配置 MySQL 安全选项&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;登录 MySQL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo mysql -u root -p&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;登录 MySQL，输入 root 用户密码&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MariaDB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;更新软件包列表&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt update&lt;/code&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;安装 MariaDB 服务器&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt install mariadb-server&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;安装 MariaDB 服务器&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;启动 MariaDB 服务&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl start mariadb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;启动 MariaDB 服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;检查 MariaDB 服务状态&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl status mariadb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;检查 MariaDB 服务的运行状态&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;code&gt;sudo mysql_secure_installation&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;配置 MariaDB 安全选项&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;登录 MariaDB&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo mysql -u root -p&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;登录 MariaDB，输入 root 用户密码&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;strong&gt;登录数据库&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mysql -u 用户名 -p&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;登录 MySQL，使用指定的用户名，&lt;code&gt;-p&lt;/code&gt; 会提示输入密码&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;显示数据库&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SHOW DATABASES;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;列出当前 MySQL 服务器中的所有数据库&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;使用数据库&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;USE 数据库名;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;切换到指定数据库&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;创建数据库&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CREATE DATABASE 数据库名;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;创建一个新的数据库&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;删除数据库&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DROP DATABASE 数据库名;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;删除指定数据库&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;显示当前数据库&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SELECT DATABASE();&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;显示当前正在使用的数据库&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;显示数据库表&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SHOW TABLES;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;列出当前数据库中的所有表&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;查看表结构&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DESCRIBE 表名;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;查看表的结构 (字段、类型、主键等)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;创建表&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CREATE TABLE 表名 (字段1 数据类型, 字段2 数据类型, ...);&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;创建一张表，指定列和数据类型&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;删除表&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DROP TABLE 表名;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;删除指定的表&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;插入数据&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;INSERT INTO 表名 (字段1, 字段2, ...) VALUES (值1, 值2, ...);&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;插入一条数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;查询数据&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SELECT 字段1, 字段2 FROM 表名 WHERE 条件;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;查询表中的数据，可以使用条件过滤&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;更新数据&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UPDATE 表名 SET 字段1=值1 WHERE 条件;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;更新指定的表数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;删除数据&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DELETE FROM 表名 WHERE 条件;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;删除符合条件的数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;显示创建表语句&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SHOW CREATE TABLE 表名;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;显示指定表的创建语句&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;清空表数据&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TRUNCATE TABLE 表名;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;清空表中的所有数据，但保留表结构&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;查看索引&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SHOW INDEX FROM 表名;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;查看表中所有的索引&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;创建索引&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CREATE INDEX 索引名 ON 表名 (字段);&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;创建索引，提升查询速度&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;删除索引&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DROP INDEX 索引名 ON 表名;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;删除指定的索引&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;备份数据库&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mysqldump -u 用户名 -p 数据库名 &amp;gt; 备份文件.sql&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;备份数据库，将数据库内容导出为 &lt;code&gt;.sql&lt;/code&gt; 文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;恢复数据库&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mysql -u 用户名 -p 数据库名 &amp;lt; 备份文件.sql&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;恢复数据库，将 &lt;code&gt;.sql&lt;/code&gt; 文件导入指定数据库&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;创建用户&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CREATE USER &apos;用户名&apos;@&apos;localhost&apos; IDENTIFIED BY &apos;密码&apos;;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;创建一个新的 MySQL 用户&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;删除用户&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DROP USER &apos;用户名&apos;@&apos;localhost&apos;;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;删除 MySQL 用户&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;授权权限&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GRANT ALL PRIVILEGES ON 数据库名.* TO &apos;用户名&apos;@&apos;localhost&apos;;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;授予用户对某个数据库的所有权限&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;显示用户权限&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SHOW GRANTS FOR &apos;用户名&apos;@&apos;localhost&apos;;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;显示指定用户的权限&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;撤销权限&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;REVOKE ALL PRIVILEGES ON 数据库名.* FROM &apos;用户名&apos;@&apos;localhost&apos;;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;撤销指定用户对某个数据库的权限&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;刷新权限&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;FLUSH PRIVILEGES;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;刷新 MySQL 权限表，使权限更改生效&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;查看服务器状态&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SHOW STATUS;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;查看 MySQL 服务器的状态和性能指标&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;查看数据库引擎&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SHOW ENGINES;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;显示 MySQL 支持的所有存储引擎&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;查看连接信息&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SHOW PROCESSLIST;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;显示当前 MySQL 的所有连接信息&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;大多数 SQL 语句都以分号 &lt;code&gt;;&lt;/code&gt; 结尾。&lt;/li&gt;
&lt;li&gt;如果 MySQL 的权限系统发生变动（如添加或撤销用户权限），建议运行 &lt;code&gt;FLUSH PRIVILEGES;&lt;/code&gt; 以刷新权限。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;SQLite常用命令&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;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;安装 SQLite&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt install sqlite3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;在 Linux（Ubuntu）中安装 SQLite&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;连接 SQLite&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sqlite3 数据库名.db&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;连接到 SQLite 数据库（如果不存在则创建）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;退出 SQLite&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.quit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;退出 SQLite 命令行工具&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;显示数据库表&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.tables&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;显示当前数据库中的所有表&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;查看表结构&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.schema 表名&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;显示指定表的创建语句&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;创建表&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CREATE TABLE 表名 (字段1 数据类型, 字段2 数据类型, ...);&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;创建一张表，指定字段和数据类型&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;插入数据&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;INSERT INTO 表名 (字段1, 字段2, ...) VALUES (值1, 值2, ...);&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;插入一条数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;查询数据&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SELECT 字段1, 字段2 FROM 表名 WHERE 条件;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;查询表中的数据，可以使用条件过滤&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;更新数据&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UPDATE 表名 SET 字段1=值1 WHERE 条件;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;更新指定的表数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;删除数据&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DELETE FROM 表名 WHERE 条件;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;删除符合条件的数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;删除表&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DROP TABLE 表名;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;删除指定的表&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;查看索引&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PRAGMA index_list(表名);&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;查看指定表的索引&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;创建索引&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CREATE INDEX 索引名 ON 表名 (字段);&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;创建索引，提升查询速度&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;删除索引&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DROP INDEX 索引名;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;删除指定的索引&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;导出数据库&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;先&lt;code&gt;.output 文件名.sql&lt;/code&gt; 再&lt;code&gt;.dump&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;导出整个数据库，保存到指定的 SQL 文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;导入数据库&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.read 文件名.sql&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;从 SQL 文件导入数据到当前数据库&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;打开数据库&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.open 数据库名.db&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;打开或切换到另一个数据库&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;PostgreSQL常用命令&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;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;安装 PostgreSQL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt update&lt;/code&gt; &amp;lt;br&amp;gt; &lt;code&gt;sudo apt install postgresql postgresql-contrib&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;更新包管理器并安装 PostgreSQL 和相关插件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;启动 PostgreSQL 服务&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl start postgresql&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;启动 PostgreSQL 服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;重启 PostgreSQL 服务&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl restart postgresql&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;重启 PostgreSQL 服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;停止 PostgreSQL 服务&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl stop postgresql&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;停止 PostgreSQL 服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;检查 PostgreSQL 状态&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl status postgresql&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;检查 PostgreSQL 服务的当前状态&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;设置 PostgreSQL 开机启动&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl enable postgresql&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;设置 PostgreSQL 随系统启动自动运行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;切换到 postgres 用户&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo -i -u postgres&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;以 &lt;code&gt;postgres&lt;/code&gt; 管理员用户登录&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;以指定用户登录&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;psql -U 用户名 -d 数据库名&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;以指定用户登录指定数据库&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;进入 PostgreSQL shell&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;psql&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;进入 PostgreSQL 的交互式命令行 shell&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;退出 PostgreSQL shell&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\q&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;退出 PostgreSQL shell&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;查看数据库列表&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;显示当前 PostgreSQL 实例中的所有数据库&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;创建数据库&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CREATE DATABASE 数据库名;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;创建一个新数据库&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;删除数据库&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DROP DATABASE 数据库名;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;删除指定的数据库&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;查看表格列表&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\dt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;显示当前数据库中的所有表格&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;创建表格&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CREATE TABLE 表格名 (列1 数据类型, 列2 数据类型, ...);&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;在数据库中创建一个新表格，并定义其列和数据类型&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;插入数据&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;INSERT INTO 表格名 (列1, 列2, ...) VALUES (值1, 值2, ...);&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;向表格中插入一条数据记录&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;查询数据&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SELECT * FROM 表格名;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;从表格中查询所有数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;创建用户&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CREATE USER 用户名 WITH PASSWORD &apos;密码&apos;;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;创建一个新用户，并为其设置密码&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;给用户授权&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GRANT ALL PRIVILEGES ON DATABASE 数据库名 TO 用户名;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;为指定的用户授权访问和操作指定数据库的所有权限&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;删除用户&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DROP USER 用户名;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;删除一个指定的用户&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;修改用户密码&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ALTER USER 用户名 WITH PASSWORD &apos;新密码&apos;;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;修改指定用户的密码&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;备份数据库&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pg_dump 数据库名 &amp;gt; 备份文件名.sql&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;将指定数据库备份到一个 &lt;code&gt;.sql&lt;/code&gt; 文件中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;还原数据库&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;psql 数据库名 &amp;lt; 备份文件名.sql&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;从备份的 &lt;code&gt;.sql&lt;/code&gt; 文件还原数据库&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</content:encoded></item><item><title>wordpress和typecho和halo三种博客部署教程</title><link>https://blog.emohe.cn/posts/wordpress%E5%92%8Ctypecho%E5%92%8Chalo/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/wordpress%E5%92%8Ctypecho%E5%92%8Chalo/</guid><pubDate>Mon, 06 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;使用Docker部署&lt;/h2&gt;
&lt;h3&gt;Docker-compose部署wordpress&lt;/h3&gt;
&lt;p&gt;创建项目文件夹&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir wordpress &amp;amp;&amp;amp; cd wordpress
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;创建&lt;code&gt;docker-compose.yml&lt;/code&gt;文件并写入以下代码配置：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  db:
    image: mysql:8.0
    volumes:
      - ./data/mysql:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: wordpressyyds
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress
    networks:
      - wp

  wordpress:
    container_name: wordpress
    depends_on:
      - db
    image: wordpress:latest
    volumes:
      - ./data/wp:/var/www/html
    ports:
      - &quot;8000:80&quot;
    restart: always
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress
      WORDPRESS_DB_NAME: wordpress
    networks:
      - wp

networks:
  wp:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker compose up -d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;站点端口为 &lt;code&gt;8000&lt;/code&gt;     后台路径 &lt;code&gt;/wp-admin&lt;/code&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;修改docker中wordpress的上传限制。&lt;/h3&gt;
&lt;p&gt;进入 wordpress 容器&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker exec -it wordpress /bin/bash
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;wordpress 容器中的这个路径&lt;code&gt;/usr/local/etc/php/&lt;/code&gt;，是存放 &lt;code&gt;php.ini&lt;/code&gt; 的地方，但是默认是没有 &lt;code&gt;php.ini&lt;/code&gt; 这个文件的，所以我们要通过复制一份&lt;code&gt;php.ini-production&lt;/code&gt;文件，来生成 &lt;code&gt;php.ini&lt;/code&gt; 文件。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd /usr/local/etc/php/
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;cp php.ini-production php.ini
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后使用vim编辑器修改即可，如果没有则需要安装一下&lt;/p&gt;
&lt;p&gt;更新及安装vim，使用如下代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt-get update
apt-get install vim
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;安装完成vim，现在就可以对php.ini进行编辑了。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vim php.ini
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;找到这几个变量，根据自己需求修改。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;upload_max_filesize = 2M      # PHP最大上传文件大小
post_max_size = 8M            # 服务器最大数据量和文件大小
memory_limit = 128M           # PHP内存占用限制
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后一步！&lt;/p&gt;
&lt;p&gt;重启wordpress&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker restart wordpress
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;hr /&gt;
&lt;h3&gt;wordpress启用redis缓存的部署方法&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;services:
  db:
    image: mysql:8.0
    volumes:
      - ./data/mysql:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: wordpressyyds
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress
    networks:
      - wp

  wordpress:
    container_name: wordpress
    depends_on:
      - db
      - redis  # 添加redis
    image: wordpress:latest
    volumes:
      - ./data/wp:/var/www/html
    ports:
      - &quot;8000:80&quot;
    restart: always
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress
      WORDPRESS_DB_NAME: wordpress
      WORDPRESS_REDIS_HOST: redis
    networks:
      - wp

  redis:
    image: redis:alpine
    container_name: redis
    restart: always
    networks:
      - wp

networks:
  wp:
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;在&lt;code&gt;wp-config.php&lt;/code&gt;文件中添加以下代码&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;define(&apos;WP_REDIS_HOST&apos;, &apos;redis&apos;);
define(&apos;WP_CACHE&apos;, true);

// 确保添加在这个代码前面
/* That&apos;s all, stop editing! Happy blogging. */
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;容器内安装redis扩展&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;docker exec -it wordpress /bin/bash
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;apt-get update &amp;amp;&amp;amp; apt-get install -y libz-dev libssl-dev

pecl install redis           # 这里直接一路回车

docker-php-ext-enable redis
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;重启容器&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;docker restart wordpress
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;插件商城搜索安装&lt;code&gt;redis&lt;/code&gt;官方插件：&lt;code&gt;Redis Object Cache&lt;/code&gt;    并启用&lt;code&gt;redis&lt;/code&gt;缓存&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;ul&gt;
&lt;li&gt;修改后台路径插件&lt;code&gt;WPS Hide Login&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;备份插件&lt;code&gt;WPvivid&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;直播播放器插件&lt;code&gt;SRS Player&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;优化性能缓存插件&lt;code&gt;WP Fastest Cache&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;压缩图片和懒加载插件&lt;code&gt;Smush&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;Docker部署halo博客&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;docker run -it -d --name halo -p 8090:8090 -v ./halo2:/root/.halo2 halohub/halo:2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;地址： &lt;code&gt;IP:8090&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;后台路径：&lt;code&gt;/console&lt;/code&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;国内阿里云镜像&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;registry.fit2cloud.com/halo/halo
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;&lt;code&gt;docker-compose.yaml&lt;/code&gt;部署&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;services:
  halo:
    image: halohub/halo:2
    restart: always
    depends_on:
      - mysql
    ports:
      - &quot;8090:8090&quot;
    volumes:
      - ./halo2:/root/.halo2
    command:
      - --spring.r2dbc.url=r2dbc:pool:mysql://mysql:3306/halo
      - --spring.r2dbc.username=halo
      - --spring.r2dbc.password=halo_password
      - --spring.sql.init.platform=mysql
      - --halo.external-url=http://公网IP:8090/

  mysql:
    image: mysql:5.7
    container_name: mysql
    environment:
      MYSQL_DATABASE: halo                  # 数据库名称
      MYSQL_USER: halo                      # 数据库用户名
      MYSQL_PASSWORD: halo_password         # 数据库密码
      MYSQL_ROOT_PASSWORD: halo_password    # 数据库root密码
    volumes:
      - ./data/mysql:/var/lib/mysql         # 映射数据库文件
    restart: always
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;hr /&gt;
&lt;h3&gt;Docker部署typecho&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;services:
  mysql:
    image: mysql:5.7
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: typechoPASSWORD
      MYSQL_DATABASE: typecho
      MYSQL_USER: typecho
      MYSQL_PASSWORD: typechoPASSWORD
    volumes:
      - ./mysql-data:/var/lib/mysql

  typecho:
    image: joyqi/typecho:nightly-php8.2-apache
    restart: always
    depends_on:
      - mysql
    ports:
      - &quot;8080:80&quot;
    environment:
      TIMEZONE: Asia/Shanghai
      TYPECHO_SITE_URL: https://your-domain.com
      TYPECHO_DB_HOST: mysql
      TYPECHO_DB_NAME: typecho
      TYPECHO_DB_USER: typecho
      TYPECHO_DB_PASS: typechoPASSWORD
    volumes:
      - ./typecho-data:/var/www/html
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;https://your-domain.com&lt;/code&gt;替换为你的网址，提前反代好。&lt;/p&gt;
&lt;p&gt;（可选）重置后台管理员密码：删除站点目录的&lt;code&gt;config.inc.php&lt;/code&gt;文件，然后重新安装选择保留原有数据库即可&lt;/p&gt;
&lt;h4&gt;主题文件目录&lt;code&gt;usr/themes&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;安装git:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install git -y
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下载主题：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone 主题Git仓库地址
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;复制到主题目录：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker cp 主题文件路径 typecho-server:/app/usr/themes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://hub.docker.com/r/joyqi/typecho&quot;&gt;仓库地址&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>frp内网穿透部署教程</title><link>https://blog.emohe.cn/posts/frp/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/frp/</guid><pubDate>Sun, 05 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;Docker部署frp内网穿透&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;可选项：&lt;code&gt;Docker CLI&lt;/code&gt;启动命令示例&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker run --restart=always --network host -d -v ./frps.toml:/etc/frp/frps.toml --name frps snowdreamtech/frps
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;code&gt;docker-compose&lt;/code&gt;部署服务端（有公网IP）&lt;/h3&gt;
&lt;p&gt;创建配置文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;touch docker-compose.yml frps.toml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;docker-compose.yaml&lt;/code&gt;配置&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  frps:
    image: snowdreamtech/frps
    container_name: frps
    network_mode: host
    volumes:
      - ./frps.toml:/etc/frp/frps.toml
    restart: always
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;服务端&lt;code&gt;frps.toml&lt;/code&gt;配置&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bindPort = 7000
auth.token = &quot;admin123&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;&lt;code&gt;docker-compose&lt;/code&gt;部署客户端（内网）&lt;/h3&gt;
&lt;p&gt;创建配置文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;touch docker-compose.yml frpc.toml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;docker-compose.yaml&lt;/code&gt;配置&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  frpc:
    image: snowdreamtech/frpc
    container_name: frpc
    network_mode: host
    volumes:
      - ./frpc.toml:/etc/frp/frpc.toml
    restart: always
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;客户端&lt;code&gt;frpc.toml&lt;/code&gt;配置&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;serverAddr = &quot;8.8.8.8&quot;      # 公网服务器IP
serverPort = 7000           # 对接端口和frps一致
auth.token = &quot;admin123&quot;     # 认证令牌和frps一致

# 暴露SSH服务
[[proxies]]
name = &quot;ssh&quot;
type = &quot;tcp&quot;
localIP = &quot;127.0.0.1&quot;
localPort = 22            # 本地服务端口
remotePort = 6000         # 让frps暴露的公网端口
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;二进制文件部署&lt;/h3&gt;
&lt;p&gt;官方二进制文件下载地址：https://github.com/fatedier/frp/releases&lt;/p&gt;
&lt;p&gt;配置文件和上面一样&lt;/p&gt;
&lt;p&gt;服务端启动命令&lt;code&gt;./frps -c ./frps.toml&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;客户端启动命令&lt;code&gt;./frpc -c ./frpc.toml&lt;/code&gt;&lt;/p&gt;
</content:encoded></item><item><title>DNS64和纯IPV6网络使用技巧</title><link>https://blog.emohe.cn/posts/dns64/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/dns64/</guid><pubDate>Sat, 04 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;NAT64服务让纯V6网络支持访问纯V4站点&lt;/h2&gt;
&lt;p&gt;众所周知纯v6网络想要访问仅支持v4的网站是无法访问的，尤其是&lt;code&gt;github.com&lt;/code&gt;和&lt;code&gt;api.github.com&lt;/code&gt;居然不支持ipv6，所以很多人都选择使用&lt;code&gt;一键warp脚本&lt;/code&gt;，这个脚本很简单方便，但是对于我这种有洁癖的人有点难受，因为会装一堆东西，想卸载干净也很麻烦。所以我就找到了最简单的方法，当然&lt;code&gt;warp一键脚本&lt;/code&gt;还是很好用的，但是我图简单，就使用NAT64服务了，唯一的不足的是提供公共免费的NAT64很少，下面是我找到的可用的DNS。&lt;/p&gt;
&lt;h3&gt;原理如下：&lt;/h3&gt;
&lt;p&gt;NAT64的作用就是把IPv6网络的数据转换为IPv4网络的数据。它通过将IPv6请求中的地址转换成IPv4地址，使得IPv6设备可以通过NAT64网关访问只支持IPv4的服务器和网站。简单来说，NAT64在IPv6和IPv4之间充当了一个桥梁，负责地址和数据包的转换。&lt;/p&gt;
&lt;h3&gt;配置命令如下：&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;sudo tee /etc/resolv.conf &amp;lt;&amp;lt;EOF
nameserver 2a00:1098:2b::1
nameserver 2a00:1098:2c::1
nameserver 2a01:4f9:c010:3f02::1
nameserver 2a01:4f8:c2c:123f::1
EOF
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;测试能否访问仅支持ipv4的站点：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;curl -I ipv4.google.com
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;温馨提示&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;以上DNS服务自于 https://nat64.net&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://nat64.xyz/&quot;&gt;更多DNS64服务&lt;/a&gt;来自互联网&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;以上都是公益DNS64，超大流量使用有限制，个人使用是完全足够的，请勿滥用&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;永久生效的配置方法&lt;/h3&gt;
&lt;p&gt;1：编辑 &lt;code&gt;/etc/systemd/resolved.conf&lt;/code&gt; 文件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo vim /etc/systemd/resolved.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2：添加或修改以下内容：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Resolve]
DNS=2a00:1098:2b::1 2a00:1098:2c::1 2a01:4f9:c010:3f02::1 2a01:4f8:c2c:123f::1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3：重启 &lt;code&gt;systemd-resolved&lt;/code&gt; 服务：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl restart systemd-resolved
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>简单方便的web服务caddy</title><link>https://blog.emohe.cn/posts/caddy/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/caddy/</guid><pubDate>Fri, 03 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;介绍&lt;/h3&gt;
&lt;p&gt;caddy是和nginx一样的强大的web服务，使用go语言编写并且也是完全开源，&lt;code&gt;优点是配置简单，并且自动申请并部署SSL证书&lt;/code&gt;，无需手动配置，大大简化了运维工作，对小白非常友好，唯一的缺点是并发量稍微不及nginx，但是这点缺点几乎可以忽略不记。除非你的网站流量高的离谱，对于一般的企业是达不到那种流量的，个人就更达不到的。&lt;/p&gt;
&lt;h3&gt;我的使用场景&lt;/h3&gt;
&lt;p&gt;除了一般的网页服务，我经常配合docker使用，由于我比较喜欢用docker，所以经常使用&lt;code&gt;caddy&lt;/code&gt; 或者 &lt;code&gt;nginx-proxy-manager&lt;/code&gt;来反代docker的端口，他俩使用都很简单，本文着重介绍caddy。&lt;/p&gt;
&lt;p&gt;1：安装caddy&lt;/p&gt;
&lt;p&gt;apt安装。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl

curl -1sLf &apos;https://dl.cloudsmith.io/public/caddy/stable/gpg.key&apos; | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg

curl -1sLf &apos;https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt&apos; | sudo tee /etc/apt/sources.list.d/caddy-stable.list

sudo apt update

sudo apt install caddy -yq
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2：配置示例&lt;/p&gt;
&lt;p&gt;配置路径 &lt;code&gt;/etc/caddy/Caddyfile&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;将域名&lt;code&gt;example.com&lt;/code&gt;反向代理到本地&lt;code&gt;8080&lt;/code&gt;端口&lt;/p&gt;
&lt;p&gt;&lt;code&gt;caddyfile&lt;/code&gt;配置&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;example.com {
    encode gzip
    reverse_proxy localhost:8080
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将域名&lt;code&gt;example.com&lt;/code&gt;代理到网站目录&lt;code&gt;/var/www&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;caddyfile&lt;/code&gt;配置&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;example.com {
    root * /var/www
    encode zstd gzip
    file_server
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3：常用命令&lt;/p&gt;
&lt;p&gt;启动caddy&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl restart caddy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;caddy会占用80和443端口，api服务会占用本地2019端口，启动后会自动接管域名的证书管理，自动申请SSL证书和开启HTTPS加密。Caddy 默认使用 Let&apos;s Encrypt 作为证书颁发机构（CA）来自动签发 SSL/TLS 证书，Let&apos;s Encrypt申请证书有频率限制，如果你是测试使用，可以在caddy配置文件的域名指定为http格式，不开启HTTPS加密，例如caddy配置中域名可以这样写 http://example.com&lt;/p&gt;
&lt;p&gt;查看运行状态&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;systemctl status caddy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;检查配置语法是否正确&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;caddy validate --config /etc/caddy/Caddyfile
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;停止服务&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl stop caddy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;完全卸载&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt-get purge --auto-remove caddy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;证书路径&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>第一篇博客</title><link>https://blog.emohe.cn/posts/%E7%AC%AC%E4%B8%80%E7%AF%87%E5%8D%9A%E5%AE%A2/</link><guid isPermaLink="true">https://blog.emohe.cn/posts/%E7%AC%AC%E4%B8%80%E7%AF%87%E5%8D%9A%E5%AE%A2/</guid><pubDate>Wed, 01 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;博客又搬家了&lt;/h3&gt;
&lt;p&gt;2025年，新的一年，新的开始。&lt;/p&gt;
&lt;p&gt;心血来潮，又开始折腾网站了，继之前的阿里云3M小水管部署的wordpress，感觉越来越卡，于是这次使用纯静态网站，网站托管在&lt;a href=&quot;https://vercel.com/&quot;&gt;vercel&lt;/a&gt;，使用的&lt;a href=&quot;https://github.com/saicaca/fuwari&quot;&gt;fuwari&lt;/a&gt;主题。之前的wordpress也没怎么发布文章，就直接复制粘贴过来了&lt;/p&gt;
&lt;p&gt;终于可以把服务器的钱省了，写作时就直接在Github上用MD语法写作，还是挺方便的，以后就在这里更新一些日常了，还有一些docker教程，因为我是docker的忠实粉，万物皆可docker，不管什么项目我都喜欢用docker部署，哈哈。&lt;/p&gt;
</content:encoded></item></channel></rss>