PHP 骨架屏实现方案
骨架屏(Skeleton Screen)是一种在页面内容加载完成前展示的占位图形,能提升用户体验,以下是几种在 PHP 项目中实现骨架屏的方案:

服务端渲染骨架屏
<?php
// 骨架屏组件类
class SkeletonScreen {
public static function render($type = 'card') {
$skeleton = '';
switch ($type) {
case 'card':
$skeleton = '
<div class="skeleton-card">
<div class="skeleton-image skeleton-animation"></div>
<div class="skeleton-text skeleton-animation"></div>
<div class="skeleton-text skeleton-animation short"></div>
</div>';
break;
case 'list':
$skeleton = '
<div class="skeleton-list">
<div class="skeleton-item">
<div class="skeleton-avatar skeleton-animation"></div>
<div class="skeleton-content">
<div class="skeleton-text skeleton-animation"></div>
<div class="skeleton-text skeleton-animation short"></div>
</div>
</div>
</div>';
break;
case 'table':
$skeleton = '
<div class="skeleton-table">
<div class="skeleton-row">
<div class="skeleton-cell skeleton-animation"></div>
<div class="skeleton-cell skeleton-animation"></div>
<div class="skeleton-cell skeleton-animation"></div>
</div>
<div class="skeleton-row">
<div class="skeleton-cell skeleton-animation"></div>
<div class="skeleton-cell skeleton-animation"></div>
<div class="skeleton-cell skeleton-animation"></div>
</div>
</div>';
break;
}
return $skeleton;
}
}
// 在页面中使用
$pageContent = getPageContent(); // 获取实际内容
$isLoading = empty($pageContent);
if ($isLoading) {
echo SkeletonScreen::render('card');
} else {
echo $pageContent;
}
?>
CSS 动画骨架屏
/* skeleton.css */
.skeleton-animation {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s ease infinite;
border-radius: 4px;
}
@keyframes skeleton-loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
.skeleton-card {
padding: 16px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.skeleton-image {
width: 100%;
height: 200px;
margin-bottom: 16px;
}
.skeleton-text {
width: 100%;
height: 16px;
margin-bottom: 8px;
}
.skeleton-text.short {
width: 60%;
}
.skeleton-list {
padding: 16px;
}
.skeleton-item {
display: flex;
align-items: center;
margin-bottom: 12px;
}
.skeleton-avatar {
width: 48px;
height: 48px;
border-radius: 50%;
margin-right: 12px;
}
.skeleton-content {
flex: 1;
}
.skeleton-table {
padding: 16px;
}
.skeleton-row {
display: flex;
margin-bottom: 8px;
}
.skeleton-cell {
flex: 1;
height: 20px;
margin-right: 8px;
}
PHP 中间件实现
<?php
// 骨架屏中间件
class SkeletonMiddleware {
public function handle($request, Closure $next) {
$response = $next($request);
// 检查是否需要显示骨架屏
if ($this->shouldShowSkeleton($request)) {
$content = $response->getContent();
// 生成骨架屏 HTML
$skeletonHtml = $this->generateSkeleton($request->getPath());
// 替换内容容器
$content = str_replace(
'<div id="content-container"></div>',
$skeletonHtml,
$content
);
$response->setContent($content);
}
return $response;
}
private function shouldShowSkeleton($request) {
// 判断条件:首次加载、API 请求中、缓存未命中等
return !$request->isAjax() && !$request->has('no_skeleton');
}
private function generateSkeleton($path) {
// 根据不同页面路径返回不同骨架屏
$skeletons = [
'/' => 'home',
'/products' => 'list',
'/users' => 'table',
];
$type = $skeletons[$path] ?? 'card';
return SkeletonScreen::render($type);
}
}
结合 JavaScript 动态切换
<?php
// PHP 生成骨架屏 + JS 切换实际内容
class HybridSkeleton {
public static function renderWithContent($content, $type = 'card') {
$skeleton = SkeletonScreen::render($type);
return <<<HTML
<div id="skeleton-container" data-type="{$type}">
{$skeleton}
</div>
<div id="content-container" style="display:none;">
{$content}
</div>
<script>
// 页面加载完成后,5秒后切换
setTimeout(function() {
document.getElementById('skeleton-container').style.display = 'none';
document.getElementById('content-container').style.display = 'block';
}, 5000);
// 或者等实际内容加载完成后切换
window.onload = function() {
document.getElementById('skeleton-container').style.display = 'none';
document.getElementById('content-container').style.display = 'block';
};
</script>
HTML;
}
}
使用缓存提高性能
<?php
class CachedSkeleton {
private $cacheDir = '/tmp/skeleton_cache/';
public function getCachedSkeleton($page, $type) {
$cacheFile = $this->cacheDir . md5($page . $type) . '.html';
if (file_exists($cacheFile) && (time() - filemtime($cacheFile)) < 3600) {
return file_get_contents($cacheFile);
}
$skeleton = SkeletonScreen::render($type);
file_put_contents($cacheFile, $skeleton);
return $skeleton;
}
}
使用示例
<?php
// 在控制器中使用
class UserController {
public function index() {
$users = UserRepository::getAll();
if (empty($users)) {
// 显示骨架屏
return SkeletonScreen::render('table');
}
// 显示实际内容
return view('users.index', ['users' => $users]);
}
}
// 或者在模板中使用
// 在 Blade 或 PHP 模板中:
@if($isLoading)
{!! App\Helpers\SkeletonScreen::render('card') !!}
@else
@foreach($items as $item)
<div class="real-content">{{ $item->name }}</div>
@endforeach
@endif
最佳实践建议
- 选择合适的骨架屏类型:根据不同页面内容选择卡片、列表、表格等类型
- 优化加载时机:在首次加载或异步请求时显示骨架屏
- 缓存骨架屏 HTML:减少重复生成的开销
- 支持多种屏幕尺寸:确保骨架屏在移动端和桌面端都显示良好
- 考虑无障碍性:给骨架屏元素添加
aria-hidden="true"属性
这样可以在 PHP 项目中有效实现骨架屏,提升用户体验。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播