<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 3920字,需瀏覽 8分鐘

           ·

          2021-01-25 06:45

          來源:橙紅年代

          https://juejin.im/post/6856784900775739400

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

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

          掘金首頁

          百度首頁

          知乎首頁

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

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

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

          骨架屏方案

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

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

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

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

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

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

          何為骨架?

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

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

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

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

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

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

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

          文字

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

          • 行高部分用透明色,
          • 內容部分用灰色

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

          .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);?//?與上面等價*/
          }
          復制代碼

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

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

          ????//?todo?處理瀏覽器默認行高、包含繼承、自定義等屬性
          ????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);
          }
          復制代碼

          因為每個文本節(jié)點的字體大小和行高可能不一樣,如果全在style標簽上添加樣式,則生成的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;
          }
          復制代碼

          圖片

          獲取原始圖片節(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"
          ????})
          }
          復制代碼

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

          按鈕、input

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

          邊框

          替換對應邊框的顏色為骨架屏灰色

          列表

          列表元素是骨架屏中一個比較常見的區(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)
          ????}
          }
          復制代碼

          遍歷DOM樹

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

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

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

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

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

          • 對于需要隱藏的節(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)
          }
          復制代碼

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

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

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

          用掘金首頁試一下

          原本的效果

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

          自定義配置后的效果

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

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

          將骨架屏嵌入應用

          骨架屏有兩種比較常見的應用場景

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

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

          獲取骨架屏代碼

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

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

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

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

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

          整頁應用

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

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

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

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

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

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

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

          局部骨架屏

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

          小結

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

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

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

          ??愛心三連擊

          1.看到這里了就點個在看支持下吧,你的點贊在看是我創(chuàng)作的動力。

          2.關注公眾號程序員成長指北,回復「1」加入高級前端交流群!「在這里有好多 前端?開發(fā)者,會討論?前端 Node 知識,互相學習」!

          3.也可添加微信【ikoala520】,一起成長。

          “在看轉發(fā)”是最大的支持

          瀏覽 72
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  美女被干舒服偷拍自拍 | 又黄又爽的视频 | 乱伦大杂烩视频 | 无码中文字幕无码视频在线观看 | yy亚洲av |