<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="pretty-atom-feed.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh">
  <title>bilipan&#39;s Weblog</title>
  <subtitle>记录有兴趣的内容</subtitle>
  <link href="https://bilipan.cc/feed/feed.xml" rel="self" />
  <link href="https://bilipan.cc/" />
  <updated>2025-12-04T00:00:00Z</updated>
  <id>https://bilipan.cc/</id>
  <author>
    <name>bilipan</name>
    <email>646740270@qq.com</email>
  </author>
  <entry>
    <title>编写 ESLint 插件（上）</title>
    <link href="https://bilipan.cc/blog/%E7%BC%96%E5%86%99eslint%E6%8F%92%E4%BB%B6%E6%A3%80%E9%AA%8Cdom%E5%B1%9E%E6%80%A7/" />
    <updated>2025-11-03T00:00:00Z</updated>
    <id>https://bilipan.cc/blog/%E7%BC%96%E5%86%99eslint%E6%8F%92%E4%BB%B6%E6%A3%80%E9%AA%8Cdom%E5%B1%9E%E6%80%A7/</id>
    <content type="html">&lt;h2 id=&quot;&quot;&gt;背景&lt;/h2&gt;
&lt;p&gt;最近在团队内落地统一规范时，需要保证“每个 SFC 模板至少包含一个 &lt;code&gt;data-xxx&lt;/code&gt; 或 &lt;code&gt;v-data-xxx&lt;/code&gt;”这类规范。研究了下，可选方案有三种：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;文档宣导，依赖自觉&lt;/li&gt;
&lt;li&gt;Code Review 阶段全局扫描&lt;/li&gt;
&lt;li&gt;在提交前通过自动化工具拦截&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;第三种方案接入成本低、反馈路径短，是目前工程化实践中的首选。实现手段通常有两种：编写构建插件（vite/webpack）或编写 ESLint 插件。后者配置统一、生态成熟，更利于长期维护，语义上也更符合 ESLint 场景。&lt;/p&gt;
&lt;h2 id=&quot;-2&quot;&gt;实现思路&lt;/h2&gt;
&lt;p&gt;（ESLint 插件的完整规范可参考&lt;a href=&quot;https://eslint.org/docs/latest/extend/plugins&quot;&gt;官方文档&lt;/a&gt;，本文仅聚焦关键步骤。）&lt;/p&gt;
&lt;p&gt;ESLint 的核心流程可简化为三步：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;解析器把源码生成 AST&lt;/li&gt;
&lt;li&gt;插件基于 AST 做规则校验&lt;/li&gt;
&lt;li&gt;若命中规则，则上报错误或警告&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;因此，开发插件的关键在于理解 AST 结构，并针对目标节点编写访问逻辑。&lt;/p&gt;
&lt;h3 id=&quot;vue&quot;&gt;Vue 文件解析&lt;/h3&gt;
&lt;p&gt;在 Vue 工程里，构建侧常用 &lt;code&gt;@vue/compiler-sfc&lt;/code&gt;。编写规则时，应改用 &lt;code&gt;vue-eslint-parser&lt;/code&gt;，它专门处理 &lt;code&gt;.vue&lt;/code&gt; 单文件，仅解析 &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; 区块并提供 &lt;code&gt;template visitor&lt;/code&gt;，可直接在插件中使用。&lt;/p&gt;
&lt;h3 id=&quot;-3&quot;&gt;规则实现示例&lt;/h3&gt;
&lt;p&gt;需求：模板中至少有一个 DOM 元素或自定义组件带 &lt;code&gt;data-xxx&lt;/code&gt; 属性或 &lt;code&gt;v-data-xxx&lt;/code&gt; 指令。&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;problem&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;docs&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Enforce at least one data-xxx or v-data-xxx attribute in template&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Best Practices&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;recommended&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;fixable&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;/*
       * 只能匹配“指令”属性（v-data-xxx），若要同时检测普通 DOM 属性（如 data-xxx），需再写一条非指令属性选择器：
       * &#39;VAttribute[directive=false][key.name^=&quot;data-&quot;]&#39;(node) { ... }
       * &#39;VAttribute[directive=true][key.name.name^=&quot;data-&quot;]&#39;(node) {...}
       * 改用 VElement 统一检查所有属性，无需区分指令与普通 DOM 属性
      */&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;VElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; hasDataAttr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;startTag&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;attributes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;attr&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; attr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;directive &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; attr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; attr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name
          &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;data-&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;hasDataAttr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            node&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Template must contain at least one data-xxx or v-data-xxx attribute&#39;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;-4&quot;&gt;用例测试&lt;/h3&gt;
