@freddy38510/dropcss

中文文档
2.5.0发布于 2023/02/23MITTypeScriptESM + CJS零依赖近 30 天下载 2420

一个特别快、彻底和微小的未使用CSS清洁器

中文使用文档
文档目录12 节
  1. 空投控制系统
  2. 导言
  3. 现场德莫: https://codepen.io/leeoniya/pen/LvbRyq 页面存档备份,存于互联网档案馆
  4. 安装
  5. 用法 & API
  6. 特征
  7. 业绩
  8. Java 脚本执行
  9. 累积白名单
  10. 特殊/ 逃脱顺序
  11. 洞穴
  12. 鸣谢

空投控制系统

一个 特别快彻底而微小(~10 KB 分钟(未使用的CSS清洁剂) (麻省理工学院许可)


导言

DropCSS 将您的 HTML 和 CSS 作为输入, 只返回使用的 CSS 作为输出 。 它的自定义 HTML 和 CSS 解析器高度优化了99%的使用大小写,从而避免了处理错误的标记或样式表的间接费用,因此需要很好的输入. 对复杂的逃逸规则的处理很少,因此总是会有无法由DropCSS处理的有效输入的情况;对于这些不常见的情况,请 开始讨论。当 HTML 特性允许时 html, (中文(简体) ), head, (中文(简体) ), body 财务报告和审定财务报表 tbody dropCSS 将不作这样的假设; 选择器将只保留给可从提供的标记中解析的标记.

通过一个结构优化器来运行你的CSS也是个好主意 干净, (中文(简体) ), 顺位, (中文(简体) ), 克斯纳诺 或者说 疯子 以重新分组选择者,合并冗余规则等。 在DropCSS之后这样做也许有道理,因为DropCSS会留下多余的块,例如. .foo, .bar { color: red; } .bar { width: 50%; } - > (中文) .bar { color: red; } .bar { width: 50%; } 若为 .foo 正在离开你的标记.

更多关于这个项目的背面故事和讨论:v0.1.0 alpha: /r/javascript, (中文(简体) ), 黑客新闻 和 v1.0.0 发布 : /r/javascript。 。 。 .


现场德莫: https://codepen.io/leeoniya/pen/LvbRyq 页面存档备份,存于互联网档案馆


安装

npm install -D dropcss

用法 & API

const dropcss = require('dropcss');

let html = `
    <html>
        <head></head>
        <body>
            <p>Hello World!</p>
        </body>
    </html>
`;

let css = `
    .card {
      padding: 8px;
    }

    p:hover a:first-child {
      color: red;
    }
`;

const whitelist = /#foo|\.bar/;

let dropped = new Set();

let cleaned = dropcss({
    html,
    css,
    shouldDrop: (sel) => {
        if (whitelist.test(sel))
            return false;
        else {
            dropped.add(sel);
            return true;
        }
    },
});

console.log(cleaned.css);

console.log(dropped);

这个 shouldDrop 钩对无法匹配的 CSS 选择器 html返回时 false 以保留选择器或 true 放弃它.


特征

  • 支持的选择者

    常见属性立场位置(类型)其它
    * - 普遍性
    <tag> - 标签
    # - 编号
    . - 类
      - 后裔
    > - 儿童
    + - 相邻的兄弟
    ~ - 将军兄弟
    [attr]
    [attr=val]
    [attr*=val]
    [attr^=val]
    [attr$=val]
    [attr~=val]
    :first-child
    :last-child
    :only-child
    :nth-child()
    :nth-last-child()
    :first-of-type
    :last-of-type
    :only-of-type
    :nth-of-type()
    :nth-last-of-type()
    :not()
    :is()
    :has()
  • 保留所有无法从解析的 HTML 中决定性地检查的瞬态伪类和伪元素选择器 .

  • 清除未使用 @font-face 财务报告和审定财务报表 @keyframes 块.

  • 清除未使用 CSS 变量。 。 。 .

  • 深入解析综合CSS变量,例如:

    :root {
      --font-style: italic;
      --font-weight: bold;
      --line-height: var(--height)em;
      --font-family: 'Open Sans';
      --font: var(--font-style) var(--font-weight) 1em/var(--line-height) var(--font-family);
      --height: 1.6;
    }
    
    @font-face {
      font-family: var(--font-family);
      src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"),
           url("/fonts/OpenSans-Regular-webfont.woff") format("woff");
    }
    
    body {
      font: var(--font);
    }
    

业绩

投入

测试.html

  • 18.8 KB 改进
  • 502个圆顶节点通过 document.querySelectorAll("*").length

样式.min.css

  • 27.67 KB 组合、优化和精密化 干净
  • 内容: 靴子 重新启动.css,一个内部弹性框网格,全局布局,导航栏,颜色和页面专用样式. (网格占起步重量的~85%,媒体查询和重复次数很多)

产出

自由大小w/ deps 输出大小 减少 时间过后 未使用的字节( test.html 覆盖)
调试CSS 58.4 KB (韩语)
6个文件, 2个文件夹
6.58 KB (韩语) 76.15% 21分钟 575个/ 8.5%
失败 13.5 甲基溴
2 829个文件, 301个文件夹
6.72 KB (韩语) 75.71% 385分钟 638%/9.3%
清洗器 2.69 甲基溴
560个文件, 119个文件夹
8.01 KB (韩语) 71.05% (中文(简体) ) 88分钟 1 806个/ 22.0%
净化CSS 3.46 甲基溴
792个文件, 207个文件夹
15.46 KB (韩语) 44.34% 173分钟 9,440人/59.6% 妇女

页:1

  • 大约400个"未被使用的字节"是由于一个明确的/共享的白名单,而不是由于工具无法检测/移动CSS.
  • 大约175个"未被使用的字节"是由于销售商前置(-moz,-ms)属性和选择器在Chrome中不活动,用于测试覆盖.
  • 清洗不支持属性或复杂选择器 : 问题110。 。 。 .

满 压力测试 还提供.


Java 脚本执行

DropCSS 不装入外部资源或执行 <script> 标记,所以您的 HTML 必须完全形成( 或 SSR' d) 。 或者,你可以使用 傀儡 和本地的 http 服务器以获得充电 <script> 执行.

这是35行脚本 具体是:

const httpServer = require('http-server');
const puppeteer = require('puppeteer');
const fetch = require('node-fetch');
const dropcss = require('dropcss');

const server = httpServer.createServer({root: './www'});
server.listen(8080);

(async () => {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto('http://127.0.0.1:8080/index.html');
    const html = await page.content();
    const styleHrefs = await page.$$eval('link[rel=stylesheet]', els => Array.from(els).map(s => s.href));
    await browser.close();

    await Promise.all(styleHrefs.map(href =>
        fetch(href).then(r => r.text()).then(css => {
            let start = +new Date();

            let clean = dropcss({
                css,
                html,
            });

            console.log({
                stylesheet: href,
                cleanCss: clean.css,
                elapsed: +new Date() - start,
            });
        })
    ));

    server.close();
})();

累积白名单

或许您想要取出一个巨大的 CSS 文件, 并针对多个 HTML 源对其进行清除, 从而保留 HTML 源中出现的任何选择器 。 在使用 Puppeteer 来引用不同的应用程序状态时,这也适用,以确保DropCSS在清理 CSS 之前将每个状态都考虑在内. 这个想法相当简单:

  1. 运行 DropCSS 以对抗 HTML 源 .
  2. 从每个结果中累积出一个白名单.
  3. 在空的 HTML 字符串下运行 DroppCSS, 只依靠累积的白名单 .

见 /减少/累积.js数字 :

const dropcss = require('dropcss');

// super mega-huge combined stylesheet
let css = `
    em {
        color: red;
    }

    p {
        font-weight: bold;
    }

    .foo {
        font-size: 10pt;
    }
`;

// html of page (or state) A
let htmlA = `
    <html>
        <head></head>
        <body>
            <em>Hello World!</em>
        </body>
    </html>
`;

// html of page (or state) B
let htmlB = `
    <html>
        <head></head>
        <body>
            <p>Soft Kitties!</p>
        </body>
    </html>
`;

// whitelist
let whitelist = new Set();

function didRetain(sel) {
    whitelist.add(sel);
}

let resA = dropcss({
    css,
    html: htmlA,
    didRetain,
});

let resB = dropcss({
    css,
    html: htmlB,
    didRetain,
});

// final purge relying only on accumulated whitelist
let cleaned = dropcss({
    html: '',
    css,
    shouldDrop: sel => !whitelist.has(sel),
});

console.log(cleaned.css);

特殊/ 逃脱顺序

DropCSS很愚蠢,会像大众一样,对不寻常的选手窒息 尾风 CSS 框架:

class 属性可以看起来像:

<div class="px-6 pt-6 overflow-y-auto text-base lg:text-sm lg:py-12 lg:pl-6 lg:pr-8 sticky?lg:h-(screen-16)"></div>
<div class="px-2 -mx-2 py-1 transition-fast relative block hover:translate-r-2px hover:text-gray-900 text-gray-600 font-medium"></div>

...CSS是这样的:

.sticky\?lg\:h-\(screen-16\){...}
.lg\:text-sm{...}
.lg\:focus\:text-green-700:focus{...}

哎哟.

解决方案是将 HTML 和 CSS 中的已逃出字符临时替换为一些匹配的独有字符串 /[\w-]/。这使得 DroppCSS 的标注器能够将类名视为一个相接的东西。 经过处理后,我们只需扭转操作.

// remap
let css2 = css
    .replace(/\\\:/gm, '__0')
    .replace(/\\\//gm, '__1')
    .replace(/\\\?/gm, '__2')
    .replace(/\\\(/gm, '__3')
    .replace(/\\\)/gm, '__4');

let html2 = html.replace(/class=["'][^"']*["']/gm, m =>
    m
    .replace(/\:/gm, '__0')
    .replace(/\//gm, '__1')
    .replace(/\?/gm, '__2')
    .replace(/\(/gm, '__3')
    .replace(/\)/gm, '__4')
);

let res = dropcss({
    css: css2,
    html: html2,
});

// undo
res.css = res.css
    .replace(/__0/gm, '\\:')
    .replace(/__1/gm, '\\/')
    .replace(/__2/gm, '\\?')
    .replace(/__3/gm, '\\(')
    .replace(/__4/gm, '\\)');

这个表演的绕行工作可以让DroppCSS在不出问题的情况下处理 Tailwind \o/,并且容易适应支持其他"感兴趣的"案例. 记住一件事 shouldDrop() 将调用含有临时替换的选取器,而不是原选取器,因此如果 shouldDrop() 用于测试某些白名单.


洞穴

  • 未测试或设计用于处理不正确的 HTML 或 CSS
  • 在您的 HTML 或 CSS 中过度逃出或保留字符可以打破 DroppCSS 的解析器

鸣谢

  • 费利克斯·伯姆 检查 - 这不是很多代码,但得到 An+B 表达式测试完全正确令人沮丧。 在发现这个微小的解决方案之前,我得到了一部分.
  • 瓦迪姆·基鲁克欣的 虚拟化 - 基准和测试代码使用这个微小的前置物,从而更容易发现输出diffs的差异.

版本与发布记录

@freddy38510/dropcss 共发布 7 个版本,首次发布于 2021/06/30;过去 12 个月发布了 0 个版本。

版本发布日期距今
2.5.0latest2023/02/233 年前
2.4.02022/08/124 年前
2.3.22021/12/214 年前
2.3.12021/08/255 年前
2.3.02021/08/245 年前
2.2.02021/07/055 年前

关于 @freddy38510/dropcss 的常见问题

@freddy38510/dropcss 是什么?

一个特别快、彻底和微小的未使用CSS清洁器

如何安装 @freddy38510/dropcss?

在项目目录运行 npm install @freddy38510/dropcss,或使用 pnpm add / yarn add / bun add。

@freddy38510/dropcss 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@freddy38510/dropcss 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@freddy38510/dropcss 可以商用吗?

@freddy38510/dropcss 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@freddy38510/dropcss 的下载量有多少?

根据 npm 官方下载统计,@freddy38510/dropcss 在 2026/10/16 – 2026/11/14 这 30 天内被下载 2,420 次,日均约 81 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@freddy38510/dropcss 还在维护吗?

最新版本 2.5.0 发布于 2023/02/23(3 年前);至今共发布 7 个版本,其中过去 12 个月发布了 0 个;首次发布于 2021/06/30。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。