欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

關于Vue3過渡動畫的踩坑記錄

 更新時間:2021年12月26日 15:26:11   作者:黃軼黃老師  
在開發(fā)中我們想要給一個組件的顯示和消失添加某種過渡動畫,可以很好的增加用戶體驗,下面這篇文章主要給大家介紹了關于Vue3過渡動畫踩坑的相關資料,需要的朋友可以參考下

背景

在我的 《Vue 3 開發(fā)企業(yè)級音樂 App》課程問答區(qū),有個同學提了個問題,在歌手列表到歌手詳情頁面到轉場動畫中,只有進入動畫,卻沒有離場動畫:

該學生確實在這個問題上研究了有一段時間,而且從他的描述,我一時半會兒也想不出哪有問題,于是讓他把代碼傳到 GitHub 上,畢竟直接從代碼層面定位問題是最靠譜的。

問題定位

一般遇到此類問題的時候,我的第一反應是他用的 Vue 3 版本可能有問題,畢竟 Vue 3 還在不斷迭代過程,某個版本有一些小 bug 是很正常的,于是我把他的項目的 Vue 3 版本升級到了最新的 3.2.26。

但運行后發(fā)現(xiàn),該問題仍然存在。我感到有些困惑,于是跑了一下自己課程項目源碼,并沒有復現(xiàn)該問題,然后我又把自己課程項目的 Vue 3 版本也升級到最新,仍然沒有復現(xiàn)該問題。

通過上述分析,我基本排除了 Vue 3 版本的問題。本質上說,從歌手頁面切換到歌手詳情頁無非就是打開歌手詳情頁這個二級路由頁面,而從歌手詳情頁退回到歌手頁面無非就是移除歌手詳情頁這個二級路由頁面。于是我開始對比兩邊項目的歌手頁面以及詳情頁的源碼:

<!-- singer.vue -->

<template>

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

  <index-list

    :data="singers"

    @select="selectSinger"

  ></index-list>

  <!-- 用router-view去承載二級路由 -->

<!--  <router-view :singer="selectedSinger"></router-view>-->

  <!-- vue3需要在router-view中使用transition, appear進入時候也會有動畫 -->

  <router-view v-slot="{ Component }">

<!--  singer-detail返回動畫無效 研究  -->

    <transition appear name="slide">

      <!-- component動態(tài)組件Component就是作用域插槽中的一個屬性,這個是由router-view這個組提供的

       Component就是你的路由表中的路由組件

       exclude="singer-detail"排除不緩存數(shù)據(jù)的組件否則會緩存數(shù)據(jù)導致每次數(shù)據(jù)都不重新請求

       -->

        <component :is="Component"

                   :singer="selectedSinger"

        ></component>

    </transition>

  </router-view>

</div>

</template>



<!-- singer-detail.vue -->

<template>

  <!-- 因為通過二級路由實現(xiàn),所以放在views下 -->

  <section class="singer-detail">

    <music-list

      :songs="songs"

      :title="title"

      :pic="pic"

      :loading="loading"

    ></music-list>

  </section>

</template>

上邊是學生的代碼,接下來貼一下我項目的源碼:

<!-- singer.vue -->

<template>

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

    <index-list

      :data="singers"

      @select="selectSinger"

    ></index-list>

    <router-view v-slot="{ Component }">

      <transition appear name="slide">

        <component :is="Component" :data="selectedSinger"/>

      </transition>

    </router-view>

  </div>

</template>

<!-- singer-detail.vue -->

<template>

  <div class="singer-detail">

    <music-list

      :songs="songs"

      :title="title"

      :pic="pic"

      :loading="loading"

    ></music-list>

  </div>

</template>

經過對比,我感覺兩邊的源碼差別并不大,除了該學生會用注釋做一些學習筆記。一時間難以找出問題,于是我祭出了殺手锏——調試源碼。因為畢竟對于 Vue 3 過渡動畫的實現(xiàn)原理,我還是如數(shù)家珍的。

如果執(zhí)行了退出過渡動畫,則一定會執(zhí)行 transition 組件包裹的子節(jié)點解析出的 leave 鉤子函數(shù)。

于是我在 leave 鉤子函數(shù)內部加了個 debugger 斷點:

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

leave(el, remove) {

  debugger

  const key = String(vnode.key);

  if (el._enterCb) {

    el._enterCb(true /* cancelled */);

  }

  // ...

}

接著運行項目,當我從歌手詳情頁回退到歌手頁面的時候,發(fā)現(xiàn)并沒有進入 debugger 斷點,也就意味著 leave 鉤子函數(shù)壓根沒有執(zhí)行。