&lt;p&gt;ESLint 提供 &lt;code&gt;RuleTester&lt;/code&gt; 工具，可在本地模拟完整校验流程：&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; RuleTester &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;eslint&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; rule &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./require-data-xxx-attributes&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; tester &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuleTester&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; require&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;vue-eslint-parser&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

tester&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;require-data-xxx-attributes&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rule&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;valid&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;div data-xxx&gt;&amp;lt;/div&gt;&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;div v-data-xxx&gt;&amp;lt;/div&gt;&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;custom data-xxx&gt;&amp;lt;/custom&gt;&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;div&gt;&amp;lt;/div&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Template must contain at least one data-xxx or v-data-xxx attribute&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;custom&gt;&amp;lt;/custom&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Template must contain at least one data-xxx or v-data-xxx attribute&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;-5&quot;&gt;发布与集成&lt;/h3&gt;
&lt;p&gt;插件开发完成后，发到 npm 即可。
另外，最好将 &lt;code&gt;eslint&lt;/code&gt; 声明为 &lt;code&gt;peerDependencies&lt;/code&gt;，毕竟eslint并不是插件的直接依赖。&lt;/p&gt;
&lt;h3 id=&quot;-6&quot;&gt;后续&lt;/h3&gt;
&lt;p&gt;理解 ESLint 工作原理后，插件本身的编码并不复杂。但在真实业务落地时，仍需解决「仅对 &lt;code&gt;.vue&lt;/code&gt; 生效」「组合多条规则」「提供统一配置入口」等细节。这些主题将在下篇展开。&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>超简单的主题切换技巧</title>
    <link href="https://bilipan.cc/blog/%E8%B6%85%E7%AE%80%E5%8D%95%E7%9A%84%E4%B8%BB%E9%A2%98%E5%88%87%E6%8D%A2%E6%8A%80%E5%B7%A7/" />
    <updated>2025-11-05T00:00:00Z</updated>
    <id>https://bilipan.cc/blog/%E8%B6%85%E7%AE%80%E5%8D%95%E7%9A%84%E4%B8%BB%E9%A2%98%E5%88%87%E6%8D%A2%E6%8A%80%E5%B7%A7/</id>
    <content type="html">&lt;p&gt;原先这个网站的代码高亮主题是默认暗色的，但是考虑到网站本身已经支持了跟随系统主题自动切换，所以为了保持统一，准备将代码高亮主题跟系统主题自动同步。&lt;/p&gt;
