ホームページ > ウェブフロントエンド > Vue.js > vite プラグインを使用してスケルトン画面を自動化する方法について話しましょう

vite プラグインを使用してスケルトン画面を自動化する方法について話しましょう

青灯夜游
リリース: 2022-10-09 19:19:24
転載
2390 人が閲覧しました

スケルトン画面はおまけです。理想的には、開発者はスケルトン画面にあまり注意を払う必要はありません。したがって、開発経験の観点から、スケルトン画面を手動で記述することは良い解決策ではありません。したがって、この記事では主に、スケルトン スクリーンを自動生成する別のスキーム、つまり vite プラグインを介してスケルトン スクリーンを自動的に挿入する方法について検討します。

vite プラグインを使用してスケルトン画面を自動化する方法について話しましょう

[関連する推奨事項: vuejs ビデオ チュートリアル]

スケルトン画面には、SPA アプリケーションのユーザー エクスペリエンスを大幅に向上させる 2 つの機能があります.機能

  • ページをロードするために初期化するときに空白スペースを回避します。エクスペリエンスは、SSR からページの初期化が完了するのを完全に待機するまでの間です。
  • ページの初期化が完了するまで空白スペースを回避します。一部のルーティング コンポーネントはレンダリング前にデータをロードする必要があります

スケルトン画面は、コンテンツが返されたようにユーザーに錯覚させます。しばらく待つ限り、完全なコンテンツを見ることができます。したがって、スケルトン画面は、実際のコンテンツが準備される前の代役として配置されます。

私は以前、スケルトン画面をすばやく生成するアイデアを検討しました: Chrome 拡張機能を使用して Web スケルトン画面を生成する. 一般原則は、content.js## を挿入することです。 # Chrome 拡張機能を使用して、ページの DOM インターフェイスを変更し、最後にスケルトン画面スタイルで HTML コードをエクスポートします。

当時のアイデアはまだ本番には反映されていませんが、最近ユーザーエクスペリエンス関連の機能をいじっていて、スケルトン画面に関する部分はまだ改善し続ける必要があることがわかりました。

業界にはスケルトン画面の適用に関するソリューションがいくつかあります

    デザイナーに直接依頼して、ページに対応するスケルトン画面の設計図を提供してもらいます
    • エクスポート
    • svg または base64 画像はコードに埋め込まれているため、プロジェクトのサイズに影響します。
    • 手動による記述スタイルを開発します。これには大きな作業負荷が必要です。
  • コンポーネントによる書き込みスケルトン画面
  • page-skeleton-webpack-plugin
  • と、カスタマイズの度合いが低いという欠点があります。他の比較的成熟した自動スケルトン スクリーン ソリューションには、さまざまな側でスケルトン スクリーンの生成を制御する特別な UI インターフェイスもあります。欠点は、生成されるスケルトン スクリーン コードが大きくなり、プロジェクト サイズに影響することです。
    • puppeteer の助けを借りてページをレンダリングします。対応するスケルトン画面コンテンツは
    • に大きく依存します。Chrome 拡張機能を使用してスケルトン画面コンテンツを生成します。これは本質的にヘッドレス ブラウザの原理と似ています。ブラウザ
    • スケルトン画面 これはおまけです。理想的には、開発者はあまり注意を払う必要はありません。したがって、開発経験の観点から、手動でスケルトン画面を作成することは良い解決策ではありません。したがって、この記事では主に、スケルトン スクリーンを自動生成する別のスキーム、つまり vite プラグインを介してスケルトン スクリーンを自動的に挿入する方法について検討します。
    • #最初にエフェクトをプレビューします
  • クリックしてスケルトン画面を生成します

#最初の画面にアクセスします

vite プラグインを使用してスケルトン画面を自動化する方法について話しましょうvite プラグインによるスケルトン画面の生成

参考

vite プラグインを使用してスケルトン画面を自動化する方法について話しましょう

フロントエンドのインテリジェント探索、スケルトン画面のローコード自動生成ソリューションの実践

#vite-plugin-vue-inspector

このプラグインの実装では、ソース コードから一部の情報がページに挿入されます たとえば、次の構造

<div>
		<div>卡片标题</div>
	  <div>卡片内容卡片内容</div>