再往前追溯,對于即將卸載的節(jié)點,執(zhí)行其 leave 鉤子函數(shù)的時機是在執(zhí)行 remove 函數(shù)時,于是我在 remove 函數(shù)內部打上斷點:

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

const remove = vnode => {

  debugger

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

  if (type === Fragment) {

    removeFragment(el, anchor);

    return;

  }

  if (type === Static) {

    removeStaticNode(vnode);

    return;

  }

  const performRemove = () => {

    hostRemove(el);

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

      transition.afterLeave();

    }

  };

  if (vnode.shapeFlag & 1 /* ELEMENT */ &&

    transition &&

    !transition.persisted) {

    const { leave, delayLeave } = transition;

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

    if (delayLeave) {

      delayLeave(vnode.el, performRemove, performLeave);

    }

    else {

      performLeave();

    }

  }

  else {

    performRemove();

  }

};

接著再次運行項目,當我從歌手詳情頁回退到歌手頁面的時候,雖然進入了斷點,但也發(fā)現(xiàn)了一些代碼的邏輯問題:從 vnode 解析到了對應的 transition 對象,由于其對應的 type 是 Fragment,執(zhí)行進入了下面這段邏輯:

if (type === Fragment) {

  removeFragment(el, anchor);

  return;

}

直接返回并沒有執(zhí)行后續(xù) transition 對象的 leave 鉤子函數(shù)。我繼續(xù)查看 vnode 的值,發(fā)現(xiàn)它有兩個子節(jié)點,一個注釋節(jié)點和一個 section 節(jié)點。我恍然大悟,原來是學生寫的注釋導致的問題:

<!-- singer-detail.vue -->

<template>

  <!-- 因為通過二級路由實現(xiàn),所以放在views下 -->

  <section class="singer-detail">

    <music-list

      :songs="songs"

      :title="title"

      :pic="pic"

      :loading="loading"

    ></music-list>

  </section>

</template>

在 Vue 的模版解析中,遇到 HTML 注釋,也會把它解析成一個注釋節(jié)點,可以借助 Vue 3 的模版導出工具看一下它編譯后的結果:

