<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>工具与效率 on 小盒子的技术分享</title><link>https://xiaobox.github.io/categories/%E5%B7%A5%E5%85%B7%E4%B8%8E%E6%95%88%E7%8E%87/</link><description>Recent content in 工具与效率 on 小盒子的技术分享</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Mon, 13 Apr 2026 03:56:46 +0000</lastBuildDate><atom:link href="https://xiaobox.github.io/categories/%E5%B7%A5%E5%85%B7%E4%B8%8E%E6%95%88%E7%8E%87/index.xml" rel="self" type="application/rss+xml"/><item><title>Postman 越来越臃肿了，我换了个开源的，还能让 AI 帮我写测试</title><link>https://xiaobox.github.io/p/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de/</link><pubDate>Mon, 13 Apr 2026 03:56:46 +0000</pubDate><guid>https://xiaobox.github.io/p/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/cover.jpg" alt="Featured image of post Postman 越来越臃肿了，我换了个开源的，还能让 AI 帮我写测试" /&gt;&lt;p&gt;Postman 我用了好几年了。&lt;/p&gt;
&lt;p&gt;从最早的 Chrome 插件时代开始用的，那时候它还是个轻量小工具，打开就能测接口，干干净净。但最近这两年，怎么说呢，它开始「端着」了。&lt;/p&gt;
&lt;p&gt;打开就让我登录。不登录不让用。&lt;/p&gt;
&lt;p&gt;好不容易登录了，又弹窗让我升级 Team 版。&lt;/p&gt;
&lt;p&gt;集合还给我存到云端去了。我就测个本地接口，你把我的请求数据往你服务器上传干嘛？&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/001-293772cd.png"&gt;&lt;/p&gt;
&lt;p&gt;我一直忍着，直到有一天，团队里两个人同时改了同一个集合，Postman 云同步直接给合并冲突了，还没法像 Git 那样 diff 看变更。那天我就想，不行了，得换。&lt;/p&gt;
&lt;p&gt;然后我遇到了 Bruno。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/002-67da7d91.png"&gt;&lt;/p&gt;
&lt;p&gt;怎么形容呢，就像你一直在用一个越来越臃肿的 IDE，突然有人递给你一个 Vim，告诉你「够用了，而且是你的」。&lt;/p&gt;
&lt;p&gt;Bruno 干了一件特别简单但特别对的事情，它把你的 API 请求存成 .bru 文件，放在你本地文件夹里。&lt;/p&gt;
&lt;p&gt;就是普通的文本文件，打开长这样。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;meta { name: 用户登录 type: http seq: 1}post { url: {{baseUrl}}/auth/login body: json auth: none}headers { Content-Type: application/json}body:json { { &amp;#34;username&amp;#34;: &amp;#34;{{username}}&amp;#34;, &amp;#34;password&amp;#34;: &amp;#34;{{password}}&amp;#34;, &amp;#34;expiresInMins&amp;#34;: 30 }}script:post-response { if (res.status === 200) { bru.setVar(&amp;#34;authToken&amp;#34;, res.body.accessToken); bru.setVar(&amp;#34;userId&amp;#34;, res.body.id); }}tests { test(&amp;#34;登录应该返回 200&amp;#34;, function() { expect(res.status).to.equal(200); }); test(&amp;#34;响应中应该包含 accessToken&amp;#34;, function() { expect(res.body.accessToken).to.be.a(&amp;#34;string&amp;#34;); expect(res.body.accessToken.length).to.be.greaterThan(0); }); test(&amp;#34;响应中应该包含用户基本信息&amp;#34;, function() { expect(res.body.id).to.be.a(&amp;#34;number&amp;#34;); expect(res.body.username).to.equal(&amp;#34;emilys&amp;#34;); expect(res.body.email).to.be.a(&amp;#34;string&amp;#34;); }); test(&amp;#34;响应时间应该小于 3 秒&amp;#34;, function() { expect(res.responseTime).to.be.lessThan(3000); });}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你看，GET 请求、Header、Body、断言，全都是纯文本。你用任何编辑器都能打开它，改完保存就行。&lt;/p&gt;
&lt;p&gt;这玩意最不同的地方在哪？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;它可以用 Git 管理。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;以前团队共享 Postman 集合，那叫一个痛苦。谁改了什么不知道，版本对不对不确定，冲突了还没法 resolve。&lt;/p&gt;
&lt;p&gt;现在用 Bruno，接口定义就是文件，扔进 Git 仓库，该 PR 就 PR，该 Code Review 就 Code Review。有人改了某个接口的 Header，diff 里看得一清二楚。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/003-c38b66fd.png"&gt;&lt;/p&gt;
&lt;p&gt;用程序员已经会的工具，解决程序员的问题。不用学新的协作方式，不用付费，不用担心数据被传到哪个云上。&lt;/p&gt;
&lt;p&gt;Bruno 官网上有一句话我印象特别深，大意是**「我们不会同步你的任何数据到云端，甚至连登录的概念都没有。我们看不到你在 Bruno 里输入了什么，也不会用你的数据训练任何 AI 模型」。**&lt;/p&gt;
&lt;p&gt;在这个年代，一个工具敢这么说，我觉得还是挺硬气的。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/004-2762e255.png"&gt;&lt;/p&gt;
&lt;p&gt;回到工具本身。说到这里，可能有朋友会想，开源 API 客户端一抓一大把，Insomnia、Hoppscotch、Thunder Client，凭什么是 Bruno？&lt;/p&gt;
&lt;p&gt;我自己用下来，让我有「这玩意不一样」的瞬间，是发现它有 CLI。&lt;/p&gt;
&lt;p&gt;但我说的不是「能在终端里跑测试」这种废话。Newman 也能跑，Postman 自己也有命令行。我说的是另一件事。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bruno 的 CLI，让 Claude Code 和 Cursor 这种 AI 编程工具，第一次能真正帮你写和跑接口测试。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这话有点大，我用两件刚发生的真事讲一下。&lt;/p&gt;
&lt;p&gt;回到 Bruno 的 CLI 本身。装它就一行，&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;npm i -g @usebruno/cli
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;装完之后命令叫 &lt;code&gt;bru&lt;/code&gt;，跑整个集合的测试是这样，&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;bru run --env production
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出长这样。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/005-95a75ce3.png"&gt;&lt;/p&gt;
&lt;p&gt;干净、彩色、有 ✓ 和 ✗、有总耗时、有失败原因。最重要的是，&lt;strong&gt;这是一段普通的终端命令，输出是普通的文本&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;为什么这件事重要？因为这两个特征，正好是 AI 编程 agent 工作的边界。&lt;/p&gt;
&lt;p&gt;Claude Code、Cursor、Codex 这些工具，它们能干什么？它们能读你的代码文件，能写新的文件，能在终端里跑命令，能读命令的输出。它们不能干什么？它们不能点击 Postman 的按钮，不能在你 GUI 里输入 token，不能登录任何账号。(&lt;strong&gt;或者说不方便，成本高，效率低&lt;/strong&gt;)&lt;/p&gt;
&lt;p&gt;Postman 的核心数据存在云端、操作靠 GUI、协作靠登录，这三件事每一件都把 AI agent 挡在外面。&lt;/p&gt;
&lt;p&gt;而 Bruno 的核心数据是 .bru 文本文件、操作靠 CLI、协作靠 Git。每一件都正好是 AI 最擅长的那种事。&lt;/p&gt;
&lt;p&gt;抽象的说完了，说点具体的。&lt;/p&gt;
&lt;p&gt;我前两天就让 Claude Code 帮我加了一个测试，过程是这样的。&lt;/p&gt;
&lt;p&gt;我跟它说，「我刚加了一个搜索商品的接口，帮我在 bruno 集合里加个测试用例」。&lt;/p&gt;
&lt;p&gt;它干了三件事，全程没问我任何问题。&lt;/p&gt;
&lt;p&gt;第一步，读了我现有的一个 .bru 文件，就是为了搞清楚我用的格式。比如我习惯加哪些 test，断言风格是什么样的。&lt;/p&gt;
&lt;p&gt;第二步，照着这个格式写了一个新的 06-搜索商品.bru 文件，放在我集合的根目录里。请求方法、URL、query 参数、4 条断言，全都给我加上了。&lt;/p&gt;
&lt;p&gt;第三步，它直接执行 &lt;code&gt;bru run 06-搜索商品.bru --env production&lt;/code&gt;，亲眼看着 4 个测试全绿，然后才回我一句「写完了，4/4 通过」。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/006-f94498cf.png"&gt;&lt;/p&gt;
&lt;p&gt;整个过程我没碰 Bruno 的 GUI，没敲一个字符的代码，没解释什么是 .bru 格式。AI 自己读自己写自己跑自己验证。&lt;/p&gt;
&lt;p&gt;你可能会问，这个事换成 Postman 不行吗？&lt;/p&gt;
&lt;p&gt;我得先把话说清楚，Postman 不是没有 CLI。Newman 一直都在，Postman Collection 也是 JSON 文件，AI 理论上能读能写。这条路是通的。&lt;/p&gt;
&lt;p&gt;但你真去试一下就会发现，那条路上全是石头。&lt;/p&gt;
&lt;p&gt;Postman 的 Collection JSON 格式不是给人手写的。我做了个最朴素的对比，同一个 GET 请求加一条「状态码等于 200」的断言，写成 .bru 是 15 行，导出成 Postman Collection JSON 是 44 行。区别在哪？.bru 里 URL 就是一个字符串 &lt;code&gt;https://api.example.com/users&lt;/code&gt;，Postman JSON 里 URL 被拆成 &lt;code&gt;protocol&lt;/code&gt;、&lt;code&gt;host&lt;/code&gt; 数组、&lt;code&gt;path&lt;/code&gt; 数组三个字段。.bru 里测试代码就是普通 JS，Postman JSON 里测试代码以字符串数组的形式塞在 event 里，每一行 JS 都得加引号、转义、再 JSON 序列化一次。还有一堆 &lt;code&gt;_postman_id&lt;/code&gt;、&lt;code&gt;_exporter_id&lt;/code&gt;、&lt;code&gt;schema&lt;/code&gt; 这种内部元数据，跟你的业务接口毫无关系，但你不写就报错。&lt;/p&gt;
&lt;p&gt;让 AI 写 .bru，它跟写 markdown 一样轻松。让 AI 写 Postman JSON，它更像在翻译一份配置文件，token 烧得多，出错概率高，写完你自己 review 都费劲，git diff 出来三行业务变更夹在二十行格式噪音里。&lt;/p&gt;
&lt;p&gt;所以不是 AI 不能写 Postman 的测试，是 Postman 的格式从一开始就没打算让人手写，更没打算让 AI 直接读写。它假设你有一个 GUI 在中间帮你管理这些内部细节。AI 进来之后，那个假设就有点尴尬了。&lt;/p&gt;
&lt;p&gt;我说的还不是最炸的场景。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;真正让我觉得 Bruno + AI 是 1 + 1 大于 10 的，是调试场景。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我又试了一次，让 Claude Code 帮我加一个购物车接口的测试。它写完跑了一遍，挂了。&lt;/p&gt;
&lt;p&gt;错误信息是这样的。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;✕ 购物车应该有 totalPrice 字段 expected undefined to be a number✕ 购物车应该有 products 数组 expected undefined to be an array
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;```bash
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;它猜错了字段名。这种事很正常，毕竟它没真的去看接口返回。但接下来才有意思。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;它没有问我、没有让我提供文档、也没有放弃。它自己执行了一条 curl 命令，把购物车接口的真实响应拉了下来。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;```bash
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;curl -s https://dummyjson.com/carts/1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;看到响应里字段是 &lt;code&gt;total&lt;/code&gt; 和 &lt;code&gt;products&lt;/code&gt;，不是 &lt;code&gt;totalPrice&lt;/code&gt; 和 &lt;code&gt;items&lt;/code&gt;。它马上把测试里的字段名改了，再跑一遍，全绿。&lt;/p&gt;
&lt;p&gt;整个调试过程，我做了什么？我什么也没做。我只是看着它一步一步跑完。&lt;/p&gt;
&lt;p&gt;这就是 Bruno + CLI + AI 的真正价值。你的 API 测试不再是一个孤立的、需要你手动维护的负担，而是变成了 AI 可以读、可以写、可以跑、可以调试的一种代码。&lt;/p&gt;
&lt;p&gt;它和你的源码、你的 Git 历史、你的 CI/CD、你的 AI agent，全都是一体的。&lt;/p&gt;
&lt;p&gt;写到这里我猜有人要拍桌子了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;「等等，国内程序员谁还用 Postman 啊？Apifox 它不香吗？」&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;发，我们专门聊聊 Apifox。&lt;/p&gt;
&lt;p&gt;我必须先承认它真的很强。一体化平台，API 文档、调试、Mock、自动化测试、团队协作全在一个工具里搞定，相当于 Postman + Swagger + JMeter 三合一。界面是中文，文档是中文，国内团队几乎零学习成本。如果你的团队 20 人以上，需要权限管理、需要实时协作、需要统一的 API 文档管理，Apifox 是比 Bruno 更合适的选择。这点我不否认。&lt;/p&gt;
&lt;p&gt;而且 Apifox 不傻，它也在跟上 AI 浪潮。它有 apifox-cli，能在终端里跑测试场景。它甚至专门做了 Apifox MCP Server，可以让 Cursor 和 Claude Desktop 读取 Apifox 项目里的 API 文档，帮你写代码。&lt;/p&gt;
&lt;p&gt;那问题来了，Bruno 和 Apifox 在 AI 这件事上到底有啥不一样？&lt;/p&gt;
&lt;p&gt;我读了一圈 Apifox 的官方文档，发现一个挺有意思的差别。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Apifox 接 AI 的方式，是让 AI 来连接 Apifox。Bruno 接 AI 的方式，是 Bruno 根本就是 AI 已经会读的格式。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;具体说，Apifox 的 AI 工作流是这样的，你的测试数据躺在 Apifox 云端项目里，你装一个 Apifox MCP Server，配置 Cursor 去连这个 MCP，AI 通过 MCP 协议向 Apifox 服务发请求，把 API 文档拿下来，再帮你生成代码。&lt;/p&gt;
&lt;p&gt;Bruno 的 AI 工作流是这样的，你的测试就是项目目录里的 .bru 文件，AI 直接 cat、edit、bru run。完了。&lt;/p&gt;
&lt;p&gt;你看出区别了吗？Apifox 把 AI 当成一个外部工具来对接，所以需要 MCP 这一层中转。而 Bruno 根本不需要 MCP，因为它跟 AI 用的是同一种原生语言：&lt;strong&gt;文本文件加终端命令。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;还有一个更现实的差别。Apifox 的 MCP Server 现在的能力，是让 AI 读取 API 文档来生成代码，它并不能让 AI 直接去编辑你的测试用例。AI 在 Apifox 这边的角色基本上是只读的。&lt;/p&gt;
&lt;p&gt;而我前面演示的那两个场景，Claude Code 帮我写新测试、自动调试、修字段名，那是完整的读写跑改循环。AI 不光在读，还在写，还在跑，还在调试。这是因为 .bru 是普通文件，Edit 工具就能改，Bash 工具就能跑。它中间没有任何一层，所以也没有任何一层会卡住。&lt;/p&gt;
&lt;p&gt;这不是说 Apifox 不好，是两条不同的路。Apifox 选的是「我是平台，AI 来连我」，Bruno 选的是「我什么都不是，我就是文件」。前者适合企业级场景，后者适合代码即测试的开发者工作流。&lt;/p&gt;
&lt;p&gt;我自己是后者。我喜欢我所有的东西都能塞进 Git 仓库，喜欢 AI 直接读我硬盘上的文件，不喜欢任何账号、登录、云端中转。所以我选 Bruno。&lt;/p&gt;
&lt;p&gt;如果你的工作场景更接近企业级 SaaS 那一套，那 Apifox 完全没问题，甚至更合适。但如果你跟我一样，希望让 AI 一句话把接口测试搞定，那 Bruno 这条路确实更顺。&lt;/p&gt;
&lt;p&gt;聊完 Apifox 我们继续。顺便提一下 CI 集成，因为这块也很丝滑。Bruno CLI 支持输出 JUnit XML，GitHub Actions、GitLab CI、Jenkins 直接吃这个格式。我的工作流文件大概长这样。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;- name: 安装 Bruno CLI run: npm install -g @usebruno/cli- name: 跑接口测试 run: bru run --env production --reporter-junit junit.xml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;两步。你的 PR 一提交，所有接口测试自动跑一遍，挂了直接拒绝合并。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/007-56b53ed9.png"&gt;&lt;/p&gt;
&lt;p&gt;生成的测试执行也挺好看的&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/008-cc895180.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/009-90333e8c.png"&gt;&lt;/p&gt;
&lt;p&gt;整套东西免费、开源、本地、Git 友好、AI 友好，不用登录、不用付钱、不用担心数据上云。&lt;/p&gt;
&lt;p&gt;安装很简单。桌面版，&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;brew install bruno
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="sb"&gt;```&lt;/span&gt;bash
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;CLI，
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="sb"&gt;```&lt;/span&gt;bash
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;npm i -g @usebruno/cli
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;用了一周之后我把 Postman 卸了。&lt;/p&gt;
&lt;p&gt;不是它不好，是我不再需要一个登录才能用、集合存在别人云上、免费版各种限制、AI 完全帮不上忙的接口测试工具了。&lt;/p&gt;
&lt;p&gt;我只需要一个文件夹、几个 .bru 文件、一条终端命令，和一个能读懂这一切的 AI。&lt;/p&gt;
&lt;p&gt;有时候工具的进步不是功能越加越多，而是把不该有的东西去掉。Bruno 去掉了登录、去掉了云端、去掉了 GUI 锁定，剩下的全是程序员真正需要的东西。&lt;/p&gt;
&lt;p&gt;而当你把这些不需要的东西去掉之后，你会发现一个意外的副作用，AI 进来了。&lt;/p&gt;
&lt;p&gt;好了就说这么多。&lt;/p&gt;
&lt;p&gt;如果你也受够了 Postman，试试 Bruno。说不定你也会像我一样，用完就回不去了。&lt;/p&gt;
&lt;p&gt;ps:我写的 demo 在这里，你可以拉下来试一下，可以 run 的 ：https://github.com/xiaobox/bruno-demo&lt;/p&gt;</description></item><item><title>如何在 mac 的 chrome 中开启 gemini 侧边栏</title><link>https://xiaobox.github.io/p/2026-01-31-ru-he-zai-mac-de-chrome-zhong-kai-qi-gemini-ce-bian-lan/</link><pubDate>Sat, 31 Jan 2026 02:03:30 +0000</pubDate><guid>https://xiaobox.github.io/p/2026-01-31-ru-he-zai-mac-de-chrome-zhong-kai-qi-gemini-ce-bian-lan/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-01-31-ru-he-zai-mac-de-chrome-zhong-kai-qi-gemini-ce-bian-lan/cover.jpg" alt="Featured image of post 如何在 mac 的 chrome 中开启 gemini 侧边栏" /&gt;&lt;p&gt;昨天折腾了半天，厕所都没上，没搞定，今天一分钟搞定了，其实非常简单。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" class="gallery-image" data-flex-basis="389px" data-flex-grow="162" height="665" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://xiaobox.github.io/p/2026-01-31-ru-he-zai-mac-de-chrome-zhong-kai-qi-gemini-ce-bian-lan/001-a19868cf.png" width="1080"&gt;&lt;/p&gt;
&lt;p&gt;前提条件什么的自不用说，主要是两步，我是 mac 系统，windows 的不知道。&lt;/p&gt;
&lt;h2 id="第一步"&gt;&lt;a href="#%e7%ac%ac%e4%b8%80%e6%ad%a5" class="header-anchor"&gt;&lt;/a&gt;第一步
&lt;/h2&gt;&lt;p&gt;把语言设置一下，注意是在操作系统中设置chrome 的自定义语言&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" class="gallery-image" data-flex-basis="283px" data-flex-grow="118" height="915" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://xiaobox.github.io/p/2026-01-31-ru-he-zai-mac-de-chrome-zhong-kai-qi-gemini-ce-bian-lan/002-f2b3a06f.png" width="1080"&gt;&lt;/p&gt;
&lt;h2 id="第二步"&gt;&lt;a href="#%e7%ac%ac%e4%ba%8c%e6%ad%a5" class="header-anchor"&gt;&lt;/a&gt;第二步
&lt;/h2&gt;&lt;p&gt;打开终端，输入以后命令后回车&lt;/p&gt;
&lt;p&gt;⚡ 代码片段&lt;code&gt;open -n -a &amp;quot;Google Chrome&amp;quot; --args --variations-override-country=us&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;也可以用这个项目设置一下 ：https://github.com/lcandy2/enable-chrome-ai&lt;/p&gt;
&lt;p&gt;然后重启 chrome 就可以了，location 如果不支持，你应该知道怎么办吧，哈哈。&lt;/p&gt;</description></item><item><title>Modern MD Editor 项目介绍</title><link>https://xiaobox.github.io/p/2025-08-16-modern-md-editor-xiang-mu-jie-shao/</link><pubDate>Sat, 16 Aug 2025 03:45:30 +0000</pubDate><guid>https://xiaobox.github.io/p/2025-08-16-modern-md-editor-xiang-mu-jie-shao/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-08-16-modern-md-editor-xiang-mu-jie-shao/cover.jpg" alt="Featured image of post Modern MD Editor 项目介绍" /&gt;&lt;h1 id="感谢"&gt;&lt;a href="#%e6%84%9f%e8%b0%a2" class="header-anchor"&gt;&lt;/a&gt;感谢
&lt;/h1&gt;&lt;p&gt;自 &lt;code&gt;mdeditor&lt;/code&gt; 项目开源以来，陆续收到了大家的许多反馈，有提建议的、有提 bug 的，每一条评论我都认认真真地看了。&lt;/p&gt;
&lt;p&gt;项目也得到了 阮一峰大佬的 &lt;strong&gt;科技爱好者周刊&lt;/strong&gt; 的支持，在最新的一期周刊（https://github.com/ruanyf/weekly/blob/master/docs/issue-361.md）进行了推荐&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-08-16-modern-md-editor-xiang-mu-jie-shao/001-88c0b3e9.png"&gt;&lt;/p&gt;
&lt;p&gt;开源一周目前项目在 github 上也获得了 160+ star&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-08-16-modern-md-editor-xiang-mu-jie-shao/002-0a729972.png"&gt;&lt;/p&gt;
&lt;p&gt;非常感谢大家对项目的关注和支持。 🙏&lt;/p&gt;
&lt;h1 id="回复疑问"&gt;&lt;a href="#%e5%9b%9e%e5%a4%8d%e7%96%91%e9%97%ae" class="header-anchor"&gt;&lt;/a&gt;回复疑问
&lt;/h1&gt;&lt;p&gt;在上篇文章的评论中很多网友说为什么不多介绍一下项目，哪怕放一张图出来?&lt;/p&gt;
&lt;p&gt;其实这与我做这个项目的初衷有关系。&lt;/p&gt;
&lt;h2 id="初衷"&gt;&lt;a href="#%e5%88%9d%e8%a1%b7" class="header-anchor"&gt;&lt;/a&gt;初衷
&lt;/h2&gt;&lt;p&gt;我是个开发者，同时也是内容创作者，平时会写公众号文章，文章内容是用 markdown 写的。目前市面上比较流行好用的 markdown 编辑器都用过，后来就只用 mdnice 和 md 这些了，因为他们对社交媒体友好，可以将预设的样式一键 copy 到微信公众号，很方便。&lt;/p&gt;
&lt;p&gt;但这些工具我用的时间久了觉得总是不如我的意，比如我想设置颜色、间距、字号什么的，这些工具用起来总是不顺手。无论是 UI 样式还是功能设置，我都不那么满意，于是就越来越想做个让自己舒服、满意的工具。&lt;/p&gt;
&lt;p&gt;这就是做这个项目的初衷，完全为了服务我自己，完全按照我自己的审美和功能取向来设计与开发的一个 markdown 编辑器。&lt;/p&gt;
&lt;p&gt;开源的原因是我想可能也会有其他创作者有我类似的需求，也许能解决大家的问题就开源了，便人便已。&lt;/p&gt;
&lt;p&gt;最开始也真没当回事儿，而且不想占太大篇幅来宣传，现在网上的信息太多了，很多人都信息过载，我想我就别添乱了，就没放图，也没怎么详细介绍。还有个原因是，其实项目的 readme 写的很清楚了，大家一看便知。&lt;/p&gt;
&lt;h1 id="有必要展开说说"&gt;&lt;a href="#%e6%9c%89%e5%bf%85%e8%a6%81%e5%b1%95%e5%bc%80%e8%af%b4%e8%af%b4" class="header-anchor"&gt;&lt;/a&gt;有必要展开说说
&lt;/h1&gt;&lt;p&gt;这几天随着关注项目的人数增多，通过大家反馈的意见，我觉得还是有必要再详细介绍一下项目。主要原因有以下几个：&lt;/p&gt;
&lt;p&gt;●很多网友访问不了 github，看不到介绍（目前我也同步到了 gitee 一份，文末有地址）&lt;/p&gt;
&lt;p&gt;●一图胜千言，颜值党很关注 UI&lt;/p&gt;
&lt;p&gt;●一些在 readme 没说透的话，可以再细说说&lt;/p&gt;
&lt;p&gt;所以，我借此机会就给大家展开介绍一下，如果已经了解过项目的朋友可以跳过了。&lt;/p&gt;
&lt;h1 id="功能介绍"&gt;&lt;a href="#%e5%8a%9f%e8%83%bd%e4%bb%8b%e7%bb%8d" class="header-anchor"&gt;&lt;/a&gt;功能介绍
&lt;/h1&gt;&lt;h2 id="主界面"&gt;&lt;a href="#%e4%b8%bb%e7%95%8c%e9%9d%a2" class="header-anchor"&gt;&lt;/a&gt;主界面
&lt;/h2&gt;&lt;h3 id="编辑--预览双栏"&gt;&lt;a href="#%e7%bc%96%e8%be%91--%e9%a2%84%e8%a7%88%e5%8f%8c%e6%a0%8f" class="header-anchor"&gt;&lt;/a&gt;编辑 + 预览双栏
&lt;/h3&gt;&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-08-16-modern-md-editor-xiang-mu-jie-shao/003-afcc3601.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;除了常规的 markdown 语法外，还支持 &lt;code&gt;mermaid&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-08-16-modern-md-editor-xiang-mu-jie-shao/004-b92846e2.png"&gt;&lt;/p&gt;
&lt;h3 id="预览窗口桌面--平板--手机"&gt;&lt;a href="#%e9%a2%84%e8%a7%88%e7%aa%97%e5%8f%a3%e6%a1%8c%e9%9d%a2--%e5%b9%b3%e6%9d%bf--%e6%89%8b%e6%9c%ba" class="header-anchor"&gt;&lt;/a&gt;预览窗口（桌面 / 平板 / 手机）
&lt;/h3&gt;&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-08-16-modern-md-editor-xiang-mu-jie-shao/005-c7d47c41.png"&gt;&lt;/p&gt;
&lt;h2 id="设置"&gt;&lt;a href="#%e8%ae%be%e7%bd%ae" class="header-anchor"&gt;&lt;/a&gt;设置
&lt;/h2&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;h3 id="主题系统"&gt;&lt;a href="#%e4%b8%bb%e9%a2%98%e7%b3%bb%e7%bb%9f" class="header-anchor"&gt;&lt;/a&gt;主题系统
&lt;/h3&gt;&lt;p&gt;目前有两个主题系统，一个是默认的，一个是 &lt;code&gt;清风排版&lt;/code&gt; ，我个人最喜欢后者，后续我将开发更多的主题系统，每个主题系统都是一套完全不同的样式风格。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-08-16-modern-md-editor-xiang-mu-jie-shao/006-331beee3.png"&gt;&lt;/p&gt;
&lt;h3 id="主题色"&gt;&lt;a href="#%e4%b8%bb%e9%a2%98%e8%89%b2" class="header-anchor"&gt;&lt;/a&gt;主题色
&lt;/h3&gt;&lt;p&gt;预设了 8 个主题色&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-08-16-modern-md-editor-xiang-mu-jie-shao/007-01ed6c72.png"&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-08-16-modern-md-editor-xiang-mu-jie-shao/008-30d8b37f.png"&gt;&lt;/p&gt;
&lt;p&gt;也可以自定义设置颜色&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-08-16-modern-md-editor-xiang-mu-jie-shao/009-16d04f06.png"&gt;&lt;/p&gt;
&lt;p&gt;也支持 &lt;code&gt;color picker&lt;/code&gt;,除了内置颜色，可以选取你看到的任何颜色&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-08-16-modern-md-editor-xiang-mu-jie-shao/010-cc479e7e.png"&gt;&lt;/p&gt;
&lt;p&gt;颜色选择并应用后，整个系统页面和 markdown 编辑器样式都会被应用，有一种统一感。&lt;/p&gt;
&lt;h3 id="代码样式"&gt;&lt;a href="#%e4%bb%a3%e7%a0%81%e6%a0%b7%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;代码样式
&lt;/h3&gt;&lt;p&gt;预设了四种我喜欢的、常规的样式&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-08-16-modern-md-editor-xiang-mu-jie-shao/011-084c1e4f.png"&gt;&lt;/p&gt;
&lt;h3 id="字体"&gt;&lt;a href="#%e5%ad%97%e4%bd%93" class="header-anchor"&gt;&lt;/a&gt;字体
&lt;/h3&gt;&lt;p&gt;字体的选择如图所示&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-08-16-modern-md-editor-xiang-mu-jie-shao/012-a4f3d121.png"&gt;&lt;/p&gt;
&lt;h3 id="字号"&gt;&lt;a href="#%e5%ad%97%e5%8f%b7" class="header-anchor"&gt;&lt;/a&gt;字号
&lt;/h3&gt;&lt;p&gt;可以从小到大，方便地选择你需要的字号大小，应用后在预览页以及复制到公众号以后都会变化&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-08-16-modern-md-editor-xiang-mu-jie-shao/013-a48225cc.png"&gt;&lt;/p&gt;
&lt;h3 id="间距"&gt;&lt;a href="#%e9%97%b4%e8%b7%9d" class="header-anchor"&gt;&lt;/a&gt;间距
&lt;/h3&gt;&lt;p&gt;与字号设置类似，支持字间距和行间距&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-08-16-modern-md-editor-xiang-mu-jie-shao/014-8547ae2f.png"&gt;&lt;/p&gt;
&lt;h2 id="一键复制"&gt;&lt;a href="#%e4%b8%80%e9%94%ae%e5%a4%8d%e5%88%b6" class="header-anchor"&gt;&lt;/a&gt;一键复制
&lt;/h2&gt;&lt;p&gt;支持一键复制到微信公众号&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-08-16-modern-md-editor-xiang-mu-jie-shao/015-843e2926.png"&gt;&lt;/p&gt;
&lt;h1 id="安装和部署"&gt;&lt;a href="#%e5%ae%89%e8%a3%85%e5%92%8c%e9%83%a8%e7%bd%b2" class="header-anchor"&gt;&lt;/a&gt;安装和部署
&lt;/h1&gt;&lt;h2 id="docker-一键部署"&gt;&lt;a href="#docker-%e4%b8%80%e9%94%ae%e9%83%a8%e7%bd%b2" class="header-anchor"&gt;&lt;/a&gt;Docker 一键部署
&lt;/h2&gt;&lt;h3 id="方式一docker推荐最简"&gt;&lt;a href="#%e6%96%b9%e5%bc%8f%e4%b8%80docker%e6%8e%a8%e8%8d%90%e6%9c%80%e7%ae%80" class="header-anchor"&gt;&lt;/a&gt;方式一：Docker（推荐最简）
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;$ terminal# 拉取并运行（默认暴露到本机 8080）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;docker run -d --name mdeditor -p 8080:80 helongisno1/mdeditor:latest
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 访问&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;open http://localhost:8080
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="方式二docker-compose"&gt;&lt;a href="#%e6%96%b9%e5%bc%8f%e4%ba%8cdocker-compose" class="header-anchor"&gt;&lt;/a&gt;方式二：Docker Compose
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;$ terminalversion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;3.9&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;mdeditor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;helongisno1/mdeditor:latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s2"&gt;&amp;#34;8080:80&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;unless-stopped&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="l"&gt;$ terminaldocker compose up -d&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="l"&gt;open http://localhost:8080&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="安装与本地运行"&gt;&lt;a href="#%e5%ae%89%e8%a3%85%e4%b8%8e%e6%9c%ac%e5%9c%b0%e8%bf%90%e8%a1%8c" class="header-anchor"&gt;&lt;/a&gt;安装与本地运行
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;$ terminal# 克隆
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;git clone https://github.com/xiaobox/mdeditor.git
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; modern-md-editor
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 安装依赖（任选其一）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;npm install
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 或&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;yarn
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 或&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;pnpm install
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 本地开发&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;npm run dev
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 生产构建&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;npm run build
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 本地预览构建产物&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;npm run preview
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 测试（可选）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;npm run &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;npm run test:ui
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;npm run test:coverage
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="后续规则"&gt;&lt;a href="#%e5%90%8e%e7%bb%ad%e8%a7%84%e5%88%99" class="header-anchor"&gt;&lt;/a&gt;后续规则
&lt;/h1&gt;&lt;p&gt;目前是想先把大家关注的一些功能做完，比如：&lt;/p&gt;
&lt;p&gt;●所见即所得&lt;/p&gt;
&lt;p&gt;●导入导出&lt;/p&gt;
&lt;p&gt;当然还有 bug 修复，大家如果有类似 bug 或 功能方面的意见，欢迎 到 github 中提 issue&lt;/p&gt;
&lt;h2 id="补充信息"&gt;&lt;a href="#%e8%a1%a5%e5%85%85%e4%bf%a1%e6%81%af" class="header-anchor"&gt;&lt;/a&gt;补充信息
&lt;/h2&gt;&lt;p&gt;Modern MD Editor 是一款面向创作者与内容团队的「高颜值 Markdown 编辑器 + 社交平台发布器」。它以极致的界面与交互为基础，提供所见即所得的实时预览与多端视口切换，并通过一键复制将 Markdown 转为适配微信公众号/社交平台的高保真富文本（自动内联样式、字体/行高/字距与主题化适配），让创作到发布的最后一步变得优雅、高效、可控。&lt;/p&gt;
&lt;h3 id="项目地址"&gt;&lt;a href="#%e9%a1%b9%e7%9b%ae%e5%9c%b0%e5%9d%80" class="header-anchor"&gt;&lt;/a&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;&lt;strong&gt;GitHub&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;a class="link" href="https://github.com/xiaobox/mdeditor" target="_blank" rel="noopener"
 &gt;https://github.com/xiaobox/mdeditor&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Gitee&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;a class="link" href="https://gitee.com/xiao-box/mdeditor" target="_blank" rel="noopener"
 &gt;https://gitee.com/xiao-box/mdeditor&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;</description></item><item><title>嘿，朋友，做了个 Markdown 编辑器，想请你来试试</title><link>https://xiaobox.github.io/p/2025-08-10-hei-peng-you-zuo-le-ge-markdown-bian-ji-qi-xiang-qing-ni-lai/</link><pubDate>Sun, 10 Aug 2025 08:49:50 +0000</pubDate><guid>https://xiaobox.github.io/p/2025-08-10-hei-peng-you-zuo-le-ge-markdown-bian-ji-qi-xiang-qing-ni-lai/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-08-10-hei-peng-you-zuo-le-ge-markdown-bian-ji-qi-xiang-qing-ni-lai/cover.jpg" alt="Featured image of post 嘿，朋友，做了个 Markdown 编辑器，想请你来试试" /&gt;&lt;p&gt;大家好，我是小盒子。&lt;/p&gt;
&lt;p&gt;写了快十五年代码，从当年的 &lt;code&gt;Dreamweaver&lt;/code&gt;、&lt;code&gt;FrontPage&lt;/code&gt; 一路玩到现在的 &lt;code&gt;VS Code&lt;/code&gt;、&lt;code&gt;JetBrains&lt;/code&gt; 全家桶。工具换了一茬又一茬，但有件事一直没变：我们总得写东西。写文档、写博客、写笔记，而 &lt;code&gt;Markdown&lt;/code&gt;，就是我们这群人的“笔”。&lt;/p&gt;
&lt;p&gt;说实话，市面上的 Markdown 编辑器，我用过的没有一百也有八十。但用着用着，总有点别扭。就像一把用了很久的锤子，总感觉重心不太对，握着也不够舒服。&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;code&gt;mdeditor&lt;/code&gt; 就这么诞生了。&lt;/p&gt;
&lt;p&gt;它没啥宏大的目标，初心很简单：&lt;strong&gt;做一款快、好用，而且能让你觉得“这是我的编辑器”的工具。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;代码都在这儿了，开诚布公，欢迎随时来坐坐：&lt;/p&gt;
&lt;p&gt;**GitHub:**&lt;a class="link" href="https://github.com/xiaobox/mdeditor" target="_blank" rel="noopener"
 &gt;https://github.com/xiaobox/mdeditor&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;（要是觉得还行，顺手点个 Star，就是对我最大的肯定。）&lt;/p&gt;
&lt;hr&gt;
&lt;h1 id="这把锤子我花了些心思去打磨"&gt;&lt;a href="#%e8%bf%99%e6%8a%8a%e9%94%a4%e5%ad%90%e6%88%91%e8%8a%b1%e4%ba%86%e4%ba%9b%e5%bf%83%e6%80%9d%e5%8e%bb%e6%89%93%e7%a3%a8" class="header-anchor"&gt;&lt;/a&gt;这把“锤子”，我花了些心思去打磨
&lt;/h1&gt;&lt;p&gt;我不想只做个“能用”的工具，我希望它能“好用”，甚至让你“爱用”。所以，我在几个自己最看重的地方下了点功夫。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 首先，它得听你的话：真正意义上的“深度定制”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我始终觉得，工具应该适应人，而不是人去适应工具。所以，我给了 &lt;code&gt;mdeditor&lt;/code&gt; 一套非常彻底的定制系统。&lt;/p&gt;
&lt;p&gt;你可以像装修自己家一样，去“装修”你的编辑器。从整体的界面风格，到每一个按钮、每一行代码的颜色，再到你习惯的字体和阅读排版……所有这些，都由你掌控。你完全可以把它调校成你最舒服、最高效的样子。对我来说，这是一种底层的安全感。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 其次，性能是基本功：绝不拖泥带水&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;一个编辑器，快，是本分。为了这个“快”字，我放弃了几个现成的轮子，自己写了 Markdown 解析的核心。这么做的好处是，&lt;code&gt;mdeditor&lt;/code&gt; 的响应速度非常跟手，无论是打字、滚动还是处理几万字的大文件，都能保持流畅。写东西就该行云流水，不应该被工具打断思路。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 最后，是给开发者朋友的“彩蛋”：一个干净的架构&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mdeditor&lt;/code&gt; 是用 &lt;code&gt;Vite + Vue 3&lt;/code&gt; 写的，代码组织上遵循了最朴素的“高内聚、低耦合”原则。如果你也玩 Vue，可以看看源码。我把很多功能逻辑都抽离成了独立的 &lt;code&gt;Composables&lt;/code&gt;，整个项目的结构清晰明了，很适合拿来做二次开发，或者作为学习 Vue 3 Composition API 的一个实践参考。&lt;/p&gt;
&lt;hr&gt;
&lt;h1 id="这只是个开始"&gt;&lt;a href="#%e8%bf%99%e5%8f%aa%e6%98%af%e4%b8%aa%e5%bc%80%e5%a7%8b" class="header-anchor"&gt;&lt;/a&gt;这只是个开始
&lt;/h1&gt;&lt;h2 id="想邀请你一起来添砖加瓦"&gt;&lt;a href="#%e6%83%b3%e9%82%80%e8%af%b7%e4%bd%a0%e4%b8%80%e8%b5%b7%e6%9d%a5%e6%b7%bb%e7%a0%96%e5%8a%a0%e7%93%a6" class="header-anchor"&gt;&lt;/a&gt;想邀请你一起来添砖加瓦
&lt;/h2&gt;&lt;p&gt;现在 &lt;code&gt;mdeditor&lt;/code&gt; 已经有了一个不错的底子，但它离“完美”还差得很远。一个人的力量终究有限，一个好的开源项目，生命力在于社区。&lt;/p&gt;
&lt;p&gt;所以，我诚心地邀请你，无论你是谁，都可以来参与这件事：&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;如果你只是想找个好用的工具&lt;/strong&gt;：欢迎你下载使用。你的使用本身，就是对它最好的检验。如果能顺手在 GitHub 上点个 Star，我会非常开心。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;如果你和我一样，是个爱折腾的开发者&lt;/strong&gt;：欢迎你来读它的源码，给我提 Issue，或者直接甩个 PR 过来。无论是发现一个 Bug，还是有个绝妙的点子，都请不要吝啬。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;如果你是位设计师，对 UI/UX 有自己的见解&lt;/strong&gt;：欢迎你来设计新的主题，或者对现有的交互提出建议。你的审美，能让它变得更美。&lt;/p&gt;
&lt;p&gt;一个优秀的开源项目，就像一场漫长的篝火晚会，需要不断有人添柴，才能一直燃烧下去。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mdeditor&lt;/code&gt; 就是我点起的第一根火柴。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;好了，就说这么多。感谢你耐心听我这个老家伙唠叨。&lt;/p&gt;
&lt;p&gt;如果你对 &lt;code&gt;mdeditor&lt;/code&gt; 有一点点兴趣，就去 GitHub 看看吧。期待在那里，看到你的身影。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GitHub传送门：https://github.com/xiaobox/mdeditor&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>想要提高命令行编写效率，快来试试这几个快捷键！</title><link>https://xiaobox.github.io/p/2024-11-16-xiang-yao-ti-gao-ming-ling-xing-bian-xie-xiao-l-kuai-lai-shi/</link><pubDate>Sat, 16 Nov 2024 11:18:48 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-11-16-xiang-yao-ti-gao-ming-ling-xing-bian-xie-xiao-l-kuai-lai-shi/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-16-xiang-yao-ti-gao-ming-ling-xing-bian-xie-xiao-l-kuai-lai-shi/cover.jpg" alt="Featured image of post 想要提高命令行编写效率，快来试试这几个快捷键！" /&gt;&lt;p&gt;最近看到一些同学在命令行写命令时的一些低效行为，比如长按左右箭头定位光标、长按 delete 删除字符等。其实只需要掌握几个常用的快捷键就能够大幅提高你写命令的效率。今天我们介绍几个常用的快捷键。&lt;/p&gt;
&lt;h2 id="移动和删除"&gt;&lt;a href="#%e7%a7%bb%e5%8a%a8%e5%92%8c%e5%88%a0%e9%99%a4" class="header-anchor"&gt;&lt;/a&gt;移动和删除
&lt;/h2&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-16-xiang-yao-ti-gao-ming-ling-xing-bian-xie-xiao-l-kuai-lai-shi/001-3acc93cf.png"&gt;&lt;/p&gt;
&lt;h3 id="移动"&gt;&lt;a href="#%e7%a7%bb%e5%8a%a8" class="header-anchor"&gt;&lt;/a&gt;移动
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ctrl + a&lt;/code&gt; : 可以直接将移动光标到命令行首&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ctrl + e&lt;/code&gt; : 可以直接移动光标到命令行尾&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-16-xiang-yao-ti-gao-ming-ling-xing-bian-xie-xiao-l-kuai-lai-shi/002-ead710e1.gif"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ctrl + b&lt;/code&gt; : 光标向后移动一个字符&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ctrl + f&lt;/code&gt; : 光标向前移动一个字符&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-16-xiang-yao-ti-gao-ming-ling-xing-bian-xie-xiao-l-kuai-lai-shi/003-20ef4005.gif"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;alt + b&lt;/code&gt; : 光标向后移动一个单词&lt;/li&gt;
&lt;li&gt;&lt;code&gt;alt + f&lt;/code&gt; : 光标向前移动一个单词&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-16-xiang-yao-ti-gao-ming-ling-xing-bian-xie-xiao-l-kuai-lai-shi/004-5c92019b.gif"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="删除"&gt;&lt;a href="#%e5%88%a0%e9%99%a4" class="header-anchor"&gt;&lt;/a&gt;删除
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ctrl + u&lt;/code&gt; : 清除光标前的内容&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ctrl + k&lt;/code&gt; : 清除光标后的内容&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="历史命令"&gt;&lt;a href="#%e5%8e%86%e5%8f%b2%e5%91%bd%e4%bb%a4" class="header-anchor"&gt;&lt;/a&gt;历史命令
&lt;/h2&gt;&lt;p&gt;只推荐一个：&lt;code&gt;ctrl + r&lt;/code&gt; : 不但可以查找历史命令，还可以按字符串寻找历史命令&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-16-xiang-yao-ti-gao-ming-ling-xing-bian-xie-xiao-l-kuai-lai-shi/005-efaaa431.gif"&gt;&lt;/p&gt;
&lt;p&gt;此外，历史命令查询还有一个更牛的命令行工具 &lt;strong&gt;fzf&lt;/strong&gt; (&lt;a class="link" href="https://github.com/junegunn/fzf" target="_blank" rel="noopener"
 &gt;https://github.com/junegunn/fzf&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-16-xiang-yao-ti-gao-ming-ling-xing-bian-xie-xiao-l-kuai-lai-shi/006-02af21ad.png"&gt;&lt;/p&gt;
&lt;p&gt;它是一个交互式过滤程序，可过滤文件、命令历史记录、进程、主机名、书签、git 提交等类型&lt;/p&gt;
&lt;p&gt;上文中关于 &lt;code&gt;ctrl + r&lt;/code&gt; 的 演示如果你在你的机器上尝试效果不一样，其实就是因为安装了 &lt;code&gt;fzf&lt;/code&gt; ，是 &lt;code&gt;fzf&lt;/code&gt; 带给我了更友好的交互体验。&lt;/p&gt;
&lt;p&gt;许多高手都在用，有许多集成和增强的历史命令查询功能，非常牛！强烈推荐！&lt;/p&gt;</description></item><item><title>HTML 中的 &lt;template&gt; 元素：提升动态网页的最佳工具</title><link>https://xiaobox.github.io/p/2024-08-10-html-zhong-de-template-yuan-su-ti-sheng-dong-tai-wang-ye-de/</link><pubDate>Sat, 10 Aug 2024 05:18:45 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-08-10-html-zhong-de-template-yuan-su-ti-sheng-dong-tai-wang-ye-de/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-08-10-html-zhong-de-template-yuan-su-ti-sheng-dong-tai-wang-ye-de-/cover.jpg" alt="Featured image of post HTML 中的 &lt;template&gt; 元素：提升动态网页的最佳工具" /&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-08-10-html-zhong-de-template-yuan-su-ti-sheng-dong-tai-wang-ye-de-/001-8c0b4ed5.png"&gt;&lt;/p&gt;
&lt;p&gt;在现代网页开发中，动态内容的生成和管理是一个重要的方面。为了简化这一过程，HTML5 引入了一个非常实用的元素——&lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt;。它为我们提供了一种灵活且高效的方式来管理和插入复杂的 HTML 结构。&lt;/p&gt;
&lt;h2 id="什么是-template-元素"&gt;&lt;a href="#%e4%bb%80%e4%b9%88%e6%98%af-template-%e5%85%83%e7%b4%a0" class="header-anchor"&gt;&lt;/a&gt;什么是 &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; 元素？
&lt;/h2&gt;&lt;p&gt;简单来说，&lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; 元素是用来存储 HTML 片段的，这些片段在初始加载时并不呈现。它们仅在需要时才会被克隆并插入到 DOM 中。与普通隐藏元素不同，&lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; 元素的内容是惰性的，这意味着其中的图片不会加载，脚本不会执行，样式也不会应用。&lt;/p&gt;
&lt;h3 id="为什么使用-template-元素"&gt;&lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e4%bd%bf%e7%94%a8-template-%e5%85%83%e7%b4%a0" class="header-anchor"&gt;&lt;/a&gt;为什么使用 &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; 元素？
&lt;/h3&gt;&lt;p&gt;在许多情况下，我们需要在网页加载后动态地添加复杂的 HTML 结构。如果完全依赖 JavaScript 来创建这些结构，可能会导致代码繁琐、复杂，尤其是当结构包含多个嵌套元素和属性时。&lt;/p&gt;
&lt;p&gt;以下是一个使用 &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; 元素的简单示例：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;template&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;burger-template&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;button&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;aria-expanded&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;false&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Menu&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;aria-controls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;mainnav&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;svg&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;24&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;24&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;aria-hidden&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt; &lt;span class="na"&gt;d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;template&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这个例子中，我们定义了一个存储按钮的模板。这个按钮只有在 JavaScript 运行时才会被插入到页面中，从而保持页面的语义分离和代码的简洁性。&lt;/p&gt;
&lt;h2 id="如何使用-template-元素"&gt;&lt;a href="#%e5%a6%82%e4%bd%95%e4%bd%bf%e7%94%a8-template-%e5%85%83%e7%b4%a0" class="header-anchor"&gt;&lt;/a&gt;如何使用 &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; 元素？
&lt;/h2&gt;&lt;p&gt;使用 &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; 元素非常简单。首先，我们在 HTML 中定义一个模板元素，并赋予它一个唯一的 ID。然后，在 JavaScript 中，我们可以通过以下步骤来克隆和插入模板内容：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#burger-template&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cloneNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;通过这种方式，我们可以将模板的内容克隆到指定的容器中。值得注意的是，这一过程不仅限于单次使用。您可以根据需要多次克隆和插入模板内容。&lt;/p&gt;
&lt;h3 id="实际应用中的例子"&gt;&lt;a href="#%e5%ae%9e%e9%99%85%e5%ba%94%e7%94%a8%e4%b8%ad%e7%9a%84%e4%be%8b%e5%ad%90" class="header-anchor"&gt;&lt;/a&gt;实际应用中的例子
&lt;/h3&gt;&lt;p&gt;在实际开发中，&lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; 元素被广泛用于创建动态列表、表格行、交互式组件等。例如，在 Sass Guidelines 网站中，使用模板来动态注入 GitHub 链接，以便用户可以直接编辑视图或每个章节。这些链接原本应该始终存在，但由于该网站是由 Markdown 文件构建的，因此需要通过 JavaScript 动态生成。&lt;/p&gt;
&lt;h2 id="浏览器支持情况"&gt;&lt;a href="#%e6%b5%8f%e8%a7%88%e5%99%a8%e6%94%af%e6%8c%81%e6%83%85%e5%86%b5" class="header-anchor"&gt;&lt;/a&gt;浏览器支持情况
&lt;/h2&gt;&lt;p&gt;令人惊讶的是，几乎 98% 的现代浏览器都支持 &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; 元素。因此，在大多数情况下，您可以放心使用这一功能。如果您需要兼容旧版浏览器，可以通过以下方式检测支持情况：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;if (&amp;#39;content&amp;#39; in document.createElement(&amp;#39;template&amp;#39;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; // `&amp;lt;template&amp;gt;` 受支持。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="为什么不使用隐藏元素"&gt;&lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e4%b8%8d%e4%bd%bf%e7%94%a8%e9%9a%90%e8%97%8f%e5%85%83%e7%b4%a0" class="header-anchor"&gt;&lt;/a&gt;为什么不使用隐藏元素？
&lt;/h2&gt;&lt;p&gt;有读者可能会问，为什么不直接使用隐藏的 DOM 元素，例如 &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;，来存储模板内容呢？&lt;/p&gt;
&lt;p&gt;虽然这两者在某种程度上看似相似，但使用 &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; 元素有几个显著优势：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;惰性加载&lt;/strong&gt;：与隐藏的容器不同，&lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; 的内容不会自动加载。这意味着其中的图片和脚本不会被执行，从而提高了页面性能。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;灵活性&lt;/strong&gt;：&lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; 可以包含任何 HTML 结构，而无需考虑 HTML 验证器的限制。例如，您可以在 &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; 中包含 &lt;code&gt;&amp;lt;td&amp;gt;&lt;/code&gt;、&lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; 或 &lt;code&gt;&amp;lt;dd&amp;gt;&lt;/code&gt; 等元素，而这些元素通常要求特定的父元素。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;语义性&lt;/strong&gt;：&lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; 是专门为存储模板设计的，因此它在语义上更为清晰，尤其是对于第三方工具和扩展来说。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;安全性&lt;/strong&gt;：使用 CSS 隐藏元素并不可靠，因为 CSS 可能被禁用或覆盖。而 &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; 元素总是默认隐藏，即使在没有 CSS 的情况下也能保持其不可见性。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;SEO 考虑&lt;/strong&gt;：搜索引擎可能会索引通过 CSS 隐藏的内容，这可能导致不必要的模板数据被索引。而使用 &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; 则无需担心这一问题。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h2&gt;&lt;p&gt;总的来说，HTML 的 &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; 元素为开发者提供了一种高效管理动态内容的方法。对于简单的节点操作，我们可以使用内置的 DOM 操作方法，但对于更复杂的结构，使用 &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; 无疑是更好的选择。&lt;/p&gt;
&lt;p&gt;希望本文能帮助您更好地理解和应用 &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; 元素。如果您有任何问题或建议，欢迎在评论区与我们分享！&lt;/p&gt;</description></item><item><title>无损图像格式比较：PNG、WebP、AVIF 和 JPEG XL</title><link>https://xiaobox.github.io/p/2024-07-22-wu-sun-tu-xiang-ge-shi-bi-jiao-png-webp-avif-he-jpeg-xl/</link><pubDate>Mon, 22 Jul 2024 06:17:58 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-07-22-wu-sun-tu-xiang-ge-shi-bi-jiao-png-webp-avif-he-jpeg-xl/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-07-22-wu-sun-tu-xiang-ge-shi-bi-jiao-png-webp-avif-he-jpeg-xl/cover.jpg" alt="Featured image of post 无损图像格式比较：PNG、WebP、AVIF 和 JPEG XL" /&gt;&lt;p&gt;在数字时代，图像质量和压缩效率成为了关键问题。本文对比了四种主流的无损图像格式：&lt;code&gt;PNG&lt;/code&gt;、&lt;code&gt;WebP&lt;/code&gt;、&lt;code&gt;AVIF&lt;/code&gt; 和 &lt;code&gt;JPEG XL&lt;/code&gt;，探讨它们在压缩效率、编码速度和使用场景等方面的优劣。&lt;/p&gt;
&lt;h2 id="什么是无损压缩"&gt;&lt;a href="#%e4%bb%80%e4%b9%88%e6%98%af%e6%97%a0%e6%8d%9f%e5%8e%8b%e7%bc%a9" class="header-anchor"&gt;&lt;/a&gt;什么是无损压缩？
&lt;/h2&gt;&lt;p&gt;无损压缩是一种在不损失任何数据的情况下减少文件大小的方法。常见的无损压缩格式包括 &lt;code&gt;ZIP&lt;/code&gt; 和 &lt;code&gt;RAR&lt;/code&gt;。在 Web 环境中，&lt;code&gt;GZIP&lt;/code&gt; 压缩通常用于缩小 JavaScript 和 CSS 文件。在图像压缩领域，&lt;code&gt;PNG&lt;/code&gt; 是一种广为人知的无损格式。&lt;/p&gt;
&lt;p&gt;无损压缩的替代方法是有损压缩，在压缩照片时经常使用。&lt;code&gt;JPEG&lt;/code&gt; 可能是最著名的有损图像格式。有损压缩会丢弃图像中的一些细节，从而导致文件大小显著减小。由于大照片太大而无法传输和存储，因此损失一些质量通常是一个很好的权衡。&lt;/p&gt;
&lt;h2 id="何时使用无损压缩"&gt;&lt;a href="#%e4%bd%95%e6%97%b6%e4%bd%bf%e7%94%a8%e6%97%a0%e6%8d%9f%e5%8e%8b%e7%bc%a9" class="header-anchor"&gt;&lt;/a&gt;何时使用无损压缩？
&lt;/h2&gt;&lt;p&gt;虽然像 &lt;code&gt;JPEG&lt;/code&gt; 和 &lt;code&gt;WebP&lt;/code&gt; 这样的有损图像格式对照片来说很好，但对图形来说就不那么好了。压缩失真（如块效应和模糊）在这类图像上很容易看出来。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-07-22-wu-sun-tu-xiang-ge-shi-bi-jiao-png-webp-avif-he-jpeg-xl/001-99b3c9f2.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-07-22-wu-sun-tu-xiang-ge-shi-bi-jiao-png-webp-avif-he-jpeg-xl/002-e1aa8f5a.png"&gt;&lt;/p&gt;
&lt;p&gt;无损图像压缩在处理包含大量连续颜色区域的图像时效果最佳，比如标志、截图、图表和图形。这类图像用无损压缩算法压缩往往能得到更小的文件大小，同时保持高质量。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-07-22-wu-sun-tu-xiang-ge-shi-bi-jiao-png-webp-avif-he-jpeg-xl/003-87fbf56a.png"&gt;&lt;/p&gt;
&lt;p&gt;无损压缩效果良好的图像示例。&lt;code&gt;PNG&lt;/code&gt; 形式的回收符号占用 3 KB。相同文件大小的 &lt;code&gt;JPEG&lt;/code&gt; 具有明显的压缩失真。具有最高质量的 &lt;code&gt;JPEG&lt;/code&gt; 看起来与 &lt;code&gt;PNG&lt;/code&gt; 相同，但大小是 &lt;code&gt;PNG&lt;/code&gt; 的八倍。&lt;/p&gt;
&lt;h3 id="png"&gt;&lt;a href="#png" class="header-anchor"&gt;&lt;/a&gt;PNG
&lt;/h3&gt;&lt;p&gt;PNG （Portable Network Graphics） 即便携式网络图形。&lt;/p&gt;
&lt;p&gt;是一种老牌的无损图像格式，于1996年首次发布。它作为 GIF 格式的替代品出现，具有许多优势，如支持 24 位颜色和透明通道。PNG 使用 DEFLATE 压缩算法，并支持多种压缩优化工具，如 PNGOUT、OptiPNG 和 OxiPNG。&lt;/p&gt;
&lt;p&gt;所有浏览器都支持 PNG 格式。&lt;/p&gt;
&lt;h3 id="webp"&gt;&lt;a href="#webp" class="header-anchor"&gt;&lt;/a&gt;WebP
&lt;/h3&gt;&lt;p&gt;Google 于 2010 年推出了基于 VP8 视频编解码器的 WebP 作为有损图像格式。2012年发布的WebP 0.3引入了无损模式，与VP8编解码器无关。有损 WebP 仅限于 4:2:0 色度子采样，会丢弃一些颜色信息，而无损 WebP 将保留所有原始图像数据。&lt;/p&gt;
&lt;p&gt;直到 2020 年，Apple 的 Safari 浏览器还是唯一抵制 WebP 的浏览器。不过到了 2021 年，所有主要浏览器均支持 WebP。&lt;/p&gt;
&lt;h3 id="avif"&gt;&lt;a href="#avif" class="header-anchor"&gt;&lt;/a&gt;AVIF
&lt;/h3&gt;&lt;p&gt;AVIF 代表 AV1 图像文件格式。它是一种新的图像格式，基于 AV1 视频编解码器。它具有许多高级功能，例如支持高位深度和 HDR。该格式支持无损和有损压缩。&lt;/p&gt;
&lt;p&gt;最新版本的 Google Chrome 支持 AVIF，并且可以通过使用配置标志在 Firefox 中启用。&lt;/p&gt;
&lt;h3 id="jpeg-xl-jpegxl"&gt;&lt;a href="#jpeg-xl-jpegxl" class="header-anchor"&gt;&lt;/a&gt;JPEG XL JPEGXL
&lt;/h3&gt;&lt;p&gt;JPEG XL 是一种即将推出的新图像格式。JPEG XL 是通过结合两种现有的图像格式而开发的，即 Google 开发的 Pik 图像格式和 Cloudinary 开发的 FUIF（免费通用图像格式）。&lt;/p&gt;
&lt;p&gt;Chrome 和 Firefox 支持 JPEG XL，但默认情况下不启用。必须使用功能标志来启用对该格式的支持。&lt;/p&gt;
&lt;h2 id="对比结果"&gt;&lt;a href="#%e5%af%b9%e6%af%94%e7%bb%93%e6%9e%9c" class="header-anchor"&gt;&lt;/a&gt;对比结果
&lt;/h2&gt;&lt;h3 id="文件大小"&gt;&lt;a href="#%e6%96%87%e4%bb%b6%e5%a4%a7%e5%b0%8f" class="header-anchor"&gt;&lt;/a&gt;文件大小
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-07-22-wu-sun-tu-xiang-ge-shi-bi-jiao-png-webp-avif-he-jpeg-xl/004-ae0818a6.png"&gt;&lt;/p&gt;
&lt;h3 id="编码速度"&gt;&lt;a href="#%e7%bc%96%e7%a0%81%e9%80%9f%e5%ba%a6" class="header-anchor"&gt;&lt;/a&gt;编码速度
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-07-22-wu-sun-tu-xiang-ge-shi-bi-jiao-png-webp-avif-he-jpeg-xl/005-257a5462.png"&gt;&lt;/p&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h2&gt;&lt;p&gt;从测试结果来看，与最优化的 PNG 相比，大多数现代无损图像格式（例如 WebP 和 JPEG XL）在效率上都有很大提高。&lt;/p&gt;
&lt;h3 id="png-1"&gt;&lt;a href="#png-1" class="header-anchor"&gt;&lt;/a&gt;png
&lt;/h3&gt;&lt;p&gt;使用 OxiPNG 优化 PNG 可以使它们稍微小一些，大约 12%，这并不是一个很大的差异。OxiPNG 速度非常快，处理一张图像只需大约 700 毫秒。&lt;/p&gt;
&lt;p&gt;将 Zopfli 设置与 OxiPNG 结合使用会使优化速度极其缓慢，平均需要大约 208 秒或三分半钟。与原始 PNG 相比，生成的文件大约小 18%。我不建议使用 Zopfli 压缩，因为它需要花费大量时间并且只能提供稍小的文件。WebP 或 JPEG XL 等较新的格式生成的文件要小得多，而且编码时间只是 Zopfli 的一小部分。&lt;/p&gt;
&lt;p&gt;由于 PNG 优化收益如此之小，并且该过程需要如此多的时间，因此如果能够使用更现代的格式，那么优化 PNG 文件可能根本不值得。即使未经优化，PNG 仍然是源图像格式的不错选择，因为它与图像编辑器和内容管理系统等大多数软件兼容。&lt;/p&gt;
&lt;p&gt;你可以将原始 PNG 文件转换为更有效的格式，然后再向最终用户显示。对于无法显示现代格式的客户端（例如某些电子邮件客户端和旧浏览器），PNG 也可以是一种有用的后备格式。&lt;/p&gt;
&lt;h3 id="webp-1"&gt;&lt;a href="#webp-1" class="header-anchor"&gt;&lt;/a&gt;webp
&lt;/h3&gt;&lt;p&gt;WebP 是无损图像的不错选择，因为它在压缩效率方面轻松胜过 PNG，平均图像小 41%。它也得到网络浏览器和其他软件的广泛支持。WebP 文件的编码速度也很快，压缩仅需约 3 秒。&lt;/p&gt;
&lt;h3 id="avif-1"&gt;&lt;a href="#avif-1" class="header-anchor"&gt;&lt;/a&gt;AVIF
&lt;/h3&gt;&lt;p&gt;不得不说，我对 AVIF 的表现有点失望。虽然有损 AVIF 确实需要很长时间来压缩，但与 JPEG 和 WebP 相比，它具有出色的压缩效果。不幸的是，无损 AVIF 却并非如此。&lt;/p&gt;
&lt;p&gt;在无损模式下，编码 AVIF 文件平均需要 30 秒，但与其他竞争格式相比，结果并不好。平均减少约 20%，生成的文件大小与使用 Zopfli 的 OxiPNG 相当，但与 WebP 或 JPEG XL 相比明显更大。使用 AVIF 时，我会坚持使用该格式效果最好的有损压缩。&lt;/p&gt;
&lt;h3 id="jpeg-xl"&gt;&lt;a href="#jpeg-xl" class="header-anchor"&gt;&lt;/a&gt;JPEG XL
&lt;/h3&gt;&lt;p&gt;JPEG XL 是新近出现的格式，给人留下了深刻的印象。平均文件大小减少约 48%，略优于 WebP。从第 75 个百分位数来看，JPEG XL 比 WebP 具有优势，这意味着 JPEG XL 在处理难以压缩的复杂图像方面做得更好。&lt;/p&gt;
&lt;p&gt;然而这个兼容性吧。。。&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-07-22-wu-sun-tu-xiang-ge-shi-bi-jiao-png-webp-avif-he-jpeg-xl/006-aba5f756.png"&gt;&lt;/p&gt;</description></item><item><title>bash 为什么叫 bash？</title><link>https://xiaobox.github.io/p/2024-04-24-bash-wei-shen-me-jiao-bash/</link><pubDate>Wed, 24 Apr 2024 10:32:56 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-04-24-bash-wei-shen-me-jiao-bash/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-04-24-bash-wei-shen-me-jiao-bash/cover.jpg" alt="Featured image of post bash 为什么叫 bash？" /&gt;&lt;h2 id="回顾"&gt;&lt;a href="#%e5%9b%9e%e9%a1%be" class="header-anchor"&gt;&lt;/a&gt;回顾
&lt;/h2&gt;&lt;p&gt;一个常规的 &lt;code&gt;shell&lt;/code&gt; 脚本长这样：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-04-24-bash-wei-shen-me-jiao-bash/001-76e1618f.png"&gt;&lt;/p&gt;
&lt;p&gt;在 &lt;a class="link" href="http://mp.weixin.qq.com/s?__biz=MzI3Njk5ODg4OQ==&amp;amp;mid=2247488536&amp;amp;idx=1&amp;amp;sn=0fbb182b30454686b94f2a9d01142a18&amp;amp;chksm=eb6dab9edc1a2288eb629b1cd8ef088e3c8ac08f3af83a7297f4f057bb0c7c387c279cf65ab5&amp;amp;scene=21#wechat_redirect" target="_blank" rel="noopener"
 &gt;为什么 shell 脚本的开头要写 #!/bin/bash&lt;/a&gt; 这篇文章中我们介绍过第一行，即&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#!/bin/bash&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第一行的作用是：&lt;strong&gt;用于指定默认情况下运行指定脚本的解释器&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;#!&lt;/code&gt; 有个专有名词叫 “蛇棒”&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/bin/bash&lt;/code&gt; 自然就是指定的那个解释器&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;于是，接下来我有了今天的这个问题: &lt;strong&gt;bash 为什么叫 bash ?&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="bash-名字的由来"&gt;&lt;a href="#bash-%e5%90%8d%e5%ad%97%e7%9a%84%e7%94%b1%e6%9d%a5" class="header-anchor"&gt;&lt;/a&gt;bash 名字的由来
&lt;/h2&gt;&lt;h3 id="shell"&gt;&lt;a href="#shell" class="header-anchor"&gt;&lt;/a&gt;shell
&lt;/h3&gt;&lt;p&gt;我们知道无论是 &lt;code&gt;bash&lt;/code&gt; &lt;code&gt;zsh&lt;/code&gt; &lt;code&gt;sh&lt;/code&gt; 都是一种 &lt;code&gt;shell&lt;/code&gt; ,那就要先从 &lt;code&gt;shell&lt;/code&gt; 讲起了。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Shell&lt;/code&gt; 是一个用户与操作系统交互的界面。可以将其视为一个命令行解释器，它提供了用户输入命令的方式，并且可以将这些命令传递给操作系统进行执行。换句话说，它是用户和操作系统之间的桥梁。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-04-24-bash-wei-shen-me-jiao-bash/002-5c18756c.png"&gt;&lt;/p&gt;
&lt;p&gt;如图所示，作为一个 “壳” &lt;code&gt;shell&lt;/code&gt; 的命名还是挺生动的。&lt;/p&gt;
&lt;p&gt;你的电脑上其实也或多或少安装了一些 &lt;code&gt;shell&lt;/code&gt; ，比如我的 Mac 电脑上安装了这些：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-04-24-bash-wei-shen-me-jiao-bash/003-be030381.png"&gt;&lt;/p&gt;
&lt;p&gt;这么多 &lt;code&gt;shell&lt;/code&gt; ,肯定有一个是当前默认正在使用的，也就是说，当你开启一个新的终端会话时，系统将运行此 &lt;code&gt;shell&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;你可以如下图所示查询默认 &lt;code&gt;shell&lt;/code&gt; 是哪个：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-04-24-bash-wei-shen-me-jiao-bash/004-1395b94d.png"&gt;&lt;/p&gt;
&lt;h3 id="bash"&gt;&lt;a href="#bash" class="header-anchor"&gt;&lt;/a&gt;bash
&lt;/h3&gt;&lt;p&gt;介绍完了 &lt;code&gt;shell&lt;/code&gt; ，我们回到问题本身&lt;/p&gt;
&lt;p&gt;&lt;code&gt;bash&lt;/code&gt; 当然也是一种 &lt;code&gt;shell&lt;/code&gt; 程序，那它为什么叫 &lt;code&gt;bash&lt;/code&gt; 呢 ？百科是这样说的：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;Bash 是 Brian Fox 为 GNU 项目编写的一种 Unix shell 和命令语言，是 Bourne shell 的自由软件替代品。它的名称是 Bourne-Again SHell 的首字母缩写，与它所替代的 Bourne shell 的名字谐音。该 shell 于 1989 年首次发布，一直被用作大多数 Linux 发行版的默认登录 shell，也是 Linus Torvalds 将 GCC 移植到 Linux 的首批程序之一。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;解释一下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;是 Bourne-Again SHell 的首字母缩写&lt;/li&gt;
&lt;li&gt;是谐音，这里指的是 Bourne-Again 谐音 &lt;strong&gt;born again&lt;/strong&gt; ,即重生的意思，非常符合，因为 bash 是替代者，替代了 Bourne shell&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="bourne-shell"&gt;&lt;a href="#bourne-shell" class="header-anchor"&gt;&lt;/a&gt;Bourne Shell
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;Bourne Shell&lt;/code&gt; 又是什么呢？&lt;/p&gt;
&lt;p&gt;它属实是 “最熟悉的陌生人” ，说 &lt;code&gt;Bourne shell&lt;/code&gt; 你不知道 ，但要说 &lt;code&gt;sh&lt;/code&gt; 你可能就明白了&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-04-24-bash-wei-shen-me-jiao-bash/005-456f21be.png"&gt;&lt;/p&gt;
&lt;p&gt;对，就是它&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;Bourne shell 由 Stephen Bourne 在贝尔实验室开发，它是 Thompson shell 的替代品。它于 1979 年在第 7 版 Unix 中发布，分发给高校使用。Bourne shell (sh) 是计算机操作系统的 shell 命令行解释器。Bourne shell 是第 7 版 Unix 的默认 shell。即使大多数用户使用其他 shell，类 Unix 系统仍会继续使用 /bin/sh，即 Bourne shell。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;虽然 &lt;code&gt;Bourne shell&lt;/code&gt; 属于 “老古董” 且被 &lt;code&gt;bash&lt;/code&gt; 替代了，但并不表示它就没有用了。&lt;/p&gt;
&lt;p&gt;在大部分的 Linux 系统中，例如 Debian/Ubuntu、Centos/RHEL 和 Fedora 等， 默认的 Shell 是 Bourne Again Shell (bash)。Bash 是 sh 的超集，包含了 sh 的所有特性，并增加了其他一些改进和新特性，但是也有一些系统使用的默认 Shell 不是 bash，如 Solaris 和 FreeBSD，默认的 Shell 是 Bourne Shell(sh)。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意：虽然在一些系统中默认 Shell 是 sh，但 sh 很可能仅仅是指向 bash 或其他 Shell 的一个链接，具体可以通过查看 /bin/sh 是连接到的哪个实际的 Shell 程序来确定。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;通常在 Linux 系统中运行 &lt;code&gt;ls -l /bin/sh&lt;/code&gt; 命令可以查看 sh 实际指向的是哪个 Shell。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;无论默认的 Shell 是什么，在大部分的 Linux 系统中，用户都可以自由选择使用哪个 Shell。这可以通过修改用户的默认 Shell 设置来实现，相关命令是 &lt;code&gt;chsh -s&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h2&gt;&lt;p&gt;至此我们清楚了 &lt;code&gt;bash&lt;/code&gt; 为什么叫 &lt;code&gt;bash&lt;/code&gt; 同时也回顾了一下相关的历史,以后再看到如 &lt;code&gt;bash&lt;/code&gt;、&lt;code&gt;sh&lt;/code&gt; 这些 shell 感觉又多了一份亲切感，不再是陌生和冰冷的技术字眼 😁&lt;/p&gt;
&lt;p&gt;哦，BTW,我本地一直用的是&lt;code&gt;zsh&lt;/code&gt; 但服务器上更多的是 &lt;code&gt;bash&lt;/code&gt; 你呢? 请在评论区告诉我。&lt;/p&gt;</description></item><item><title>git flow</title><link>https://xiaobox.github.io/p/2024-03-22-git-flow/</link><pubDate>Fri, 22 Mar 2024 08:52:42 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-03-22-git-flow/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-03-22-git-flow/cover.jpg" alt="Featured image of post git flow" /&gt;&lt;h2 id="一--什么是-git-flow"&gt;&lt;a href="#%e4%b8%80--%e4%bb%80%e4%b9%88%e6%98%af-git-flow" class="header-anchor"&gt;&lt;/a&gt;一 什么是 git flow
&lt;/h2&gt;&lt;p&gt;就像代码需要代码规范一样，代码管理同样需要一个清晰的流程和规范。Git 作为一个源码管理系统，不可避免涉及到多人协作。协作必须有一个规范的工作流程，让大家有效地合作，使得项目井井有条地发展下去。&amp;ldquo;工作流程&amp;quot;在英语里，叫做&amp;quot;workflow&amp;quot;或者&amp;quot;flow&amp;rdquo;，原意是水流，比喻项目像水流那样，顺畅、自然地向前流动，不会发生冲击、对撞、甚至漩涡。&lt;/p&gt;
&lt;p&gt;Gitflow 工作流定义了一个围绕项目发布的严格分支模型。 下图能说明整个流程，该模式来自 Nvie&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-03-22-git-flow/001-f3b9593e.png"&gt;&lt;/p&gt;
&lt;p&gt;解释上图&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;master 默认分支，只能用来包括产品代码（线上生产环境代码），不许提交，只能合并。&lt;/li&gt;
&lt;li&gt;develop 最新的开发状态 ，是你进行任何新的开发的基础分支。当你开始一个新的功能分支时，它将是_开发_的基础。另外，该分支也汇集所有已经完成的功能，并等待被整合到 master 分支中。develop 上的代码总是从 feature 上合并过来的。不许提交，只能合并。&lt;/li&gt;
&lt;li&gt;master develop 这两个分支被称作为 长期分支。它们会存活在项目的整个生命周期中，而其他的分支，例如针对功能的分支，针对发行的分支，仅仅只是临时存在的&lt;/li&gt;
&lt;li&gt;feature 开发新功能的分支，基于 develop, 完成后 merge 回 develop&lt;/li&gt;
&lt;li&gt;release: 准备要发布版本的分支，用来修复 bug. 基于 develop, 完成后 merge 回 develop 和 master&lt;/li&gt;
&lt;li&gt;hotfix: 修复 master 上的问题，等不及 release 版本就必须马上上线。基于 master, 完成后 merge 回 master 和 develop&lt;/li&gt;
&lt;li&gt;hotfix 分支是基于 “master” 分支。 这也是和 release 分支最明显的区别，release 分支都是基于 “develop” 分支的&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="二-为什么要使用-git-flow"&gt;&lt;a href="#%e4%ba%8c-%e4%b8%ba%e4%bb%80%e4%b9%88%e8%a6%81%e4%bd%bf%e7%94%a8-git-flow" class="header-anchor"&gt;&lt;/a&gt;二 为什么要使用 git flow
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;虽然大多数团队已由 svn 切换到 git，但代码控制方式仍然是“集中式”的，没有发挥 git 的强大功能。&lt;/li&gt;
&lt;li&gt;在团队开发中使用版本控制系统时，商定一个统一的工作流程是至关重要的 ，git flow 有着清晰的流程和规范。&lt;/li&gt;
&lt;li&gt;简单并可定义适合各自团队的 flow&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="三-如何使用"&gt;&lt;a href="#%e4%b8%89-%e5%a6%82%e4%bd%95%e4%bd%bf%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;三 如何使用
&lt;/h2&gt;&lt;p&gt;初始分支，所有在 Master 分支上的 Commit 应该 Tag，目前只有 master 角色可提交，其它角色提交不了，被保护了&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-03-22-git-flow/002-b3cbcfda.png"&gt;&lt;/p&gt;
&lt;p&gt;Feature 分支 Feature 分支做完后，必须合并回 Develop 分支，合并完分支后一般会删点这个 Feature 分支，但是我们也可以保留&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-03-22-git-flow/003-0bb54033.png"&gt;&lt;/p&gt;
&lt;p&gt;Release 分支 Release 分支基于 Develop 分支创建，打完 Release 分之后，我们可以在这个 Release 分支上测试，修改 Bug 等。同时，其它开发人员可以基于开发新的 Feature （记住：一旦打了 Release 分支之后不要从 Develop 分支上合并新的改动到 Release 分支） 发布 Release 分支时，合并 Release 到 Master 和 Develop， 同时在 Master 分支上打个 Tag 记住 Release 版本号，然后可以删除 Release 分支了。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-03-22-git-flow/004-1486da11.png"&gt;&lt;/p&gt;
&lt;p&gt;维护分支 Hotfix hotfix 分支基于 Master 分支创建，开发完后需要合并回 Master 和 Develop 分支，同时在 Master 上打一个 tag&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-03-22-git-flow/005-c9370ce8.png"&gt;&lt;/p&gt;
&lt;h2 id="四-命令规范"&gt;&lt;a href="#%e5%9b%9b-%e5%91%bd%e4%bb%a4%e8%a7%84%e8%8c%83" class="header-anchor"&gt;&lt;/a&gt;四 命令规范
&lt;/h2&gt;&lt;p&gt;|
|
|
|&lt;/p&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;/td&gt;
 &lt;td&gt;规则&lt;/td&gt;
 &lt;td&gt;示例&lt;/td&gt;
 &lt;td&gt;说明&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;master&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;master&lt;/td&gt;
 &lt;td&gt;默认不可变&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;develop&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;develop&lt;/td&gt;
 &lt;td&gt;默认不可变&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;feature&lt;/td&gt;
 &lt;td&gt;feature_*&lt;/td&gt;
 &lt;td&gt;feature_order-detail&lt;/td&gt;
 &lt;td&gt;* 为功能简述，如多个单词，中间用横线连接&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;release&lt;/td&gt;
 &lt;td&gt;release_*.rc&lt;/td&gt;
 &lt;td&gt;release_1.0.1.rc&lt;/td&gt;
 &lt;td&gt;* 为版本号， 版本号规则如下所述&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;hotfix&lt;/td&gt;
 &lt;td&gt;hotfix_*&lt;/td&gt;
 &lt;td&gt;hotfix_1.0.1&lt;/td&gt;
 &lt;td&gt;* 为版本号， 版本号规则如下所述&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;tag&lt;/td&gt;
 &lt;td&gt;（对应项目名，全部大写拼写）_ tag _ V *&lt;/td&gt;
 &lt;td&gt;JARVISPMS_tag_V1.0.1&lt;/td&gt;
 &lt;td&gt;* 为版本号， 版本号规则如下所述&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;ol&gt;
&lt;li&gt;大版本号更新表示一次里程碑开发的完成，包含了若干个 feature 的实现。&lt;/li&gt;
&lt;li&gt;小版本号更新表示一个 feature 的完成。&lt;/li&gt;
&lt;li&gt;补丁号更新表示发布的 feature 不变，只是修改 bug&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;举例：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;某次发布是里程碑开发的结束，版本号为 1.0.0&lt;/li&gt;
&lt;li&gt;很快，上次发布的版本发现了 bug，紧急修复，再次发布，版本号为 1.0.1&lt;/li&gt;
&lt;li&gt;再次发现 bug，修复，重新发布，版本号为 1.0.2&lt;/li&gt;
&lt;li&gt;几个星期后，新增了几个功能，再次发布，版本号为 1.1.0&lt;/li&gt;
&lt;li&gt;几天后发现新增的功能有 bug，紧急修复，发布，版本号为 1.1.1&lt;/li&gt;
&lt;li&gt;再次新增功能发布，版本号为 1.2.0&lt;/li&gt;
&lt;li&gt;发现 bug，修复并发布，版本号为 1.2.1&lt;/li&gt;
&lt;li&gt;再次完成一次里程碑开发，发布，版本号为 2.0.0&lt;/li&gt;
&lt;li&gt;……以此类推&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="五-code-review"&gt;&lt;a href="#%e4%ba%94-code-review" class="header-anchor"&gt;&lt;/a&gt;五 code review
&lt;/h2&gt;&lt;p&gt;由于使用了 git flow ，可以将 code review 机制很好的结合起来，当我们合并代码的时候，是需要 MR（merge request）的，这时候需要向代码审核人提交你的合并请求。具体流程可参考 GitLab Flow 里的最后一部分。&lt;/p&gt;
&lt;h2 id="六-使用-git-flow-开发流程简述"&gt;&lt;a href="#%e5%85%ad-%e4%bd%bf%e7%94%a8-git-flow-%e5%bc%80%e5%8f%91%e6%b5%81%e7%a8%8b%e7%ae%80%e8%bf%b0" class="header-anchor"&gt;&lt;/a&gt;六 使用 git flow 开发流程简述
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;从 develop 打 feature 分支，确定 feature 分支负责人（今后这个分支分出去的其它分支都由他负责，他负责 MR 时的代码评审）。&lt;/li&gt;
&lt;li&gt;在 feature 分支上开发，如其它同事的其它 feature 分支合并回了 develop, 会要求通过大家。如需要（一般是公共影响部分）在你自己的 feature 分支上合并 develop 上的代码。&lt;/li&gt;
&lt;li&gt;开发完 feature 分支代码，提交合并请求，合并至 develop, 由代码评审人评审通过后合并。合并完成后，删除 feature 分支。&lt;/li&gt;
&lt;li&gt;提测前，从 develop 中打出 release 分支给测试使用，如测试通过不需要修改，则合并回 master 和 develop 并删除；如有问题，可以在 release 分支上修改，修改完毕后，再合并回 master,develop, 并删除 release 分支。&lt;/li&gt;
&lt;li&gt;上线前，在 maser 分支上打出 tag&lt;/li&gt;
&lt;li&gt;线上有 bug，从 master 打出 hotfix 分支修复，完成后，合并回 master 和 develop , 删除 hotfix 分支。&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>我有罪啊--用了这么多年 mac 不知道访达有标签页</title><link>https://xiaobox.github.io/p/2024-02-18-wo-you-zui-a-yong-le-zhe-me-duo-nian-mac-bu-zhi-dao-fang-da/</link><pubDate>Sun, 18 Feb 2024 02:30:58 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-02-18-wo-you-zui-a-yong-le-zhe-me-duo-nian-mac-bu-zhi-dao-fang-da/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-02-18-wo-you-zui-a-yong-le-zhe-me-duo-nian-mac-bu-zhi-dao-fang-da-/cover.jpg" alt="Featured image of post 我有罪啊--用了这么多年 mac 不知道访达有标签页" /&gt;&lt;p&gt;就像使用 Chrome、Safari 浏览器的标签页一样，Mac 电脑中的 Finder(访达) 应用也具有类似的功能。&lt;/p&gt;
&lt;p&gt;使用 Mac 电脑好多年了，一直以为 Finder（访达）没有标签页，还在双击打开文件夹，有时候打开的文件夹一多，一通乱找。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-02-18-wo-you-zui-a-yong-le-zhe-me-duo-nian-mac-bu-zhi-dao-fang-da-/001-14deabca.png"&gt;&lt;/p&gt;
&lt;p&gt;我有罪啊，这个功能已经开发出来 10 年了，2013 年的时候就有了，我居然不知道！！！！&lt;/p&gt;
&lt;p&gt;使用起来很简单，当你打开一个文件夹后，如果需要再打开另外一个文件夹，只需要在第二个文件夹打开时按住 &lt;strong&gt;command +双击&lt;/strong&gt; 就可以在一开始那个文件夹上打开标签页了。再也不用打开多个文件夹后，一通乱找了。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-02-18-wo-you-zui-a-yong-le-zhe-me-duo-nian-mac-bu-zhi-dao-fang-da-/002-e829d989.png"&gt;&lt;/p&gt;
&lt;p&gt;打开新标签页的快捷键是 &lt;strong&gt;command + t&lt;/strong&gt; ，跟 Chrome 打开新标签页的一样。&lt;/p&gt;
&lt;p&gt;如果你真的单独打开了好多文件夹，那么可以一下把它们全部合并在一个窗口中，Finder 提供了这个功能：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-02-18-wo-you-zui-a-yong-le-zhe-me-duo-nian-mac-bu-zhi-dao-fang-da-/003-73cccb79.png"&gt;&lt;/p&gt;
&lt;p&gt;每个选项卡的行为就像它自己的 Finder 窗口一样；你可以相应地调整每个选项卡的视图设置，&lt;strong&gt;因此一个选项卡可以显示图标视图，另一个选项卡可以显示列表视图。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;打开多个选项卡后，你可以通过将文件拖放到选项卡上来将文件从一个选项卡移动到另一个选项卡。该过程的行为就像将文件拖放到文件夹中一样。&lt;/p&gt;
&lt;p&gt;Finder 现在还提供全屏模式。因此，如果你有桌面恐惧症，你可以将 Finder 转移到一个充满选项卡的窗口中，并使用 Mission Control 移入和移出该窗口。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Mac 系统的隐藏功能是真多啊！&lt;/p&gt;
&lt;p&gt;我以前默认在新窗口中打开所有文件夹。真是旧习难改。当然，如果你使用了类似 Path Finder 这种解决方案，当我没说。。。&lt;/p&gt;
&lt;p&gt;我有罪，平常使用的时候不动脑子，人家都开发出来 10 年了。。。&lt;/p&gt;</description></item></channel></rss>