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>

        使用Chrome擴展程序生成網(wǎng)頁骨架屏

        共 3920字,需瀏覽 8分鐘

         ·

        2020-08-07 15:21

        來源:橙紅年代

        https://juejin.im/post/6856784900775739400

        對于依賴接口渲染的頁面,在拿到數(shù)據(jù)之前頁面往往是空白的,為了提示用戶當(dāng)前正在加載中,往往會使用進(jìn)度條、loading圖標(biāo)或骨架屏的方式。對于前兩種方案而言,實現(xiàn)比較簡單;本文主要研究骨架屏的應(yīng)用及實現(xiàn),并給出一種使用Chrome擴展工具快速生成骨架屏的方案。

        首先看看效果 先放一個動圖展示

        掘金首頁

        百度首頁

        知乎首頁

        安裝插件后訪問任意網(wǎng)頁,基本上均可以一鍵生成骨架屏(由于時間關(guān)系,部分節(jié)點并沒有完全實現(xiàn),不過目前展示Demo應(yīng)該是夠了

        本文所有代碼均放在https://github.com/tangxiangmin/web-skeleton-extension上面了,時間關(guān)系代碼寫的比較潦草~

        (寫完準(zhǔn)備提交Chrome應(yīng)用商店的時候才發(fā)現(xiàn),居然有一個類似的應(yīng)用:skeleton extention~囧,就當(dāng)瞎折騰一番了

        骨架屏方案

        目前有幾種比較常見的骨架屏方案

        1. 使用圖片、SVG實現(xiàn)骨架屏效果,可以讓設(shè)計在提供頁面設(shè)計稿的時候同步一份當(dāng)前頁面的骨架屏圖片資源,這種方案開發(fā)成本較低,缺點是不太靈活,如果頁面迭代比較頻繁,會導(dǎo)致設(shè)計同事的工作量增大
        2. 手寫HTML+CSS實現(xiàn)骨架屏,較第一種圖片方案而言,可以更靈活地定制骨架屏UI和動畫效果,缺點也很明顯,開發(fā)和維護(hù)成本都較高;目前可以使用諸如react-content-loader等現(xiàn)有的骨架屏庫,通過配置快速生成對應(yīng)的骨架屏,但對于某些高度定制的頁面也不能很好地滿足業(yè)務(wù)需求
        3. 餓了么前端團(tuán)隊提供的一套方案:page-skeleton-webpack-plugin,其大致原理是使用puppeteer向指定頁面注入代碼,遍歷頁面節(jié)點,根據(jù)節(jié)點的類型渲染對應(yīng)的骨架屏樣式,然后結(jié)合webpack將返回的HTML自動注入到項目模板文件中,傳送門:一種自動化生成骨架屏的方案:https://github.com/Jocs/jocs.github.io/issues/22

        在最開始調(diào)研骨架屏方案的時候也注意到了page-skeleton-webpack-plugin,體驗了一下發(fā)現(xiàn)并不能完全滿足我的需求,對于骨架屏方案,我的期望是

        • 使用方便,能夠根據(jù)給定頁面,自動生成對應(yīng)的骨架屏,不需要安裝額外的工具(不用安裝puppeteer)
        • 能夠靈活地處理骨架屏生成的HTML文件,支持首屏或者部分區(qū)域使用骨架屏(不需要webapck自動注入模板文件,反之,需要實現(xiàn)為項目單個或多個頁面配置對應(yīng)骨架屏)
        • 能夠自定義骨架屏各個區(qū)塊的樣式,能夠指定包含或者忽略某些節(jié)點,生成的代碼體積要足夠小(骨架屏并不需要完全還原原本頁面)

        恰好調(diào)研骨架屏方案的那段時間正在處理Chrome擴展程序的工單,發(fā)現(xiàn)page-skeleton-webpack-plugin借助puppeteer執(zhí)行頁面腳本的方案,完全可以通過擴展程序的content.js實現(xiàn),這樣,不需要借助本地開發(fā)環(huán)境也可以渲染骨架屏了

        骨架屏實現(xiàn)原理

        何為骨架?

        首先需要保留節(jié)點的布局信息,這樣就可以復(fù)用節(jié)點原有的樣式,不會破壞整體布局,最后生成的骨架屏樣式就可以與原本的頁面保持一致。

        然后為了保證生成樣式的統(tǒng)一,需要覆蓋節(jié)點原本的UI信息,包括背景色、圖片、邊框等。

        由于骨架屏僅僅作為數(shù)據(jù)返回之前的占位,并不需要完全復(fù)原原本的頁面,因此為了使整個骨架屏看起來比較簡潔,可以忽略不必要的節(jié)點。

        那么要處理哪些節(jié)點、忽略哪些節(jié)點呢?因此針對不同節(jié)點,我們需要進(jìn)行判斷并分別處理。

        區(qū)分不同節(jié)點

        這部分大量參考了一種自動化生成骨架屏的方案:https://github.com/Jocs/jocs.github.io/issues/22 這篇文章的思路,不妨移步閱讀,下面簡單整理一下各種不同節(jié)點的處理方案,并增加了一些額外的節(jié)點處理策略。

        由于涉及大量的節(jié)點操作,因此使用了jQuery。

        文字

        把文字占據(jù)的空間看做上行高、內(nèi)容、下行高,

        • 行高部分用透明色,
        • 內(nèi)容部分用灰色

        這樣就可以展示原本的文字區(qū)域了,對于多行文字而言,顯示的就是斑馬狀條紋形式的骨架屏結(jié)構(gòu)

        .line?{
        ????background-image:?linear-gradient(red?25%,blue?25%,?blue?75%,?red?75%);
        ????/*background-image:?linear-gradient(red?25%,blue?0,?blue?75%,?red?0);?//?與上面等價*/
        }
        復(fù)制代碼

        這里需要計算元素的行高、字體大小等信息

        renderText($dom)?{
        ????let?fontSize?=?parseFloat($dom.css("font-size"));
        ????let?lineHeight?=?$dom.css("line-height");

        ????//?todo?處理瀏覽器默認(rèn)行高、包含繼承、自定義等屬性
        ????if?(lineHeight?===?"normal")?{
        ????????lineHeight?=?fontSize?*?1.4;
        ????}?else?{
        ????????lineHeight?=?parseFloat(lineHeight);
        ????}

        ????const?textHeightRatio?=?fontSize?/?lineHeight;
        ????const?firstColorPoint?=?(((1?-?textHeightRatio)?/?2)?*?100).toFixed(2);
        ????const?secondColorPoint?=?(((1?-?textHeightRatio)?/?2?+?textHeightRatio)?*?100).toFixed(2);

        ????const?style?=?`--fp:${firstColorPoint}%;--sp:${secondColorPoint}%;--lh:${lineHeight}px;`;
        ????$dom.addClass('sk-text');
        ????$dom.attr("style",?style);
        }
        復(fù)制代碼

        因為每個文本節(jié)點的字體大小和行高可能不一樣,如果全在style標(biāo)簽上添加樣式,則生成的HTML文件可能比較大,因此這里使用了CSS Var,然后統(tǒng)一在sk-text的樣式類中處理背景色

        .sk-text?{
        ????--c:?#eee;
        ????--fp:?0%;
        ????--sp:?0%;
        ????--lh:?0;

        ????display:?inline-block;
        ????background-origin:?content-box?!important;
        ????background-clip:?content-box?!important;
        ????background-color:?transparent?!important;
        ????background-repeat:?repeat-y?!important;
        ????background-image:?linear-gradient(transparent?var(--fp),?var(--c)?0,?var(--c)?var(--sp),?transparent?0);
        ????background-size:?100%?var(--lh);
        ????color:?transparent?!important;
        }
        復(fù)制代碼

        圖片

        獲取原始圖片節(jié)點的寬高,然后使用1像素的base64灰色圖片替換原始節(jié)點的src屬性

        renderImg($img)?{
        ????let?width?=?$img.width()
        ????let?height?=?$img.height()
        ????//?一像素灰色圖片
        ????let?emptyImage?=?"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
        ????$img.attr("src",?emptyImage);

        ????$img.css({
        ????????background:?"#eee",
        ????????width:?width?+?"px",
        ????????height:?height?+?"px"
        ????})
        }
        復(fù)制代碼

        對于包含背景圖片的區(qū)塊而言,只需要將其背景覆蓋成灰色即可。

        按鈕、input

        使用灰色背景的塊占據(jù)

        邊框

        替換對應(yīng)邊框的顏色為骨架屏灰色

        列表

        列表元素是骨架屏中一個比較常見的區(qū)塊,列表元素可以由上面這些區(qū)塊組成,但是為了保證生成規(guī)整的骨架屏,采取的策略是使用移除多余的元素,使用第一個元素克隆占位

        function?renderList($dom)?{
        ????$dom.addClass("sk-list")

        ????let?$children?=?$dom.children()
        ????let?$child?=?$children.first()
        ????let?len?=?$children.length

        ????//?列表元素子節(jié)點統(tǒng)一,保證頁面骨架整齊
        ????for?(let?i?=?1;?i?????????$children.eq(i).remove()
        ????}
        ????for?(let?i?=?1;?i?????????let?tmp?=?$child.clone(true)
        ????????$dom.append(tmp)
        ????}
        }
        復(fù)制代碼

        遍歷DOM樹

        當(dāng)確定了不同類型節(jié)點的處理策略之后,就可以從入口節(jié)點遍歷整個DOM樹執(zhí)行處理方法了

        function?preorder($dom)?{
        ????//?...獲取節(jié)點類型,執(zhí)行相關(guān)策略放啊
        ????
        ????//?遍歷子節(jié)點
        ????$dom.children().each(function?()?{
        ????????const?$this?=?$(this)
        ????????preorder($this)
        ????});
        }
        復(fù)制代碼

        在遍歷期間還需要處理一些特殊情況

        • 不可見的元素及其子節(jié)點應(yīng)該停止遍歷
        • 對于某些節(jié)點而言,可能存在一些定制操作
          • 由用戶指定節(jié)點類型,而不是默認(rèn)根據(jù)節(jié)點nodeType推斷
          • 忽略某些節(jié)點的處理,如一個ul標(biāo)簽我們可能并不希望將其轉(zhuǎn)換成列表
          • 直接隱藏節(jié)點,如某些小圖標(biāo)等,為了保證骨架屏簡潔,可能需要直接隱藏節(jié)點

        基于這些場景,引入了skeleton-type的概念,對應(yīng)上面章節(jié)提到的節(jié)點類型,在遍歷時會優(yōu)先讀取節(jié)點上的該屬性值,只有當(dāng)屬性值不存在時,才會根據(jù)nodeType自行推斷相關(guān)的類型;

        • 對于需要隱藏的節(jié)點,直接指定skeleton-typeignore
        • 對于忽略節(jié)點類型而言,則使用skeleton-exclude-type
        ?let?type?=?$dom.attr(KEY)?||?getNodeSkeletonType($dom)??//?自動檢測節(jié)點類型,并附上type
        let?excludeType?=?$dom.attr(KEY_EXCLUDE)

        if?(!excludeType?||?type?!==?excludeType)?{
        ????let?handlers?=?{
        ????????[TEXT]:?renderText,
        ????????[IMAGE]:?renderImg,
        ????????[BLOCK]:?renderBlock,
        ????????[BORDER]:?renderBorder,
        ????????[BUTTON]:?renderButton,
        ????????[LIST]:?renderList,
        ????????[BACKGROUND_IMAGE]:?renderBackgroundImage,
        ????????[INPUT]:?renderInput,
        ????????[IGNORE]:?renderIgnore
        ????}

        ????let?handler?=?handlers[type]
        ????handler?&&?handler($dom)
        }
        復(fù)制代碼

        這樣相當(dāng)于暴露了skeleton-typeskeleton-exclude-type兩個HTML屬性,在開發(fā)頁面的時候就可以直接指定骨架屏區(qū)塊類型,方便定制業(yè)務(wù)需要的骨架屏。

        當(dāng)然逐個去配置skeleton-type也會顯得比較繁瑣,在getNodeSkeletonType中會盡可能地推斷并生成比較符合要求的骨架屏效果;此外,基于Chrome擴展程序的工具還暴露了一個配置參數(shù)

        ?renderSkeleton("body",?{
        ????ignore:?'',
        ????selector:?{
        ????????[key]:?{include:?'',?exclude:?''}
        ????},
        })
        復(fù)制代碼

        用掘金首頁試一下

        原本的效果

        如果不過濾的話,因為頂部導(dǎo)航欄使用的是ul,會默認(rèn)識別成list,導(dǎo)致展示出現(xiàn)問題

        自定義配置后的效果

        {
        ????ignore:?['.banner?.label'].join(','),?//?隱藏右側(cè)廣告欄的提示按鈕
        ????selector:?{
        ????????block:?{
        ????????????//?將表單轉(zhuǎn)換成一個BLOCK
        ????????????include:?['.add-group?.more',?'.search-form'].join(',')
        ????????},
        ????????list:?{
        ????????????//?排除導(dǎo)航欄的ul標(biāo)簽
        ????????????exclude:?['.nav-list'].join(',')
        ????????},
        ????},
        }
        復(fù)制代碼

        此外,我們也可以只傳入某個節(jié)點而非body的選擇器,這樣就只會生成對應(yīng)節(jié)點的HTML

        將骨架屏嵌入應(yīng)用

        骨架屏有兩種比較常見的應(yīng)用場景

        • 用于首屏或某個頁面打開時等待數(shù)據(jù)返回時的占位
        • 用于滾動加載等列表頁面占位

        接下來研究在這兩種場景下如何嵌入骨架屏

        獲取骨架屏代碼

        在前面遍歷入口節(jié)點DOM樹渲染骨架屏之后,我們只需要獲取對應(yīng)節(jié)點的HTML代碼即可。由于沒有修改原本布局信息,因此這段HTML代碼再同一個頁面是完全可以展示的。我們要做的就是拿到對應(yīng)的骨架屏HTML代碼。

        由于整個操作都是在當(dāng)前頁面上執(zhí)行的,拿到當(dāng)前頁面上某個節(jié)點的的html內(nèi)容就變得十分簡單了

        • 通過控制臺,找到節(jié)點然后訪問innerHTML
        • 通過調(diào)試工具Elements面板,然后Copy HTML

        上面的方式需要手動去操作,我們可以使用擴展程序在骨架屏渲染結(jié)束之后自動保存對應(yīng)的HTML代碼

        //?chromeMsg是自己封裝的一個`chrome.runtime.onMessage`工具
        chromeMsg.on("createSkeleton",?(params)?=>?{
        ????const?{config}?=?params
        ????//?默認(rèn)頁面根節(jié)點,可以導(dǎo)出某個dom容器的骨架屏結(jié)構(gòu)
        ????let?content?=?renderSkeleton(".page",?config)
        ????//?處理對應(yīng)的骨架屏HTML代碼,這里只是簡單打印,也可以直接保存文件到本地,或者通過HTTP接口調(diào)用某些鉤子服務(wù)完成自動注入等功能
        ????console.log(content)
        })
        復(fù)制代碼

        整頁應(yīng)用

        對于整頁骨架屏,我們需要先使用測試數(shù)據(jù)生成對應(yīng)的骨架屏,然后將得到的HTML放入頁面中即可。

        以Vue等單頁項目而言,在整個應(yīng)用初始化之前,頁面上只存在

        根節(jié)點,用戶看見的是一片空白,對于這種首屏應(yīng)用,我們可以將得到的骨架屏代碼直接嵌入到根節(jié)點中,這樣當(dāng)頁面加載至應(yīng)用初始化之前,用戶都能看見完整的骨架屏效果。

        對于其他單頁應(yīng)用其他頁面而言,也可以采用類似的原理,等待接口返回前展示骨架屏

        <SkeletonFrame?:frame="html"?:loading="isLoading">
        ????<RealPage>RealPage>
        SkeletonFrame>
        復(fù)制代碼

        比如我們可以封裝一個SkeletonFrame組件,其接口包括

        • frameloading兩個props,表示骨架屏HTML和是否正在加載,當(dāng)loading為true時展示骨架屏
        • default slot需要真實渲染的頁面組件,當(dāng)loading為false時展示真正頁面組件

        局部骨架屏

        對于一些需要持續(xù)加載的數(shù)據(jù),如滾動加載等,可以先獲取對應(yīng)區(qū)塊的骨架屏HTML代碼(不需要整個頁面的骨架屏代碼),然后將其封裝成待展示組件,與上面SkeletonFrame邏輯比較類似

        小結(jié)

        本文整理了骨架屏的實現(xiàn)原理,然后提供了一種通過Chrome擴展程序生成任意網(wǎng)頁的骨架屏方案,主要包括

        • 將頁面按節(jié)點類型拆分成不同區(qū)塊
        • 支持自定義節(jié)點類型、忽略或隱藏節(jié)點
        • 導(dǎo)出并使用骨架屏HTML代碼

        當(dāng)然,上面只是介紹了大概的實現(xiàn)思路,并給出了一個簡單的Demo,距離實際應(yīng)用還有一些距離,等后面有時間再進(jìn)一步完善吧。本文所有代碼均放在github上面了,歡迎提PR和issue。

        —————END—————



        喜歡本文的朋友,歡迎關(guān)注公眾號?達(dá)達(dá)前端,收看更多精彩內(nèi)容



        點個[在看],是對達(dá)達(dá)最大的支持!



        瀏覽 119
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

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

        手機掃一掃分享

        分享
        舉報
        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>
            青青草成人视频在线观看 | 久久伊人精品网 | 日韩A毛片| 男人日女人下面视频 | TS人妖一区二区三区 | 日本三级韩国三级国产三级 | 影音先锋色色 | 我要操大逼| 性生交片免费看人无人区 | 韩国三级hd中文字幕有哪些 |