Vue3 トランジションアニメーションの落とし穴記録について

Vue3 トランジションアニメーションの落とし穴記録について

背景

私のコース「Vue 3 エンタープライズレベルの音楽アプリの開発」の Q&A セクションで、クラスメートが質問しました。歌手リストから歌手の詳細ページへの遷移アニメーションには、入場アニメーションのみがあり、退場アニメーションがありません。

学生は確かにしばらくこの問題に取り組んでいましたが、彼の説明からはしばらく問題が何なのかわからなかったので、コードを GitHub にアップロードするように依頼しました。結局のところ、コード レベルで直接問題を特定するのが最も信頼性が高いのです。

問題の場所

通常、このような問題に遭遇したとき、私が最初に考えるのは、彼が使用している Vue 3 バージョンに問題があるのではないかということです。結局のところ、Vue 3 はまだ継続的な反復の過程にあり、特定のバージョンにいくつかの小さなバグがあるのは普通のことです。そこで、彼のプロジェクトの Vue 3 バージョンを最新の 3.2.26 にアップグレードしました。

しかし、実行してみると、問題がまだ存在していることがわかりました。少し混乱したので、コース プロジェクトのソース コードを実行しましたが、問題を再現できませんでした。その後、コース プロジェクトの Vue 3 バージョンを最新バージョンにアップグレードしましたが、それでも問題を再現できませんでした。

上記の分析を通じて、Vue 3 バージョンの問題は基本的に排除されました。基本的に、歌手ページから歌手詳細ページに切り替えることは、セカンダリ ルーティング ページである歌手詳細ページを開くことに過ぎず、歌手詳細ページから歌手ページに戻ることは、セカンダリ ルーティング ページである歌手詳細ページを削除することに過ぎません。そこで、2 つのプロジェクトの歌手ページと詳細ページのソース コードを比較し始めました。

<!-- 歌手.vue -->

<テンプレート>

<div class="歌手" v-loading="!singers.length">

  <インデックスリスト

    :data="歌手"

    @select="歌手を選択"

  </インデックスリスト>

  <!-- セカンダリルーティングを実行するにはルータビューを使用します -->

<!-- <ルータービュー:singer="選択された歌手"></ルータービュー>-->

  <!-- Vue3 はルータービューでトランジションを使用する必要があり、appear に入るとアニメーションが表示されます -->

  <router-view v-slot="{ コンポーネント }">

<!-- singer-detail はアニメーションの無効な調査を返します -->

    <transition appear name="スライド">

      <!-- コンポーネント 動的コンポーネント コンポーネントは、スコープ スロットのプロパティで、router-view グループによって提供されます。 コンポーネントは、ルーティング テーブル内のルーティング コンポーネントです。 exclude="singer-detail" は、データをキャッシュしないコンポーネントを除外します。そうでない場合は、データがキャッシュされ、毎回再要求されることはありません -->

        <component :is="コンポーネント"

                   :singer="選択した歌手"

        </コンポーネント>

    </トランジション>

  </ルータービュー>

</div>

</テンプレート>



<!-- 歌手の詳細.vue -->

<テンプレート>

  <!-- セカンダリ ルーティングを通じて実装されるため、ビューの下に配置されます -->

  <section class="歌手の詳細">

    <音楽リスト

      :songs="曲"

      :title="タイトル"

      :pic="写真"

      :loading="読み込み中"

    </音楽リスト>

  </セクション>

</テンプレート>

上記は学生のコードです。次に、私のプロジェクトのソース コードを貼り付けます。

<!-- 歌手.vue -->

<テンプレート>

  <div class="歌手" v-loading="!singers.length">

    <インデックスリスト

      :data="歌手"

      @select="歌手を選択"

    </インデックスリスト>

    <router-view v-slot="{ コンポーネント }">

      <transition appear name="スライド">

        <component :is="コンポーネント" :data="選択された歌手"/>

      </トランジション>

    </ルータービュー>

  </div>

</テンプレート>

<!-- 歌手の詳細.vue -->

<テンプレート>

  <div class="歌手の詳細">

    <音楽リスト

      :songs="曲"

      :title="タイトル"

      :pic="写真"

      :loading="読み込み中"

    </音楽リスト>

  </div>

</テンプレート>

比較してみると、学生がコメントを使って勉強ノートを作っている点を除けば、両方のソースコードに大きな違いはないように感じます。最初は問題を見つけるのが難しかったので、ソース コードをデバッグするという最善の策を使いました。結局のところ、私は Vue 3 トランジションアニメーションの実装原則を非常によく知っています。

終了遷移アニメーションが実行されると、遷移コンポーネントによってラップされた子ノードから解析された Leave フック関数が実行されます。

そこで、Leave フック関数内にデバッガー ブレークポイントを追加しました。

// @vue/runtime-core/dist/runtime.core-bundler.esm.js

残す(el, 削除) {

  デバッガ

  定数キー = String(vnode.key);

  el._enterCbの場合{

    el._enterCb(true /* キャンセルされました */);

  }

  // ...

}

次に、プロジェクトを実行します。歌手の詳細ページから歌手ページに戻ると、デバッガーのブレークポイントに入っていないことがわかります。これは、leave hook 関数がまったく実行されていないことを意味します。

