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 に変更する手順

推薦する

JavaScript でプライベート変数を宣言する 2 つの方法

序文JavaScript は、キーワードを使用してプライベート変数を宣言できる他の言語とは異なります...

ウェブサイトのフロントエンドをエレガントでユーザーにとって魅力的なものにする方法

ウェブフロントエンドのウェブサイトの気質は感情であり、言葉なしでユーザーを魅了できる感情です。では、...

MySQLで論理SQLを置き換える際の落とし穴を回避する方法の詳細な説明

重複キーの置換と挿入の違い置換の使用法競合がない場合、挿入と同等となり、他の列のデフォルト値が使用さ...

Vue.js パフォーマンス最適化 N 個のヒント (収集する価値あり)

目次機能コンポーネント子コンポーネントの分割ローカル変数v-show によるDOMの再利用キープアラ...

Linux における効果的なユーザー グループと初期ユーザー グループの実装

まず、/etc/group ファイルを確認します。 [root@localhost /]# cat ...

js でオブジェクトを作成するさまざまな方法とその長所と短所のまとめ

目次初期作成方法ファクトリーパターンコンストラクターパターンコンストラクタパターンの最適化プロトタイ...

Ubuntu16.04にCUDA9.0をインストールするための詳細なチュートリアル

序文:この記事は、CUDA 9.0 をインストールした経験に基づいています。CUDA 9.0 は現在...

MySQL 8.0.17 のインストールと設定方法のグラフィックチュートリアル

この記事では、MySQL 8.0.17のインストールと設定方法を参考までに紹介します。具体的な内容は...

Vueコンポーネントドキュメントを自動生成する方法を分析する

目次1. 現状2. コミュニティソリューション2.1 事業レビュー3. 技術的ソリューション3.1....

ウェブ標準学習リソースの素晴らしいコレクション

これらの仕様は、下位互換性のあるドキュメントを Web 上で公開し、できるだけ幅広いユーザーがアクセ...

Linux システムで grub.cfg ファイルの破損を修復する手順

目次1. grub.cfg ファイルの紹介1. grub.cfg ファイルの場所2. grub.cf...

ウェブページ制作時のコードコメントの書き方

<br />私の仕事で使用しているアノテーションの書き方の基準をまとめました。技術的な内...

HTML割引価格計算の実装原理とスクリプトコード

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

Windowsタイムサーバーの設定方法の詳しい説明

最近、会社のサーバーの時間が不正確で、外部の時間ソースと同期できないことがわかりました。会社はドメイ...

jQueryは従業員情報の追加と削除の機能を実装します

この記事では、従業員情報の追加と削除の機能を実装するためのjQueryの具体的なコードを参考までに共...