承接 dxgx/blade-tailwind-extract 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

dxgx/blade-tailwind-extract

Composer 安装命令:

composer require dxgx/blade-tailwind-extract

包简介

Extract Tailwind CSS classes to reduce Livewire wire transfer size

README 文档

README

Latest Version on Packagist Total Downloads License

A Laravel package that dramatically reduces Livewire component wire transfer sizes by extracting long Tailwind CSS class strings into short, reusable CSS class names. Perfect for applications with large lists of Livewire components.

The Problem

When using Livewire with Tailwind CSS, long class strings in Blade templates get transferred over the wire on every component update:

<div class="flex flex-col gap-2 p-4 bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow">
    <!-- Large payload repeated for every list item -->
</div>

With 100 items in a list, this verbose markup significantly impacts performance.

The Solution

Blade Tailwind Extract extracts your Tailwind classes into a CSS file using @apply, replacing them with short class names:

Development Mode (Injected):

<div class="__card-wrapper__ flex flex-col gap-2 p-4 bg-white rounded-lg shadow-md __">
    <!-- Easy to edit inline -->
</div>

Production Mode (Extracted):

<div class="TW-a40f-card-wrapper">
    <!-- Minimal wire payload -->
</div>

Generated CSS:

.TW-a40f-card-wrapper {
    @apply flex flex-col gap-2 p-4 bg-white rounded-lg shadow-md;
}

The a40f hash is derived from the file path, ensuring no conflicts between files.

Features

  • Reduces Livewire wire payload - Short class names instead of verbose Tailwind strings
  • Automated class identification - Wrap command finds and marks repeated class lists
  • Intelligent deduplication - Identical class lists share the same wrapper name
  • Comprehensive pattern support - Works with class="", wire:class="", :class (static & ternary), x-bind:class, @class([]) (simple & conditional)
  • Bidirectional - Extract for production, restore for development
  • Safe - Reserved classes like group, group/, and peer are automatically skipped with informative warnings
  • Collision-free - File-based hashing prevents class name conflicts
  • Pattern matching - Process specific files or entire directories
  • Configurable - Customize prefix, hash length, output path, and more

Installation

Install via Composer as a dev dependency (this is a development tool, not needed in production):

composer require dxgx/blade-tailwind-extract --dev

Publish the configuration file (optional):

php artisan vendor:publish --tag=blade-tailwind-extract-config

Usage

Quick Start: 3-Step Workflow

  1. Wrap - Automatically identify and mark class lists with semantic names
  2. Extract - Convert marked classes into compact CSS
  3. Restore - Bring back inline classes for editing

Wrap Classes (Preparation Step)

Before extraction, use the wrap command to automatically identify and mark repeated Tailwind class lists with semantic wrapper names. This helps you see which classes will be extracted and ensures consistent naming for identical class lists.

# Wrap classes in a specific file
php artisan dg:blade-tailwind:wrap resources/views/components/card.blade.php

# Wrap classes in an entire directory (recursive)
php artisan dg:blade-tailwind:wrap ./resources/views/livewire

# Wrap classes using a pattern
php artisan dg:blade-tailwind:wrap "*preview*"
php artisan dg:blade-tailwind:wrap "*card*.blade.php"

# Wrap multiple files or patterns (comma-separated)
php artisan dg:blade-tailwind:wrap "card.blade.php,list.blade.php"
php artisan dg:blade-tailwind:wrap "*preview*,*card*"

# Wrap all files in search_path (with confirmation prompts)
php artisan dg:blade-tailwind:wrap

# Skip confirmations for automated workflows
php artisan dg:blade-tailwind:wrap --yy

# Preview changes without modifying files
php artisan dg:blade-tailwind:wrap "*preview*" --dry-run

# Customize minimum class count (default: 3)
php artisan dg:blade-tailwind:wrap components/card.blade.php --min=4

# Skip class lists containing specific prefix (default: TW)
php artisan dg:blade-tailwind:wrap components/card.blade.php --skip-prefix=CUSTOM

What it does:

  • Scans for class lists with 3+ classes (configurable via --min)
  • Automatically deduplicates: identical class lists get the same wrapper name
  • Generates semantic names: __adjective-noun-number__ (e.g., __happy-cat-1__)
  • Skips patterns that should never be extracted: __, material-symbols-outlined, TW-
  • Shows summary of changes with occurrence counts

Before wrapping:

<div class="flex items-center justify-between gap-4 p-4">Item 1</div>
<div class="flex items-center justify-between gap-4 p-4">Item 2</div>
<div class="bg-blue-500 text-white rounded p-2">Button</div>

After wrapping:

<div class="__happy-cat-1__ flex items-center justify-between gap-4 p-4 __">Item 1</div>
<div class="__happy-cat-1__ flex items-center justify-between gap-4 p-4 __">Item 2</div>
<div class="__quick-fox-2__ bg-blue-500 text-white rounded p-2 __">Button</div>

Notice how identical class lists share the same wrapper name (happy-cat-1), while different lists get unique names.

Supported Class Attribute Patterns

The wrap command supports all major Blade/Livewire/Alpine.js class attribute patterns:

1. Static class Attribute

Standard HTML class attribute:

<!-- Before -->
<div class="flex items-center justify-between gap-4 p-4">Content</div>

<!-- After wrapping -->
<div class="__happy-cat-1__ flex items-center justify-between gap-4 p-4 __">Content</div>

2. Livewire wire:class Attribute

Livewire's reactive class binding:

<!-- Before -->
<div wire:class="flex items-center space-x-2 p-4">Content</div>

<!-- After wrapping -->
<div wire:class="__sunny-owl-2__ flex items-center space-x-2 p-4 __">Content</div>

3. Alpine.js :class with Static Classes

Dynamic class binding with static string:

<!-- Before -->
<button :class="flex items-center justify-center rounded-md p-2">Click</button>

<!-- After wrapping -->
<button :class="__quick-fox-3__ flex items-center justify-center rounded-md p-2 __">Click</button>

4. Alpine.js :class with Ternary Expression

Conditional class switching with ternary operator:

<!-- Before -->
<button :class="isActive 
    ? 'bg-blue-500 text-white border-blue-500 hover:bg-blue-700' 
    : 'bg-white text-gray-700 border-gray-300 hover:bg-gray-100'">
    Toggle
</button>

<!-- After wrapping (both branches wrapped independently) -->
<button :class="isActive 
    ? '__brave-ant-4__ bg-blue-500 text-white border-blue-500 hover:bg-blue-700 __' 
    : '__calm-bee-5__ bg-white text-gray-700 border-gray-300 hover:bg-gray-100 __'">
    Toggle
</button>

5. Alpine.js x-bind:class Attribute

Explicit Alpine binding syntax:

<!-- Before -->
<div x-bind:class="flex flex-col gap-4 p-6">Content</div>

<!-- After wrapping -->
<div x-bind:class="__lazy-ram-6__ flex flex-col gap-4 p-6 __">Content</div>

6. Blade @class Directive (Simple Array)

Blade's class directive with simple string:

<!-- Before -->
<div @class(['flex items-center justify-between p-4 border rounded'])>Item</div>

<!-- After wrapping -->
<div @class(['__eager-jay-7__ flex items-center justify-between p-4 border rounded __'])>Item</div>

7. Blade @class Directive (Conditional Array)

Blade's class directive with conditional classes:

<!-- Before -->
<button @class([
    'flex items-center justify-center size-9',
    'text-gray-200 cursor-not-allowed opacity-50' => !$isEnabled,
    'text-blue-500 hover:text-blue-600 hover:bg-blue-50' => $isEnabled
])>Action</button>

<!-- After wrapping (all qualifying strings wrapped) -->
<button @class([
    '__gentle-fox-8__ flex items-center justify-center size-9 __',
    '__jolly-cat-9__ text-gray-200 cursor-not-allowed opacity-50 __' => !$isEnabled,
    '__kind-dog-10__ text-blue-500 hover:text-blue-600 hover:bg-blue-50 __' => $isEnabled
])>Action</button>

8. Combined Static and Dynamic Classes

Elements with both static class and dynamic :class attributes (wrapped independently):

<!-- Before -->
<button 
    :class="active ? 'bg-blue-500 text-white' : 'bg-gray-200 text-gray-700'"
    class="flex items-center justify-center rounded-md">
    Toggle
</button>

<!-- After wrapping -->
<button 
    :class="active ? 'bg-blue-500 text-white' : 'bg-gray-200 text-gray-700'"
    class="__lively-hen-11__ flex items-center justify-center rounded-md __">
    Toggle