import { createCommentVNode as _createCommentVNode, resolveComponent as _resolveComponent, createVNode as _createVNode, createElementVNode as _createElementVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"

const _hoisted_1 = { class: "singer-detail" }

function render(_ctx, _cache) {

  const _component_music_list = _resolveComponent("music-list")

  return (_openBlock(), _createElementBlock(_Fragment, null, [

    _createCommentVNode(" 因為通過二級路由實現(xiàn),所以放在views下 "),

    _createElementVNode("section", _hoisted_1, [

      _createVNode(_component_music_list, {

        songs: _ctx.songs,

        title: _ctx.title,

        pic: _ctx.pic,

        loading: _ctx.loading

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

    ])

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

}

由于 Vue 3 支持了模版可以有不止一個的根節(jié)點,上述模版的根就會被解析成一個 Fragment 節(jié)點,這就導致了該組件在移除的時候并不會執(zhí)行對應的過渡動畫。

進一步分析

那么為啥 Fragment 節(jié)點就不需要過渡動畫呢?我找到了代碼對應的提交注釋:

fix(fragment): perform direct remove when removing fragments This avoids trying to grab .el from hoisted child nodes (which can be created by another instance), and also skips transition check since fragment children cannot have transitions.

注釋給的解釋就是 Fragment 節(jié)點不可以有 transition 過渡。但這里還有一個問題,為什么這么寫不會影響進入過渡動畫呢?

因為在運行時執(zhí)行組件 render 函數(shù)渲染組件的子樹 subTree 的時候,renderComponentRoot 函數(shù)內部做了一些特殊處理:

function renderComponentRoot(instance) {

  let result

  // ...

  // call render funtion to get the result

  // attr merging

  // in dev mode, comments are preserved, and it's possible for a template

  // to have comments along side the root element which makes it a fragment

  let root = result;

  let setRoot = undefined;

  if ((process.env.NODE_ENV !== 'production') &&

    result.patchFlag > 0 &&

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

    [root, setRoot] = getChildRoot(result);

  }

  // inherit transition data

  if (vnode.transition) {

    // ...

    root.transition = vnode.transition;

  }

  return result

}

在通過執(zhí)行組件實例的 render 方法拿到渲染的子樹后,在開發(fā)環(huán)境下通過 getChildRoot 函數(shù)對注釋節(jié)點做了一層過濾,得到結果 root,并且給它的根節(jié)點繼承了其 parent vnode 的 transition 對象。但是注意到,整個 renderComponentRoot 返回的還是 result 對象。

對于我們的示例 SingerDetil 歌手詳情組件,它的子樹 vnode 是一個 Fragment,但是在執(zhí)行 renderComponentRoot 的時候,由于第一個節(jié)點是注釋節(jié)點,則被過濾,只有后面的實體節(jié)點 singer-detail 對應的 vnode 才有 transition 屬性,因此它有進入過渡動畫。

但是在組件移除的時候,由于組件的子樹 vnode 是一個 Fragment,因此不會有離開過渡動畫。

總結

找到了 bug 的原因后,修復就很簡單了,直接把注釋節(jié)點刪除即可,當然生產環(huán)境不會有該問題,因為在默認情況下,生產環(huán)境會刪除注釋節(jié)點。

從這個案例來看,寫注釋雖然是個好習慣,但是一不小心可能會踩了 Vue 3 的坑。

學會源碼調試還是很重要的,如果不了解源碼,遇到此類 bug 就會一臉懵逼,非常被動,因為文檔不會告訴你原因。因此我還是鼓勵大家多學習源碼,通過調試源碼,你才能最接近事實的真相。?

到此這篇關于關于Vue3過渡動畫的踩坑記錄的文章就介紹到這了,更多相關Vue3過渡動畫踩坑內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Vue實現(xiàn)一個圖片懶加載插件

    Vue實現(xiàn)一個圖片懶加載插件

    這篇文章主要給大家介紹了關于利用Vue實現(xiàn)一個圖片懶加載的插件的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者使用vue具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-03-03
  • Vue使用axios進行數(shù)據(jù)異步交互的方法

    Vue使用axios進行數(shù)據(jù)異步交互的方法

    大家都知道在Vue里面有兩種出名的插件能夠支持發(fā)起異步數(shù)據(jù)傳輸和接口交互,分別是axios和vue-resource,同時vue更新到2.0之后,宣告不再對vue-resource更新,而是推薦的axios,今天就講一下怎么引入axios,需要的朋友可以參考下
    2024-01-01
  • 老生常談vue的生命周期

    老生常談vue的生命周期

    這篇文章主要為大家詳細介紹了vue的生命周期,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02
  • Vue3常用的通訊方式總結與實例代碼

    Vue3常用的通訊方式總結與實例代碼

    Vue.js中一個很重要的知識點是組件通信,不管是業(yè)務類的開發(fā)還是組件庫開發(fā),都有各自的通訊方法,下面這篇文章主要給大家介紹了關于Vue3常用的通訊方式的相關資料,需要的朋友可以參考下
    2022-05-05
  • vue Nprogress進度條功能實現(xiàn)常見問題

    vue Nprogress進度條功能實現(xiàn)常見問題

    這篇文章主要介紹了vue Nprogress進度條功能實現(xiàn),NProgress是頁面跳轉是出現(xiàn)在瀏覽器頂部的進度條,本文通過實例代碼給大家講解,需要的朋友可以參考下
    2021-07-07
  • vite+vue3中如何使用router

    vite+vue3中如何使用router

    這篇文章主要介紹了vite+vue3中如何使用router問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • elementPlus?的el-select在提示框關閉時自動彈出的問題解決

    elementPlus?的el-select在提示框關閉時自動彈出的問題解決

    這篇文章主要介紹了elementPlus?的el-select在提示框關閉時自動彈出閉時自動彈出的問題,主要問題就是因為filterable屬性,根本解決方案是選中的時候讓他失去焦點?el-select有一個visible-change事件,下拉框出現(xiàn)/隱藏時觸發(fā),感興趣的朋友跟隨小編一起看看吧
    2024-01-01
  • Vue3中使用Pinia修改State的五種方式

    Vue3中使用Pinia修改State的五種方式

    這篇文章主要介紹了Vue3中使用Pinia修改State的五種方式,本文通過實例代碼給大家介紹的非常詳細,感興趣的朋友一起看看吧
    2023-11-11
  • vue富文本編輯器組件vue-quill-edit使用教程

    vue富文本編輯器組件vue-quill-edit使用教程

    這篇文章主要為大家詳細介紹了vue富文本編輯器組件vue-quill-edit的使用教程,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-09-09
  • Vue中正確使用Element-UI組件的方法實例

    Vue中正確使用Element-UI組件的方法實例

    這篇文章主要給大家介紹了關于Vue中正確使用Element-UI組件的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-10-10

最新評論