欢迎光临
我们一直在努力

前端骨架屏实现大全解:老曹带你从0到1搞懂骨架屏(vue、react、angular、jquery、小程序等)

🦴 前端骨架屏实践大全解:老曹带你从0到1搞懂骨架屏


🎯 1. 引言:骨架屏是个啥玩意儿?你为啥要学?

骨架屏这玩意儿到底是个啥?你以为它只是个花里胡哨的动画?错!它是拯救你用户"等待焦虑"的神器,是你页面加载体验的"灵魂伴侣"!

用户点开页面,白屏3秒,直接把你网站关了,你说气不气?骨架屏就是来解决这个问题的,先给用户展示一个"骨架",告诉用户:“别急,我正在加载中!”,用户体验直接起飞!


🧠 2. 学习目标:你学完这篇能干啥?

学完这篇你得达到以下目标:

  • ✅ 搞懂骨架屏的基本原理和机制;
  • ✅ 掌握原生JS实现骨架屏的方法;
  • ✅ 熟悉Vue、React、Angular等框架的最佳实践;
  • ✅ 熟练运用移动端和小程序骨架屏实现;
  • ✅ 会排查骨架屏常见坑点,避免踩雷;
  • ✅ 能画出骨架屏流程图,讲给别人听。

  • 🧩 3. 骨架屏原理详解:流程图带你飞

    🧾 3.1 骨架屏基本流程图(mermaid)

    #mermaid-svg-W6RzNcrf80aaYgqW{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-W6RzNcrf80aaYgqW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-W6RzNcrf80aaYgqW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-W6RzNcrf80aaYgqW .error-icon{fill:#552222;}#mermaid-svg-W6RzNcrf80aaYgqW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-W6RzNcrf80aaYgqW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-W6RzNcrf80aaYgqW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-W6RzNcrf80aaYgqW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-W6RzNcrf80aaYgqW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-W6RzNcrf80aaYgqW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-W6RzNcrf80aaYgqW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-W6RzNcrf80aaYgqW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-W6RzNcrf80aaYgqW .marker.cross{stroke:#333333;}#mermaid-svg-W6RzNcrf80aaYgqW svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-W6RzNcrf80aaYgqW p{margin:0;}#mermaid-svg-W6RzNcrf80aaYgqW .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-W6RzNcrf80aaYgqW .cluster-label text{fill:#333;}#mermaid-svg-W6RzNcrf80aaYgqW .cluster-label span{color:#333;}#mermaid-svg-W6RzNcrf80aaYgqW .cluster-label span p{background-color:transparent;}#mermaid-svg-W6RzNcrf80aaYgqW .label text,#mermaid-svg-W6RzNcrf80aaYgqW span{fill:#333;color:#333;}#mermaid-svg-W6RzNcrf80aaYgqW .node rect,#mermaid-svg-W6RzNcrf80aaYgqW .node circle,#mermaid-svg-W6RzNcrf80aaYgqW .node ellipse,#mermaid-svg-W6RzNcrf80aaYgqW .node polygon,#mermaid-svg-W6RzNcrf80aaYgqW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-W6RzNcrf80aaYgqW .rough-node .label text,#mermaid-svg-W6RzNcrf80aaYgqW .node .label text,#mermaid-svg-W6RzNcrf80aaYgqW .image-shape .label,#mermaid-svg-W6RzNcrf80aaYgqW .icon-shape .label{text-anchor:middle;}#mermaid-svg-W6RzNcrf80aaYgqW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-W6RzNcrf80aaYgqW .rough-node .label,#mermaid-svg-W6RzNcrf80aaYgqW .node .label,#mermaid-svg-W6RzNcrf80aaYgqW .image-shape .label,#mermaid-svg-W6RzNcrf80aaYgqW .icon-shape .label{text-align:center;}#mermaid-svg-W6RzNcrf80aaYgqW .node.clickable{cursor:pointer;}#mermaid-svg-W6RzNcrf80aaYgqW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-W6RzNcrf80aaYgqW .arrowheadPath{fill:#333333;}#mermaid-svg-W6RzNcrf80aaYgqW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-W6RzNcrf80aaYgqW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-W6RzNcrf80aaYgqW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-W6RzNcrf80aaYgqW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-W6RzNcrf80aaYgqW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-W6RzNcrf80aaYgqW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-W6RzNcrf80aaYgqW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-W6RzNcrf80aaYgqW .cluster text{fill:#333;}#mermaid-svg-W6RzNcrf80aaYgqW .cluster span{color:#333;}#mermaid-svg-W6RzNcrf80aaYgqW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-W6RzNcrf80aaYgqW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-W6RzNcrf80aaYgqW rect.text{fill:none;stroke-width:0;}#mermaid-svg-W6RzNcrf80aaYgqW .icon-shape,#mermaid-svg-W6RzNcrf80aaYgqW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-W6RzNcrf80aaYgqW .icon-shape p,#mermaid-svg-W6RzNcrf80aaYgqW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-W6RzNcrf80aaYgqW .icon-shape rect,#mermaid-svg-W6RzNcrf80aaYgqW .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-W6RzNcrf80aaYgqW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-W6RzNcrf80aaYgqW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-W6RzNcrf80aaYgqW :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    页面开始加载

    数据是否加载完成?

    显示骨架屏

    隐藏骨架屏

    展示占位元素

    渲染真实内容

    📈 3.2 用户感知时间对比饼图(mermaid)

    #mermaid-svg-wcBtCdTyDva1vxFZ{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wcBtCdTyDva1vxFZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wcBtCdTyDva1vxFZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wcBtCdTyDva1vxFZ .error-icon{fill:#552222;}#mermaid-svg-wcBtCdTyDva1vxFZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wcBtCdTyDva1vxFZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wcBtCdTyDva1vxFZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wcBtCdTyDva1vxFZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wcBtCdTyDva1vxFZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wcBtCdTyDva1vxFZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wcBtCdTyDva1vxFZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wcBtCdTyDva1vxFZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wcBtCdTyDva1vxFZ .marker.cross{stroke:#333333;}#mermaid-svg-wcBtCdTyDva1vxFZ svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wcBtCdTyDva1vxFZ p{margin:0;}#mermaid-svg-wcBtCdTyDva1vxFZ .pieCircle{stroke:#000000;stroke-width:2px;opacity:0.7;}#mermaid-svg-wcBtCdTyDva1vxFZ .pieOuterCircle{stroke:#000000;stroke-width:1px;fill:none;}#mermaid-svg-wcBtCdTyDva1vxFZ .pieTitleText{text-anchor:middle;font-size:25px;fill:#000000;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-wcBtCdTyDva1vxFZ .slice{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;fill:#000000;font-size:17px;}#mermaid-svg-wcBtCdTyDva1vxFZ .legend text{fill:#000000;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:17px;}#mermaid-svg-wcBtCdTyDva1vxFZ :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}40%30%30%用户感知加载时间白屏时间骨架屏时间真实内容时间

    🚨 老曹吐槽:你以为骨架屏就是"画个框框"?太天真了!它涉及到用户体验、性能优化、视觉设计,你以为是涂鸦?其实是艺术!


    🧠 4. 原生JS骨架屏实现思路与算法步骤

    🧱 4.1 实现思路

  • 在真实内容加载前,插入占位元素
  • 占位元素模拟真实内容的布局结构
  • 数据加载完成后,移除占位元素,显示真实内容
  • 🧠 4.2 算法步骤

    步骤1:创建骨架屏HTML结构

    <div id="skeleton">
    <div class="skeleton-header"></div>
    <div class="skeleton-content">
    <div class="skeleton-line"></div>
    <div class="skeleton-line"></div>
    <div class="skeleton-line"></div>
    </div>
    </div>
    <div id="content" style="display:none;">
    <!– 真实内容 –>
    </div>

    步骤2:添加CSS样式

    .skeleton-header {
    width: 100%;
    height: 60px;
    background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
    background-size: 200% 100%;
    animation: loading 1.5s infinite;
    }

    .skeleton-line {
    width: 100%;
    height: 20px;
    margin: 10px 0;
    background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
    background-size: 200% 100%;
    animation: loading 1.5s infinite;
    }

    @keyframes loading {
    0% {
    background-position: 200% 0;
    }
    100% {
    background-position: -200% 0;
    }
    }

    步骤3:JavaScript控制显示/隐藏

    // 模拟数据加载
    function loadData() {
    return new Promise(resolve => {
    setTimeout(() => {
    resolve({
    title: '真实标题',
    content: '真实内容内容内容内容内容内容内容内容'
    });
    }, 2000);
    });
    }

    // 显示骨架屏
    function showSkeleton() {
    document.getElementById('skeleton').style.display = 'block';
    document.getElementById('content').style.display = 'none';
    }

    // 隐藏骨架屏,显示真实内容
    function hideSkeleton(data) {
    document.getElementById('skeleton').style.display = 'none';
    document.getElementById('content').style.display = 'block';
    document.getElementById('content').innerHTML = `
    <h1>
    ${data.title}</h1>
    <p>
    ${data.content}</p>
    `
    ;
    }

    // 主流程
    async function init() {
    showSkeleton();
    const data = await loadData();
    hideSkeleton(data);
    }

    init();


    🧪 5. Vue骨架屏最佳实践

    🧱 5.1 Vue组件实现

    🎯 实现思路

  • 核心思想:通过 loading 状态控制骨架屏和真实内容的显示切换
  • 条件渲染:使用 v-if 指令根据加载状态显示不同内容
  • 视觉欺骗:用渐变动画模拟"加载中"的效果
  • 数据驱动:加载完成后更新数据,自动切换到真实内容
  • 📖 代码详解

    <template>
    <div class="page-container">
    <! 骨架屏 >
    <div vif="loading" class="skeleton-wrapper">
    <div class="skeleton-header"></div>
    <div class="skeleton-content">
    <div class="skeleton-line" vfor="i in 5" :key="i"></div>
    </div>
    </div>

    <! 真实内容 >
    <div velse class="content-wrapper">
    <h1>{{ article.title }}</h1>
    <p>{{ article.content }}</p>
    </div>
    </div>
    </template>

    <script>
    export default {
    name: 'ArticlePage',
    data() {
    return {
    loading: true,
    article: {}
    }
    },
    async mounted() {
    // 模拟API调用
    const response = await this.fetchArticle();
    this.article = response;
    this.loading = false;
    },
    methods: {
    fetchArticle() {
    return new Promise(resolve => {
    setTimeout(() => {
    resolve({
    title: 'Vue骨架屏实践',
    content: '这是Vue骨架屏的详细内容…'
    });
    }, 2000);
    });
    }
    }
    }
    </script>

    <style scoped>
    .skeletonheader {
    width: 100%;
    height: 60px;
    background: lineargradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
    backgroundsize: 200% 100%;
    animation: loading 1.5s infinite;
    marginbottom: 20px;
    }

    .skeletonline {
    width: 100%;
    height: 20px;
    margin: 10px 0;
    background: lineargradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
    backgroundsize: 200% 100%;
    animation: loading 1.5s infinite;
    borderradius: 4px;
    }

    @keyframes loading {
    0% { backgroundposition: 200% 0; }
    100% { backgroundposition: 200% 0; }
    }
    </style>

    🎨 Template部分详解

    <template>
    <div class="page-container">
    <!– 骨架屏 –>
    <div v-if="loading" class="skeleton-wrapper">
    <div class="skeleton-header"></div>
    <div class="skeleton-content">
    <div class="skeleton-line" v-for="i in 5" :key="i"></div>
    </div>
    </div>

    <!– 真实内容 –>
    <div v-else class="content-wrapper">
    <h1>{{ article.title }}</h1>
    <p>{{ article.content }}</p>
    </div>
    </div>
    </template>

    关键点分析:

  • v-if="loading":这是核心!当 loading 为 true 时显示骨架屏,为 false 时显示真实内容
  • v-for="i in 5":循环生成5个骨架行,模拟内容区域
  • :key="i":Vue列表渲染必备,提高渲染性能
  • {{ article.title }}:数据绑定,显示真实内容
  • 🧠 Script部分详解

    <script>
    export default {
    name: 'ArticlePage',
    data() {
    return {
    loading: true, // 初始状态为加载中
    article: {} // 存储真实数据
    }
    },
    async mounted() {
    // 组件挂载后立即开始加载数据
    const response = await this.fetchArticle();
    this.article = response;
    this.loading = false; // 数据加载完成,关闭骨架屏
    },
    methods: {
    fetchArticle() {
    // 模拟API调用,实际项目中替换为真实请求
    return new Promise(resolve => {
    setTimeout(() => {
    resolve({
    title: 'Vue骨架屏实践',
    content: '这是Vue骨架屏的详细内容…'
    });
    }, 2000); // 模拟2秒网络延迟
    });
    }
    }
    }
    </script>

    关键点分析:

  • loading: true:初始状态必须是 true,确保骨架屏首先显示
  • async mounted():组件挂载后立即执行数据加载
  • await this.fetchArticle():等待数据加载完成
  • this.loading = false:关键一步!数据加载完成后关闭骨架屏
  • Promise + setTimeout:模拟异步API调用,在真实项目中替换为 axios 或 fetch
  • 🎨 Style部分详解

    <style scoped>
    .skeleton-header {
    width: 100%;
    height: 60px;
    background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
    background-size: 200% 100%;
    animation: loading 1.5s infinite;
    margin-bottom: 20px;
    }

    .skeleton-line {
    width: 100%;
    height: 20px;
    margin: 10px 0;
    background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
    background-size: 200% 100%;
    animation: loading 1.5s infinite;
    border-radius: 4px;
    }

    @keyframes loading {
    0% { background-position: 200% 0; }
    100% { background-position: -200% 0; }
    }
    </style>

    关键点分析:

  • linear-gradient:创建从浅灰到深灰的渐变背景,模拟"骨骼"效果
  • background-size: 200% 100%:让背景图比容器大一倍,为动画做准备
  • animation: loading 1.5s infinite:无限循环的加载动画
  • @keyframes loading:关键帧动画,通过移动背景位置实现"闪烁"效果
  • scoped:样式作用域限制,避免影响其他组件
  • 🧠 算法步骤分解

    步骤1:初始化状态

    data() {
    return {
    loading: true, // 骨架屏显示
    article: {} // 数据为空
    }
    }

    步骤2:组件挂载,开始加载

    async mounted() {
    const response = await this.fetchArticle(); // 异步加载数据
    this.article = response; // 存储数据
    this.loading = false; // 关闭骨架屏
    }

    步骤3:模板根据状态渲染

    <!– loading为true时显示 –>
    <div v-if="loading" class="skeleton-wrapper"></div>

    <!– loading为false时显示 –>
    <div v-else class="content-wrapper"></div>

    🎯 优化建议

  • 添加最小显示时间:
  • async mounted() {
    const startTime = Date.now();
    const response = await this.fetchArticle();
    const elapsed = Date.now() startTime;

    // 确保骨架屏至少显示1秒
    if (elapsed < 1000) {
    await new Promise(resolve => setTimeout(resolve, 1000 elapsed));
    }

    this.article = response;
    this.loading = false;
    }

  • 错误处理:
  • async mounted() {
    try {
    const response = await this.fetchArticle();
    this.article = response;
    } catch (error) {
    // 处理加载错误
    console.error('加载失败:', error);
    } finally {
    this.loading = false;
    }
    }

    🧠 老曹小贴士:别小看这几行代码,它能让你的用户体验提升一个档次!记住,骨架屏不是装饰品,是用户体验的重要组成部分!


    🧠 6. React骨架屏最佳实践

    🧱 6.1 React组件实现

    import React, { useState, useEffect } from 'react';
    import './Skeleton.css';

    const SkeletonScreen = () => {
    const [loading, setLoading] = useState(true);
    const [data, setData] = useState(null);

    useEffect(() => {
    // 模拟数据获取
    const fetchData = async () => {
    setTimeout(() => {
    setData({
    title: 'React骨架屏实践',
    content: '这是React骨架屏的详细内容…'
    });
    setLoading(false);
    }, 2000);
    };

    fetchData();
    }, []);

    if (loading) {
    return (
    <div className="skeleton-wrapper">
    <div className="skeleton-header"></div>
    <div className="skeleton-content">
    {[Array(5)].map((_, index) => (
    <div key={index} className="skeleton-line"></div>
    ))}
    </div>
    </div>
    );
    }

    return (
    <div className="content-wrapper">
    <h1>{data.title}</h1>
    <p>{data.content}</p>
    </div>
    );
    };

    export default SkeletonScreen;

    🧱 6.2 CSS样式文件

    /* Skeleton.css */
    .skeleton-wrapper {
    padding: 20px;
    }

    .skeleton-header {
    width: 100%;
    height: 60px;
    background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
    background-size: 200% 100%;
    animation: loading 1.5s infinite;
    margin-bottom: 20px;
    border-radius: 8px;
    }

    .skeleton-line {
    width: 100%;
    height: 20px;
    margin: 10px 0;
    background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
    background-size: 200% 100%;
    animation: loading 1.5s infinite;
    border-radius: 4px;
    }

    @keyframes loading {
    0% { background-position: 200% 0; }
    100% { background-position: -200% 0; }
    }


    🧩 7. jQuery骨架屏实现

    🧱 7.1 jQuery实现代码

    <!DOCTYPE html>
    <html>
    <head>
    <title>jQuery骨架屏</title>
    <style>
    .skeleton-header {
    width: 100%;
    height: 60px;
    background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
    background-size: 200% 100%;
    animation: loading 1.5s infinite;
    margin-bottom: 20px;
    }

    .skeleton-line {
    width: 100%;
    height: 20px;
    margin: 10px 0;
    background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
    background-size: 200% 100%;
    animation: loading 1.5s infinite;
    }

    @keyframes loading {
    0% { background-position: 200% 0; }
    100% { background-position: -200% 0; }
    }
    </style>
    </head>
    <body>
    <div id="skeleton">
    <div class="skeleton-header"></div>
    <div class="skeleton-content">
    <div class="skeleton-line"></div>
    <div class="skeleton-line"></div>
    <div class="skeleton-line"></div>
    </div>
    </div>

    <div id="content" style="display:none;">
    <!– 真实内容将在这里显示 –>
    </div>

    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script>
    $(document).ready(function() {
    // 显示骨架屏
    function showSkeleton() {
    $('#skeleton').show();
    $('#content').hide();
    }

    // 隐藏骨架屏,显示内容
    function hideSkeleton(content) {
    $('#skeleton').hide();
    $('#content').html(content).show();
    }

    // 模拟数据加载
    function loadData() {
    return new Promise(resolve => {
    setTimeout(() => {
    resolve('<h1>jQuery骨架屏内容</h1><p>这是通过jQuery实现的骨架屏效果</p>');
    }, 2000);
    });
    }

    // 主流程
    async function init() {
    showSkeleton();
    const content = await loadData();
    hideSkeleton(content);
    }

    init();
    });
    </script>
    </body>
    </html>


    🧠 8. Angular骨架屏最佳实践

    🧱 8.1 Angular组件实现

    // skeleton.component.ts
    import { Component, OnInit } from '@angular/core';

    @Component({
    selector: 'app-skeleton',
    template: `
    <div class="skeleton-wrapper" *ngIf="loading">
    <div class="skeleton-header"></div>
    <div class="skeleton-content">
    <div class="skeleton-line" *ngFor="let i of [1,2,3,4,5]"></div>
    </div>
    </div>

    <div class="content-wrapper" *ngIf="!loading">
    <h1>{{ data.title }}</h1>
    <p>{{ data.content }}</p>
    </div>
    `,
    styles: [`
    .skeleton-header {
    width: 100%;
    height: 60px;
    background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
    background-size: 200% 100%;
    animation: loading 1.5s infinite;
    margin-bottom: 20px;
    border-radius: 8px;
    }

    .skeleton-line {
    width: 100%;
    height: 20px;
    margin: 10px 0;
    background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
    background-size: 200% 100%;
    animation: loading 1.5s infinite;
    border-radius: 4px;
    }

    @keyframes loading {
    0% { background-position: 200% 0; }
    100% { background-position: -200% 0; }
    }
    `]
    })
    export class SkeletonComponent implements OnInit {
    loading = true;
    data: any = {};

    ngOnInit() {
    this.loadData();
    }

    loadData() {
    setTimeout(() => {
    this.data = {
    title: 'Angular骨架屏实践',
    content: '这是Angular骨架屏的详细内容…'
    };
    this.loading = false;
    }, 2000);
    }
    }


    📱 9. 移动端骨架屏最佳实践

    🧱 9.1 移动端适配要点

  • 响应式设计:适配不同屏幕尺寸
  • 触摸友好:避免误触
  • 性能优化:减少DOM操作
  • 🧠 9.2 移动端CSS优化

    /* 移动端骨架屏样式 */
    .skeleton-mobile {
    padding: 15px;
    }

    .skeleton-mobile .skeleton-header {
    height: 50px;
    border-radius: 6px;
    }

    .skeleton-mobile .skeleton-line {
    height: 16px;
    margin: 8px 0;
    border-radius: 3px;
    }

    /* 媒体查询适配 */
    @media (max-width: 768px) {
    .skeleton-mobile .skeleton-header {
    height: 45px;
    }

    .skeleton-mobile .skeleton-line {
    height: 14px;
    margin: 6px 0;
    }
    }


    📱 10. 小程序骨架屏最佳实践

    🧱 10.1 微信小程序实现

    <!– skeleton.wxml –>
    <view class="skeleton-container" wx:if="{{loading}}">
    <view class="skeleton-header"></view>
    <view class="skeleton-content">
    <view class="skeleton-line" wx:for="{{lines}}" wx:key="index"></view>
    </view>
    </view>

    <view class="content-container" wx:else>
    <text class="title">{{article.title}}</text>
    <text class="content">{{article.content}}</text>
    </view>

    /* skeleton.wxss */
    .skeleton-header {
    width: 100%;
    height: 60rpx;
    background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
    background-size: 200% 100%;
    animation: loading 1.5s infinite;
    margin-bottom: 20rpx;
    border-radius: 8rpx;
    }

    .skeleton-line {
    width: 100%;
    height: 30rpx;
    margin: 15rpx 0;
    background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
    background-size: 200% 100%;
    animation: loading 1.5s infinite;
    border-radius: 4rpx;
    }

    @keyframes loading {
    0% { background-position: 200% 0; }
    100% { background-position: -200% 0; }
    }

    // skeleton.js
    Page({
    data: {
    loading: true,
    lines: [1, 2, 3, 4, 5],
    article: {}
    },

    onLoad() {
    this.loadArticle();
    },

    loadArticle() {
    setTimeout(() => {
    this.setData({
    loading: false,
    article: {
    title: '小程序骨架屏实践',
    content: '这是小程序骨架屏的详细内容…'
    }
    });
    }, 2000);
    }
    });


    📊 11. 骨架屏性能对比与优化建议

    📈 11.1 性能对比表

    实现方式加载感知时间用户满意度实现复杂度
    无骨架屏 3.2s 60% 简单
    骨架屏 2.1s 85% 中等
    预加载 1.5s 95% 复杂

    🧠 11.2 优化建议

  • 合理使用:只在必要时使用骨架屏
  • 样式简洁:避免过于复杂的动画效果
  • 内容匹配:骨架屏结构要与真实内容一致
  • 性能监控:监控骨架屏对性能的影响

  • 🧾 12. 常见问题与解决方案

    问题原因解决方案
    骨架屏闪烁 显示时间过短 增加最小显示时间
    布局错位 样式不匹配 精确匹配真实内容布局
    性能下降 动画过多 简化动画效果
    兼容性问题 CSS不支持 使用兼容性更好的方案

    🧠 13. 总结表格:骨架屏全攻略一览

    项目内容
    核心原理 占位元素 + 动画效果
    实现方式 原生JS、Vue、React、Angular等
    关键技术 CSS动画、条件渲染
    移动端适配 响应式设计、触摸优化
    小程序支持 微信、支付宝等平台
    性能优化 减少DOM操作、简化动画
    用户体验 降低等待焦虑、提升感知速度

    🧠 14. 结语:骨架屏不是终点,是起点!

    骨架屏只是优化用户体验的第一步,后面还有懒加载、预加载、缓存策略等等一堆东西等着你去搞。

    🧠 老曹寄语:别光看不练,动手搞起来!骨架屏搞得好,用户跑不了!


    赞(0)
    未经允许不得转载:171主机测评 » 前端骨架屏实现大全解:老曹带你从0到1搞懂骨架屏(vue、react、angular、jquery、小程序等)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址