</div>
ログイン後にコピー
ログイン後にコピー
で生成されるスケルトン画面コードは

<div>
		<div>卡片标题</div>
	  <div>卡片内容卡片内容</div>
</div>
ログイン後にコピー
ログイン後にコピー
となり、その内

sk- block

sk- text などのスタイル クラスは生成時に追加され、元のレイアウト スタイルを保持したままスケルトン画面の灰色の背景を表示するために元のスタイルを上書きするために使用されます。 renderSkeleton

の呼び出しタイミングは開発者によって制御されます。ページにボタンを挿入し、クリックされたときに

function createTrigger() {
  const div: HTMLDivElement = document.createElement('div')
  div.setAttribute('style', 'position:fixed;right:0;bottom:20px;width:50px;height:50px;background:red;')
  div.addEventListener('click', function () {
    renderSkeleton('[data-skeleton-root]')
  })
  document.body.appendChild(div)
}

if(process.end.NODE_ENV ==='development'){
 createTrigger() 
}
ログイン後にコピー
を呼び出すことができます。スケルトン画面のコードを取得した後、ビジネスコード内 loading フラグは、スケルトン画面と実際のコンテンツのどちらを表示するかを制御するために使用されます。

<script>
import {ref, onMounted} from "vue";

const loading = ref(true);
const list = ref<number>([]);

async function fetchList() {
  await sleep(1000)
  list.value = [1, 2, 3, 4, 5]
  loading.value = false
}

onMounted(() => {
  fetchList()
})

</script>

<template>
  <div>
    <div>
      <!--这里的都是骨架屏代码-->
      <div>
          <div>卡片标题</div>
          <div>卡片内容卡片内容</div>
      </div>
   </div>
    <div>
     <div>
          <div>卡片标题</div>
          <div>卡片内容卡片内容</div>
      </div>
    </div>
  </div>

</template>

<style>
// 相关的样式
</style>
ログイン後にコピー
v-if= 内のコードが確認できます。 「loading」タグで生成されたスケルトン画面の内容です。スケルトン画面はビジネス コードとともにあるため、Vue の SFC コンパイルにも参加するため、スケルトン画面ラベルの

scopeid などのいくつかの動的属性を削除する必要があることに注意してください。スコープ ID によって引き起こされるその他の問題については後で説明しますが、これは renderSkeleton 全体の実装にも影響します。 renderSkeleton

を呼び出してスケルトン画面コードを取得した後、毎回手動でビジネス コードを修正して読み込み中の表示内容を置き換えるのは間違いなく非常に面倒です。ここでこれを自動的に解決する方法を検討しましょう。問題。

前述したように、スケルトン画面は主に最初の画面のレンダリングが必要な場合とルーティング ページの切り替え時に使用されます

SPA の最初の画面レンダリングの最適化

    ルーティング コンポーネントを切り替えるときの占有率ビット コンテンツ
  • これら 2 つのシナリオでスケルトン スクリーン コードを自動的に挿入する方法を見てみましょう

コンポーネント内でのスケルトン スクリーンのレンダリング

us プレースホルダーを使用して、
<div>__SKELETON_APP_CONTENT__</div>
<div>真实业务代码</div>
ログイン後にコピー

のように、現在のコンポーネントがスケルトン スクリーン コードに対応する場所を宣言できます。スケルトン スクリーン コードを取得した後、ここのコンテンツを実際のスケルトン スクリーン コードに置き換えることができます。

交換方法は? vite プラグインは、

transform フック

const filename = './src/skeleton/content.json'

function SkeletonPlaceholderPlugin() {
  return {
    name: 'skeleton-placeholder-plugin',
    enforce: 'pre',
    transform(src, id) {
      if (/\.vue$/.test(id)) {
        const {content} = fs.readJsonSync(filename)
        // 约定对应的骨架屏占位符
        let code = src.replace(/__SKELETON_(.*?)_CONTENT__/igm, function (match) {
          return content
        })

        return {
          code,
        }
      }
      return src
    },
  } as Plugin
}
ログイン後にコピー
を提供します。

./skeleton.txt のコンテンツは、renderSkeleton