&lt;p&gt;一开始我想的还是传统的方式，比如通过添加类，或是在css中添加media查询来实现。后来让AI去实现这个功能时，发现了一个更简单的方式，直接在&lt;code&gt;&amp;lt;link /&amp;gt;&lt;/code&gt;标签中添加&lt;code&gt;media&lt;/code&gt;属性，利用&lt;code&gt;prefers-color-scheme&lt;/code&gt;媒体查询在不同的系统主题下加载不同的样式。&lt;/p&gt;
&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://unpkg.com/prismjs@1.29.0/themes/prism.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(prefers-color-scheme: light)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://unpkg.com/prismjs@1.29.0/themes/prism-okaidia.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(prefers-color-scheme: dark)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;分析了下这个方案，发现了一些有趣的细节。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;当页面为亮色主题时，会同时下载&lt;code&gt;prism.css&lt;/code&gt;和&lt;code&gt;prism-okaidia.css&lt;/code&gt;，但是没有命中的media（暗色主题）不会参与渲染（也就不会阻塞渲染）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;虽然两个主题文件都会下载，但是它们的优先级不一样，比如当页面为亮色主题时，&lt;code&gt;prism.css&lt;/code&gt;比&lt;code&gt;prism-okaidia.css&lt;/code&gt;的优先级高。
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://bilipan.cc/blog/%E8%B6%85%E7%AE%80%E5%8D%95%E7%9A%84%E4%B8%BB%E9%A2%98%E5%88%87%E6%8D%A2%E6%8A%80%E5%B7%A7/PgIqMhzoJI-1566.avif 1566w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://bilipan.cc/blog/%E8%B6%85%E7%AE%80%E5%8D%95%E7%9A%84%E4%B8%BB%E9%A2%98%E5%88%87%E6%8D%A2%E6%8A%80%E5%B7%A7/PgIqMhzoJI-1566.webp 1566w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://bilipan.cc/blog/%E8%B6%85%E7%AE%80%E5%8D%95%E7%9A%84%E4%B8%BB%E9%A2%98%E5%88%87%E6%8D%A2%E6%8A%80%E5%B7%A7/PgIqMhzoJI-1566.png&quot; alt=&quot;&quot; width=&quot;1566&quot; height=&quot;190&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果此时切换为系统或浏览器的主题为暗色，则&lt;code&gt;prism-okaidia.css&lt;/code&gt;会命中media，就直接渲染了。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果是刷新页面（比如当前系统主题为暗色），两个主题文件会重新下载，但是由于浏览器的缓存机制，所以一般情况下不会重新请求。此时，&lt;code&gt;prism-okaidia.css&lt;/code&gt;的优先级就比&lt;code&gt;prism.css&lt;/code&gt;高了。
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://bilipan.cc/blog/%E8%B6%85%E7%AE%80%E5%8D%95%E7%9A%84%E4%B8%BB%E9%A2%98%E5%88%87%E6%8D%A2%E6%8A%80%E5%B7%A7/KQOJffIhHr-1554.avif 1554w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://bilipan.cc/blog/%E8%B6%85%E7%AE%80%E5%8D%95%E7%9A%84%E4%B8%BB%E9%A2%98%E5%88%87%E6%8D%A2%E6%8A%80%E5%B7%A7/KQOJffIhHr-1554.webp 1554w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://bilipan.cc/blog/%E8%B6%85%E7%AE%80%E5%8D%95%E7%9A%84%E4%B8%BB%E9%A2%98%E5%88%87%E6%8D%A2%E6%8A%80%E5%B7%A7/KQOJffIhHr-1554.png&quot; alt=&quot;alt text&quot; width=&quot;1554&quot; height=&quot;184&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>我的 AI 当着面骂了我一顿</title>
    <link href="https://bilipan.cc/blog/%E5%88%A9%E7%94%A8AI%E6%B4%9E%E5%AF%9F%E7%9C%8B%E8%87%AA%E5%B7%B1/" />
    <updated>2025-11-13T00:00:00Z</updated>
    <id>https://bilipan.cc/blog/%E5%88%A9%E7%94%A8AI%E6%B4%9E%E5%AF%9F%E7%9C%8B%E8%87%AA%E5%B7%B1/</id>
    <content type="html">&lt;p&gt;&lt;strong&gt;那天早上，我照例送完孩子，在公园里溜达。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;阳光穿过树叶，斑驳的光影打在地上。我打开 flomo，想随便记点什么。&lt;/p&gt;
