1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        分享一個自用實用骨架屏實現(xiàn)方案!

        共 18801字,需瀏覽 38分鐘

         ·

        2024-04-15 07:55

        作者:背對疾風(fēng)
        原文:https://juejin.cn/post/6945748911147450405

        在我的前前公司,接口經(jīng)常出問題,基本每個接口都會出問題,這就要求我們前端對每個接口都要做錯誤信息的提醒,非常麻煩,當(dāng)然這也是必須的,所以就想著做一個組件能夠幫我們自動處理和展示這些錯誤信息,當(dāng)時開發(fā)用的是Flutter,所以應(yīng)該叫widget。后來又做vue和小程序了,想著能不能把那個解決方法帶到vue上來,于是就有了這個

        功能

        • 以骨架屏的形式展示加載中,而且可以絲滑過渡到加載完成
        • 加載失敗展示錯誤信息

        先看效果

        test3.gif

        原理

        一般情況下,我們請求接口然后渲染數(shù)據(jù)時會先判斷是否有數(shù)據(jù)來來渲染不同的視圖,比如加載的展位圖,空數(shù)據(jù)的占位圖,有數(shù)據(jù)時就展示數(shù)據(jù)。

        ClamView的思路則是 數(shù)據(jù)加載未完成時也給渲染的模板一個數(shù)據(jù),然后通過給負(fù)責(zé)顯示數(shù)據(jù)span、img等標(biāo)簽設(shè)置背景色和字體顏色來達到“骨架”的效果,待數(shù)據(jù)請求完成后,再使用動畫將骨架隱去,完成過渡。

        代碼

        • ClamView.tsx

          ClamView 需要傳入一個 ResponseBean點此了解[1] 對象 res 來判斷當(dāng)前數(shù)據(jù)的狀態(tài),還需要傳入一個加載過程中用到的假數(shù)據(jù) emptyData 用來撐起你的 span 標(biāo)簽

        import { defineComponent,computed } from 'vue';
        import {ResponseBean} from "bdjf_http";
        import './clam_view.css'
        import './skeleton.css'

        /**
         * 定義 ClamView 的四種狀態(tài)
         * 1. LOADING res 為空或者res.code === -100 時狀態(tài)為LOADING,此時顯示骨架屏
         * 2. EMPTY res.code  === 0 且 res.data 為空時
         * 3. SHOW res.code  === 0 且 res.data 不為空時
         * 4. ERROR res.code  !== 0 時
         **/

        type ViewStatusType = 'LOADING'|'EMPTY'|'SHOW'|'ERROR';

        export default defineComponent({
            name:'ClamView',
            props:{
                res: {
                    type:ResponseBean,
                    default:()=>{
                        // 默認(rèn)顯示一個loading
                        return new ResponseBean().loading();
                    }
                },
                showLoading :{
                    typeBoolean,
                    default:()=>{
                        return false;
                    }
                },
                emptyText:{
                    typeString,
                    default:()=>{
                        return '暫無數(shù)據(jù)';
                    }
                },
                emptyData:{
                    type:Object,
                    default:()=>{
                        return {}
                    }
                },
                noPackage:{
                    typeBoolean,
                    default:()=>{
                        return false;
                    }
                }
            },
            setup(props,{  slots }) {

                // 根據(jù) res 的狀態(tài)來判斷如何顯示
                const viewStatusAdapter = (response: ResponseBean): ViewStatusType => {
                    // console.log('----viewStatusAdapter----',response)
                    if (props.showLoading) {
                        return "LOADING";
                    }
                    if (!response) {
                        return "LOADING";
                    }
                    switch (response.code) {
                        case 0:
                            if (!response.data || response.data.length === 0) {
                                return "EMPTY";
                            } else {
                                return "SHOW";
                            }
                        case -100:
                            return "LOADING";
                        default:
                            return "ERROR";
                    }
                }

                // 用 computed 包一下
                const viewStatus = computed<ViewStatusType>(()=>{
                    return viewStatusAdapter(props.res)
                })

                const noDataView = (text:string)=>{
                    return (
                        <div class="empty_view col-center item-center">
                            {text}
                        </div>

                    )
                }

                const emptyView = ()=>{
                    if(viewStatus.value === 'EMPTY'){
                        return slots.empty?slots.empty():noDataView(props.emptyText);
                    }
                }

                const errorView = ()=>{
                    if(viewStatus.value === 'ERROR'){
                         return slots.error?slots.error():noDataView(props.res.msg);
                    }
                }



                return () => {
                    if(viewStatus.value === 'EMPTY'){
                        return emptyView();
                    }else if(viewStatus.value === 'ERROR'){
                        return errorView();
                    }else {
                        // noPackage 為 false 時,ClamView將會在 slots 外面包一層 div ,通過給div更換樣式來實現(xiàn)狀態(tài)切換;
                        // 為 true 時,將不會包裹div,會通過 vClass 屬性傳替給 需要使用的地方綁定樣式進行切換
                        if(props.noPackage){
                            return slots.default({
                                data:viewStatus.value==='LOADING'?props.emptyData:props.res.data,
                                vClass:viewStatus.value==='LOADING'?'skeleton-view-empty-view':'skeleton-view-default-view'
                            })
                        }else {
                            return (
                                <div  class={viewStatus.value==='LOADING'?'skeleton-view-empty-view':'skeleton-view-default-view'}>
                                    {slots.default({
                                        data:viewStatus.value==='LOADING'?props.emptyData:props.res.data
                                    })}
                                </div>

                            )
                        }
                    }
                }
            }
        })
        • skeleton.css
        /**
        正常狀態(tài)下的 樣式,
        設(shè)置 transition 來讓過渡平滑
        */

        .skeleton-view-default-view span,
        .skeleton-view-default-view a,
        .skeleton-view-default-view img
        {
            transition: all .7s ease;
            background-colorrgba(0000);
        }


        /**
        加載時的樣式,首先設(shè)置不監(jiān)聽任何事件,省的用戶亂點
        然后給 span、a、img標(biāo)簽設(shè)置可以動的背景,字體顏色設(shè)成透明,
        就形成 “骨架” 了
        */

        .skeleton-view-empty-view {
            pointer-events: none;
        }
        .skeleton-view-empty-view span,
        .skeleton-view-empty-view a {
            colorrgba(0000!important;
            border-radius2px;
            backgroundlinear-gradient(
                    -45deg,
                    #F5F5F5 0%,
                    #DCDCDC 25%,
                    #F5F5F5 50%,
                    #DCDCDC 75%,
                    #F5F5F5 100%
            );
            animation: gradientBG 4s ease infinite;
            background-size400% 400%;
            background-color:#DCDCDC;
            transition: all 1s ease;
        }

        .skeleton-view-empty-view img {
            /* 這里是一個透明的小圖片 */
            contenturl(../../assets/img/no_url.png);
            border-radius2px;
            backgroundlinear-gradient(
                    -45deg,
                    #F5F5F5 0%,
                    #DCDCDC 25%,
                    #F5F5F5 50%,
                    #DCDCDC 75%,
                    #F5F5F5 100%
            );
            animation: gradientBG 4s ease infinite;
            background-size400% 400%;
            background-color:#DCDCDC;
            transition: all 1s ease;
        }
        @keyframes gradientBG {
            0% {
                background-position100% 100%;
            }
            50% {
                background-position0% 0%;
            }
            100% {
                background-position100% 100%;
            }

        }
        • clam_view.css
        .clam-box{
            width100%;
            height100%;
        }
        .empty_view{
            padding-top100px;
            width100%;
            height100%;
            padding-bottom100px;
        }
        .empty_img{
            width310px;
            height218px;
        }
        .trip_text{
            font-size28px;
            color#999999;
        }

        使用

        <template>
          <div class="home col">
            <clam-view :res="response" v-slot="{data}" :empty-data="emptyData">
              <p><span>{{data.name}}</span></p>
              <p>Home</p>
              <router-link to="/about" >{{data.route}}</router-link>
            </clam-view>
          </div>
        </template>
        ts
        復(fù)制代碼
        <script lang="ts">
        import { defineComponent,reactive,toRefs,onMounted } from 'vue';
        import {ResponseBean} from 'bdjf_http'

        export default defineComponent({
          name: 'Home',
          setup(){

            const state = reactive({
              response:new ResponseBean().loading()
            })

            onMounted(()=>{
              setTimeout(()=>{
                state.response = new ResponseBean(0,'',{
                  name:'Home',
                  route:'About'
                })
              },2500)
            })

            const emptyData = {
              name:'站位文字',
              route:'站位文字'
            }

            return {
              ...toRefs(state),
              emptyData
            }
          }
        });
        </script>

        <style scoped>
        </
        style>

        配合bdjf_http

        如果你配合 bdjf_http 使用,就能用極少的代碼完成所需功能 點此了解bdjf\_http[2]

        post(API.getData())
        .then(res => state.response = res;)
        參考資料
        [1]

        https://juejin.cn/post/6945373247659573278

        [2]

        https://juejin.cn/post/6945373247659573278

        最后



        如果你覺得這篇內(nèi)容對你挺有啟發(fā),我想邀請你幫我個小忙:

        1. 點個「喜歡」或「在看」,讓更多的人也能看到這篇內(nèi)容

        2. 我組建了個氛圍非常好的前端群,里面有很多前端小伙伴,歡迎加我微信「sherlocked_93」拉你加群,一起交流和學(xué)習(xí)

        3. 關(guān)注公眾號「前端下午茶」,持續(xù)為你推送精選好文,也可以加我為好友,隨時聊騷。



        點個喜歡支持我吧,在看就更好了


        瀏覽 154
        10點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        10點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            欧美亚洲高清 | 军人妓女院BD高清片在线播放 | 几天没c你这么骚 | 欧美+国产+无码+麻豆 | 日本中文字幕乱伦 | 国产精品久久久999 | 91亚洲成人精品性色 | 女女同の激しい爱与 | 国产午夜a | 欧美日韩精品在线观看视频 |