</button>

Note: In this example, the ternary branches have only 2 classes each (below the default minimum of 3), so they aren't wrapped. The static class attribute with 4 classes is wrapped.

Pattern Behavior Summary

  • All patterns respect the minimum class count (default: 3, configurable via --min)
  • Identical class lists across any pattern type share the same wrapper name
  • Already wrapped class lists are skipped (won't double-wrap)
  • Protected patterns (containing __, material-symbols-outlined, or starting with TW-) are never wrapped
  • Ternary expressions have both branches examined and wrapped independently if they meet the threshold
  • Conditional arrays in @class have all string values examined and wrapped independently
  • Static and dynamic attributes on the same element are processed independently

Special Cases Handled

The wrap command correctly handles complex Tailwind patterns:

Arbitrary values with quotes:

<!-- Classes with arbitrary values containing quotes are fully supported -->
<div class="after:content-[''] before:content-['>'] w-full">Content</div>

<!-- After wrapping (closing __ correctly placed at end) -->
<div class="__happy-cat-1__ after:content-[''] before:content-['>'] w-full __">Content</div>

Reserved classes (automatically skipped during extraction):

  • Classes containing group, group/, or peer can be wrapped but won't be extracted
  • The extract command will skip these patterns with informative warnings
  • This prevents breaking Tailwind's parent-child and sibling selectors

Extract Classes (Development → Production)

The target parameter accepts multiple formats:

0. No target (processes all files in search_path with confirmation):

# Prompts for confirmation before processing all .blade.php files
php artisan dg:blade-tailwind:extract

# Shows:
# 1. First confirmation: "Are you sure you want to process X file(s)?"
# 2. List of files to be processed (max 50 shown)
# 3. Second confirmation: "Proceed with extract operation?"
# Cancel at any prompt to abort

1. Directory (processes all .blade.php files recursively):

php artisan dg:blade-tailwind:extract ./resources/views
php artisan dg:blade-tailwind:extract ./resources/views/components

2. Single file path:

php artisan dg:blade-tailwind:extract resources/views/livewire/garage/list/item.blade.php
php artisan dg:blade-tailwind:extract components/card.blade.php

3. Pattern matching (searches in configured search_path):

php artisan dg:blade-tailwind:extract *preview*
php artisan dg:blade-tailwind:extract *card*.blade.php
php artisan dg:blade-tailwind:extract list-item

4. Multiple targets (comma-separated, can mix patterns and files):

php artisan dg:blade-tailwind:extract image-preview.blade.php,card-list.blade.php
php artisan dg:blade-tailwind:extract *preview*,*card*,header.blade.php

Restore Classes (Production → Development)

Restore Tailwind classes for editing (accepts same target formats as extract):

# No target (with confirmation prompts)
php artisan dg:blade-tailwind:restore

# Directory
php artisan dg:blade-tailwind:restore ./resources/views

# Pattern
php artisan dg:blade-tailwind:restore *preview*

# Specific file
php artisan dg:blade-tailwind:restore components/card.blade.php

# Multiple
php artisan dg:blade-tailwind:restore *card*,*list*

Automatic CSS Cleanup: When you restore classes, the command automatically removes the restored CSS rules from the output file, keeping your CSS clean and preventing stale rules from accumulating.

Workflow

Option A: Automated (New Files)

  1. Wrap your Blade file to identify and mark repeated class lists
  2. Extract marked classes to generate compact CSS
  3. Commit both Blade files and CSS file

Option B: Manual (Existing Workflow)

  1. Manually add __name__ classes __ markers around classes you want to extract
  2. Extract to generate compact CSS
  3. Commit both Blade files and CSS file

Option C: Editing Existing Extracted Files

  1. Restore before editing (restore Tailwind classes to inline format)
  2. Edit your Blade files normally
  3. Extract after editing (compact back to short class names)
  4. Commit both Blade files and CSS file

You can add markers manually or use the wrap command to add them automatically.

Manual approach: Wrap classes you want to extract with double underscores:

<div class="__card-wrapper__ flex flex-col gap-2 p-4 __">
    <!-- Content -->
</div>

Automated approach: Use the wrap command to automatically identify and mark class lists:

php artisan dg:blade-tailwind:wrap your.view.namelass="__card-wrapper__ flex flex-col gap-2 p-4 __">
    <!-- Content -->
</div>

After extraction:

<div class="TW-a40f-card-wrapper">
    <!-- Content -->
</div>

Important: The group and peer Tailwind classes cannot be extracted (they break parent-child selectors). The tool will warn and skip them automatically.

Configuration

Configuration file: config/dg-blade-tailwind-extract.php

return [
    // CSS output file path
    'css_output_path' => resource_path('css/tw-extracted.css'),

    // Class prefix (default: TW)
    'class_prefix' => 'TW',

    // Hash length for file-based collision avoidance (default: 4)
    'hash_length' => 4,

    // Default search path when using pattern matching
    'search_path' => resource_path('views'),

    // Directories to ignore during file scanning
    'ignored_directories' => [
        './vendor/',
        'vendor/',
        './node_modules/',
        'node_modules/',
    ],

    // Reserved Tailwind classes that cannot be extracted
    // These use parent-child selectors that break when extracted to @apply
    // Also checks for variants like 'group/' and 'peer/'
    'reserved_classes' => [
        'group',
        'peer',
    ],

    // Safety limit for extraction loops
    'max_iterations' => 10,
];

Note: When the extract command encounters patterns containing reserved classes (like group, group/item, or peer), it will:

  • Skip extraction for that pattern
  • Display an informative warning showing which patterns were skipped and why
  • Continue processing other patterns in the same file

Advanced Usage

Custom CSS Output Path

php artisan dg:blade-tailwind:extract ./resources/views --css-file=resources/css/my-custom.css

Working with Livewire Components

The tool works seamlessly with Livewire's ->class([...]) method and Blade's @class([...]) directive:

Before Extraction:

<div @class([
    '__item-wrapper__ flex gap-2 p-4 __',
    'bg-green-50' => $selected,
])>

After Extraction:

<div @class([
    'TW-a40f-item-wrapper',
    'bg-green-50' => $selected,
])>

Wrap Mode (Optional Preparation):**

  • Scans for class lists with minimum number of classes (default: 3)
  • Automatically deduplicates identical class lists
  • Generates semantic wrapper names: __adjective-noun-number__
  • Marks class lists: __name__ classes __
  • Skips protected patterns (material-symbols-outlined, TW-, etc.)
  • Shows summary with occurrence counts
  1. Extract Mode:

    • Scans for __name__ tailwind classes __ patterns
    • Generates short class names: {PREFIX}-{HASH}-{NAME}
    • Writes @apply rules to the CSS file
    • Replaces inline classes with short names
  2. Restore Mode:

    • Reads existing @apply rules from CSS file
    • Finds short class names in Blade files
    • Restores original Tailwind class strings
  3. Extract Mode:

    • Scans for __name__ tailwind classes __ patterns
    • Generates short class names: {PREFIX}-{HASH}-{NAME}
    • Writes @apply rules to the CSS file
    • Replaces inline classes with short names
  4. Restore Mode:

    • Reads existing @apply rules from CSS file
    • Finds short class names in Blade files
    • Restores original Tailwind class strings
  5. File Hash:

    • Each file gets a unique 4-character hash (configurable)
    • Prevents class name collisions between files
    • Allows same semantic name (card-wrapper) across different files

Gotchas & Best Practices

  • ⚠️ Never manually edit TW-* class names in extracted files
  • ⚠️ Always restore → edit → extract workflow
  • ⚠️ Don't extract group, peer, or other parent-modifier classes
  • ✅ Commit both Blade and CSS files together
  • ✅ Run extract before deploying to production
  • ✅ Run restore before starting development

Testing

composer test

Security

If you discover any security-related issues, please email packages@dxgx.dev instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.

Changelog

Please see CHANGELOG.md for recent changes.

Contributing

Please see CONTRIBUTING.md for details.

dxgx/blade-tailwind-extract 适用场景与选型建议

dxgx/blade-tailwind-extract 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 65 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 05 月 18 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「performance」 「laravel」 「blade」 「tailwindcss」 「livewire」 「dxgx」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 dxgx/blade-tailwind-extract 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 dxgx/blade-tailwind-extract 我们能提供哪些服务?
定制开发 / 二次开发

基于 dxgx/blade-tailwind-extract 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 65
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 12
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-05-18