さらに遡って、アンインストールしようとしているノードの場合、Leave Hook 関数が実行されるタイミングは、remove 関数の実行時なので、remove 関数内にブレークポイントを設定します。

// @vue/runtime-core/dist/runtime.core-bundler.esm.js

定数削除 = vnode => {

  デバッガ

  const { type、el、anchor、transition } = vnode;

  if (type === フラグメント) {

    フラグメントを削除します(el、アンカー);

    戻る;

  }

  if (type === 静的) {

    staticNode を削除します(vnode);

    戻る;

  }

  定数実行削除 = () => {

    ホストを削除します(el);

    if (遷移 && !transition.persisted && transition.afterLeave) {

      遷移後離れる();

    }

  };

  if (vnode.shapeFlag & 1 /* 要素 */ &&

    遷移 &&

    !transition.persisted) {

    const { leave, delayLeave } = transition;

    const performLeave = () => leave(el, performRemove);

    (遅延離脱)の場合{

      delayLeave(vnode.el、performRemove、performLeave);

    }

    それ以外 {

      実行終了();

    }

  }

  それ以外 {

    削除を実行します();

  }

};

その後、プロジェクトを再度実行しました。歌手の詳細ページから歌手ページに戻ったとき、ブレークポイントに入ったにもかかわらず、コードにいくつかの論理的な問題があることがわかりました。対応する遷移オブジェクトが vnode から解析されました。対応するタイプは Fragment であるため、実行は次のロジックに入りました。

if (type === フラグメント) {

  フラグメントを削除します(el、アンカー);

  戻る;

}

後続の遷移オブジェクトのLeaveフック関数を実行せずに直接戻ります。引き続き vnode の値を確認したところ、コメント ノードとセクション ノードの 2 つの子ノードがあることがわかりました。突然、この問題は学生たちが書いたコメントによって引き起こされたことに気づきました。

<!-- 歌手の詳細.vue -->

<テンプレート>

  <!-- セカンダリ ルーティングを通じて実装されるため、ビューの下に配置されます -->

  <section class="歌手の詳細">

    <音楽リスト

      :songs="曲"

      :title="タイトル"

      :pic="写真"

      :loading="読み込み中"

    </音楽リスト>

  </セクション>

</テンプレート>

Vue のテンプレート解析で HTML コメントに遭遇すると、それもコメント ノードに解析されます。コンパイルされた結果を確認するには、Vue 3 テンプレート エクスポート ツールを使用できます。

import { createCommentVNode を _createCommentVNode として、resolveComponent を _resolveComponent として、createVNode を _createVNode として、createElementVNode を _createElementVNode として、Fragment を _Fragment として、openBlock を _openBlock として、createElementBlock を _createElementBlock として } from "vue"

const _hoisted_1 = { クラス: "singer-detail" }

関数レンダリング(_ctx, _cache) {

  const _component_music_list = _resolveComponent("音楽リスト")

  戻り値 (_openBlock(), _createElementBlock(_Fragment, null, [

    _createCommentVNode("セカンダリルーティングを介して実装されるため、ビューの下に配置されます")、

    _createElementVNode("セクション", _hoisted_1, [

      _createVNode(_component_music_list, {

        曲: _ctx.songs、

        タイトル: _ctx.title,

        画像: _ctx.pic,

        読み込み中: _ctx.loading

      }, null, 8 /* PROPS */, ["songs", "title", "pic", "loading"])

    ])

  ], 2112 /* STABLE_FRAGMENT、DEV_ROOT_FRAGMENT */))

}

Vue 3 は複数のルート ノードを持つテンプレートをサポートしているため、上記のテンプレートのルートは Fragment ノードに解析されます。つまり、コンポーネントが削除されたときに対応する遷移アニメーションは実行されません。

さらなる分析

では、なぜフラグメント ノードには遷移アニメーションが必要ないのでしょうか?コードに対応するコミットコメントを見つけました:

fix(fragment): フラグメントを削除するときに直接削除を実行します。これにより、持ち上げられた子ノード (別のインスタンスによって作成される可能性があります) から .el を取得しようとすることが回避され、フラグメントの子には遷移がないため、遷移チェックもスキップされます。

コメントに記載されている説明によると、フラグメント ノードには遷移を設定できないということです。しかし、ここでまだ疑問が残ります。なぜこれを書いても遷移アニメーションに入ることに影響しないのでしょうか?

コンポーネントのレンダリング関数が実行時に実行され、コンポーネントのサブツリー subTree がレンダリングされると、renderComponentRoot 関数内で特別な処理が実行されるためです。

関数renderComponentRoot(インスタンス) {

  結果を出す

  // ...

  // 結果を取得するためにレンダリング関数を呼び出す

  // 属性のマージ

  // 開発モードではコメントは保持され、テンプレートで

  // ルート要素の横にコメントを付けてフラグメントにする

  ルート = 結果;

  setRoot = undefined とします。

  ((process.env.NODE_ENV !== 'production') の場合 &&

    結果.patchFlag > 0 &&

    result.patchFlag & 2048 /* DEV_ROOT_FRAGMENT */) {

    [ルート、setRoot] = getChildRoot(結果);

  }

  // 遷移データを継承する

  (vnode.transition) の場合 {

    // ...

    ルートの遷移 = vnode の遷移;

  }

  結果を返す

}

コンポーネント インスタンスの render メソッドを実行してレンダリングされたサブツリーを取得した後、開発環境でコメント ノードが getChildRoot 関数を介してフィルタリングされ、結果のルートが取得され、そのルート ノードは親 vnode の遷移オブジェクトを継承します。ただし、renderComponentRoot 全体が依然として結果オブジェクトを返すことに注意してください。

例として挙げた SingerDetail 歌手詳細コンポーネントの場合、そのサブツリー vnode は Fragment ですが、renderComponentRoot を実行すると、最初のノードがコメント ノードであるためフィルタリングされ、後続のエンティティ ノード singer-detail に対応する vnode のみが transition 属性を持ち、遷移アニメーションに入ります。

ただし、コンポーネントが削除されると、コンポーネントのサブツリー vnode がフラグメントであるため、終了遷移アニメーションは実行されません。

要約する

バグの原因がわかったら、修正は非常に簡単です。コメントノードを削除するだけです。もちろん、本番環境ではデフォルトでコメントノードが削除されるため、この問題は本番環境では発生しません。

このケースから、コメントを書くことは良い習慣ではあるものの、誤って Vue 3 の落とし穴に陥ってしまう可能性があることがわかります。

ソース コードをデバッグする方法を学ぶことは非常に重要です。ソース コードを理解していない場合、ドキュメントでは原因が説明されないため、そのようなバグに遭遇したときに混乱し、消極的になります。したがって、私は皆さんにソースコードをもっと勉強することを勧めます。ソースコードをデバッグすることで、真実に最も近づくことができます。

Vue3 トランジションアニメーションの落とし穴に関するこの記事はこれで終わりです。Vue3 トランジションアニメーションの落とし穴に関する関連記事をもっと知りたい場合は、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続きご覧ください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • vue3 トランジションアニメーションの詳しい説明
  • Vue.jsは毎日トランジションとアニメーションを学習する必要があります
  • アニメーション遷移効果を実現するVue要素
  • Vue アニメーショントランジションアニメーション効果の入門
  • Vue コンポーネントの transition と transition-group を使用してトランジションアニメーションを実装します。
  • Vue トランジション(アニメーション)トランジションコンポーネント事例詳細説明
  • Vue3 トランジションアニメーションを 10 分ですぐに開始できます

<<:  HTMLとは何ですか?

>>:  Linux システムで MySQL の文字セットを UTF8 に変更する手順

推薦する

nginx を使用したプロキシ サーバーの設定

Nginx は、リバース プロキシ機能を使用して負荷分散を実装できるほか、フォワード プロキシ機能を...

Vueでブラウザ共有機能を呼び出す方法

序文Vue(発音は /vjuː/、view に似ています)は、ユーザーインターフェイスを構築するため...

Vscode が Ubuntu にリモート接続する際のエラー問題の解決方法

1. 事件の背景:仕事上、Ubuntu への vscode リモート接続を使用する必要があります。 ...

フォーム要素の垂直方向の中央揃えに最適なソリューション

コードをコピーコードは次のとおりです。 <!DOCTYPE html PUBLIC "...

Linux で履歴コマンドを表示および実行する方法

履歴コマンドを表示し、指定されたコマンドを実行します owen@owen:~/owen/softwa...

動的なテーブル効果を実現するJavaScript

この記事では、動的なテーブル効果を実現するためのJavaScriptの具体的なコードを参考までに紹介...

相対幅と絶対幅が競合する場合の HTML+CSS div ソリューション

相対幅と絶対幅が競合する場合のdivソリューション概要: 一般的に、絶対幅を使用する場合は px を...

HTMLを教える記事

アーティストになるつもりがない場合は、開発者として HTML を読んで、必要に応じて簡単な変更を加え...

2つのVirtualBox仮想ネットワークをブリッジするLinuxブリッジメソッドの手順

この記事は、この時期の「ピーターから奪ってポールに払う」という仕事のスタイルに対する私の不満から生ま...

サブメニューをクリックする効果を実現するJavaScript

この記事では、クリック時にサブメニューを表示するためのJavaScriptの具体的なコードを参考まで...

擬似分散グラフィックを実現するための VMware 構成 Hadoop チュートリアル

1. 実験環境シリアルナンバープロジェクトソフトウェアとバージョン1オペレーティング·システムCen...

ウェブデザインのためのロイヤルブルーのカラーマッチング入門

古典的な色の組み合わせは力と権威を伝え、強いロイヤルブルーはあらゆる古典的な色の組み合わせの中心的な...

MySql で、存在しない場合は挿入し、存在する場合は更新する方法

まとめシナリオによっては、レコードがない場合は挿入し、レコードがある場合は更新するという要件がある場...

JS WebSocket 切断理由とハートビートの仕組みの詳しい説明

1. 切断理由WebSocket が切断される理由は多数あります。WebSocket が切断されたと...