<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://next.tw.icebreaker.top/blog</id>
    <title>weapp-tailwindcss 博客订阅</title>
    <updated>2025-11-02T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://next.tw.icebreaker.top/blog"/>
    <subtitle>tailwindcss 在小程序与多端开发中的更新、案例与教程资讯。</subtitle>
    <icon>https://next.tw.icebreaker.top/favicon.ico</icon>
    <entry>
        <title type="html"><![CDATA[重新思考 weapp-tailwindcss 的未来]]></title>
        <id>https://next.tw.icebreaker.top/blog/2025/11/v4.7.0-merge-runtime</id>
        <link href="https://next.tw.icebreaker.top/blog/2025/11/v4.7.0-merge-runtime"/>
        <updated>2025-11-02T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[从编译期豁免到**运行时**自管理，weapp-tailwindcss/merge 的重构思路与未来规划。]]></summary>
        <content type="html"><![CDATA[<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>
<p>最近我一直在思考 <code>weapp-tailwindcss</code> 的未来，以至于都没有怎么玩，最近回归的星际争霸2。</p>
<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>
<p>为什么？因为之前有一个很重要的问题，严重阻碍了 <code>weapp-tailwindcss</code> 发展的脚步。</p>
<p>那就是 <code>tailwind-merge</code> / <code>class-variance-authority</code> / <code>tailwind-variants</code> 这些极其重要的原子化样式基础包，没有什么很好的办法在小程序里使用。</p>
<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>
<p>简短一点来说，核心原因是，小程序 <code>wxml</code> 类名中，不允许很多特殊字符串，比如 <code>!</code>、<code>[</code>、<code>]</code>、<code>#</code> 等字符。</p>
<p>所以 <code>weapp-tailwindcss</code> 根据这个设计，在<strong>编译时</strong>，就对 <code>tailwindcss</code> 类名进行转换，从而达到了兼容市面上众多小程序的编译插件。</p>
<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>
<p>而 <code>tailwind-merge</code> 它们都是在<strong>运行时</strong>进行计算的，那时候它们接收到的，已经是 <code>bg-_h123456_</code> 这种转译之后的字符串，自然合并不了，导致到处出错。</p>
<p>为了兼容，我做了非常多的尝试！给大家展示一下我的受苦之路吧！</p>
<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>
<p>最直观的念头，就是给 <code>tailwind-merge</code> 写一个 <code>weapp-tailwindcss</code> 专用插件就好了!</p>
<p>于是我开始阅读 <code>tailwind-merge</code> 源代码，并尝试使用 <code>extendTailwindMerge</code> 和 <code>createTailwindMerge</code> 完全创建出一个属于我自己的 <code>weapp-tailwind-merge</code> 来。</p>
<p>在尝试过程中，我把 <code>tailwind-merge</code> 的内部冲突表导出，尝试用自定义 <code>escape hook</code> 覆盖那些非法字符；甚至写了一个半成品的 <code>createTailwindMerge</code> 变体，希望能在编译阶段就生成完全符合小程序命名规则的类名。</p>
<p>然而，现实很快给了我当头棒喝：<code>tailwind-merge</code> 对<strong>运行时</strong>字符串的依赖极强，部分字符是强依赖，根本无法替换。</p>
<p>下面这几个字符串都是写在常量里的，无法通过配置更换</p>
<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>
<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>
<p>所以这已经不是 <code>extendTailwindMerge</code> 和 <code>createTailwindMerge</code> 能够解决的问题了。</p>
<p>摆在我面前的，是一条看不到未来的路：为了强行兼容，我需要重写它的核心，fork 一个全新的包，这个成本是巨大的。</p>
<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>
<p>第二条路看起来更务实：沿用我熟悉的编译期管线，给 <code>twMerge</code> / <code>twJoin</code> / <code>cva</code> 等函数做“豁免处理”。</p>
<p>我当时是这样想的，只要在<strong>编译时</strong>忽略它们内部的转义，<strong>运行时</strong>拿到的就是完整的 class 字符串，那 tailwind-merge 不就能工作了吗？</p>
<p>然后我再包装一下 <code>twMerge</code> 函数，让它获取最后的结果的时候 escape 不就行了吗？</p>
<p>大概长这样：</p>
<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>
<p>然后我让 <code>cn</code> 里面的字面量和模板字符串跳过转义不就行了吗？</p>
<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>
<p>看上去运行良好，然而情况正在变得越来越复杂：</p>
<p>嘿，变量引用来了：</p>
<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>
<p>嘿嘿，变量引用 + 表达式来了：</p>
<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>
<p>嘿嘿嘿，变量引用链路 + 表达式 + 模板插值来了：</p>
<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>
<p>哈哈，只是在考验我操作 ast 进行预编译的水平而已！</p>
<p>吃我一拳：<code>ASTNodePathWalker</code> + <code>scope.getBinding</code> + <code>WeakMap</code>，哈哈轻松消灭！</p>
<p>于是我以为这条思路可行，编写了 <code>@weapp-tailwindcss/merge</code> 的 v1 版本。</p>
<p>直到用户提交了新的 case！</p>
<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>
<p>什么，怎么还有你们这种相互引用的情况！</p>
<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>
<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>
<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>
<p>……我吐了，这是要我自己去实现一个 webpack / rollup 打包器嘛？有点搞不定啊！</p>
<p>不过困难怕什么，我要迎难而上！于是我仿照了 <code>rollup</code> 的思路，收集了每个模块的 <code>import</code> / <code>export</code> 这里面大量的 ast 节点，并构建出了一个 <code>ModuleGraph</code>。</p>
<p>另外表面上看这条路是可行的，我甚至找到了几个 <code>demo</code> 可以跑通，我还把豁免名单抽离出来，变成了 <code>ignoreCallExpressionIdentifiers</code> 配置项，以为自己解决了问题。</p>
<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>
<p>这套方案高度依赖 AST 解析和构建工具的配合，我写的插件无法保证<strong>运行时</strong>得到的类名永远完整。构建链路上的任一环节——Terser、esbuild、rollup 插件甚至手写 Babel 宏——都可能把函数名或模板字符串的标识符压缩重命名，导致最后留给<strong>运行时</strong>的是一个残缺的字符串。</p>
<p>用人话说就是 <code>cn</code> , <code>twMerge</code>, <code>tv</code> 这种方法，在产物里面被重命名成 <code>e</code>/<code>a</code>/<code>c</code> 这种玩意，所以我必须在压缩之前就进行豁免操作，但是那时候我似乎无法去准确收集产物的模块依赖情况(可能是水平不够导致的)。</p>
<p>那一刻我意识到，所谓“编译期豁免”只是在延迟爆炸时间，而不是解除危机。</p>
<p>在两条路都走到尽头之后，只剩下一个选择：彻底重构 merge，让逃逸逻辑回归<strong>运行时</strong>，让编译阶段恢复简单纯粹。</p>
<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>
<p>复盘 1.x 旧版 merge，我发现我当时的设计基于两个假设：一是 <code>tailwind-merge</code> 的输入输出始终可控，二是编译器可以精准标记所有“需要放行”的调用。这两个假设已经被现实击碎。</p>
<p>早期的 <code>@weapp-tailwindcss/merge</code> 主要目标是“把 tailwind-merge 的结果变成小程序合法类名”。我采取的策略是：</p>
<ul>
<li class="">继续使用 <code>tailwind-merge</code> 做冲突解析；</li>
<li class="">在编译阶段通过函数名黑名单 <code>ignoreCallExpressionIdentifiers</code> 跳过对 <code>twMerge</code> / <code>twJoin</code> / <code>cva</code> 等调用的转义；</li>
<li class="">把责任交给开发者：<strong>运行时</strong>得到的类名包含非法字符，需要手动再 escape。</li>
</ul>
<p>这种模式在 Tailwind CSS v3 勉强能用，但一到 v4 就崩溃了：</p>
<ol>
<li class=""><strong>编译期豁免并不等于安全</strong><br>
<code>twMerge('text-[#ececec]', 'text-(--my-custom-color)')</code> 最终仍然输出原始字符串。稍微复杂一点的条件拼接、链式调用、动态导入，编译器根本判断不出来该不该跳过。</li>
<li class=""><strong>函数名黑名单无法覆盖新的 API</strong><br>
<!-- -->新版本开始导出 <code>create()</code>、variants（<code>tv</code>）等工厂，调用形式千奇百怪，编译阶段根本匹配不到。</li>
<li class=""><strong>任意值语法越来越灵活</strong><br>
<!-- -->Tailwind v4 的任意值可以是 <code>text-[theme(my.scale.foo)]</code> 这种无法静态推断类型的写法。靠黑名单永远落后，反而让用户更困惑。</li>
</ol>
<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>
<p>决定“把锅背回<strong>运行时</strong>”以后，我做的第一件事就是把入口全部进行统一：<code>twMerge</code> / <code>twJoin</code> / <code>createTailwindMerge</code> / <code>extendTailwindMerge</code> / <code>cva</code> / <code>variants</code>……统统绑进同一套 transformer 里。</p>
<p>思路很简单：先找出它们共有的“进场”和“退场”动作，再把逃逸拆成前后两个钩子， <code>escape</code> 和 <code>unescape</code>。</p>
<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>
<p>在实现里我刻意把 escape 和 unescape 拆成两个“齿轮”。不管是用户直接手点 <code>twMerge</code>，还是 variants 工厂兜一圈回来，都会先进统一的预处理，再丢给 tailwind-merge。</p>
<p>这等于在<strong>运行时</strong>补了一层“语义编译器”。</p>
<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>
<p>所以现在每次 merge 现在都得过一遍 <code>unescape -&gt; tailwind-merge -&gt; escape</code> 这样的流程：</p>
<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>
<p>但是这样还不够，为了实现 <code>escape</code> 和 <code>unescape</code> 我还必须从源头上出发，更改 <code>@weapp-core/escape</code> 的转译规则，才能让每一个字符串映射变得独一无二</p>
<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>
<p>老 escape 工具一直挂在 <code>@weapp-core/escape</code> 上，它走的是“多对一”映射，贴一段旧代码大家感受一下：</p>
<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>
<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>
<p>所以我直接把 <code>@weapp-core/escape</code> 推倒重练，写成一个可逆的“状态机”。每个非法字符都分到独一无二的逃逸片段，还带长度前缀，跑完 <code>unescape(escape(input))</code> 就一定回到原样。为了防止它在极端输入上翻车，我拉了十几组 property-based 测试，emoji、空格、重复 escape 全安排上写了大量的单元测试，确保往返都符合预期。</p>
<p>下面是当前版本的核心映射表，展示了我如何为每个非法字符分配唯一的 escape 片段，便于和旧版多对一的写法做对比：</p>
<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>
<p>文章里我只放这份“简化表”，因为它才是<strong>运行时</strong>默认用的版本，开发者平时看到的也是它。更复杂的兼容映射我留在文档和测试里。</p>
<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>
<p>新的 <code>create()</code> 可以随手关掉任意环节，这是和社区聊得最多的诉求。有团队想“开箱默认就好”，也有老项目背着一堆历史包袱，得慢慢迁移。所以我直接给了一排明确开关，想保守就保守，想激进就激进。</p>
<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>
<p>配合 SSR 或老数据兼容的时候，也不用再额外写工具函数：服务端直接把 escape 全关掉，只做 merge 校验；到小程序再开回完整逃逸步奏，迁移过程就能一步一步踩稳。</p>
<p>另外还开放了 <code>map</code> 字段，用于统一用自己的字符映射。</p>
<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>
<p>绕了这么多弯，所有成果最终都塞进了 <code>weapp-tailwindcss@4.7.x</code> 和 <code>@weapp-tailwindcss/merge@2.x</code> 中。算是 weapp-tailwindcss <strong>运行时</strong>时代的第一声号角。</p>
<p>欢迎大家把新版 <code>@weapp-tailwindcss/merge</code> 用到真实项目里，更欢迎在社区继续砸想法，我会把这些反馈当作下一轮迭代的燃料，让 Tailwind CSS 在小程序世界里始终“开箱即用”。</p>
<p>有时候我也在想，为小程序这个逐渐感觉不怎么活跃的生态，花了这么多时间，感觉有点不值。但是转念一想，起码在我这个领域我已经通过不断的学习，真的掌握了很多东西。</p>
<p>起码，对 Tailwind CSS 进行符合中国小程序技术特色的改造方面，我也算是第一人了吧。每每想到这，就感觉自己好像还稍微有这么一点点自豪呢，哈哈哈。</p>
<p>如果你也在思考工具链，编译，AST 等等方面的问题，希望这篇文章能给你一点启发。</p>
<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>
<ul>
<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>
<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>
<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>
</ul>]]></content>
        <author>
            <name>icebreaker</name>
            <email>hi@sonofmagic.top</email>
            <uri>https://github.com/sonofmagic</uri>
        </author>
        <category label="merge" term="merge"/>
        <category label="runtime" term="runtime"/>
        <category label="tailwindcss" term="tailwindcss"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[4.3.0 🚀]]></title>
        <id>https://next.tw.icebreaker.top/blog/2025/9/v4.3-release</id>
        <link href="https://next.tw.icebreaker.top/blog/2025/9/v4.3-release"/>
        <updated>2025-09-13T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[weapp-tailwindcss@4.3.0 默认开启 CSS 变量计算模式，并补充了更细粒度的 cssCalc、px2rpx 等配置，显著提升了多端 rpx 与 calc 的兼容性。本文带你了解这些变化的原理与使用方法。]]></summary>
        <content type="html"><![CDATA[<p>weapp-tailwindcss@4.3.0 默认开启 CSS 变量计算模式，并补充了更细粒度的 <code>cssCalc</code>、<code>px2rpx</code> 等配置，显著提升了多端 <code>rpx</code> 与 <code>calc</code> 的兼容性。本文带你了解这些变化的原理与使用方法。</p>
<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>
<p>在 <code>tailwindcss@4</code> 下，默认启用 CSS 变量计算模式。<code>tailwindcss@3</code> 默认不启用。</p>
<p>此模式下会去预编译所有的 <code>css</code> 变量和 <code>calc</code> 计算表达式。</p>
<p>比如 <code>tailwindcss@4</code> 下原先生成的样式为:</p>
<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>
<p>在CSS变量计算模式启动，进行预编译之后，现在的结果为:</p>
<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>
<p>这个模式可以解决很多手机机型 <code>calc</code> <code>rpx</code> 单位的兼容问题</p>
<blockquote>
<p>可通过给插件，传入 <code>cssCalc</code> 配置项 <code>false</code> 来手动关闭这个功能</p>
</blockquote>
<p>假如这时候你需要去除 CSS 变量的声明，你可以传入</p>
<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>
<blockquote>
<p>你也可以传入正则表达式</p>
</blockquote>
<p>这样生成的结果就是:</p>
<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>
<p>通过这种方式可以解决手机机型 <code>calc</code> <code>rpx</code> 单位的兼容问题</p>
<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>
<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>
<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>
<p>添加 <code>px2rpx</code> 配置项， 用于控制是否将 <code>px</code> 单位转换为 <code>rpx</code> 单位， 默认为 <code>false</code></p>
<p>传入 <code>true</code> 则会将所有的 <code>px</code> 单位, <code>1:1</code> 转换为 <code>rpx</code> 单位</p>
<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>
<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>
<p>添加 <code>logLevel</code> 配置项，用于控制日志输出级别， 默认为 <code>info</code></p>]]></content>
        <author>
            <name>icebreaker</name>
            <email>hi@sonofmagic.top</email>
            <uri>https://github.com/sonofmagic</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[4.2.0 🚀]]></title>
        <id>https://next.tw.icebreaker.top/blog/2025/8/v4.2-release</id>
        <link href="https://next.tw.icebreaker.top/blog/2025/8/v4.2-release"/>
        <updated>2025-08-01T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[weapp-tailwindcss@4.2.x 现已适配 uni-app x 的多端构建能力，让 Tailwind CSS 原子类能够无缝跨端使用。本文梳理了从项目初始化到插件配置的完整流程，帮助你快速完成集成与发布。]]></summary>
        <content type="html"><![CDATA[<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>
<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>
<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>
<p>最近，我很荣幸在 <code>weapp-tailwindcss@4.2.x</code> 版本中，适配了 <code>uni-app x</code> 的多端构建，使 <code>Tailwind CSS</code> 的原子化样式能够无缝应用于多端项目。</p>
<p>所以就有了这篇文章，给大家介绍(<strong>秀</strong>)一下怎么在 <code>uni-app x</code> 项目中，集成 <code>weapp-tailwindcss</code> 这个样式解决方案。</p>
<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>
<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>
<p>使用最新版本 <code>Hbuilderx</code> 创建一个 <code>uni-app x</code> 项目，然后在项目根目录执行</p>
<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>
<p>初始化一个 <code>package.json</code> 文件 (当然你也可以手动创建)</p>
<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>
<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>
<p>然后，在你的根目录中的 <code>App.uvue</code> 中引入 <code>tailwindcss</code> 使它在应用全局生效</p>
<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>
<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>
<p>在项目目录下，执行:</p>
<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>
<p>然后把下列脚本，添加进你的 <code>package.json</code> 的 <code>scripts</code> 字段里:</p>
<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>
<p>想知道原因的同学可以查看 <a href="https://tw.icebreaker.top/docs/quick-start/this-plugin" target="_blank" rel="noopener noreferrer" class=""><strong>这个链接</strong></a></p>
<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>
<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>
<p>在项目中创建 <code>shared.js</code> 文件，用于存放一些工具函数：</p>
<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>
<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>
<p>创建 <code>vite.config.ts</code> 文件，注册插件：</p>
<blockquote>
<p>这里特别注意 <code>uniAppX</code> 是从 <code>weapp-tailwindcss/presets</code> 这个预设中导出的</p>
</blockquote>
<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>
<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>
<p>你需要在 <code>tailwind.config.js</code> 的 <code>content</code> 配置中，使用绝对路径去包括所有的提取文件</p>
<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>
<blockquote>
<p>如需从更多目录提取样式 <code>token</code>，可按需扩展 <code>content</code>。</p>
</blockquote>
<hr>
<p>在完成这些步骤之后， <code>uni-app x</code> 就集成 <code>tailwindcss</code> 原子化样式解决方案。</p>
<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>
<p>目前 <code>uni-app x</code> 并没有提供任何 <code>cli</code> 的方式去使用它，所以目前我们使用 <code>Hbuilderx</code> 去进行开发和构建的。</p>
<p>这里可以使用 <code>Hbuilderx</code> 的运行，来运行到各个平台。</p>
<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>
<p>一般来说，<code>CSS</code> 平台的兼容程度，是 <code>Web</code> &gt; <code>小程序</code> &gt; <code>App</code>(安卓/IOS/鸿蒙)</p>
<p>所以，假如你有跨多端的需求，建议你一开始使用 <code>安卓模拟器</code> 来进行开发和调试，这是综合考虑下来 <strong>成本最低</strong> 的方案。</p>
<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>
<p>因为 <code>uni-app x</code> 开发原生 <code>app</code> 是有一些限制的，</p>
<ol>
<li class="">
<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>
</li>
<li class="">
<p>还有很多 <code>css</code> 的样式，目前 <code>uni-app x</code> 暂时是不兼容的，强行使用 <code>Hbuilderx</code> 的控制台会爆出警告，要求你进行更改。</p>
</li>
</ol>
<p>这就相当于，你写的代码，能够满足了 <code>安卓</code> 这一端，那就大概率能满足 <code>小程序</code> 端，甚至 <code>Web</code> 端了(当然实战中还需要很多条件编译处理)</p>
<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>
<p>IOS 模拟器，需要你有 Mac 才能运行，IOS 调试需要你有苹果手机，考虑到大部分开发应该都是用的 Windows 机器，所以选择 Android 模拟器开发，相对成本低一些，你都有的话当我没说</p>
<p>至于鸿蒙，uni-app x 官方文档上都写着:</p>
<blockquote>
<p>鸿蒙整体处于发展初期，能用，有坑，大部分坑有规避方案。但开发者应建议其领导、客户、质量部门降低期望，不能严格比照Android和iOS的验收标准要求鸿蒙。</p>
</blockquote>
<p>不如安卓/IOS 稳定的话，自然优先选择安卓平台</p>
<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>
<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>
<p>安装 <code>DCloud</code> 自己推出的 <code>uni-app x语言服务</code>, 插件市场一搜 <code>uni-app x</code> 就搜到了</p>
<ul>
<li class="">ID: dcloud-ide.hbuilderx-language-services</li>
<li class="">说明: 支持uni-app x项目的代码提示、悬浮、转到定义、查找引用、大纲、校验等</li>
<li class="">发布者: DCloud</li>
<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>
</ul>
<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>
<p>目前 <code>Tailwindcss</code> 智能提示最好的还是 <code>vscode</code>，但是 <code>vscode Tailwindcss</code> 肯定是不认识 <code>uvue/uts</code> 文件的，</p>
<p>要让插件认识，只需要在你的项目中添加 <code>.vscode/settings.json</code> 文件</p>
<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>
<p>当然，你直接全局修改你的 <code>vscode Tailwindcss</code> 插件配置肯定也是可以的。</p>
<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>
<p><code>uni-app x</code> 是一个 <strong>庞大的工程</strong>, <code>DCloud</code> 团队能做出这样的技术解决方案，令人倾佩。</p>
<p>假如你遇到什么问题，可以在 <a href="https://github.com/sonofmagic/weapp-tailwindcss" target="_blank" rel="noopener noreferrer" class="">weapp-tailwindcss</a> 的 <code>issue/disscussions</code> 中提出。</p>
<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>
<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>
<p>使用方式见这个项目中的 <code>README.md</code></p>
<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>
<p><a href="https://doc.dcloud.net.cn/uni-app-x/" target="_blank" rel="noopener noreferrer" class="">uni-app x 官方文档</a></p>
<p><a href="https://tw.icebreaker.top/" target="_blank" rel="noopener noreferrer" class="">weapp-tailwindcss 官网</a></p>
<p><a href="https://tw.icebreaker.top/docs/uni-app-x" target="_blank" rel="noopener noreferrer" class="">weapp-tailwindcss 的 uni-app x 专题</a></p>]]></content>
        <author>
            <name>icebreaker</name>
            <email>hi@sonofmagic.top</email>
            <uri>https://github.com/sonofmagic</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[4.0.0 🚀]]></title>
        <id>https://next.tw.icebreaker.top/blog/2025/3/v4-release</id>
        <link href="https://next.tw.icebreaker.top/blog/2025/3/v4-release"/>
        <updated>2025-03-01T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[weapp-tailwindcss v4.0 正式发布，核心亮点是兼容 tailwindcss@4 并引入 tailwind-merge 运行时整合能力。文章总结了升级背后的思路，并提供 uni-app 示例帮助你快速落地。]]></summary>
        <content type="html"><![CDATA[<p>weapp-tailwindcss v4.0 正式发布，核心亮点是兼容 <code>tailwindcss@4</code> 并引入 <code>tailwind-merge</code> 运行时整合能力。文章总结了升级背后的思路，并提供 uni-app 示例帮助你快速落地。</p>
<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>
<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>
<p>经历了一番自虐式开发后，<code>weapp-tailwindcss</code> <strong>v4.0</strong> 终于发布了！</p>
<p>在清理了一些自己当初写的屎山代码后，我终于实现了心心念念的两个大功能：</p>
<ol>
<li class=""><strong>支持 <code>tailwindcss@4.x</code> 版本</strong></li>
<li class=""><strong>支持 <code>tailwind-merge</code></strong></li>
</ol>
<blockquote>
<p>因为 <code>tailwindcss@4</code> 直接变成了一个样式预处理器，定位上类似 <code>Sass</code> / <code>Less</code>，所以相关的改动还是挺大的。目前 <code>weapp-tailwindcss@4</code> 版本也同时兼容 <code>tailwindcss 4+3+2(jit)</code> 三个版本了。</p>
</blockquote>
<p>想快速上手集成？欢迎访问 <a href="https://tw.icebreaker.top/" target="_blank" rel="noopener noreferrer" class="">weapp-tailwindcss 官网</a>！
如果你想进一步了解细节，下面有个示例，看看就懂了。</p>
<hr>
<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>
<p>这里我们以 <strong><code>uni-app</code>（Vue3 + Vite）</strong> 为例。</p>
<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>
<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>
<p>然后，把下面这段脚本加入 <code>package.json</code> 的 <code>scripts</code> 字段里：</p>
<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>
<blockquote>
<p>这个补丁是为了让 <code>tailwindcss@4</code> 认识 <code>rpx</code> 单位，否则它会以为 <code>rpx</code> 是个颜色单位，导致 <code>text-[40rpx]</code> 这样的样式翻车。</p>
</blockquote>
<hr>
<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>
<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>
<hr>
<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>
<p>在项目目录下创建 <code>main.css</code>，然后添加以下内容：</p>
<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>
<blockquote>
<p><strong>注意</strong>：对于实际运行时入口，现在更推荐直接写 <code>@import 'weapp-tailwindcss/index.css'</code>，而不是继续依赖 <code>rewriteCssImports</code> 去改写 <code>@import 'tailwindcss'</code>。</p>
</blockquote>
<p>接着在 <code>main.js</code> 里引用这个文件作为全局样式，然后直接运行：</p>
<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>
<p>在微信开发者工具中导入这个项目，在项目中使用 <code>tailwindcss</code> 原子类，即可看到效果。</p>
<hr>
<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>
<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>
<p><code>tailwindcss@4</code> 生成的样式广泛使用了 <code>@layer</code>、<code>color-mix</code>、<code>oklch</code> 等新特性，现代浏览器兼容性都不咋地，更别说小程序了。</p>
<p>所以无论是 H5 还是小程序，都需要 <code>postcss-preset-env</code> 来降级这些样式的生成结果。降级的程度取决于你的 <code>browserslist</code> 配置。</p>
<hr>
<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>
<p><code>tailwind-merge</code> 是一个 <code>tailwindcss</code> 运行时样式合并工具，它能自动合并重复的样式。不过，小程序端用 <code>tailwind-merge</code> 有点挑战，因为：</p>
<ul>
<li class=""><code>weapp-tailwindcss</code> <strong>在编译时</strong> 对 <code>tailwindcss</code> 样式做转义。</li>
<li class=""><code>tailwind-merge</code> <strong>在运行时</strong> 处理样式合并。</li>
</ul>
<p>这导致 <code>tailwind-merge</code> 处理已转义的 <code>class</code> 时，误以为它们是普通 <code>class</code>，而不是 <code>tailwindcss</code> 的原子类。</p>
<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>
<p>我研究了一下 <code>tailwind-merge</code> 的源码，尝试写一个 <code>plugin</code> 来解决这个问题，结果发现路走不通，因为：</p>
<ul>
<li class=""><code>tailwind-merge</code> 内部有一些硬编码规则，比如 <code>!</code> 代表 <code>!important</code>，无法通过 <code>plugin</code> 自定义。</li>
</ul>
<p>于是，我换了个思路，<strong>采用编译时感应、忽略转义，运行时合并转义的方式</strong> 解决了这个问题，这就是 <strong><code>@weapp-tailwindcss/merge</code></strong> 诞生的原因。</p>
<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>
<p>我利用 <code>babel</code> 的 <code>scope</code>（作用域） 和 <code>binding</code>（绑定） 增强了识别能力</p>
<p><code>scope</code> 简而言之就是 <code>js</code> 基础里的大括号, <code>binding</code> 就是一个变量真正的绑定</p>
<p>比如：</p>
<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>
<p>这段代码的作用域是全局，然后 <code>cn</code> 中 <code>a</code> 的绑定是一个 <code>VariableDeclaration</code> 它的 <code>init</code> 中，又引用了 <code>b</code> ，它的绑定也是一个 <code>VariableDeclaration</code> 初始化是一个 <code>StringLiteral</code></p>
<p>于是，在编译时，我们可以从 <code>cn</code> 递归向上找到 <code>a</code> 和 <code>b</code>，识别出完整的样式链。</p>
<p>当然，另一种方式是 <strong>自上而下查找</strong>，通过 <code>import</code> 语句分析模块依赖关系，甚至可以利用打包工具的模块分析图进行静态分析。</p>
<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>
<hr>
<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>
<p>在开发的时候，一定要把可能影响结果的方式，拆成多多阶段运行，比如在修改 <code>js ast</code> 的时候，不应该一边 <code>traverse</code> ，一边再用 <code>MagicString</code> 去修改，而是应该把需要修改的地方，先全部收集出来，然后在一个阶段统一去修改。</p>
<p>另外鉴别一个 <code>ast</code> 工具的成熟度，也应该考虑一下，作用域和绑定的关系，能否被解析出来。比如我们都知道 <code>js</code> 里面大概有 <code>5</code> 种行为会产生作用域:</p>
<ul>
<li class=""><strong>Program</strong>：全局作用域，整个文件的顶层作用域。</li>
<li class=""><strong>FunctionDeclaration / FunctionExpression / ArrowFunctionExpression</strong>：函数作用域（包括箭头函数）。</li>
<li class=""><strong>BlockStatement</strong>：块级作用域（由 <code>{}</code> 包裹，例如 if、for、while 中的块，在 ES6 中支持 let 和 const）。</li>
<li class=""><strong>ClassDeclaration / ClassExpression</strong>：类作用域（类的主体和方法会引入作用域）。</li>
<li class=""><strong>CatchClause</strong>：try-catch 中 catch 块的作用域。</li>
</ul>
<p>而绑定本身也是通过 <code>scope</code> 和它的 <code>parent</code> 依次向上去找的。</p>
<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>
<p>转眼我也到了而立之年，在这个行情下，毕业（失业）随时可能发生，当然也不敢毕业，毕竟上有老下有小，还有各种贷款。</p>
<p>技术学得越多，越觉得是个无底洞。而且掌握的东西多了，好像也没什么收益。更何况前端技术和那种 AI 技术相比，就像是过家家一样的。</p>
<p>开源呢？搞了半天也没啥产出，想成为开源明星，但自己技术水平也没到那个级别。</p>
<p>最近，我开始深入学习 <strong>云原生</strong>，因为我意识到，国内这种国情下 <strong>"技术是工具，业务才是目的"</strong>。</p>
<p>这导致我们技术人员的地位是非常低的，这点只要还在国内混就无法改变。</p>
<p>所以我们自己也不要对自己或者团队写的东西，搞得有多好多完美的似的。够用就行，多尝试，先快速搞他个 100 个应用，然后挑没死的去进行进一步的开发。</p>
<p>举个例子，现在让我从 0 到 1 搭建一个完整的业务系统，我可以全程自主搞定：</p>
<ul>
<li class=""><strong>前端</strong>：<code>Vue</code> / <code>React</code></li>
<li class=""><strong>后端</strong>：<code>Node.js</code>（任意框架）</li>
<li class=""><strong>数据库</strong>：<code>MongoDB</code> / <code>PostgreSQL</code></li>
<li class=""><strong>运维</strong>：<code>Docker</code> / <code>Serverless</code></li>
</ul>
<p>但问题是——<strong>开发成本和时间成本太高了！</strong></p>
<p>这种情况，我要自己去设计数据库，写 <code>openapi.yml</code> ，然后实现接口，还要自己写前端，和自己对接。写的差不多了还要测试，又是一堆 <code>bug</code>。</p>
<p>而且还可能遇到技术难点，之前我在 <code>Node.js</code> 中实现 <code>PostgreSQL</code> 的 <code>GraphQL</code> 方案，就折腾了好久。这种情况，为什么不直接上 <code>pg_graphql</code> 这种成熟的中间件呢？</p>
<p>所以，从商业角度来看：</p>
<ol>
<li class=""><strong>写尽可能少的代码</strong>，尽量利用成熟方案快速搭建业务。</li>
<li class=""><strong>赚钱才是第一要务</strong>，哪里有问题，先堆机器解决，实在搞不定了，再招专业人才处理。</li>
<li class=""><strong>多利用AI来降本增效</strong>，AI 成本比人低，所以招个高级，不如招个初级的，会用 AI 的，学习能力好的程序员，毕竟前端静态页面，AI 生成可快了</li>
</ol>
<p>所以，从一个程序员的视角出发:</p>
<p><strong>这是最好的时代，也是最坏的时代。</strong></p>
<hr>
<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>
<ul>
<li class=""><a href="https://tw.icebreaker.top/docs/migrations/v3" target="_blank" rel="noopener noreferrer" class="">weapp-tailwindcss 迁移文档</a></li>
<li class=""><a href="https://tw.icebreaker.top/docs/quick-start/v4" target="_blank" rel="noopener noreferrer" class="">Tailwindcss@4 各个框架集成方式</a></li>
<li class=""><a href="https://tailwindcss.com/docs/upgrade-guide" target="_blank" rel="noopener noreferrer" class="">Tailwindcss@4 升级指南</a></li>
<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>
</ul>
<blockquote>
<p>补充：classic <code>uni-app vue2 / webpack</code> 路线已经不再推荐，新项目请优先选择 <code>uni-app vue3 vite</code>。</p>
</blockquote>]]></content>
        <author>
            <name>icebreaker</name>
            <email>hi@sonofmagic.top</email>
            <uri>https://github.com/sonofmagic</uri>
        </author>
    </entry>
</feed>