&lt;p&gt;然后我看到了它。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI 给我生成了一份&amp;quot;洞察报告&amp;quot;——说的全是我，但比我更了解我自己。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;&quot;&gt;原来我一直在骗自己&lt;/h3&gt;
&lt;p&gt;报告里说：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&amp;quot;从2018年那个未发布的应用开始，你就明白&#39;能跑就发&#39;的道理。但奇怪的是，7年过去了，你还在笔记里反复告诫自己——&#39;快速发布&#39;、&#39;别追求完美&#39;。&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;7年。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;同样的教训，我写了不知道多少遍。&lt;/p&gt;
&lt;p&gt;AI 毫不客气地戳穿：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&amp;quot;你的认知已经到位，但执行时总被&#39;还没准备好&#39;的心态羁绊。&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我盯着这句话看了很久。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;它说的对。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我知道该快速发布，但每次到临门一脚，我就会想：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;这个功能还没优化好...&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;界面可以再精致一点...&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;等我把这个技术栈研究透...&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;然后，项目就进了坟墓。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-2&quot;&gt;完美主义是个体面的借口&lt;/h3&gt;
&lt;p&gt;我以前以为这是&lt;strong&gt;对技术的执着&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;但 AI 说得更狠：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&amp;quot;你不断涌出的新想法和对新技术的探索，可能是一种逃避——用规划替代行动，用可能性取代交付成果。&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;操。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这话说得太准了。&lt;/p&gt;
&lt;p&gt;学新技术很爽，因为你在&amp;quot;进步&amp;quot;。
写计划很爽，因为你在&amp;quot;准备&amp;quot;。
但真正的发布？&lt;strong&gt;那意味着可能被评判、可能失败。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;所以我一直&amp;quot;准备&amp;quot;，从不&amp;quot;交付&amp;quot;。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-3&quot;&gt;代价不只是没发布的项目&lt;/h3&gt;
&lt;p&gt;AI 还指出了我更不想面对的东西：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&amp;quot;这种延迟发布的习惯，已经在生活的其他领域产生连锁反应。&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我开始回想。&lt;/p&gt;
&lt;p&gt;工作上，我越来越麻木——因为一直在为别人的项目赶 deadline，自己的却永远&amp;quot;明天再说&amp;quot;。&lt;/p&gt;
&lt;p&gt;家庭里，我愧疚陪伴时间太少——因为下班后的时间都被&amp;quot;还没准备好&amp;quot;的个人项目吞噬了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;最可怕的是——我开始怀疑这些时间投入的价值。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如果7年前那个应用发布了，现在会怎样？
如果每次&amp;quot;准备&amp;quot;的时间都用来迭代，我现在会有多少产品？&lt;/p&gt;
&lt;p&gt;这些问题，我不敢深想。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-4&quot;&gt;所以，怎么破？&lt;/h3&gt;
&lt;p&gt;好消息是，AI 也看到我的出路了。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&amp;quot;你早已总结出应对之策：熟悉的技术栈、可复用的模板、让 AI 处理杂事。&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;对，我知道怎么做。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;但知道和做到之间，差着一整个太平洋。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;真正的问题不是技术能力，而是我&lt;strong&gt;对&amp;quot;未完成&amp;quot;状态的容忍度&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;我要的不是更多准备，而是&lt;strong&gt;更短的交付周期&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-5&quot;&gt;我的新实验&lt;/h3&gt;
&lt;p&gt;读完那份 AI 报告，我做了一个决定：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;下一个想法，不管做成什么样，1周内必须发布。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;粗糙？没关系。
没人用？没关系。
被嘲笑？也没关系。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;发布本身，就是胜利。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;因为7年了，我总算明白：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;产品的成败与投入的工作量没有必然联系。
真正值得交付的，不是完美代码，而是为用户解决问题的能力。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-6&quot;&gt;给同样拖延的你&lt;/h3&gt;
&lt;p&gt;如果你也像我一样：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;笔记里写满了&amp;quot;道理&amp;quot;，生活里重复同样的错误&lt;/li&gt;
&lt;li&gt;永远在&amp;quot;准备&amp;quot;，从不&amp;quot;交付&amp;quot;&lt;/li&gt;
&lt;li&gt;用新想法逃避真正的行动&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;试试这个：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;找一个你信任的工具，让它&amp;quot;审视&amp;quot;你的记录。&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;p&gt;&lt;strong&gt;现在，去发布那个&amp;quot;还没准备好&amp;quot;的东西吧。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;它永远不会准备好的。&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>我不再吃三餐了</title>
    <link href="https://bilipan.cc/blog/%E8%BD%BB%E6%96%AD%E9%A3%9F/" />
    <updated>2025-12-04T00:00:00Z</updated>
    <id>https://bilipan.cc/blog/%E8%BD%BB%E6%96%AD%E9%A3%9F/</id>
    <content type="html">&lt;p&gt;不是因为穷，不是因为忙。&lt;/p&gt;
&lt;p&gt;是因为我终于理解了身体这台机器的真实运作逻辑。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;&quot;&gt;我们被&amp;quot;一日三餐&amp;quot;骗了太久&lt;/h3&gt;
&lt;p&gt;从小到大，我们被告知：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;早餐要吃好&lt;/li&gt;
&lt;li&gt;午餐要吃饱&lt;/li&gt;
&lt;li&gt;晚餐要吃少&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但没人告诉你：&lt;strong&gt;每一次进食，身体都在切换模式&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;你吃完早餐，身体进入「储能模式」——忙着把碳水变成糖原，存进肝脏。你刚吃完午餐，它还没处理完早餐。晚餐又来了。&lt;/p&gt;
&lt;p&gt;你的消化系统像个996的打工人，永远没有「大扫除」的时间。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-2&quot;&gt;脂肪燃烧的开关藏在哪里？&lt;/h3&gt;
&lt;p&gt;这里有个反直觉的事实：&lt;strong&gt;你的身体不愿意燃烧脂肪&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;脂肪是你祖先留给你的「末日储备粮」，只有糖原耗尽时，身体才会不情愿地分解脂肪，产生酮体供能。&lt;/p&gt;
&lt;p&gt;问题是——你从未给过它这个机会。&lt;/p&gt;
&lt;p&gt;三餐+零食，让你的血糖永远在波动，糖原永远有库存。&lt;strong&gt;脂肪？锁得死死的。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;14/10轻断食的核心就一句话：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;给身体14小时不进食的时间，让它被迫动用脂肪。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-3&quot;&gt;自噬：身体的深度清洁模式&lt;/h3&gt;
&lt;p&gt;还有一个更少人知道的好处：&lt;strong&gt;自噬&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;当你进食时，身体忙着消化，没空打扫卫生。断食10-14小时后，糖原耗尽，身体终于闲下来，开始「大扫除」——清理衰老细胞、修复受损组织、降低炎症。&lt;/p&gt;
&lt;p&gt;这不仅是减肥，这是&lt;strong&gt;细胞层面的抗衰&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-4&quot;&gt;但别做傻事&lt;/h3&gt;
&lt;p&gt;断食不是挨饿竞赛。&lt;/p&gt;
&lt;p&gt;时间太短（&amp;lt;10小时），糖原没耗尽，脂肪不燃烧。时间太长（&amp;gt;16小时），代谢可能变慢，还容易报复性暴食。&lt;/p&gt;
&lt;p&gt;14小时断食+10小时进食窗口，是&lt;strong&gt;可执行性和效果的平衡点&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-5&quot;&gt;我的做法&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;晚上8点后不吃东西&lt;/li&gt;
&lt;li&gt;早上10点后开始第一餐&lt;/li&gt;
&lt;li&gt;断食期间只喝水、黑咖啡&lt;/li&gt;
&lt;li&gt;进食窗口正常吃，但控制碳水和总热量&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不是节食，是&lt;strong&gt;重新安排进食时间&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-6&quot;&gt;最后&lt;/h3&gt;
&lt;p&gt;现代人最大的问题不是吃得太多，而是&lt;strong&gt;吃得太频繁&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;给你的消化系统放个假。让脂肪有机会离开你的身体。让细胞有机会自我修复。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;你不是在断食，你是在夺回身体的控制权。&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
</feed>