スケルトン画面の呼び出し後に生成されます。

transformpre を使用すると、vue プラグインが SFC を解析する前に、スケルトン画面のプレースホルダーを実際のコードに置き換えることができ、その後のコンパイル プロセスに参加できます。 ここで解決する必要がある別の問題があります。renderSkeleton はクライアント上でトリガーされ、skeleton.txt

は開発サーバー環境にあり、通信が行われます。クライアントが生成したスケルトン画面コードをプロジェクトディレクトリに送信する仕組みです。

vite プラグインは、vite 開発サーバーを構成するための configureServer フックを提供します。スケルトン画面コードを保存するためのインターフェイスを提供するミドルウェアを追加できます。

function SkeletonApiPlugin() {
  async function saveSkeletonContent(name, content) {
    await fs.ensureFile(filename)
    const file = await fs.readJson(filename)
    file[name] = {
      content,
    }
    await fs.writeJson(filename, file)
  }

  return {
    name: 'skeleton-api-plugin',
    configureServer(server) {
      server.middlewares.use(bodyParser())
      server.middlewares.use('/update_skeleton', async (req, res, next) => {
        const {name, content, pathname} = req.body
        await saveSkeletonContent(name, content, pathname)
        // 骨架屏代码更新之后,重启服务
        server.restart()
        res.end('success')
      })
    },
  }
}
ログイン後にコピー
その後

renderSkeleton、このインターフェイスを呼び出して、生成されたスケルトン画面コードをアップロードします

async function sendContent(body: any) {
  const response = await fetch('/update_skeleton', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(body)
  })
  const data = await response.text()
}

const __renderSkeleton = async function () {
  const {name, content} = renderSkeleton(".card-list", {})

  await sendContent({
    name,
    content
  })
}
ログイン後にコピー
bingo! これで完了です。プロセスの整理

#開発者がある時点で手動で

__renderSkeleton

