mathiasgrimm/laravel-puff 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

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

mathiasgrimm/laravel-puff

Composer 安装命令:

composer require mathiasgrimm/laravel-puff

包简介

On-demand warming for scale-to-zero Laravel apps - no cold start when it counts. A tiny activity-driven warmer that wakes the stack just before the user's request.

README 文档

README

On-demand warming for scale-to-zero Laravel apps - no cold start when it counts.

Laravel Cloud's scale-to-zero is spectacular: it parks your environment when idle and wakes it in ~500ms. laravel-puff makes it even better. When a visitor shows intent to act (moving the mouse, typing, scrolling, or returning to the tab), the browser fires a lightweight, throttled POST /puff that warms your database and Redis ahead of the real request, so the cold start is already paid for by the time they click.

It is not a poller. A heartbeat that pings on a timer would keep the environment awake forever and defeat scale-to-zero. laravel-puff only fires on genuine human activity, so an idle app still sleeps (and stops billing) the moment everyone leaves.

  • Tiny, framework-agnostic JS core (no axios, no Wayfinder, no Inertia coupling).
  • Throttled (one request per 60s by default), public by default, runs everywhere.
  • Ships Vue and React adapters today; Svelte and Livewire/Blade are additive.

Requirements

  • PHP 8.2+
  • Laravel 11, 12, or 13

Installation

composer require mathiasgrimm/laravel-puff
php artisan puff:install

puff:install does three things:

  1. Publishes the config and the warm-up JS (the framework-agnostic core + a framework adapter) into resources/js/laravel-puff/.
  2. Wires a global startPuff() call into your JS entry (resources/js/app.ts or resources/js/app.tsx), so warming runs on every page out of the box.
  3. Adds puff:publish to your composer.json post-update-cmd (a one-line string edit that leaves the rest of the file untouched) so the stub re-syncs with the installed package version on every composer update. If you have no post-update-cmd yet, it prints the line to add instead of reformatting.

The stack is auto-detected (Vue or React) from your entry file and package.json, so the Vue and React starter kits both work with a bare php artisan puff:install. If it can't tell, the command stops and asks you to pass --stack=vue or --stack=react rather than guessing.

Flags: --no-wire to skip step 2, --no-scripts to skip step 3, --entry=path/to/app.tsx to target a different entry file, --force to overwrite published files, --stack=vue|react to override auto-detection.

That's it. Move the mouse or switch back to the tab and you'll see a single POST /puff204, throttled to at most one per 60 seconds.

Keeping the stub up to date

The published JS (resources/js/laravel-puff/) is a copy, so composer update alone won't refresh it. Treat that folder as package-owned (don't edit it). puff:install already adds the following to your composer.json, so every update re-publishes the stub from the installed version:

"scripts": {
    "post-update-cmd": [
        "@php artisan puff:publish --ansi"
    ]
}

If you installed with --no-scripts, add it yourself. puff:publish force-republishes the core + the adapter for whichever stack you installed (it never touches config/puff.php, which stays yours). You can also run it by hand any time: php artisan puff:publish.

Wiring it yourself

startPuff() is the framework-agnostic core (no Vue required). If you skipped --no-wire, add it to your entry manually:

import { startPuff } from '@/laravel-puff/puff';

startPuff();

Vue composable (opt-in)

Prefer per-component control with automatic cleanup on unmount? Use the Vue composable in a layout's <script setup> instead:

import { usePuff } from '@/laravel-puff/usePuff';

usePuff();

To restrict warming (e.g. authenticated users only), pass an isEnabled predicate:

import { usePage } from '@inertiajs/vue3';

usePuff({ isEnabled: () => !!usePage().props.auth?.user });

React hook (opt-in)

On React (e.g. the React starter kit), the published usePuff is a hook that starts warming on mount and cleans up on unmount. Call it once in a layout:

import { usePuff } from '@/laravel-puff/usePuff';

usePuff();

To restrict warming (e.g. authenticated users only), pass an isEnabled predicate:

import { usePage } from '@inertiajs/react';

usePuff({ isEnabled: () => !!usePage().props.auth?.user });

Configuration

config/puff.php:

return [
    'register_route' => true,          // set false to define the route yourself
    'path'           => 'puff',        // POST /puff
    'name'           => 'puff',        // route name
    'middleware'     => ['web'],       // public by default; add 'auth' to restrict
    'throttle'       => '60,1',         // rate limit (maxAttempts,decayMinutes); null to disable
    'warm' => [
        'database' => [
            'enabled'     => true,
            'connections' => [],       // empty = default; or ['mysql', 'reports']
        ],
        'redis' => [
            'enabled'     => true,
            'connections' => [],       // empty = default; or ['default', 'cache']
        ],
    ],
];

CSRF works out of the box: the core reads Laravel's XSRF-TOKEN cookie and sends it as the X-XSRF-TOKEN header, so no meta tag or extra setup is needed.

Frontend options

usePuff(options) / startPuff(options) accept:

Option Default Description
url '/puff' Endpoint to POST to
intervalSeconds 60 Minimum gap between requests, also measured from page load
events ['mousemove','keydown','scroll','touchstart'] Activity events (on window) that trigger a warm
method 'POST' HTTP method
warmOnVisible true Also warm when the user returns to the tab (visibilitychange)
isEnabled always-on Return false to skip (e.g. for guests)

The framework-agnostic core (resources/js/laravel-puff/puff.ts) exports startPuff(options): () => void and returns a stop() cleanup, if you want to wire it up yourself in another framework.

When the first puff fires

Puff does not warm on page load. The page you just loaded was served by the app, so the stack is already warm, and firing then would be wasted. The throttle is seeded from load time, so the earliest a puff can go out is one intervalSeconds (60s by default) later, and only on genuine activity (mouse, keyboard, scroll, touch, or returning to the tab). That is by design: it covers the window where an idle stack may have scaled to zero, without spending a request while it is still warm.

Tuning intervalSeconds to your scale-to-zero setting

The best value mirrors how long your environment stays up while idle. If Laravel Cloud is configured to sleep after 5 minutes of inactivity, a puff only needs to land inside that window to keep the stack warm for an active user, so an intervalSeconds around that idle timeout (e.g. 300 for 5 minutes) is a good fit: it warms often enough to stay ahead of a sleep, while keeping request volume to a minimum. Setting it much shorter than your idle timeout just adds requests without buying earlier coverage; setting it much longer risks the stack napping between puffs. When in doubt, match it to your scale-to-zero idle setting.

Testing

composer test

License

MIT. See LICENSE.md.

统计信息

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

GitHub 信息

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

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-06-28

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固