如何在Laravel Vapor中加载Critical CSS?
适配Laravel Vapor的Critical CSS最优加载方案
问题背景
环境版本:PHP 8.0.2,Laravel 9.19,使用laravel-mix-criticalcss生成Critical CSS,通过Laravel Vapor部署时静态资源自动上传至S3,需要找到最优方案将Critical CSS内联到页面头部。曾尝试将Critical CSS生成为Blade模板但无法修改输出文件名,也考虑过从S3加载但担心速度问题,疑问file_get_contents是否为通用最优方案、是否会拖慢Vapor服务器。
最优解决方案
1. 构建阶段生成Blade模板(推荐)
既然laravel-mix-criticalcss无法直接自定义输出路径/文件名,可通过构建后脚本将生成的Critical CSS文件转换为Blade模板,这样在页面中直接@include即可,完全避免运行时文件读取操作。
修改webpack.mix.js,添加构建完成后的处理逻辑:
const fs = require('fs'); const path = require('path'); mix.postCss('resources/css/app.css', 'public/css', [ require('tailwindcss'), ]) .criticalCss({ enabled: mix.inProduction(), paths: { base: 'http://localhost:8081/', templates: 'public/css/', }, urls: [ {url: '', template: 'index'}, ], options: { minify: true, }, }) .version() .then(() => { // 构建完成后,将critical css转换为blade模板 const criticalCssPath = path.resolve(__dirname, 'public/css/index_critical.min.css'); const bladeTemplatePath = path.resolve(__dirname, 'resources/views/components/critical-index.blade.php'); if (fs.existsSync(criticalCssPath)) { const cssContent = fs.readFileSync(criticalCssPath, 'utf8'); fs.writeFileSync(bladeTemplatePath, `<style>${cssContent}</style>`); } });
之后在header.blade.php中直接引入该模板:
@include('components.critical-index')
2. 优化file_get_contents方案(适配Vapor)
如果不想修改构建流程,可优化现有file_get_contents方案,结合Laravel缓存避免重复读取磁盘文件——Vapor的Lambda容器文件系统读取开销不大,但频繁读取仍会浪费资源,缓存能彻底解决这个问题。
修改header.blade.php:
@php $criticalCss = Cache::rememberForever('critical:index', function () { $cssPath = public_path('css/index_critical.min.css'); return file_exists($cssPath) ? file_get_contents($cssPath) : ''; }); @endphp @if($criticalCss) <style>{!! $criticalCss !!}</style> @endif
- 用
Cache::rememberForever将Critical CSS缓存到Vapor支持的缓存系统(默认是Redis),只有第一次请求会读取磁盘,后续直接从缓存获取,完全不影响性能。 - 注意:如果后续Critical CSS内容更新,部署时需要清除对应缓存键(可在部署脚本中添加
php artisan cache:forget critical:index)。
3. S3读取+缓存备选方案
如果Critical CSS文件仅存在于S3(本地无文件),可通过Laravel的Storage facade读取S3文件,同样结合缓存优化速度:
@php $criticalCss = Cache::rememberForever('critical:index', function () { return Storage::disk('s3')->exists('css/index_critical.min.css') ? Storage::disk('s3')->get('css/index_critical.min.css') : ''; }); @endphp @if($criticalCss) <style>{!! $criticalCss !!}</style> @endif
- 该方案仅在无法获取本地文件时使用,因为S3网络请求的延迟比本地磁盘读取+缓存高,优先级低于前两个方案。
关键疑问解答
file_get_contents会拖慢Vapor吗?
直接每次请求读取磁盘会在Lambda冷启动或高并发时产生一定开销,但结合缓存后,只有第一次请求会读取磁盘,后续完全从缓存获取,性能无影响。- Vapor适配的最优方案?
优先选择构建阶段生成Blade模板,完全避免运行时IO操作;其次是本地文件读取+缓存,兼顾简单性和性能;S3读取仅作为备选。
内容的提问来源于stack exchange,提问作者alexmcfarlane
相关产品推荐
相关产品推荐