を呼び出すと、現在のページのスケルトン画面が自動的に生成されます

  • スケルトン画面コードを vite インターフェイスに送信し、ローカルの skeleton/content.json でスケルトン画面コードを更新し、

  • vite 後にリトリガーしますサービスを再起動しますpreキュー内の

    skeleton-content-component
  • プラグインは、スケルトン画面のプレースホルダーを置き換え、スケルトン画面のコードを挿入し、スケルトン画面の挿入プロセス全体を完了します。
  • プロセス全体で、開発者は次の 2 つの手順を完了するだけで済みます。

  • ビジネス コードでスケルトン画面のプレースホルダーを宣言します。

    ボタンをクリックしてスケルトン画面コードの生成をトリガーします
  • ルーティング切り替え用のスケルトン画面は主にルーティング コンポーネントで使用され、さらにカプセル化と統合が考えられます ローディングとスケルトン画面表示の管理 詳細については、ここで説明しますので、一つ一つ説明しません。
  • 首屏渲染骨架屏

    骨架屏对于SPA首屏渲染优化,需要在应用初始化之前就开始渲染,即需要在id="app"的组件内植入初始化的骨架屏代码

    如果是服务端预渲染,可以直接返回填充后的代码;如果是客户端处理,可以通过document.write处理,我们这里只考虑纯SPA引用,由前端处理骨架屏的插入。

    我们可以通过vite插件提供的transformIndexHtml钩子注入这段逻辑

    function SkeletonApiPlugin() {
      return {
        name: 'skeleton-aip-plugin',
        transformIndexHtml(html) {
          let {content} = fs.readJsonSync(filename)
          const code = `
    <script>
    var map = ${JSON.stringify(content)}
    var pathname = window.location.pathname
    var target = Object.values(map).find(function (row){
      return row.pathname === pathname
    })
    var content = target && target.content || &#39;&#39;
    document.write(content)
    </script>
          `
          return html.replace(/__SKELETON_CONTENT__/, code)
        }
    
      }
    }
    ログイン後にコピー

    对应的index.html代码为

    <div>__SKELETON_CONTENT__</div>
    ログイン後にコピー
    ログイン後にコピー

    根据用户当前访问的url,读取该url对应的骨架屏代码,然后通过document.write写入骨架屏代码。这里可以看出,在生成骨架屏代码时,我们还需要保留对应页面url的映射,甚至需要考虑动态化路由的匹配问题。这个也比较简单,在提交到服务端保存时,加个当前页面的路径参数就行了

      const {name, content} = renderSkeleton(sel, defaultConfig)
      // 如果是hash路由,就替换成fragment
      const {pathname} = window.location
      await sendContent({
        name,
        content,
        pathname // 保存骨架屏代码的时候顺道把pathname也保存了
      })
    ログイン後にコピー

    整理一下流程

    • 用户访问url
    • 根据页面url,加载对应的骨架屏代码,填充在根节点下
      • 如果是服务端预渲染,可以直接返回填充后的代码
      • 如果是客户端处理,可以通过document.write处理
    • 用户看见渲染的骨架屏内容
    • 初始化应用,加载页面数据,渲染出真实页面

    开发者在点击生成当前页面的骨架屏时,保存的骨架屏代码,既可以用在路由组件切换时的骨架屏,也可以用在首屏渲染时的骨架屏,Nice~

    存在的一些问题

    利用vite插件注入骨架屏的代码,看起来是可行的,但在方案落地时,发现了一些需要解决的问题。

    存在原始样式不生效的场景

    由于生成的骨架屏代码是依赖原始样式的,

    <div></div>
    ログイン後にコピー
    ログイン後にコピー

    对应的骨架屏代码

    <div></div>
    ログイン後にコピー
    ログイン後にコピー

    其中的sk-block只会添加一些灰色背景和动画,至于整体的尺寸和布局,还是card这个类来控制的。

    这么设计的主要原因是:即使card的尺寸布局发生了变化,对应的骨架屏样式也会一同更新。

    但在某些场景下,原始样式类无法生效,最具有代表性的问题就Vue项目的的scoped css

    我们知道,vue-loader@vitejs/plugin-vue等工具解析SFC文件时,会为对应组件生成scopeId(参考之前的源码分析:从vue-loader源码分析CSS-Scoped的实现),然后通过postcss插件,通过组合选择器实现了类似于css作用域的样式表

    .card[data-v-xxx] {}
    ログイン後にコピー
    ログイン後にコピー
    ログイン後にコピー

    我们的生成骨架屏的时机是在开发环境下进行的,这就导致在生产环境下,看到的骨架屏并没有原始样式类对应的尺寸和布局。

    下面是vite vue插件的源码

    export function createDescriptor(
      filename: string,
      source: string,
      { root, isProduction, sourceMap, compiler }: ResolvedOptions
    ): SFCParseResult {
      const { descriptor, errors } = compiler.parse(source, {
        filename,
        sourceMap
      })
    
      const normalizedPath = slash(path.normalize(path.relative(root, filename)))
      descriptor.id = hash(normalizedPath + (isProduction ? source : ''))
    
      cache.set(filename, descriptor)
      return { descriptor, errors }
    }
    ログイン後にコピー

    vue-loader中生成scopeid的方法类似,看了一下貌似并没有提供自定义scopeid的API。

    因此对于同一个文件而言,生产环境和非生产环境参与生产hash的参数是不一样的,导致最后得到的scopeid 也不一样。

    对于组件内渲染骨架屏这种场景,我们也许可以不考虑scopeid,因为在SFC编译之前,我们就已经通过transform钩子注入了对应的骨架屏模板,对于SFC编译器而言,骨架屏代码和业务代码都在同一个组件内,也就是说他们最后都会获得相同的scopeid,这也是为什么生成的骨架屏代码,要擦除HTML标签上面的scopeid的原因。

    但如果骨架屏依赖的外部样式并不在同一个SFC文件内,也会导致原始的骨架屏样式不生效。

    <template>
      <div>
        <div>
          <div>
              <div>卡片标题</div>
              <div>卡片内容卡片内容</div>
          </div>
       </div>
        <div>
         <card></card>
        </div>
      </div>
    
    </template>
    
    <style>
    // card 样式不在这个SFC下面,但是插入的骨架屏代码却在这个SFC文件下
    </style>
    ログイン後にコピー

    此外,对于首屏渲染骨架屏这种场景,就不得不考虑scopeid了。如果骨架屏依赖的原始样式是携带作用域的,那就必须要保证骨架屏代码与生产环境的样式表一致

    .card[data-v-xxx] {}
    ログイン後にコピー
    ログイン後にコピー
    ログイン後にコピー
    <div></div>
    ログイン後にコピー

    这样,首屏渲染依赖的骨架屏和组件内渲染的骨架屏就产生了冲突,前者需要携带scopeid,而后者又需要擦除scopeid。

    为了解决这个冲突,有两种办法

    • 在保存骨架屏代码时,同时保存对应的scopeid,并在首屏渲染时,为每个标签上手动添加scopeid。
    • 原始骨架屏代码就携带scopeid,而在替换组件内的骨架屏占位符时再擦除scopeid

    但不论通过何种方式保证两个环境下生成的scopeid 一致(甚至是通过修改插件源码的方式),可能也会存在旧版本的骨架屏携带的scopeid和新版本对应的scopeid 不一致的问题,即旧版本的class和新版本的class不一致。

    要解决这个问题,除非在每次修改源码之后,都更新一下骨架屏,由于生成骨架屏这一步是手动的,这与自动化的目的背道而驰了。

    因此,看起来利用原始类同步真实DOM的布局和尺寸,在scoped css中并不是一个十分完善的设计。

    骨架屏代码质量

    第二个不是那么重要的问题是生成的骨架屏代码,相较于手动编写,不够精简。

    虽然在源代码中,骨架屏代码被占位符替代,但在编译阶段,骨架屏会编译到render函数中,可能造成代码体积较大,甚至影响页面性能的问题。

    这个问题并不是一个阻塞性问题,可以后面考虑如何优化,比如骨架屏仍旧保留v-for等指令,组件可以正常编译,而首屏渲染的骨架屏需要通过自己解析生成完整的HTML代码。

    解决方案

    上面这两个问题的本质都是因为骨架屏生成方案导致的,跟后续保存骨架屏代码并自动替换并没有多大关系,因此我们只需要优化骨架屏生成方案即可。

    既然依赖于原始样式生成的骨架屏代码存在这些缺点,有没有什么解决办法呢?

    事实上,我们对于骨架屏是否更真实内容结构的还原程度并没有那么高的要求,也并没有要求骨架屏要跟业务代码一直保持一致,既然导出HTML骨架屏代码比较冗余和繁琐,我们可以换一换思路。

    不使用scoped css

    其他比较常用的CSS方案如css moudlecss-in-js或者是全局原子类css如tailwindwindicss等,如果输出的是纯粹的CSS代码,且生产环境和线上保持一致,理论上是不会出现scopeid这个问题的。

    但Vue项目中,scoped css方案应该占据了半壁江山,加上我自己也比较喜欢scoped css,因此这是一个绕不过去的问题。

    将骨架屏页面自动转成图片

    第一种思路将骨架屏页面保存为图片,这样就不用再依赖原始样式了。

    大概思路就是:在解析当前页面获得骨架屏代码之后,再通过html2canvas等工具,将已经渲染的HTML内容转成canvas,再导出base64图片。

    import html2canvas from 'html2canvas'
    
    const __renderSkeleton = async function (sel = 'body') { 
      const {name, content} = renderSkeleton(sel, defaultConfig)
      const canvas = await html2canvas(document.querySelector(sel)!)
      document.body.appendChild(canvas);
      
      const imgData = canvas.toDataURL()
      // 保存<img  alt="vite プラグインを使用してスケルトン画面を自動化する方法について話しましょう" >作为骨架屏代码
    }
    ログイン後にコピー

    这种通过图片替代HTML骨架屏代码的优点在于兼容性好(对应的页面骨架屏甚至可以用在App或小程序中),容易迁移,不需要依赖项目代码中的样式类。

    但是html2canvas生成的图片也不是百分百还原UI,需要足够纯净的代码原始结构才能生成符合要求的图片。此外图片也存在分辨率和清晰度等问题。

    也许又要回到最初的起点,让设计大佬直接导出一张SVG?(开个玩笑,我们还是要走自动化的道路

    复制一份独立的样式表

    如果能够找到骨架屏代码中每个标签对应的class在样式表中定义的样式,类似于Chrome dev tools中的Elements Styles面板,我们就可以将这些样式复制一份,然后将scopeid替换成其他的选择器

    vite プラグインを使用してスケルトン画面を自動化する方法について話しましょう

    开发环境下的样式都是通过style标签引入,因此可以拿到页面上所有的样式表对象,提取符合对应选择器的样式,包括.className.className[scopeId]这两类

    写一个Demo

    const { getClassStyle } = (() => {
        const styleNodes = document.querySelectorAll("style");
        const allRules = Array.from(styleNodes).reduce(
            (acc, styleNode) => {
                const rules = styleNode.sheet.cssRules;
                acc = acc.concat(Array.from(rules));
                return acc;
            },
            []
        );
        const getClassStyle = (selectorText) => {
            return allRules.filter(
                (row) => row.selectorText === selectorText
            );
        };
    
        return {
            getClassStyle,
        };
    })();
    
    const getNodeAttrByRegex = (node, re) => {
        const attr = Array.from(node.attributes).find((row) => {
            return re.test(row.name);
        });
        return attr && attr.name;
    };
    
    const parseNodeStyle = (node) => {
        const scopeId = getNodeAttrByRegex(node, /^data-v-/);
        return Array.from(myBox.classList).reduce((acc, row) => {
            const rules = getClassStyle(`.${row}`);
    
          	// 这里没有再考虑两个类.A.B之类的组合样式了,排列组合比较多
            return acc
                .concat(getClassStyle(`.${row}`))
                .concat(getClassStyle(`.${row}[${scopeId}]`));
        }, []);
    };
    const rules = parseNodeStyle(myBox);
    console.log(rules);
    ログイン後にコピー

    这样就可以得到每个节点在scoped css的样式,然后替换成骨架屏依赖的样式。

    但现在要保存的骨架屏代码的HTML结构之外,还需要保存对应的那份CSS代码,十分繁琐

    提取必要的布局信息生成骨架屏

    能否像html2canvas的思路一样,重新绘制一份骨架屏页面出来呢

    通过getComputedStyle可以获取骨架屏每个节点的计算样式

    const width = getComputedStyle(myBox,null).getPropertyValue('width');
    ログイン後にコピー

    复用页面结构,把所有布局和尺寸相关的属性都枚举出来,一一获取然后转成行内样式,看起来也是可行的。

    但这个方案需要逐步尝试完善对应的属性列表,相当于复刻一下浏览器的布局规则,工作量较大,此外还需要考虑rem、postcss等问题,看起来也不是一个明智的选择。

    postcss插件

    既然scopeid是通过postcss插入的,能不能在对应的样式规则里面加一个分组选择器,额外支持一下骨架屏的呢

    比如

    .card[data-v-xxx] {}
    ログイン後にコピー
    ログイン後にコピー
    ログイン後にコピー

    修改为

    .card[data-v-xxx], .sk-wrap .card {}
    ログイン後にコピー

    这样,只要解决生产环境和开发环境scopeid不一致的问题就可以了。

    编写postcss插件可以参考官方文档:编写一个postcss 插件

    vue/compuler-sfc源码中发现,scopedPlugin插件位于传入的postcssPlugins之后,而我们编写的插件需要位于scopedPlugin之后才行,

    vite プラグインを使用してスケルトン画面を自動化する方法について話しましょう

    如果不能修改源码,只有继续从vite 插件的transform钩子入手了,在transform中手动执行postcss进行编译

    继续编写一个SkeletonStylePlugin插件

    const wrapSelector = '.sk-wrap'
    export function SkeletonStylePlugin() {
      return {
        name: 'skeleton-style-plugin',
        transform(src: string, id: string) {
          const {query} = parseVueRequest(id)
    
          if (query.type === 'style') {
            const result = postcss([cssSkeletonGroupPlugin({wrapSelector})]).process(src)
            return result.css
          }
          return src
        }
      }
    }
    ログイン後にコピー

    注意该插件要放在vue插件后面执行,因为此时得到的内容才是经过vue-compiler编译后的携带有scopeid 的样式。

    其中cssSkeletonGroupPlugin是一个postcss插件

    import {Rule} from 'postcss'
    
    const processedRules = new WeakSet<rule>()
    
    type PluginOptions = {
      wrapSelector: string
    }
    const plugin = (opts: PluginOptions) => {
      const {wrapSelector} = opts
    
      function processRule(rule: Rule) {
        if (processedRules.has(rule)) {
          return
        }
        processedRules.add(rule)
        rule.selector = rewriteSelector(rule)
      }
    
      function rewriteSelector(rule: Rule): string {
        const selector = rule.selector || ''
    
        const group: string[] = []
        selector.split(',').forEach(sel => {
          // todo 这里需要排除不在骨架屏中使用的样式
          const re = /\[data-v-.*?\]/igm
          if (re.test(sel)) {
            group.push(wrapSelector + ' ' + sel.replace(re, ''))
          }
        })
    
        if(!group.length) return selector
        return selector + ', ' + group.join(',')
      }
    
      return {
        postcssPlugin: 'skeleton-group-selector-plugin',
        Rule(rule: Rule) {
          processRule(rule)
        },
      }
    }
    plugin.postcss = true
    
    export default plugin</rule>
    ログイン後にコピー

    这个插件写的比较粗糙,只考虑了常规的选择器,并依次追加分组选择器。测试一下

    .test1[data-v-xxx] {}
    ログイン後にコピー

    成功编译成了

    .test1[data-v-xxx], .sk-wrap .test1 {}
    ログイン後にコピー

    这样,只需要将骨架屏代码外边包一层sk-wrap,骨架屏中的样式就可以正常生效了!

    content && document.write('<div>' +content+'</div>')
    ログイン後にコピー

    看起来解决了一个困扰我很久的问题。

    小结

    至此,一个借助于Vite插件实现自动骨架屏的方案就实现了,总结一下整体流程

    首先初始化插件

    import {SkeletonPlaceholderPlugin, SkeletonApiPlugin} from '../src/plugins/vitePlugin'
    
    export default defineConfig({
      plugins: [
        SkeletonPlaceholderPlugin(),
        vue(),
        SkeletonApiPlugin(),
      ],
      build: {
        cssCodeSplit: false
      }
    })
    ログイン後にコピー

    然后填写占位符,对于首屏渲染的骨架屏

    <div>__SKELETON_CONTENT__</div>
    ログイン後にコピー
    ログイン後にコピー

    对于组件内的骨架屏

    
    
    __SKELETON_APP_CONTENT__
    <div></div>
    ログイン後にコピー

    接着初始化客户端触发器,同时向页面插入一个可以点击生成骨架屏的按钮

    import '../../src/style/skeleton.scss'
    import {initInject} from '../../src/inject'
    
    createApp(App).use(router).mount('#app')
    
    // 开发环境下才注入
    if (import.meta.env.DEV) {
      setTimeout(initInject)
    }
    ログイン後にコピー

    点击触发器,自动将当前页面转换成骨架屏

    通过HTTP将骨架屏代码发送到插件接口,通过fs写入本地文件./src/skeleton/content.json中,然后自动重启vite server

    页面内组件的占位符会通过SkeletonPlaceholderPlugin替换对应占位符的骨架屏代码,loading生效时展示骨架屏

    首屏渲染页面时,通过location.pathname插入当前路径对应的骨架屏代码,直接看见骨架屏代码

    所有骨架屏依赖的当前样式通过cssSkeletonGroupPlugin解析,通过分组选择器输出在css文件,不再依赖scopeid。

    这样,一个基本自动的骨架屏工具就集成到项目中,需要进行的手动工作包括

    • 配置插件
    • 定义组件的骨架屏占位符,以及骨架屏入口data-skeleton-root="APP"
    • 必要时在标签上声明data-skeleton-type,定制骨架屏节点

    整个项目比较依赖vite插件开发知识,也参考了vite@vitejs/plugin-vue@vue/compile-sfc等源码的实现细节。

    所有Demo已经放在github上面了,剩下要解决的就是优化生成骨架屏的效果和质量了,期待后续吧

    (学习视频分享:web前端开发编程基础视频

以上がvite プラグインを使用してスケルトン画面を自動化する方法について話しましょうの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:juejin.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート