成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

VueJS源碼學(xué)習(xí)——元素在插入和移出 dom 時(shí)的過(guò)渡邏輯

Dogee / 2267人閱讀

摘要:原文地址項(xiàng)目地址關(guān)于中使用效果,官網(wǎng)上的解釋如下當(dāng)元素插入到樹(shù)或者從樹(shù)中移除的時(shí)候,屬性提供變換的效果,可以使用來(lái)定義變化效果,也可以使用來(lái)定義首先第一個(gè)函數(shù)是將元素插入,函數(shù)實(shí)現(xiàn)調(diào)用了實(shí)現(xiàn)代碼如下寫(xiě)的好的代碼就是文檔,從注釋和命名上就

src/transition

原文地址
項(xiàng)目地址

關(guān)于 vue 中使用 transition 效果,官網(wǎng)上的解釋如下:

With Vue.js’ transition system you can apply automatic transition effects when elements are inserted into or removed from the DOM. Vue.js will automatically add/remove CSS classes at appropriate times to trigger CSS transitions or animations for you, and you can also provide JavaScript hook functions to perform custom DOM manipulations during the transition.

當(dāng)元素插入到 DOM 樹(shù)或者從 DOM 樹(shù)中移除的時(shí)候, transition 屬性提供變換的效果,可以使用 css 來(lái)定義變化效果,也可以使用 JS 來(lái)定義

src/transition/index.js
import {
  before,
  remove,
  transitionEndEvent
} from "../util/index"

/**
 * Append with transition.
 *
 * @param {Element} el
 * @param {Element} target
 * @param {Vue} vm
 * @param {Function} [cb]
 */

export function appendWithTransition (el, target, vm, cb) {
  applyTransition(el, 1, function () {
    target.appendChild(el)
  }, vm, cb)
}
...

首先第一個(gè)函數(shù)是將元素插入 DOM, 函數(shù)實(shí)現(xiàn)調(diào)用了 applyTransition, 實(shí)現(xiàn)代碼如下:

/**
 * Apply transitions with an operation callback.
 *
 * @param {Element} el
 * @param {Number} direction
 *                  1: enter
 *                 -1: leave
 * @param {Function} op - the actual DOM operation
 * @param {Vue} vm
 * @param {Function} [cb]
 */

export function applyTransition (el, direction, op, vm, cb) {
  var transition = el.__v_trans
  if (
    !transition ||
    // skip if there are no js hooks and CSS transition is
    // not supported
    (!transition.hooks && !transitionEndEvent) ||
    // skip transitions for initial compile
    !vm._isCompiled ||
    // if the vm is being manipulated by a parent directive
    // during the parent"s compilation phase, skip the
    // animation.
    (vm.$parent && !vm.$parent._isCompiled)
  ) {
    op()
    if (cb) cb()
    return
  }
  var action = direction > 0 ? "enter" : "leave"
  transition[action](op, cb)
}

寫(xiě)的好的代碼就是文檔,從注釋和命名上就能很好的理解這個(gè)函數(shù)的作用, el 是要操作的元素, direction 代表是插入還是刪除, op 代表具體的操作方法函數(shù), vm 從之前的代碼或者官方文檔可以知道指 vue 實(shí)例對(duì)象, cb 是回調(diào)函數(shù)

vue 將解析后的transition作為 DOM 元素的屬性 __v_trans ,這樣每次操作 DOM 的時(shí)候都會(huì)做以下判斷:

如果元素沒(méi)有被定義了 transition

如果元素沒(méi)有 jshook 且 css transition 的定義不支持

如果元素還沒(méi)有編譯完成

如果元素有父元素且父元素沒(méi)有編譯完成

存在以上其中一種情況的話則直接執(zhí)行操作方法 op 而不做變化,否則執(zhí)行:

var action = direction > 0 ? "enter" : "leave"
transition[action](op, cb)

除了添加,還有插入和刪除兩個(gè)操作方法:

export function beforeWithTransition (el, target, vm, cb) {
  applyTransition(el, 1, function () {
    before(el, target)
  }, vm, cb)
}

export function removeWithTransition (el, vm, cb) {
  applyTransition(el, -1, function () {
    remove(el)
  }, vm, cb)
}

那么 transitoin 即 el.__v_trans 是怎么實(shí)現(xiàn)的,這個(gè)還得繼續(xù)深挖

src/transition/queue.js
import { nextTick } from "../util/index"

let queue = []
let queued = false

/**
 * Push a job into the queue.
 *
 * @param {Function} job
 */

export function pushJob (job) {
  queue.push(job)
  if (!queued) {
    queued = true
    nextTick(flush)
  }
}

/**
 * Flush the queue, and do one forced reflow before
 * triggering transitions.
 */

function flush () {
  // Force layout
  var f = document.documentElement.offsetHeight
  for (var i = 0; i < queue.length; i++) {
    queue[i]()
  }
  queue = []
  queued = false
  // dummy return, so js linters don"t complain about
  // unused variable f
  return f
}

這是 transition 三個(gè)文件中的第二個(gè),從字面量上理解是一個(gè)隊(duì)列,從代碼上看實(shí)現(xiàn)的是一個(gè)任務(wù)隊(duì)列,每當(dāng)調(diào)用 pushJob 的時(shí)候,都會(huì)往任務(wù)隊(duì)列 queue 里面推一個(gè)任務(wù),并且有一個(gè)標(biāo)識(shí)queued, 如果為 false 則會(huì)在 nextTick 的時(shí)候?qū)?queued 置為 true同時(shí)調(diào)用 flush 方法,這個(gè)方法會(huì)執(zhí)行所有在任務(wù)隊(duì)列 queue 的方法,并將 queued 置為 false

還記得 nextTick 的實(shí)現(xiàn)嗎?實(shí)現(xiàn)在 src/util/env 中:

/**
 * Defer a task to execute it asynchronously. Ideally this
 * should be executed as a microtask, so we leverage
 * MutationObserver if it"s available, and fallback to
 * setTimeout(0).
 *
 * @param {Function} cb
 * @param {Object} ctx
 */

export const nextTick = (function () {
  var callbacks = []
  var pending = false
  var timerFunc
  function nextTickHandler () {
    pending = false
    var copies = callbacks.slice(0)
    callbacks = []
    for (var i = 0; i < copies.length; i++) {
      copies[i]()
    }
  }
  /* istanbul ignore if */
  if (typeof MutationObserver !== "undefined") {
    var counter = 1
    var observer = new MutationObserver(nextTickHandler)
    var textNode = document.createTextNode(counter)
    observer.observe(textNode, {
      characterData: true
    })
    timerFunc = function () {
      counter = (counter + 1) % 2
      textNode.data = counter
    }
  } else {
    timerFunc = setTimeout
  }
  return function (cb, ctx) {
    var func = ctx
      ? function () { cb.call(ctx) }
      : cb
    callbacks.push(func)
    if (pending) return
    pending = true
    timerFunc(nextTickHandler, 0)
  }
})()

官網(wǎng)的解釋如下

Defer the callback to be executed after the next DOM update cycle. Use it immediately after you’ve changed some data to wait for the DOM update.

即在下一次 DOM 更新循環(huán)中執(zhí)行回調(diào),用在你需要等待 DOM 節(jié)點(diǎn)更新后才能執(zhí)行的情況,實(shí)現(xiàn)的簡(jiǎn)單方法是利用 setTimeout 函數(shù),我們知道 setTimeout 方法會(huì)將回調(diào)函數(shù)放入時(shí)間隊(duì)列里,并在計(jì)時(shí)結(jié)束后放到事件隊(duì)列里執(zhí)行,從而實(shí)現(xiàn)異步執(zhí)行的功能,當(dāng)然尤大只把這種情況作為備用選擇,而采用模擬DOM創(chuàng)建并利用觀察者M(jìn)utationObserver監(jiān)聽(tīng)其更新來(lái)實(shí)現(xiàn):

var observer = new MutationObserver(nextTickHandler) // 創(chuàng)建一個(gè)觀察者
var textNode = document.createTextNode(counter) // 創(chuàng)建一個(gè)文本節(jié)點(diǎn)
observer.observe(textNode, { // 監(jiān)聽(tīng) textNode 的 characterData 是否為 true
  characterData: true
})
timerFunc = function () { // 每次調(diào)用 nextTick,都會(huì)調(diào)用timerFunc從而再次更新文本節(jié)點(diǎn)的值
  counter = (counter + 1) % 2 // 值一直在0和1中切換,有變化且不重復(fù)
  textNode.data = counter
}

不了解MutationObserver 和 characterData 的可以參考MDN的解釋?zhuān)?MutaitionObserver
& CharacterData

mutationObserver 例子

flush 函數(shù)聲明變量f: var f = document.documentElement.offsetHeight 從注釋上看應(yīng)該是強(qiáng)制DOM更新,因?yàn)檎{(diào)用offsetHeight的時(shí)候會(huì)讓瀏覽器重新計(jì)算出文檔的滾動(dòng)高度的緣故吧

src/transition/transition.js

transition 實(shí)現(xiàn)了元素過(guò)渡變換的邏輯和狀態(tài),Transition 的原型包含了 enter, enterNextTick, enterDone, leave, leaveNextTick, leaveDone 這幾個(gè)狀態(tài),以 enter 為例子:

/**
 * Start an entering transition.
 *
 * 1. enter transition triggered
 * 2. call beforeEnter hook
 * 3. add enter class
 * 4. insert/show element
 * 5. call enter hook (with possible explicit js callback)
 * 6. reflow
 * 7. based on transition type:
 *    - transition:
 *        remove class now, wait for transitionend,
 *        then done if there"s no explicit js callback.
 *    - animation:
 *        wait for animationend, remove class,
 *        then done if there"s no explicit js callback.
 *    - no css transition:
 *        done now if there"s no explicit js callback.
 * 8. wait for either done or js callback, then call
 *    afterEnter hook.
 *
 * @param {Function} op - insert/show the element
 * @param {Function} [cb]
 */

p.enter = function (op, cb) {
  this.cancelPending()
  this.callHook("beforeEnter")
  this.cb = cb
  addClass(this.el, this.enterClass)
  op()
  this.entered = false
  this.callHookWithCb("enter")
  if (this.entered) {
    return // user called done synchronously.
  }
  this.cancel = this.hooks && this.hooks.enterCancelled
  pushJob(this.enterNextTick)
}

cancelPending 只有在 enterleave 里被調(diào)用了,實(shí)現(xiàn)如下:

/**
 * Cancel any pending callbacks from a previously running
 * but not finished transition.
 */

p.cancelPending = function () {
  this.op = this.cb = null
  var hasPending = false
  if (this.pendingCssCb) {
    hasPending = true
    off(this.el, this.pendingCssEvent, this.pendingCssCb)
    this.pendingCssEvent = this.pendingCssCb = null
  }
  if (this.pendingJsCb) {
    hasPending = true
    this.pendingJsCb.cancel()
    this.pendingJsCb = null
  }
  if (hasPending) {
    removeClass(this.el, this.enterClass)
    removeClass(this.el, this.leaveClass)
  }
  if (this.cancel) {
    this.cancel.call(this.vm, this.el)
    this.cancel = null
  }
}

調(diào)用 cancelPending 取消之前的正在運(yùn)行的或者等待運(yùn)行的 js 或 css 變換事件和類(lèi)名,然后觸發(fā)腳本 beforeEnter, 添加 enterClass 類(lèi)名,執(zhí)行具體的元素插入操作,將 entered 置為 false,因?yàn)榇藭r(shí)還沒(méi)有完成插入操作,然后執(zhí)行 callHookWithCb,最后確定 this.cancel 的值以及進(jìn)入下一步操作 enterNextTick, 最后操作為 enterDone

/**
 * The "cleanup" phase of an entering transition.
 */

p.enterDone = function () {
  this.entered = true
  this.cancel = this.pendingJsCb = null
  removeClass(this.el, this.enterClass)
  this.callHook("afterEnter")
  if (this.cb) this.cb()
}

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/88380.html

相關(guān)文章

  • CSS transition delay簡(jiǎn)介與進(jìn)階應(yīng)用

    摘要:用來(lái)定義元素兩種狀態(tài)之間的過(guò)渡。到目前為止,我們利用完全模擬了第一部分我們使用實(shí)現(xiàn)的功能,而且看上去更簡(jiǎn)潔。附上利用來(lái)實(shí)現(xiàn)該方案的代碼用于參考。由于代碼效果時(shí)好時(shí)壞,猜測(cè)可能與的容器相關(guān)。 背景 在日常的項(xiàng)目開(kāi)發(fā)中,我們會(huì)很經(jīng)常的遇見(jiàn)如下的需求: 在瀏覽器頁(yè)面中,當(dāng)鼠標(biāo)移動(dòng)到某個(gè)部分后,另一個(gè)部分在延遲若干時(shí)間后出現(xiàn) 在鼠標(biāo)移除該區(qū)域后,另一部分也在延遲若干時(shí)間后消失 我相信這是一...

    e10101 評(píng)論0 收藏0
  • Vue2 transition源碼分析

    摘要:至此算是找到了源碼位置。至此進(jìn)入過(guò)渡的部分完畢。在動(dòng)畫(huà)結(jié)束后,調(diào)用了由組件生命周期傳入的方法,把這個(gè)元素的副本移出了文檔流。這篇并沒(méi)有去分析相關(guān)的內(nèi)容,推薦一篇講非常不錯(cuò)的文章,對(duì)構(gòu)造函數(shù)如何來(lái)的感興趣的同學(xué)可以看這里 Vue transition源碼分析 本來(lái)打算自己造一個(gè)transition的輪子,所以決定先看看源碼,理清思路。Vue的transition組件提供了一系列鉤子函數(shù),...

    Genng 評(píng)論0 收藏0
  • 手把手教你用原生JavaScript造輪子(2)——輪播圖(更新:ES6版本)

    摘要:綁定輪播事件然后是鼠標(biāo)移入移出事件的綁定鼠標(biāo)移入移出事件移入時(shí)停止輪播播放的定時(shí)器,移出后自動(dòng)開(kāi)始下一張的播放。 通過(guò)上一篇文章的學(xué)習(xí),我們基本掌握了一個(gè)輪子的封裝和開(kāi)發(fā)流程。那么這次將帶大家開(kāi)發(fā)一個(gè)更有難度的項(xiàng)目——輪播圖,希望能進(jìn)一步加深大家對(duì)于面向?qū)ο蟛寮_(kāi)發(fā)的理解和認(rèn)識(shí)。 So, Lets begin! 目前項(xiàng)目使用 ES5及UMD 規(guī)范封裝,所以在前端暫時(shí)只支持標(biāo)簽的引入方式...

    jasperyang 評(píng)論0 收藏0
  • vue內(nèi)置組件——transition簡(jiǎn)單原理圖文詳解

    摘要:在元素被插入之前生效,在元素被插入之后的下一幀移除。在整個(gè)進(jìn)入過(guò)渡的階段中應(yīng)用,在元素被插入之前生效,在過(guò)渡動(dòng)畫(huà)完成之后移除。這個(gè)類(lèi)可以被用來(lái)定義進(jìn)入過(guò)渡的過(guò)程時(shí)間,延遲和曲線函數(shù)。版及以上定義進(jìn)入過(guò)渡的結(jié)束狀態(tài)。 基本概念 Vue 在插入、更新或者移除 DOM 時(shí),提供多種不同方式的應(yīng)用過(guò)渡效果 在 CSS 過(guò)渡和動(dòng)畫(huà)中自動(dòng)應(yīng)用 class 可以配合使用第三方 CSS 動(dòng)畫(huà)庫(kù),如...

    lingdududu 評(píng)論0 收藏0
  • vue內(nèi)置組件——transition簡(jiǎn)單原理圖文詳解

    摘要:在元素被插入之前生效,在元素被插入之后的下一幀移除。在整個(gè)進(jìn)入過(guò)渡的階段中應(yīng)用,在元素被插入之前生效,在過(guò)渡動(dòng)畫(huà)完成之后移除。這個(gè)類(lèi)可以被用來(lái)定義進(jìn)入過(guò)渡的過(guò)程時(shí)間,延遲和曲線函數(shù)。版及以上定義進(jìn)入過(guò)渡的結(jié)束狀態(tài)。 基本概念 Vue 在插入、更新或者移除 DOM 時(shí),提供多種不同方式的應(yīng)用過(guò)渡效果 在 CSS 過(guò)渡和動(dòng)畫(huà)中自動(dòng)應(yīng)用 class 可以配合使用第三方 CSS 動(dòng)畫(huà)庫(kù),如...

    nihao 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

Dogee

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<