{
    "version": "https://jsonfeed.org/version/1",
    "title": "weapp-tailwindcss 博客订阅",
    "home_page_url": "https://next.tw.icebreaker.top/blog",
    "description": "tailwindcss 在小程序与多端开发中的更新、案例与教程资讯。",
    "items": [
        {
            "id": "https://next.tw.icebreaker.top/blog/2025/11/v4.7.0-merge-runtime",
            "content_html": "<p>大家好，我是 <a href=\"https://github.com/sonofmagic/weapp-tailwindcss\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">weapp-tailwindcss</a>、<a href=\"https://github.com/weapp-vite/weapp-vite\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">weapp-vite</a> 的作者 icebreaker。</p>\n<p>最近我一直在思考 <code>weapp-tailwindcss</code> 的未来，以至于都没有怎么玩，最近回归的星际争霸2。</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"巨大的阻碍\">巨大的阻碍<a href=\"https://next.tw.icebreaker.top/blog/2025/11/v4.7.0-merge-runtime#%E5%B7%A8%E5%A4%A7%E7%9A%84%E9%98%BB%E7%A2%8D\" class=\"hash-link\" aria-label=\"巨大的阻碍的直接链接\" title=\"巨大的阻碍的直接链接\" translate=\"no\">​</a></h2>\n<p>为什么？因为之前有一个很重要的问题，严重阻碍了 <code>weapp-tailwindcss</code> 发展的脚步。</p>\n<p>那就是 <code>tailwind-merge</code> / <code>class-variance-authority</code> / <code>tailwind-variants</code> 这些极其重要的原子化样式基础包，没有什么很好的办法在小程序里使用。</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"为什么无法在小程序里使用\">为什么无法在小程序里使用？<a href=\"https://next.tw.icebreaker.top/blog/2025/11/v4.7.0-merge-runtime#%E4%B8%BA%E4%BB%80%E4%B9%88%E6%97%A0%E6%B3%95%E5%9C%A8%E5%B0%8F%E7%A8%8B%E5%BA%8F%E9%87%8C%E4%BD%BF%E7%94%A8\" class=\"hash-link\" aria-label=\"为什么无法在小程序里使用？的直接链接\" title=\"为什么无法在小程序里使用？的直接链接\" translate=\"no\">​</a></h3>\n<p>简短一点来说，核心原因是，小程序 <code>wxml</code> 类名中，不允许很多特殊字符串，比如 <code>!</code>、<code>[</code>、<code>]</code>、<code>#</code> 等字符。</p>\n<p>所以 <code>weapp-tailwindcss</code> 根据这个设计，在<strong>编译时</strong>，就对 <code>tailwindcss</code> 类名进行转换，从而达到了兼容市面上众多小程序的编译插件。</p>\n<p>比如用户写的是 <code>bg-[#123456]</code>，被 <code>weapp-tailwindcss</code> 捕获到了之后，在<strong>编译</strong>的时候，就会同时把 <code>wxml</code>、<code>js</code>、<code>wxss</code> 里面的这个类名转换成小程序可以接受的 <code>bg-_h123456_</code>。</p>\n<p>而 <code>tailwind-merge</code> 它们都是在<strong>运行时</strong>进行计算的，那时候它们接收到的，已经是 <code>bg-_h123456_</code> 这种转译之后的字符串，自然合并不了，导致到处出错。</p>\n<p>为了兼容，我做了非常多的尝试！给大家展示一下我的受苦之路吧！</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"1-tailwind-merge-plugin--createtailwindmerge\">1. tailwind-merge plugin / createTailwindMerge<a href=\"https://next.tw.icebreaker.top/blog/2025/11/v4.7.0-merge-runtime#1-tailwind-merge-plugin--createtailwindmerge\" class=\"hash-link\" aria-label=\"1. tailwind-merge plugin / createTailwindMerge的直接链接\" title=\"1. tailwind-merge plugin / createTailwindMerge的直接链接\" translate=\"no\">​</a></h2>\n<p>最直观的念头，就是给 <code>tailwind-merge</code> 写一个 <code>weapp-tailwindcss</code> 专用插件就好了!</p>\n<p>于是我开始阅读 <code>tailwind-merge</code> 源代码，并尝试使用 <code>extendTailwindMerge</code> 和 <code>createTailwindMerge</code> 完全创建出一个属于我自己的 <code>weapp-tailwind-merge</code> 来。</p>\n<p>在尝试过程中，我把 <code>tailwind-merge</code> 的内部冲突表导出，尝试用自定义 <code>escape hook</code> 覆盖那些非法字符；甚至写了一个半成品的 <code>createTailwindMerge</code> 变体，希望能在编译阶段就生成完全符合小程序命名规则的类名。</p>\n<p>然而，现实很快给了我当头棒喝：<code>tailwind-merge</code> 对<strong>运行时</strong>字符串的依赖极强，部分字符是强依赖，根本无法替换。</p>\n<p>下面这几个字符串都是写在常量里的，无法通过配置更换</p>\n<div class=\"language-ts codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token keyword\" style=\"color:#C586C0\">export</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">const</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:#4FC1FF\">IMPORTANT_MODIFIER</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'!'</span><span class=\"token plain\"> </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// 小程序不行</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">const</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:#4FC1FF\">MODIFIER_SEPARATOR</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">':'</span><span class=\"token plain\"> </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// 小程序不行</span><br></div></code></pre></div></div>\n<p>详见 <a href=\"https://github.com/dcastil/tailwind-merge/blob/v3.3.1/src/lib/parse-class-name.ts%E3%80%82\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">https://github.com/dcastil/tailwind-merge/blob/v3.3.1/src/lib/parse-class-name.ts。</a></p>\n<p>所以这已经不是 <code>extendTailwindMerge</code> 和 <code>createTailwindMerge</code> 能够解决的问题了。</p>\n<p>摆在我面前的，是一条看不到未来的路：为了强行兼容，我需要重写它的核心，fork 一个全新的包，这个成本是巨大的。</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"2-编译期豁免\">2. 编译期豁免<a href=\"https://next.tw.icebreaker.top/blog/2025/11/v4.7.0-merge-runtime#2-%E7%BC%96%E8%AF%91%E6%9C%9F%E8%B1%81%E5%85%8D\" class=\"hash-link\" aria-label=\"2. 编译期豁免的直接链接\" title=\"2. 编译期豁免的直接链接\" translate=\"no\">​</a></h2>\n<p>第二条路看起来更务实：沿用我熟悉的编译期管线，给 <code>twMerge</code> / <code>twJoin</code> / <code>cva</code> 等函数做“豁免处理”。</p>\n<p>我当时是这样想的，只要在<strong>编译时</strong>忽略它们内部的转义，<strong>运行时</strong>拿到的就是完整的 class 字符串，那 tailwind-merge 不就能工作了吗？</p>\n<p>然后我再包装一下 <code>twMerge</code> 函数，让它获取最后的结果的时候 escape 不就行了吗？</p>\n<p>大概长这样：</p>\n<div class=\"language-ts codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token keyword\" style=\"color:#C586C0\">export</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">function</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">cn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token plain\">inputs</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> ClassValue</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:#C586C0\">const</span><span class=\"token plain\"> result </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">twMerge</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">inputs</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:#C586C0\">return</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">escape</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">result</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></div></code></pre></div></div>\n<p>然后我让 <code>cn</code> 里面的字面量和模板字符串跳过转义不就行了吗？</p>\n<div class=\"language-ts codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// 第一个是字符串，第二个是模板字符串，它们对应的 ast 类型不同，需要分开处理</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// 里面的不转译</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">cn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'bg-[#123456]'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">bg-[#987654]</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// 假如转译那么，结果如下</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// cn('bg-_h123456_',`bg-_h987654_`)</span><br></div></code></pre></div></div>\n<p>看上去运行良好，然而情况正在变得越来越复杂：</p>\n<p>嘿，变量引用来了：</p>\n<div class=\"language-ts codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token keyword\" style=\"color:#C586C0\">const</span><span class=\"token plain\"> a </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'bg-[#123456]'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">cn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">a</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"xx\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"yy\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><br></div></code></pre></div></div>\n<p>嘿嘿，变量引用 + 表达式来了：</p>\n<div class=\"language-ts codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token keyword\" style=\"color:#C586C0\">const</span><span class=\"token plain\"> a </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'bg-[#123456]'</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">+</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">' bb'</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">+</span><span class=\"token plain\"> </span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\"> text-[#123456]</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">cn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">a</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"xx\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"yy\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><br></div></code></pre></div></div>\n<p>嘿嘿嘿，变量引用链路 + 表达式 + 模板插值来了：</p>\n<div class=\"language-ts codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token keyword\" style=\"color:#C586C0\">const</span><span class=\"token plain\"> b </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'after:xx'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token keyword\" style=\"color:#C586C0\">const</span><span class=\"token plain\"> a </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'bg-[#123456]'</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">+</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">' bb'</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">+</span><span class=\"token plain\"> </span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">b</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\"> text-[#123456]</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">cn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">a</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"xx\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"yy\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><br></div></code></pre></div></div>\n<p>哈哈，只是在考验我操作 ast 进行预编译的水平而已！</p>\n<p>吃我一拳：<code>ASTNodePathWalker</code> + <code>scope.getBinding</code> + <code>WeakMap</code>，哈哈轻松消灭！</p>\n<p>于是我以为这条思路可行，编写了 <code>@weapp-tailwindcss/merge</code> 的 v1 版本。</p>\n<p>直到用户提交了新的 case！</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"新的挑战\">新的挑战<a href=\"https://next.tw.icebreaker.top/blog/2025/11/v4.7.0-merge-runtime#%E6%96%B0%E7%9A%84%E6%8C%91%E6%88%98\" class=\"hash-link\" aria-label=\"新的挑战的直接链接\" title=\"新的挑战的直接链接\" translate=\"no\">​</a></h3>\n<p>什么，怎么还有你们这种相互引用的情况！</p>\n<div class=\"language-js codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// shared2.js</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">export</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">const</span><span class=\"token plain\"> ddd </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'bg-[#123456]'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">const</span><span class=\"token plain\"> a </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'bg-[#123456]'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">export</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  a </span><span class=\"token keyword\" style=\"color:#C586C0\">as</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">default</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></div></code></pre></div></div>\n<div class=\"language-js codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// shared.js</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">export</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">const</span><span class=\"token plain\"> a </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'bg-[#123456]'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">const</span><span class=\"token plain\"> b </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'bg-[#123456]'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">const</span><span class=\"token plain\"> c </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'bg-[#123456]'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">const</span><span class=\"token plain\"> d </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'bg-[#123456]'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">export</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">default</span><span class=\"token plain\"> d</span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">export</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  b</span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">export</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  c </span><span class=\"token keyword\" style=\"color:#C586C0\">as</span><span class=\"token plain\"> xaxaxaxa</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">export</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">*</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'./shared2'</span><br></div></code></pre></div></div>\n<div class=\"language-js codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// main.js</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">import</span><span class=\"token plain\"> cc</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> a </span><span class=\"token keyword\" style=\"color:#C586C0\">as</span><span class=\"token plain\"> bb</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> b </span><span class=\"token keyword\" style=\"color:#C586C0\">as</span><span class=\"token plain\"> aa </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'./shared'</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">import</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">*</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">as</span><span class=\"token plain\"> shared </span><span class=\"token keyword\" style=\"color:#C586C0\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'./shared'</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">cn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">bb</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> cc</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> aa</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> shared</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">default</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> shared</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">a</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"[]\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"()\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><br></div></code></pre></div></div>\n<p>……我吐了，这是要我自己去实现一个 webpack / rollup 打包器嘛？有点搞不定啊！</p>\n<p>不过困难怕什么，我要迎难而上！于是我仿照了 <code>rollup</code> 的思路，收集了每个模块的 <code>import</code> / <code>export</code> 这里面大量的 ast 节点，并构建出了一个 <code>ModuleGraph</code>。</p>\n<p>另外表面上看这条路是可行的，我甚至找到了几个 <code>demo</code> 可以跑通，我还把豁免名单抽离出来，变成了 <code>ignoreCallExpressionIdentifiers</code> 配置项，以为自己解决了问题。</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"然而理想很丰满现实很骨感\">然而理想很丰满，现实很骨感<a href=\"https://next.tw.icebreaker.top/blog/2025/11/v4.7.0-merge-runtime#%E7%84%B6%E8%80%8C%E7%90%86%E6%83%B3%E5%BE%88%E4%B8%B0%E6%BB%A1%E7%8E%B0%E5%AE%9E%E5%BE%88%E9%AA%A8%E6%84%9F\" class=\"hash-link\" aria-label=\"然而理想很丰满，现实很骨感的直接链接\" title=\"然而理想很丰满，现实很骨感的直接链接\" translate=\"no\">​</a></h3>\n<p>这套方案高度依赖 AST 解析和构建工具的配合，我写的插件无法保证<strong>运行时</strong>得到的类名永远完整。构建链路上的任一环节——Terser、esbuild、rollup 插件甚至手写 Babel 宏——都可能把函数名或模板字符串的标识符压缩重命名，导致最后留给<strong>运行时</strong>的是一个残缺的字符串。</p>\n<p>用人话说就是 <code>cn</code> , <code>twMerge</code>, <code>tv</code> 这种方法，在产物里面被重命名成 <code>e</code>/<code>a</code>/<code>c</code> 这种玩意，所以我必须在压缩之前就进行豁免操作，但是那时候我似乎无法去准确收集产物的模块依赖情况(可能是水平不够导致的)。</p>\n<p>那一刻我意识到，所谓“编译期豁免”只是在延迟爆炸时间，而不是解除危机。</p>\n<p>在两条路都走到尽头之后，只剩下一个选择：彻底重构 merge，让逃逸逻辑回归<strong>运行时</strong>，让编译阶段恢复简单纯粹。</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"为什么要重写-merge\">为什么要重写 merge？<a href=\"https://next.tw.icebreaker.top/blog/2025/11/v4.7.0-merge-runtime#%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E9%87%8D%E5%86%99-merge\" class=\"hash-link\" aria-label=\"为什么要重写 merge？的直接链接\" title=\"为什么要重写 merge？的直接链接\" translate=\"no\">​</a></h2>\n<p>复盘 1.x 旧版 merge，我发现我当时的设计基于两个假设：一是 <code>tailwind-merge</code> 的输入输出始终可控，二是编译器可以精准标记所有“需要放行”的调用。这两个假设已经被现实击碎。</p>\n<p>早期的 <code>@weapp-tailwindcss/merge</code> 主要目标是“把 tailwind-merge 的结果变成小程序合法类名”。我采取的策略是：</p>\n<ul>\n<li class=\"\">继续使用 <code>tailwind-merge</code> 做冲突解析；</li>\n<li class=\"\">在编译阶段通过函数名黑名单 <code>ignoreCallExpressionIdentifiers</code> 跳过对 <code>twMerge</code> / <code>twJoin</code> / <code>cva</code> 等调用的转义；</li>\n<li class=\"\">把责任交给开发者：<strong>运行时</strong>得到的类名包含非法字符，需要手动再 escape。</li>\n</ul>\n<p>这种模式在 Tailwind CSS v3 勉强能用，但一到 v4 就崩溃了：</p>\n<ol>\n<li class=\"\"><strong>编译期豁免并不等于安全</strong><br>\n<code>twMerge('text-[#ececec]', 'text-(--my-custom-color)')</code> 最终仍然输出原始字符串。稍微复杂一点的条件拼接、链式调用、动态导入，编译器根本判断不出来该不该跳过。</li>\n<li class=\"\"><strong>函数名黑名单无法覆盖新的 API</strong><br>\n<!-- -->新版本开始导出 <code>create()</code>、variants（<code>tv</code>）等工厂，调用形式千奇百怪，编译阶段根本匹配不到。</li>\n<li class=\"\"><strong>任意值语法越来越灵活</strong><br>\n<!-- -->Tailwind v4 的任意值可以是 <code>text-[theme(my.scale.foo)]</code> 这种无法静态推断类型的写法。靠黑名单永远落后，反而让用户更困惑。</li>\n</ol>\n<h2 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"新版-merge-的核心思路\">新版 merge 的核心思路<a href=\"https://next.tw.icebreaker.top/blog/2025/11/v4.7.0-merge-runtime#%E6%96%B0%E7%89%88-merge-%E7%9A%84%E6%A0%B8%E5%BF%83%E6%80%9D%E8%B7%AF\" class=\"hash-link\" aria-label=\"新版 merge 的核心思路的直接链接\" title=\"新版 merge 的核心思路的直接链接\" translate=\"no\">​</a></h2>\n<p>决定“把锅背回<strong>运行时</strong>”以后，我做的第一件事就是把入口全部进行统一：<code>twMerge</code> / <code>twJoin</code> / <code>createTailwindMerge</code> / <code>extendTailwindMerge</code> / <code>cva</code> / <code>variants</code>……统统绑进同一套 transformer 里。</p>\n<p>思路很简单：先找出它们共有的“进场”和“退场”动作，再把逃逸拆成前后两个钩子， <code>escape</code> 和 <code>unescape</code>。</p>\n<div class=\"language-ts codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token keyword\" style=\"color:#C586C0\">const</span><span class=\"token plain\"> transformers </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">resolveTransformers</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">options</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">const</span><span class=\"token plain\"> aggregators </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  escape</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> transformers</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">escape</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  unescape</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> transformers</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">unescape</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></div></code></pre></div></div>\n<p>在实现里我刻意把 escape 和 unescape 拆成两个“齿轮”。不管是用户直接手点 <code>twMerge</code>，还是 variants 工厂兜一圈回来，都会先进统一的预处理，再丢给 tailwind-merge。</p>\n<p>这等于在<strong>运行时</strong>补了一层“语义编译器”。</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"双向处理链\">双向处理链<a href=\"https://next.tw.icebreaker.top/blog/2025/11/v4.7.0-merge-runtime#%E5%8F%8C%E5%90%91%E5%A4%84%E7%90%86%E9%93%BE\" class=\"hash-link\" aria-label=\"双向处理链的直接链接\" title=\"双向处理链的直接链接\" translate=\"no\">​</a></h3>\n<p>所以现在每次 merge 现在都得过一遍 <code>unescape -&gt; tailwind-merge -&gt; escape</code> 这样的流程：</p>\n<div class=\"language-ts codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">const</span><span class=\"token plain\"> normalized </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> transformers</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">unescape</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">clsx</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token plain\">inputs</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">return</span><span class=\"token plain\"> transformers</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">escape</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">fn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">normalized</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><br></div></code></pre></div></div>\n<p>但是这样还不够，为了实现 <code>escape</code> 和 <code>unescape</code> 我还必须从源头上出发，更改 <code>@weapp-core/escape</code> 的转译规则，才能让每一个字符串映射变得独一无二</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"重写-weapp-coreescape\">重写 @weapp-core/escape<a href=\"https://next.tw.icebreaker.top/blog/2025/11/v4.7.0-merge-runtime#%E9%87%8D%E5%86%99-weapp-coreescape\" class=\"hash-link\" aria-label=\"重写 @weapp-core/escape的直接链接\" title=\"重写 @weapp-core/escape的直接链接\" translate=\"no\">​</a></h3>\n<p>老 escape 工具一直挂在 <code>@weapp-core/escape</code> 上，它走的是“多对一”映射，贴一段旧代码大家感受一下：</p>\n<div class=\"language-ts codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token keyword\" style=\"color:#C586C0\">export</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">const</span><span class=\"token plain\"> MappingChars2String</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> MappingStringDictionary </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'['</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">']'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// for tailwindcss v4</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'('</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'y'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">')'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'y'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'{'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'z'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'}'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'z'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'+'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'a'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">','</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'b'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">':'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'c'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'.'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'d'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'='</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'e'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">';'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'f'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'&gt;'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'g'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'#'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'h'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'!'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'i'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'@'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'j'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'^'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'k'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'&lt;'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'l'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'*'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'m'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'&amp;'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'n'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'?'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'o'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'%'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'p'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'\\''</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'q'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'$'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'r'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'/'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'s'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'~'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'t'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'|'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'u'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'`'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'v'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'\\\\'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'w'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'\"'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'x'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></div></code></pre></div></div>\n<p>问题马上就来了：它完全做不到配对 <code>unescape</code>。<code>[</code> 和 <code>]</code> 被一起砸成 <code>_</code>，<code>(</code> / <code>)</code>、<code>{</code> / <code>}</code> 也全堆在同一个值上，<strong>运行时</strong>根本还原不回去。举个让人头疼的例子：<code>escape('[bg:red]') === '__bg_red_'</code>。</p>\n<p>所以我直接把 <code>@weapp-core/escape</code> 推倒重练，写成一个可逆的“状态机”。每个非法字符都分到独一无二的逃逸片段，还带长度前缀，跑完 <code>unescape(escape(input))</code> 就一定回到原样。为了防止它在极端输入上翻车，我拉了十几组 property-based 测试，emoji、空格、重复 escape 全安排上写了大量的单元测试，确保往返都符合预期。</p>\n<p>下面是当前版本的核心映射表，展示了我如何为每个非法字符分配唯一的 escape 片段，便于和旧版多对一的写法做对比：</p>\n<div class=\"language-ts codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token keyword\" style=\"color:#C586C0\">export</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">const</span><span class=\"token plain\"> MappingChars2String </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'['</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_b'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">']'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_B'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'('</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_p'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">')'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_P'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'#'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_h'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'!'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_e'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'/'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_f'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'\\\\'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_r'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'.'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_d'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">':'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_c'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'%'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_v'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">','</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_m'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'\\''</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_a'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'\"'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_q'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'*'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_x'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'&amp;'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_n'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'@'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_t'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'{'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_k'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'}'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_K'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'+'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_u'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">';'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_j'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'&lt;'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_l'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'~'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_w'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'='</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_z'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'&gt;'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_g'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'?'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_Q'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'^'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_y'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'`'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_i'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'|'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_o'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token string-property property\" style=\"color:#9CDCFE\">'$'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_s'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">as</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">const</span><br></div></code></pre></div></div>\n<p>文章里我只放这份“简化表”，因为它才是<strong>运行时</strong>默认用的版本，开发者平时看到的也是它。更复杂的兼容映射我留在文档和测试里。</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"运行时配置\"><strong>运行时</strong>配置<a href=\"https://next.tw.icebreaker.top/blog/2025/11/v4.7.0-merge-runtime#%E8%BF%90%E8%A1%8C%E6%97%B6%E9%85%8D%E7%BD%AE\" class=\"hash-link\" aria-label=\"运行时配置的直接链接\" title=\"运行时配置的直接链接\" translate=\"no\">​</a></h3>\n<p>新的 <code>create()</code> 可以随手关掉任意环节，这是和社区聊得最多的诉求。有团队想“开箱默认就好”，也有老项目背着一堆历史包袱，得慢慢迁移。所以我直接给了一排明确开关，想保守就保守，想激进就激进。</p>\n<div class=\"language-ts codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token keyword\" style=\"color:#C586C0\">const</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> twMerge</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> passthrough </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">create</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> escape</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#569CD6\">false</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> unescape</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#569CD6\">false</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><br></div></code></pre></div></div>\n<p>配合 SSR 或老数据兼容的时候，也不用再额外写工具函数：服务端直接把 escape 全关掉，只做 merge 校验；到小程序再开回完整逃逸步奏，迁移过程就能一步一步踩稳。</p>\n<p>另外还开放了 <code>map</code> 字段，用于统一用自己的字符映射。</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"发布-47x-版本\">发布 4.7.x 版本<a href=\"https://next.tw.icebreaker.top/blog/2025/11/v4.7.0-merge-runtime#%E5%8F%91%E5%B8%83-47x-%E7%89%88%E6%9C%AC\" class=\"hash-link\" aria-label=\"发布 4.7.x 版本的直接链接\" title=\"发布 4.7.x 版本的直接链接\" translate=\"no\">​</a></h2>\n<p>绕了这么多弯，所有成果最终都塞进了 <code>weapp-tailwindcss@4.7.x</code> 和 <code>@weapp-tailwindcss/merge@2.x</code> 中。算是 weapp-tailwindcss <strong>运行时</strong>时代的第一声号角。</p>\n<p>欢迎大家把新版 <code>@weapp-tailwindcss/merge</code> 用到真实项目里，更欢迎在社区继续砸想法，我会把这些反馈当作下一轮迭代的燃料，让 Tailwind CSS 在小程序世界里始终“开箱即用”。</p>\n<p>有时候我也在想，为小程序这个逐渐感觉不怎么活跃的生态，花了这么多时间，感觉有点不值。但是转念一想，起码在我这个领域我已经通过不断的学习，真的掌握了很多东西。</p>\n<p>起码，对 Tailwind CSS 进行符合中国小程序技术特色的改造方面，我也算是第一人了吧。每每想到这，就感觉自己好像还稍微有这么一点点自豪呢，哈哈哈。</p>\n<p>如果你也在思考工具链，编译，AST 等等方面的问题，希望这篇文章能给你一点启发。</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"源代码附录\">源代码附录<a href=\"https://next.tw.icebreaker.top/blog/2025/11/v4.7.0-merge-runtime#%E6%BA%90%E4%BB%A3%E7%A0%81%E9%99%84%E5%BD%95\" class=\"hash-link\" aria-label=\"源代码附录的直接链接\" title=\"源代码附录的直接链接\" translate=\"no\">​</a></h2>\n<ul>\n<li class=\"\"><a href=\"https://github.com/dcastil/tailwind-merge/blob/v3.3.1/docs/writing-plugins.md\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">tailwind-merge 插件文档</a></li>\n<li class=\"\"><a href=\"https://github.com/sonofmagic/weapp-tailwindcss/blob/main/packages/weapp-tailwindcss/src/js/NodePathWalker.ts\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">NodePathWalker</a></li>\n<li class=\"\"><a href=\"https://github.com/sonofmagic/weapp-tailwindcss/blob/main/packages/weapp-tailwindcss/src/js/ModuleGraph.ts\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">ModuleGraph</a></li>\n</ul>",
            "url": "https://next.tw.icebreaker.top/blog/2025/11/v4.7.0-merge-runtime",
            "title": "重新思考 weapp-tailwindcss 的未来",
            "summary": "从编译期豁免到**运行时**自管理，weapp-tailwindcss/merge 的重构思路与未来规划。",
            "date_modified": "2025-11-02T00:00:00.000Z",
            "author": {
                "name": "icebreaker",
                "url": "https://github.com/sonofmagic"
            },
            "tags": [
                "merge",
                "runtime",
                "tailwindcss"
            ]
        },
        {
            "id": "https://next.tw.icebreaker.top/blog/2025/9/v4.3-release",
            "content_html": "<p>weapp-tailwindcss@4.3.0 默认开启 CSS 变量计算模式，并补充了更细粒度的 <code>cssCalc</code>、<code>px2rpx</code> 等配置，显著提升了多端 <code>rpx</code> 与 <code>calc</code> 的兼容性。本文带你了解这些变化的原理与使用方法。</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"css变量计算模式\">CSS变量计算模式<a href=\"https://next.tw.icebreaker.top/blog/2025/9/v4.3-release#css%E5%8F%98%E9%87%8F%E8%AE%A1%E7%AE%97%E6%A8%A1%E5%BC%8F\" class=\"hash-link\" aria-label=\"CSS变量计算模式的直接链接\" title=\"CSS变量计算模式的直接链接\" translate=\"no\">​</a></h2>\n<p>在 <code>tailwindcss@4</code> 下，默认启用 CSS 变量计算模式。<code>tailwindcss@3</code> 默认不启用。</p>\n<p>此模式下会去预编译所有的 <code>css</code> 变量和 <code>calc</code> 计算表达式。</p>\n<p>比如 <code>tailwindcss@4</code> 下原先生成的样式为:</p>\n<div class=\"language-css codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-css codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token selector\" style=\"color:rgb(215, 186, 125)\">page,</span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token selector\" style=\"color:rgb(215, 186, 125)\">:root</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token property\" style=\"color:#9CDCFE\">--spacing</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> 8rpx</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token selector\" style=\"color:rgb(215, 186, 125)\">.h-2</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token property\" style=\"color:#9CDCFE\">height</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">calc</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">var</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">--spacing</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> * 2</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></div></code></pre></div></div>\n<p>在CSS变量计算模式启动，进行预编译之后，现在的结果为:</p>\n<div class=\"language-css codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-css codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token selector\" style=\"color:rgb(215, 186, 125)\">page,</span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token selector\" style=\"color:rgb(215, 186, 125)\">:root</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token property\" style=\"color:#9CDCFE\">--spacing</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> 8rpx</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token selector\" style=\"color:rgb(215, 186, 125)\">.h-2</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token property\" style=\"color:#9CDCFE\">height</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> 16rpx</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token property\" style=\"color:#9CDCFE\">height</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">calc</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">var</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">--spacing</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> * 2</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></div></code></pre></div></div>\n<p>这个模式可以解决很多手机机型 <code>calc</code> <code>rpx</code> 单位的兼容问题</p>\n<blockquote>\n<p>可通过给插件，传入 <code>cssCalc</code> 配置项 <code>false</code> 来手动关闭这个功能</p>\n</blockquote>\n<p>假如这时候你需要去除 CSS 变量的声明，你可以传入</p>\n<div class=\"language-js codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token literal-property property\" style=\"color:#9CDCFE\">cssCalc</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'--spacing'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// 或者更精确的</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token literal-property property\" style=\"color:#9CDCFE\">cssCalc</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">    </span><span class=\"token literal-property property\" style=\"color:#9CDCFE\">includeCustomProperties</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'--spacing'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></div></code></pre></div></div>\n<blockquote>\n<p>你也可以传入正则表达式</p>\n</blockquote>\n<p>这样生成的结果就是:</p>\n<div class=\"language-css codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-css codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token selector\" style=\"color:rgb(215, 186, 125)\">page,</span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token selector\" style=\"color:rgb(215, 186, 125)\">:root</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token property\" style=\"color:#9CDCFE\">--spacing</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> 8rpx</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token selector\" style=\"color:rgb(215, 186, 125)\">.h-2</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token property\" style=\"color:#9CDCFE\">height</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> 16rpx</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></div></code></pre></div></div>\n<p>通过这种方式可以解决手机机型 <code>calc</code> <code>rpx</code> 单位的兼容问题</p>\n<p>详见: <a href=\"https://tw.icebreaker.top/docs/api/interfaces/UserDefinedOptions#csscalc\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">https://tw.icebreaker.top/docs/api/interfaces/UserDefinedOptions#csscalc</a></p>\n<h2 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"新增配置项\">新增配置项<a href=\"https://next.tw.icebreaker.top/blog/2025/9/v4.3-release#%E6%96%B0%E5%A2%9E%E9%85%8D%E7%BD%AE%E9%A1%B9\" class=\"hash-link\" aria-label=\"新增配置项的直接链接\" title=\"新增配置项的直接链接\" translate=\"no\">​</a></h2>\n<h3 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"px2rpx\">px2rpx<a href=\"https://next.tw.icebreaker.top/blog/2025/9/v4.3-release#px2rpx\" class=\"hash-link\" aria-label=\"px2rpx的直接链接\" title=\"px2rpx的直接链接\" translate=\"no\">​</a></h3>\n<p>添加 <code>px2rpx</code> 配置项， 用于控制是否将 <code>px</code> 单位转换为 <code>rpx</code> 单位， 默认为 <code>false</code></p>\n<p>传入 <code>true</code> 则会将所有的 <code>px</code> 单位, <code>1:1</code> 转换为 <code>rpx</code> 单位</p>\n<p>假如需要更多的转化方式，可以传入一个 <code>object</code>, 配置项见 <a href=\"https://www.npmjs.com/package/postcss-pxtransform\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">https://www.npmjs.com/package/postcss-pxtransform</a></p>\n<h3 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"loglevel\">logLevel<a href=\"https://next.tw.icebreaker.top/blog/2025/9/v4.3-release#loglevel\" class=\"hash-link\" aria-label=\"logLevel的直接链接\" title=\"logLevel的直接链接\" translate=\"no\">​</a></h3>\n<p>添加 <code>logLevel</code> 配置项，用于控制日志输出级别， 默认为 <code>info</code></p>",
            "url": "https://next.tw.icebreaker.top/blog/2025/9/v4.3-release",
            "title": "4.3.0 🚀",
            "summary": "weapp-tailwindcss@4.3.0 默认开启 CSS 变量计算模式，并补充了更细粒度的 cssCalc、px2rpx 等配置，显著提升了多端 rpx 与 calc 的兼容性。本文带你了解这些变化的原理与使用方法。",
            "date_modified": "2025-09-13T00:00:00.000Z",
            "author": {
                "name": "icebreaker",
                "url": "https://github.com/sonofmagic"
            },
            "tags": []
        },
        {
            "id": "https://next.tw.icebreaker.top/blog/2025/8/v4.2-release",
            "content_html": "<p><a href=\"mailto:weapp-tailwindcss@4.2.x\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">weapp-tailwindcss@4.2.x</a> 现已适配 <code>uni-app x</code> 的多端构建能力，让 Tailwind CSS 原子类能够无缝跨端使用。本文梳理了从项目初始化到插件配置的完整流程，帮助你快速完成集成与发布。</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"前言\">前言<a href=\"https://next.tw.icebreaker.top/blog/2025/8/v4.2-release#%E5%89%8D%E8%A8%80\" class=\"hash-link\" aria-label=\"前言的直接链接\" title=\"前言的直接链接\" translate=\"no\">​</a></h2>\n<p><a href=\"https://doc.dcloud.net.cn/uni-app-x/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\"><code>uni-app x</code></a> 是 <code>DCloud</code> 团队开发的跨平台编译方案，可将同一份代码编译到 Web、小程序、Android、iOS、鸿蒙等多端。</p>\n<p>最近，我很荣幸在 <code>weapp-tailwindcss@4.2.x</code> 版本中，适配了 <code>uni-app x</code> 的多端构建，使 <code>Tailwind CSS</code> 的原子化样式能够无缝应用于多端项目。</p>\n<p>所以就有了这篇文章，给大家介绍(<strong>秀</strong>)一下怎么在 <code>uni-app x</code> 项目中，集成 <code>weapp-tailwindcss</code> 这个样式解决方案。</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"快速集成\">快速集成<a href=\"https://next.tw.icebreaker.top/blog/2025/8/v4.2-release#%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90\" class=\"hash-link\" aria-label=\"快速集成的直接链接\" title=\"快速集成的直接链接\" translate=\"no\">​</a></h2>\n<h3 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"0-创建项目\">0. 创建项目<a href=\"https://next.tw.icebreaker.top/blog/2025/8/v4.2-release#0-%E5%88%9B%E5%BB%BA%E9%A1%B9%E7%9B%AE\" class=\"hash-link\" aria-label=\"0. 创建项目的直接链接\" title=\"0. 创建项目的直接链接\" translate=\"no\">​</a></h3>\n<p>使用最新版本 <code>Hbuilderx</code> 创建一个 <code>uni-app x</code> 项目，然后在项目根目录执行</p>\n<div class=\"language-bash codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">npm</span><span class=\"token plain\"> init </span><span class=\"token parameter variable\" style=\"color:rgb(156, 220, 254)\">-y</span><br></div></code></pre></div></div>\n<p>初始化一个 <code>package.json</code> 文件 (当然你也可以手动创建)</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"1-安装并引入-tailwindcss3\">1. 安装并引入 tailwindcss@3<a href=\"https://next.tw.icebreaker.top/blog/2025/8/v4.2-release#1-%E5%AE%89%E8%A3%85%E5%B9%B6%E5%BC%95%E5%85%A5-tailwindcss3\" class=\"hash-link\" aria-label=\"1. 安装并引入 tailwindcss@3的直接链接\" title=\"1. 安装并引入 tailwindcss@3的直接链接\" translate=\"no\">​</a></h3>\n<div class=\"language-bash codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\"># 安装 tailwindcss@3 版本的依赖</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">npm</span><span class=\"token plain\"> i </span><span class=\"token parameter variable\" style=\"color:rgb(156, 220, 254)\">-D</span><span class=\"token plain\"> tailwindcss@3 postcss autoprefixer</span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\"># 初始化一个 tailwind.config.js 文件</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">npx tailwindcss init</span><br></div></code></pre></div></div>\n<p>然后，在你的根目录中的 <code>App.uvue</code> 中引入 <code>tailwindcss</code> 使它在应用全局生效</p>\n<div class=\"language-html codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockTitle_lZNB\">App.uvue</div><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-html codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:#569CD6\">style</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token style language-css\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token style language-css\"></span><span class=\"token style language-css atrule rule\" style=\"color:#C586C0\">@tailwind</span><span class=\"token style language-css atrule\" style=\"color:#569CD6\"> base</span><span class=\"token style language-css atrule punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token style language-css\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token style language-css\"></span><span class=\"token style language-css atrule rule\" style=\"color:#C586C0\">@tailwind</span><span class=\"token style language-css atrule\" style=\"color:#569CD6\"> components</span><span class=\"token style language-css atrule punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token style language-css\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token style language-css\"></span><span class=\"token style language-css atrule rule\" style=\"color:#C586C0\">@tailwind</span><span class=\"token style language-css atrule\" style=\"color:#569CD6\"> utilities</span><span class=\"token style language-css atrule punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token style language-css\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token style language-css\"></span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:#569CD6\">style</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><br></div></code></pre></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"2-安装-weapp-tailwindcss\">2. 安装 weapp-tailwindcss<a href=\"https://next.tw.icebreaker.top/blog/2025/8/v4.2-release#2-%E5%AE%89%E8%A3%85-weapp-tailwindcss\" class=\"hash-link\" aria-label=\"2. 安装 weapp-tailwindcss的直接链接\" title=\"2. 安装 weapp-tailwindcss的直接链接\" translate=\"no\">​</a></h3>\n<p>在项目目录下，执行:</p>\n<div class=\"language-bash codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">npm</span><span class=\"token plain\"> i </span><span class=\"token parameter variable\" style=\"color:rgb(156, 220, 254)\">-D</span><span class=\"token plain\"> weapp-tailwindcss</span><br></div></code></pre></div></div>\n<p>然后把下列脚本，添加进你的 <code>package.json</code> 的 <code>scripts</code> 字段里:</p>\n<div class=\"language-json codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockTitle_lZNB\">package.json</div><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"> </span><span class=\"token property\" style=\"color:#9CDCFE\">\"scripts\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line theme-code-block-highlighted-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">   </span><span class=\"token property\" style=\"color:#9CDCFE\">\"postinstall\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"weapp-tw patch\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></div></code></pre></div></div>\n<p>想知道原因的同学可以查看 <a href=\"https://tw.icebreaker.top/docs/quick-start/this-plugin\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\"><strong>这个链接</strong></a></p>\n<h3 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"3-uni-app-x-中注册-weapp-tailwindcss\">3. uni-app x 中注册 weapp-tailwindcss<a href=\"https://next.tw.icebreaker.top/blog/2025/8/v4.2-release#3-uni-app-x-%E4%B8%AD%E6%B3%A8%E5%86%8C-weapp-tailwindcss\" class=\"hash-link\" aria-label=\"3. uni-app x 中注册 weapp-tailwindcss的直接链接\" title=\"3. uni-app x 中注册 weapp-tailwindcss的直接链接\" translate=\"no\">​</a></h3>\n<h4 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"30-创建辅助函数\">3.0. 创建辅助函数<a href=\"https://next.tw.icebreaker.top/blog/2025/8/v4.2-release#30-%E5%88%9B%E5%BB%BA%E8%BE%85%E5%8A%A9%E5%87%BD%E6%95%B0\" class=\"hash-link\" aria-label=\"3.0. 创建辅助函数的直接链接\" title=\"3.0. 创建辅助函数的直接链接\" translate=\"no\">​</a></h4>\n<p>在项目中创建 <code>shared.js</code> 文件，用于存放一些工具函数：</p>\n<div class=\"language-js codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockTitle_lZNB\">shared.js</div><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token keyword\" style=\"color:#C586C0\">const</span><span class=\"token plain\"> path </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">require</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'node:path'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// 绝对路径处理</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">function</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">r</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token parameter\" style=\"color:#9CDCFE\">args</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:#C586C0\">return</span><span class=\"token plain\"> path</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">resolve</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">__dirname</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token plain\">args</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">module</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">exports </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  r</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></div></code></pre></div></div>\n<h4 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"31-配置-vite\">3.1. 配置 <code>Vite</code><a href=\"https://next.tw.icebreaker.top/blog/2025/8/v4.2-release#31-%E9%85%8D%E7%BD%AE-vite\" class=\"hash-link\" aria-label=\"31-配置-vite的直接链接\" title=\"31-配置-vite的直接链接\" translate=\"no\">​</a></h4>\n<p>创建 <code>vite.config.ts</code> 文件，注册插件：</p>\n<blockquote>\n<p>这里特别注意 <code>uniAppX</code> 是从 <code>weapp-tailwindcss/presets</code> 这个预设中导出的</p>\n</blockquote>\n<div class=\"language-js codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockTitle_lZNB\">vite.config.ts</div><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token keyword\" style=\"color:#C586C0\">import</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> defineConfig </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'vite'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">import</span><span class=\"token plain\"> uni </span><span class=\"token keyword\" style=\"color:#C586C0\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'@dcloudio/vite-plugin-uni'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">import</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> UnifiedViteWeappTailwindcssPlugin </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'weapp-tailwindcss/vite'</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">import</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> r </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'./shared'</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">import</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> uniAppX </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'weapp-tailwindcss/presets'</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">import</span><span class=\"token plain\"> tailwindcss </span><span class=\"token keyword\" style=\"color:#C586C0\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'tailwindcss'</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">export</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">default</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">defineConfig</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">    </span><span class=\"token literal-property property\" style=\"color:#9CDCFE\">plugins</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">        </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">uni</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">        </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">UnifiedViteWeappTailwindcssPlugin</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">            </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">uniAppX</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">                </span><span class=\"token literal-property property\" style=\"color:#9CDCFE\">base</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> __dirname</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">                </span><span class=\"token literal-property property\" style=\"color:#9CDCFE\">rem2rpx</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#569CD6\">true</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">            </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">    </span><span class=\"token literal-property property\" style=\"color:#9CDCFE\">css</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">        </span><span class=\"token literal-property property\" style=\"color:#9CDCFE\">postcss</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">            </span><span class=\"token literal-property property\" style=\"color:#9CDCFE\">plugins</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">                </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">tailwindcss</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">                    </span><span class=\"token literal-property property\" style=\"color:#9CDCFE\">config</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">r</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'./tailwind.config.js'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">                </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">            </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></div></code></pre></div></div>\n<h4 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"32-更改-tailwindcss-配置\">3.2. 更改 <code>tailwindcss</code> 配置<a href=\"https://next.tw.icebreaker.top/blog/2025/8/v4.2-release#32-%E6%9B%B4%E6%94%B9-tailwindcss-%E9%85%8D%E7%BD%AE\" class=\"hash-link\" aria-label=\"32-更改-tailwindcss-配置的直接链接\" title=\"32-更改-tailwindcss-配置的直接链接\" translate=\"no\">​</a></h4>\n<p>你需要在 <code>tailwind.config.js</code> 的 <code>content</code> 配置中，使用绝对路径去包括所有的提取文件</p>\n<div class=\"language-js codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockTitle_lZNB\">tailwind.config.js</div><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token keyword\" style=\"color:#C586C0\">const</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> r </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">require</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'./shared'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">/** @type {import('tailwindcss').Config} */</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">module</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">exports </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token literal-property property\" style=\"color:#9CDCFE\">content</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">    </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">r</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'./pages/**/*.{uts,uvue}'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">    </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">r</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'./components/**/*.{uts,uvue}'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token literal-property property\" style=\"color:#9CDCFE\">corePlugins</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">    </span><span class=\"token literal-property property\" style=\"color:#9CDCFE\">preflight</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#569CD6\">false</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div></code></pre></div></div>\n<blockquote>\n<p>如需从更多目录提取样式 <code>token</code>，可按需扩展 <code>content</code>。</p>\n</blockquote>\n<hr>\n<p>在完成这些步骤之后， <code>uni-app x</code> 就集成 <code>tailwindcss</code> 原子化样式解决方案。</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"如何运行\">如何运行<a href=\"https://next.tw.icebreaker.top/blog/2025/8/v4.2-release#%E5%A6%82%E4%BD%95%E8%BF%90%E8%A1%8C\" class=\"hash-link\" aria-label=\"如何运行的直接链接\" title=\"如何运行的直接链接\" translate=\"no\">​</a></h2>\n<p>目前 <code>uni-app x</code> 并没有提供任何 <code>cli</code> 的方式去使用它，所以目前我们使用 <code>Hbuilderx</code> 去进行开发和构建的。</p>\n<p>这里可以使用 <code>Hbuilderx</code> 的运行，来运行到各个平台。</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"优先使用安卓进行开发\">优先使用安卓进行开发<a href=\"https://next.tw.icebreaker.top/blog/2025/8/v4.2-release#%E4%BC%98%E5%85%88%E4%BD%BF%E7%94%A8%E5%AE%89%E5%8D%93%E8%BF%9B%E8%A1%8C%E5%BC%80%E5%8F%91\" class=\"hash-link\" aria-label=\"优先使用安卓进行开发的直接链接\" title=\"优先使用安卓进行开发的直接链接\" translate=\"no\">​</a></h3>\n<p>一般来说，<code>CSS</code> 平台的兼容程度，是 <code>Web</code> &gt; <code>小程序</code> &gt; <code>App</code>(安卓/IOS/鸿蒙)</p>\n<p>所以，假如你有跨多端的需求，建议你一开始使用 <code>安卓模拟器</code> 来进行开发和调试，这是综合考虑下来 <strong>成本最低</strong> 的方案。</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"为什么\">🧐为什么?<a href=\"https://next.tw.icebreaker.top/blog/2025/8/v4.2-release#%E4%B8%BA%E4%BB%80%E4%B9%88\" class=\"hash-link\" aria-label=\"🧐为什么?的直接链接\" title=\"🧐为什么?的直接链接\" translate=\"no\">​</a></h4>\n<p>因为 <code>uni-app x</code> 开发原生 <code>app</code> 是有一些限制的，</p>\n<ol>\n<li class=\"\">\n<p>比如文字必须包括在 <code>&lt;text&gt;</code> 标签中，文字的样式(比如 <code>text-2xl text-center text-red-400</code>) 也必须设置在这个元素上，设置在 <code>&lt;view&gt;</code> 上是没有效果的。而 <code>Web</code> 和 <code>小程序</code> 都是兼容的。</p>\n</li>\n<li class=\"\">\n<p>还有很多 <code>css</code> 的样式，目前 <code>uni-app x</code> 暂时是不兼容的，强行使用 <code>Hbuilderx</code> 的控制台会爆出警告，要求你进行更改。</p>\n</li>\n</ol>\n<p>这就相当于，你写的代码，能够满足了 <code>安卓</code> 这一端，那就大概率能满足 <code>小程序</code> 端，甚至 <code>Web</code> 端了(当然实战中还需要很多条件编译处理)</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"为什么不是-ios-或者鸿蒙\">🧐为什么不是 IOS 或者鸿蒙?<a href=\"https://next.tw.icebreaker.top/blog/2025/8/v4.2-release#%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%8D%E6%98%AF-ios-%E6%88%96%E8%80%85%E9%B8%BF%E8%92%99\" class=\"hash-link\" aria-label=\"🧐为什么不是 IOS 或者鸿蒙?的直接链接\" title=\"🧐为什么不是 IOS 或者鸿蒙?的直接链接\" translate=\"no\">​</a></h4>\n<p>IOS 模拟器，需要你有 Mac 才能运行，IOS 调试需要你有苹果手机，考虑到大部分开发应该都是用的 Windows 机器，所以选择 Android 模拟器开发，相对成本低一些，你都有的话当我没说</p>\n<p>至于鸿蒙，uni-app x 官方文档上都写着:</p>\n<blockquote>\n<p>鸿蒙整体处于发展初期，能用，有坑，大部分坑有规避方案。但开发者应建议其领导、客户、质量部门降低期望，不能严格比照Android和iOS的验收标准要求鸿蒙。</p>\n</blockquote>\n<p>不如安卓/IOS 稳定的话，自然优先选择安卓平台</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"常见问题\">常见问题<a href=\"https://next.tw.icebreaker.top/blog/2025/8/v4.2-release#%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98\" class=\"hash-link\" aria-label=\"常见问题的直接链接\" title=\"常见问题的直接链接\" translate=\"no\">​</a></h2>\n<h3 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"vscode-uvueuts-代码染色\">Vscode uvue/uts 代码染色<a href=\"https://next.tw.icebreaker.top/blog/2025/8/v4.2-release#vscode-uvueuts-%E4%BB%A3%E7%A0%81%E6%9F%93%E8%89%B2\" class=\"hash-link\" aria-label=\"Vscode uvue/uts 代码染色的直接链接\" title=\"Vscode uvue/uts 代码染色的直接链接\" translate=\"no\">​</a></h3>\n<p>安装 <code>DCloud</code> 自己推出的 <code>uni-app x语言服务</code>, 插件市场一搜 <code>uni-app x</code> 就搜到了</p>\n<ul>\n<li class=\"\">ID: dcloud-ide.hbuilderx-language-services</li>\n<li class=\"\">说明: 支持uni-app x项目的代码提示、悬浮、转到定义、查找引用、大纲、校验等</li>\n<li class=\"\">发布者: DCloud</li>\n<li class=\"\">VS Marketplace 链接: <a href=\"https://marketplace.visualstudio.com/items?itemName=dcloud-ide.hbuilderx-language-services\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">https://marketplace.visualstudio.com/items?itemName=dcloud-ide.hbuilderx-language-services</a></li>\n</ul>\n<h3 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"tailwindcss-智能提示\">Tailwindcss 智能提示<a href=\"https://next.tw.icebreaker.top/blog/2025/8/v4.2-release#tailwindcss-%E6%99%BA%E8%83%BD%E6%8F%90%E7%A4%BA\" class=\"hash-link\" aria-label=\"Tailwindcss 智能提示的直接链接\" title=\"Tailwindcss 智能提示的直接链接\" translate=\"no\">​</a></h3>\n<p>目前 <code>Tailwindcss</code> 智能提示最好的还是 <code>vscode</code>，但是 <code>vscode Tailwindcss</code> 肯定是不认识 <code>uvue/uts</code> 文件的，</p>\n<p>要让插件认识，只需要在你的项目中添加 <code>.vscode/settings.json</code> 文件</p>\n<div class=\"language-json codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">    </span><span class=\"token property\" style=\"color:#9CDCFE\">\"tailwindCSS.includeLanguages\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">        </span><span class=\"token property\" style=\"color:#9CDCFE\">\"uvue\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"html\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">        </span><span class=\"token property\" style=\"color:#9CDCFE\">\"uts\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"javascript\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></div></code></pre></div></div>\n<p>当然，你直接全局修改你的 <code>vscode Tailwindcss</code> 插件配置肯定也是可以的。</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"尾言\">尾言<a href=\"https://next.tw.icebreaker.top/blog/2025/8/v4.2-release#%E5%B0%BE%E8%A8%80\" class=\"hash-link\" aria-label=\"尾言的直接链接\" title=\"尾言的直接链接\" translate=\"no\">​</a></h2>\n<p><code>uni-app x</code> 是一个 <strong>庞大的工程</strong>, <code>DCloud</code> 团队能做出这样的技术解决方案，令人倾佩。</p>\n<p>假如你遇到什么问题，可以在 <a href=\"https://github.com/sonofmagic/weapp-tailwindcss\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">weapp-tailwindcss</a> 的 <code>issue/disscussions</code> 中提出。</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"配置好的模板\">配置好的模板<a href=\"https://next.tw.icebreaker.top/blog/2025/8/v4.2-release#%E9%85%8D%E7%BD%AE%E5%A5%BD%E7%9A%84%E6%A8%A1%E6%9D%BF\" class=\"hash-link\" aria-label=\"配置好的模板的直接链接\" title=\"配置好的模板的直接链接\" translate=\"no\">​</a></h2>\n<p><a href=\"https://github.com/icebreaker-template/uni-app-x-hbuilderx\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">https://github.com/icebreaker-template/uni-app-x-hbuilderx</a></p>\n<p>使用方式见这个项目中的 <code>README.md</code></p>\n<h2 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"参考文档\">参考文档<a href=\"https://next.tw.icebreaker.top/blog/2025/8/v4.2-release#%E5%8F%82%E8%80%83%E6%96%87%E6%A1%A3\" class=\"hash-link\" aria-label=\"参考文档的直接链接\" title=\"参考文档的直接链接\" translate=\"no\">​</a></h2>\n<p><a href=\"https://doc.dcloud.net.cn/uni-app-x/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">uni-app x 官方文档</a></p>\n<p><a href=\"https://tw.icebreaker.top/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">weapp-tailwindcss 官网</a></p>\n<p><a href=\"https://tw.icebreaker.top/docs/uni-app-x\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">weapp-tailwindcss 的 uni-app x 专题</a></p>",
            "url": "https://next.tw.icebreaker.top/blog/2025/8/v4.2-release",
            "title": "4.2.0 🚀",
            "summary": "weapp-tailwindcss@4.2.x 现已适配 uni-app x 的多端构建能力，让 Tailwind CSS 原子类能够无缝跨端使用。本文梳理了从项目初始化到插件配置的完整流程，帮助你快速完成集成与发布。",
            "date_modified": "2025-08-01T00:00:00.000Z",
            "author": {
                "name": "icebreaker",
                "url": "https://github.com/sonofmagic"
            },
            "tags": []
        },
        {
            "id": "https://next.tw.icebreaker.top/blog/2025/3/v4-release",
            "content_html": "<p>weapp-tailwindcss v4.0 正式发布，核心亮点是兼容 <code>tailwindcss@4</code> 并引入 <code>tailwind-merge</code> 运行时整合能力。文章总结了升级背后的思路，并提供 uni-app 示例帮助你快速落地。</p>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"https://cdn.jsdelivr.net/gh/sonofmagic/static/mbpv4-release.png\" alt=\"4.0 release\" class=\"img___pm\"></p>\n<h2 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"总结\"><strong>总结</strong><a href=\"https://next.tw.icebreaker.top/blog/2025/3/v4-release#%E6%80%BB%E7%BB%93\" class=\"hash-link\" aria-label=\"总结的直接链接\" title=\"总结的直接链接\" translate=\"no\">​</a></h2>\n<p>经历了一番自虐式开发后，<code>weapp-tailwindcss</code> <strong>v4.0</strong> 终于发布了！</p>\n<p>在清理了一些自己当初写的屎山代码后，我终于实现了心心念念的两个大功能：</p>\n<ol>\n<li class=\"\"><strong>支持 <code>tailwindcss@4.x</code> 版本</strong></li>\n<li class=\"\"><strong>支持 <code>tailwind-merge</code></strong></li>\n</ol>\n<blockquote>\n<p>因为 <code>tailwindcss@4</code> 直接变成了一个样式预处理器，定位上类似 <code>Sass</code> / <code>Less</code>，所以相关的改动还是挺大的。目前 <code>weapp-tailwindcss@4</code> 版本也同时兼容 <code>tailwindcss 4+3+2(jit)</code> 三个版本了。</p>\n</blockquote>\n<p>想快速上手集成？欢迎访问 <a href=\"https://tw.icebreaker.top/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">weapp-tailwindcss 官网</a>！\n如果你想进一步了解细节，下面有个示例，看看就懂了。</p>\n<hr>\n<h2 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"uni-app-快速集成示例\"><strong>uni-app 快速集成示例</strong><a href=\"https://next.tw.icebreaker.top/blog/2025/3/v4-release#uni-app-%E5%BF%AB%E9%80%9F%E9%9B%86%E6%88%90%E7%A4%BA%E4%BE%8B\" class=\"hash-link\" aria-label=\"uni-app-快速集成示例的直接链接\" title=\"uni-app-快速集成示例的直接链接\" translate=\"no\">​</a></h2>\n<p>这里我们以 <strong><code>uni-app</code>（Vue3 + Vite）</strong> 为例。</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"1-安装依赖\"><strong>1. 安装依赖</strong><a href=\"https://next.tw.icebreaker.top/blog/2025/3/v4-release#1-%E5%AE%89%E8%A3%85%E4%BE%9D%E8%B5%96\" class=\"hash-link\" aria-label=\"1-安装依赖的直接链接\" title=\"1-安装依赖的直接链接\" translate=\"no\">​</a></h3>\n<div class=\"theme-tabs-container tabs-container tabList_xTD4\"><ul role=\"tablist\" aria-orientation=\"horizontal\" class=\"tabs\"><li role=\"tab\" tabindex=\"0\" aria-selected=\"true\" class=\"tabs__item tabItem_v2HU tabs__item--active\">npm</li><li role=\"tab\" tabindex=\"-1\" aria-selected=\"false\" class=\"tabs__item tabItem_v2HU\">pnpm</li></ul><div class=\"margin-top--md\"><div role=\"tabpanel\" class=\"tabItem_Wt29\"><div class=\"language-bash codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">npm</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">install</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:rgb(156, 220, 254)\">-D</span><span class=\"token plain\"> tailwindcss @tailwindcss/vite weapp-tailwindcss</span><br></div></code></pre></div></div></div><div role=\"tabpanel\" class=\"tabItem_Wt29\" hidden=\"\"><div class=\"language-bash codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">pnpm</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">add</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:rgb(156, 220, 254)\">-D</span><span class=\"token plain\"> tailwindcss @tailwindcss/vite weapp-tailwindcss</span><br></div></code></pre></div></div></div></div></div>\n<p>然后，把下面这段脚本加入 <code>package.json</code> 的 <code>scripts</code> 字段里：</p>\n<div class=\"language-json codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockTitle_lZNB\">package.json</div><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token property\" style=\"color:#9CDCFE\">\"scripts\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line theme-code-block-highlighted-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">    </span><span class=\"token property\" style=\"color:#9CDCFE\">\"postinstall\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"weapp-tw patch\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></div></code></pre></div></div>\n<blockquote>\n<p>这个补丁是为了让 <code>tailwindcss@4</code> 认识 <code>rpx</code> 单位，否则它会以为 <code>rpx</code> 是个颜色单位，导致 <code>text-[40rpx]</code> 这样的样式翻车。</p>\n</blockquote>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"2-配置-viteconfigts\"><strong>2. 配置 <code>vite.config.ts</code></strong><a href=\"https://next.tw.icebreaker.top/blog/2025/3/v4-release#2-%E9%85%8D%E7%BD%AE-viteconfigts\" class=\"hash-link\" aria-label=\"2-配置-viteconfigts的直接链接\" title=\"2-配置-viteconfigts的直接链接\" translate=\"no\">​</a></h3>\n<div class=\"language-ts codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockTitle_lZNB\">vite.config.ts</div><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token keyword\" style=\"color:#C586C0\">import</span><span class=\"token plain\"> uni </span><span class=\"token keyword\" style=\"color:#C586C0\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'@dcloudio/vite-plugin-uni'</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">import</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> defineConfig </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'vite'</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">import</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> UnifiedViteWeappTailwindcssPlugin </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'weapp-tailwindcss/vite'</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">export</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">default</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">defineConfig</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token keyword\" style=\"color:#C586C0\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// 这里必须这样引用，因为 uni 只提供 cjs 版本，而 @tailwindcss/vite 只提供 esm 版本</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:#C586C0\">const</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">default</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> tailwindcss </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">await</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#C586C0\">import</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'@tailwindcss/vite'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:#C586C0\">return</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">    plugins</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">      </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">uni</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">      </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">tailwindcss</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">      </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">UnifiedViteWeappTailwindcssPlugin</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> rem2rpx</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#569CD6\">true</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><br></div></code></pre></div></div>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"3-添加样式\"><strong>3. 添加样式</strong><a href=\"https://next.tw.icebreaker.top/blog/2025/3/v4-release#3-%E6%B7%BB%E5%8A%A0%E6%A0%B7%E5%BC%8F\" class=\"hash-link\" aria-label=\"3-添加样式的直接链接\" title=\"3-添加样式的直接链接\" translate=\"no\">​</a></h3>\n<p>在项目目录下创建 <code>main.css</code>，然后添加以下内容：</p>\n<div class=\"language-css codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockTitle_lZNB\">main.css</div><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-css codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token atrule rule\" style=\"color:#C586C0\">@import</span><span class=\"token atrule\" style=\"color:#569CD6\"> </span><span class=\"token atrule string\" style=\"color:rgb(206, 145, 120)\">'weapp-tailwindcss/index.css'</span><span class=\"token atrule punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></div></code></pre></div></div>\n<blockquote>\n<p><strong>注意</strong>：对于实际运行时入口，现在更推荐直接写 <code>@import 'weapp-tailwindcss/index.css'</code>，而不是继续依赖 <code>rewriteCssImports</code> 去改写 <code>@import 'tailwindcss'</code>。</p>\n</blockquote>\n<p>接着在 <code>main.js</code> 里引用这个文件作为全局样式，然后直接运行：</p>\n<div class=\"theme-tabs-container tabs-container tabList_xTD4\"><ul role=\"tablist\" aria-orientation=\"horizontal\" class=\"tabs\"><li role=\"tab\" tabindex=\"0\" aria-selected=\"true\" class=\"tabs__item tabItem_v2HU tabs__item--active\">npm</li><li role=\"tab\" tabindex=\"-1\" aria-selected=\"false\" class=\"tabs__item tabItem_v2HU\">pnpm</li></ul><div class=\"margin-top--md\"><div role=\"tabpanel\" class=\"tabItem_Wt29\"><div class=\"language-bash codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">npm</span><span class=\"token plain\"> run dev:mp-weixin</span><br></div></code></pre></div></div></div><div role=\"tabpanel\" class=\"tabItem_Wt29\" hidden=\"\"><div class=\"language-bash codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">pnpm</span><span class=\"token plain\"> run dev:mp-weixin</span><br></div></code></pre></div></div></div></div></div>\n<p>在微信开发者工具中导入这个项目，在项目中使用 <code>tailwindcss</code> 原子类，即可看到效果。</p>\n<hr>\n<h2 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"遇到的问题以及解决方案\"><strong>遇到的问题以及解决方案</strong><a href=\"https://next.tw.icebreaker.top/blog/2025/3/v4-release#%E9%81%87%E5%88%B0%E7%9A%84%E9%97%AE%E9%A2%98%E4%BB%A5%E5%8F%8A%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88\" class=\"hash-link\" aria-label=\"遇到的问题以及解决方案的直接链接\" title=\"遇到的问题以及解决方案的直接链接\" translate=\"no\">​</a></h2>\n<h3 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"1-tailwindcss4-的样式兼容性问题\"><strong>1. <code>tailwindcss@4</code> 的样式兼容性问题</strong><a href=\"https://next.tw.icebreaker.top/blog/2025/3/v4-release#1-tailwindcss4-%E7%9A%84%E6%A0%B7%E5%BC%8F%E5%85%BC%E5%AE%B9%E6%80%A7%E9%97%AE%E9%A2%98\" class=\"hash-link\" aria-label=\"1-tailwindcss4-的样式兼容性问题的直接链接\" title=\"1-tailwindcss4-的样式兼容性问题的直接链接\" translate=\"no\">​</a></h3>\n<p><code>tailwindcss@4</code> 生成的样式广泛使用了 <code>@layer</code>、<code>color-mix</code>、<code>oklch</code> 等新特性，现代浏览器兼容性都不咋地，更别说小程序了。</p>\n<p>所以无论是 H5 还是小程序，都需要 <code>postcss-preset-env</code> 来降级这些样式的生成结果。降级的程度取决于你的 <code>browserslist</code> 配置。</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"2-tailwind-merge-样式合并问题\"><strong>2. <code>tailwind-merge</code> 样式合并问题</strong><a href=\"https://next.tw.icebreaker.top/blog/2025/3/v4-release#2-tailwind-merge-%E6%A0%B7%E5%BC%8F%E5%90%88%E5%B9%B6%E9%97%AE%E9%A2%98\" class=\"hash-link\" aria-label=\"2-tailwind-merge-样式合并问题的直接链接\" title=\"2-tailwind-merge-样式合并问题的直接链接\" translate=\"no\">​</a></h3>\n<p><code>tailwind-merge</code> 是一个 <code>tailwindcss</code> 运行时样式合并工具，它能自动合并重复的样式。不过，小程序端用 <code>tailwind-merge</code> 有点挑战，因为：</p>\n<ul>\n<li class=\"\"><code>weapp-tailwindcss</code> <strong>在编译时</strong> 对 <code>tailwindcss</code> 样式做转义。</li>\n<li class=\"\"><code>tailwind-merge</code> <strong>在运行时</strong> 处理样式合并。</li>\n</ul>\n<p>这导致 <code>tailwind-merge</code> 处理已转义的 <code>class</code> 时，误以为它们是普通 <code>class</code>，而不是 <code>tailwindcss</code> 的原子类。</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"解决方案编译时感应--运行时合并转义\"><strong>解决方案：编译时感应 + 运行时合并转义</strong><a href=\"https://next.tw.icebreaker.top/blog/2025/3/v4-release#%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88%E7%BC%96%E8%AF%91%E6%97%B6%E6%84%9F%E5%BA%94--%E8%BF%90%E8%A1%8C%E6%97%B6%E5%90%88%E5%B9%B6%E8%BD%AC%E4%B9%89\" class=\"hash-link\" aria-label=\"解决方案编译时感应--运行时合并转义的直接链接\" title=\"解决方案编译时感应--运行时合并转义的直接链接\" translate=\"no\">​</a></h3>\n<p>我研究了一下 <code>tailwind-merge</code> 的源码，尝试写一个 <code>plugin</code> 来解决这个问题，结果发现路走不通，因为：</p>\n<ul>\n<li class=\"\"><code>tailwind-merge</code> 内部有一些硬编码规则，比如 <code>!</code> 代表 <code>!important</code>，无法通过 <code>plugin</code> 自定义。</li>\n</ul>\n<p>于是，我换了个思路，<strong>采用编译时感应、忽略转义，运行时合并转义的方式</strong> 解决了这个问题，这就是 <strong><code>@weapp-tailwindcss/merge</code></strong> 诞生的原因。</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"编译时感应的原理\"><strong>编译时感应的原理</strong><a href=\"https://next.tw.icebreaker.top/blog/2025/3/v4-release#%E7%BC%96%E8%AF%91%E6%97%B6%E6%84%9F%E5%BA%94%E7%9A%84%E5%8E%9F%E7%90%86\" class=\"hash-link\" aria-label=\"编译时感应的原理的直接链接\" title=\"编译时感应的原理的直接链接\" translate=\"no\">​</a></h4>\n<p>我利用 <code>babel</code> 的 <code>scope</code>（作用域） 和 <code>binding</code>（绑定） 增强了识别能力</p>\n<p><code>scope</code> 简而言之就是 <code>js</code> 基础里的大括号, <code>binding</code> 就是一个变量真正的绑定</p>\n<p>比如：</p>\n<div class=\"language-js codeBlockContainer_WZB6 theme-code-block\" style=\"--prism-color:#D4D4D4;--prism-background-color:#212121\"><div class=\"codeBlockContent_UwIo\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_B4I7 thin-scrollbar\" style=\"color:#D4D4D4;background-color:#212121\"><code class=\"codeBlockLines_yRgA\"><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token keyword\" style=\"color:#C586C0\">const</span><span class=\"token plain\"> b </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'after:xx'</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#C586C0\">const</span><span class=\"token plain\"> a </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">b</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\"> text-[#123456]</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#D4D4D4\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">cn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">a</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'xx'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'yy'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><br></div></code></pre></div></div>\n<p>这段代码的作用域是全局，然后 <code>cn</code> 中 <code>a</code> 的绑定是一个 <code>VariableDeclaration</code> 它的 <code>init</code> 中，又引用了 <code>b</code> ，它的绑定也是一个 <code>VariableDeclaration</code> 初始化是一个 <code>StringLiteral</code></p>\n<p>于是，在编译时，我们可以从 <code>cn</code> 递归向上找到 <code>a</code> 和 <code>b</code>，识别出完整的样式链。</p>\n<p>当然，另一种方式是 <strong>自上而下查找</strong>，通过 <code>import</code> 语句分析模块依赖关系，甚至可以利用打包工具的模块分析图进行静态分析。</p>\n<p>当然这块的实现被用在了 <strong><code>@weapp-tailwindcss/merge</code></strong> 中，详见 <a href=\"https://tw.icebreaker.top/docs/community/merge\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">tailwind-merge 快速开始</a>。</p>\n<hr>\n<h2 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"开发心得\"><strong>开发心得</strong><a href=\"https://next.tw.icebreaker.top/blog/2025/3/v4-release#%E5%BC%80%E5%8F%91%E5%BF%83%E5%BE%97\" class=\"hash-link\" aria-label=\"开发心得的直接链接\" title=\"开发心得的直接链接\" translate=\"no\">​</a></h2>\n<p>在开发的时候，一定要把可能影响结果的方式，拆成多多阶段运行，比如在修改 <code>js ast</code> 的时候，不应该一边 <code>traverse</code> ，一边再用 <code>MagicString</code> 去修改，而是应该把需要修改的地方，先全部收集出来，然后在一个阶段统一去修改。</p>\n<p>另外鉴别一个 <code>ast</code> 工具的成熟度，也应该考虑一下，作用域和绑定的关系，能否被解析出来。比如我们都知道 <code>js</code> 里面大概有 <code>5</code> 种行为会产生作用域:</p>\n<ul>\n<li class=\"\"><strong>Program</strong>：全局作用域，整个文件的顶层作用域。</li>\n<li class=\"\"><strong>FunctionDeclaration / FunctionExpression / ArrowFunctionExpression</strong>：函数作用域（包括箭头函数）。</li>\n<li class=\"\"><strong>BlockStatement</strong>：块级作用域（由 <code>{}</code> 包裹，例如 if、for、while 中的块，在 ES6 中支持 let 和 const）。</li>\n<li class=\"\"><strong>ClassDeclaration / ClassExpression</strong>：类作用域（类的主体和方法会引入作用域）。</li>\n<li class=\"\"><strong>CatchClause</strong>：try-catch 中 catch 块的作用域。</li>\n</ul>\n<p>而绑定本身也是通过 <code>scope</code> 和它的 <code>parent</code> 依次向上去找的。</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"最后一点碎碎念\">最后一点碎碎念<a href=\"https://next.tw.icebreaker.top/blog/2025/3/v4-release#%E6%9C%80%E5%90%8E%E4%B8%80%E7%82%B9%E7%A2%8E%E7%A2%8E%E5%BF%B5\" class=\"hash-link\" aria-label=\"最后一点碎碎念的直接链接\" title=\"最后一点碎碎念的直接链接\" translate=\"no\">​</a></h2>\n<p>转眼我也到了而立之年，在这个行情下，毕业（失业）随时可能发生，当然也不敢毕业，毕竟上有老下有小，还有各种贷款。</p>\n<p>技术学得越多，越觉得是个无底洞。而且掌握的东西多了，好像也没什么收益。更何况前端技术和那种 AI 技术相比，就像是过家家一样的。</p>\n<p>开源呢？搞了半天也没啥产出，想成为开源明星，但自己技术水平也没到那个级别。</p>\n<p>最近，我开始深入学习 <strong>云原生</strong>，因为我意识到，国内这种国情下 <strong>\"技术是工具，业务才是目的\"</strong>。</p>\n<p>这导致我们技术人员的地位是非常低的，这点只要还在国内混就无法改变。</p>\n<p>所以我们自己也不要对自己或者团队写的东西，搞得有多好多完美的似的。够用就行，多尝试，先快速搞他个 100 个应用，然后挑没死的去进行进一步的开发。</p>\n<p>举个例子，现在让我从 0 到 1 搭建一个完整的业务系统，我可以全程自主搞定：</p>\n<ul>\n<li class=\"\"><strong>前端</strong>：<code>Vue</code> / <code>React</code></li>\n<li class=\"\"><strong>后端</strong>：<code>Node.js</code>（任意框架）</li>\n<li class=\"\"><strong>数据库</strong>：<code>MongoDB</code> / <code>PostgreSQL</code></li>\n<li class=\"\"><strong>运维</strong>：<code>Docker</code> / <code>Serverless</code></li>\n</ul>\n<p>但问题是——<strong>开发成本和时间成本太高了！</strong></p>\n<p>这种情况，我要自己去设计数据库，写 <code>openapi.yml</code> ，然后实现接口，还要自己写前端，和自己对接。写的差不多了还要测试，又是一堆 <code>bug</code>。</p>\n<p>而且还可能遇到技术难点，之前我在 <code>Node.js</code> 中实现 <code>PostgreSQL</code> 的 <code>GraphQL</code> 方案，就折腾了好久。这种情况，为什么不直接上 <code>pg_graphql</code> 这种成熟的中间件呢？</p>\n<p>所以，从商业角度来看：</p>\n<ol>\n<li class=\"\"><strong>写尽可能少的代码</strong>，尽量利用成熟方案快速搭建业务。</li>\n<li class=\"\"><strong>赚钱才是第一要务</strong>，哪里有问题，先堆机器解决，实在搞不定了，再招专业人才处理。</li>\n<li class=\"\"><strong>多利用AI来降本增效</strong>，AI 成本比人低，所以招个高级，不如招个初级的，会用 AI 的，学习能力好的程序员，毕竟前端静态页面，AI 生成可快了</li>\n</ol>\n<p>所以，从一个程序员的视角出发:</p>\n<p><strong>这是最好的时代，也是最坏的时代。</strong></p>\n<hr>\n<h2 class=\"anchor anchorTargetStickyNavbar_i8My\" id=\"参考链接\"><strong>参考链接</strong><a href=\"https://next.tw.icebreaker.top/blog/2025/3/v4-release#%E5%8F%82%E8%80%83%E9%93%BE%E6%8E%A5\" class=\"hash-link\" aria-label=\"参考链接的直接链接\" title=\"参考链接的直接链接\" translate=\"no\">​</a></h2>\n<ul>\n<li class=\"\"><a href=\"https://tw.icebreaker.top/docs/migrations/v3\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">weapp-tailwindcss 迁移文档</a></li>\n<li class=\"\"><a href=\"https://tw.icebreaker.top/docs/quick-start/v4\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Tailwindcss@4 各个框架集成方式</a></li>\n<li class=\"\"><a href=\"https://tailwindcss.com/docs/upgrade-guide\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Tailwindcss@4 升级指南</a></li>\n<li class=\"\"><a href=\"https://github.com/icebreaker-template/uni-app-tailwindcss-v4\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">uni-app-tailwindcss-v4 参考模板</a></li>\n</ul>\n<blockquote>\n<p>补充：classic <code>uni-app vue2 / webpack</code> 路线已经不再推荐，新项目请优先选择 <code>uni-app vue3 vite</code>。</p>\n</blockquote>",
            "url": "https://next.tw.icebreaker.top/blog/2025/3/v4-release",
            "title": "4.0.0 🚀",
            "summary": "weapp-tailwindcss v4.0 正式发布，核心亮点是兼容 tailwindcss@4 并引入 tailwind-merge 运行时整合能力。文章总结了升级背后的思路，并提供 uni-app 示例帮助你快速落地。",
            "date_modified": "2025-03-01T00:00:00.000Z",
            "author": {
                "name": "icebreaker",
                "url": "https://github.com/sonofmagic"
            },
            "tags": []
        }
    ]
}