主頁 > 企業開發 > Iframe在Vue中的狀態保持技術

Iframe在Vue中的狀態保持技術

2023-05-25 14:05:29 企業開發

引言

Iframe是一個歷史悠久的HTML元素,根據MDN WEB DOCS官方介紹,Iframe定義為HTML行內框架元素,表示嵌套的Browsing Context,它能夠將另一個HTML頁面嵌入到當前頁面中,Iframe可以廉價實作跨應用級的頁面共享,并且具有使用簡單、高兼容性、內容隔離等優點,因此以Iframe為核心形成了前端平臺架構領域第1代技術,

眾所周知,當Iframe在DOM中初始渲染時,會自動加載其指向的資源鏈接Url,并重置內部的狀態,在一個典型的平臺應用中,一個父應用主頁面要掛載多個視窗(每一個視窗對應一個Iframe),那么如何在切換視窗時,實作每一個視窗中的狀態(包括輸入狀態、錨點資訊等)不丟失,也即“狀態保持”呢?

如果采用父子應用通信來記錄視窗狀態,那么改造成本是非常巨大的,答案是利用Iframe的CSS Display特性,切換視窗時,非激活狀態的視窗并不消失,僅是Display狀態變更為none,激活狀態視窗的Display狀態變更為非none,在Display狀態切換時,Iframe不會重新加載,在Vue應用中,一行v-show指令即可替我們實作這一需求,

競爭機制

上述的狀態保持模型存在一個性能缺陷,即父應用主頁面實際上要提前擺放多個Iframe視窗,即使是這些不可見的視窗,也會發出資源request請求,大量的并發請求,會導致頁面性能下降,(值得一提的是,Chrome最新版本已經支持了Iframe的滾動懶加載策略,但是在此場景下,并不能改善并發請求的問題,)因此,我們需要引入資源池和競爭機制來管理多個Iframe,

引入一個容量為N的Iframe資源池來管理多開視窗,當資源池未滿時,新激活的視窗可以直接插入至資源池中;當資源池已滿時,資源池按照競爭策略,淘汰若干池中的視窗并丟棄,然后插入新激活的視窗至資源池中,通過調整容量N,可以限制父應用主頁面上多開視窗的數量,從而限制并發請求數量,實作資源管控的目的,

Vue Patch原理探索

日前遇到了一個基于Vue應用的Iframe狀態保持問題,在上述模型下,資源池不僅保存視窗物件,而且記錄了每個視窗的點擊激活時間,資源池使用以下競爭淘汰策略:對視窗激活時間進行先后次序排序,激活時間排序次序較前的視窗優先被淘汰,當資源池滿時,會偶發池中視窗狀態不能保持的問題,

在Vue中,組件是一個可復用的Vue實體,Vue 會盡可能高效地渲染元素,通常會復用已有元素而不是從頭開始渲染,組件狀態是否正確保持,依賴關鍵屬性key,基于此,首先排查了Iframe組件的key屬性,事實上,Iframe組件已經正確分配了唯一的Uid,此種情況可以排除,

既然不是組件復用的問題,那么在Vue內部的Diff Patch機制到底是如何運行的呢?讓我們看一下Vue 2.0的源代碼:

/**
 * 頁面首次渲染和后續更新的入口位置,也是 patch 的入口位置 
 */
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
  if (!prevVnode) {
    // 老 VNode 不存在,表示首次渲染,即初始化頁面時走這里
    ……
  } else {
    // 回應式資料更新時,即更新頁面時走這里
    vm.$el = vm.__patch__(prevVnode, vnode)
  }
}

(1)在update生命周期下,主要執行了vm.__patch__方法,

/** 
* vm.__patch__ 
* 1、新節點不存在,老節點存在,呼叫 destroy,銷毀老節點 
* 2、如果 oldVnode 是真實元素,則表示首次渲染,創建新節點,并插入 body,然后移除老節點 
* 3、如果 oldVnode 不是真實元素,則表示更新階段,執行 patchVnode 
*/
function patch(oldVnode, vnode, hydrating, removeOnly) {
  …… // 1、新節點不存在,老節點存在,呼叫 destroy,銷毀老節點
  if (isUndef(oldVnode)) {
    …… // 2、老節點不存在,執行創建新節點
  } else {
    // 判斷 oldVnode 是否為真實元素
    const isRealElement = isDef(oldVnode.nodeType)
    if (!isRealElement && sameVnode(oldVnode, vnode)) {
      // 3、不是真實元素,但是老節點和新節點是同一個節點,則是更新階段,執行 patch 更新節點
      patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)
    } else {
      ……// 是真實元素,則表示初次渲染
    }
  }
  invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)
  return vnode.elm
}

(2)在__patch__方法內部,觸發patchVnode方法,

function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) {
  ……
  if (isUndef(vnode.text)) {// 新節點不為文本節點
    if (isDef(oldCh) && isDef(ch)) {// 新舊節點的子節點都存在,執行diff遞回
      if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)
    } else {
      ……
    }
  } else {
    ……
  }
}

(3)在patchVnode方法內部,觸發updateChildren方法,

/**
 * diff 程序:
 *   diff 優化:做了四種假設,假設新老節點開頭結尾有相同節點的情況,一旦命中假設,就避免了一次回圈,以提高執行效率
 *   如果不幸沒有命中假設,則執行遍歷,從老節點中找到新開始節點
 *   找到相同節點,則執行 patchVnode,然后將老節點移動到正確的位置
 *   如果老節點先于新節點遍歷結束,則剩余的新節點執行新增節點操作
 *   如果新節點先于老節點遍歷結束,則剩余的老節點執行洗掉操作,移除這些老節點
 */
function updateChildren(parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {
  // 老節點的開始索引
  let oldStartIdx = 0
  // 新節點的開始索引
  let newStartIdx = 0
  // 老節點的結束索引
  let oldEndIdx = oldCh.length - 1
  // 第一個老節點
  let oldStartVnode = oldCh[0]
  // 最后一個老節點
  let oldEndVnode = oldCh[oldEndIdx]
  // 新節點的結束索引
  let newEndIdx = newCh.length - 1
  // 第一個新節點
  let newStartVnode = newCh[0]
  // 最后一個新節點
  let newEndVnode = newCh[newEndIdx]
  let oldKeyToIdx, idxInOld, vnodeToMove, refElm

  // 遍歷新老兩組節點,只要有一組遍歷完(開始索引超過結束索引)則跳出回圈
  while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
    if (isUndef(oldStartVnode)) {
      // 如果節點被移動,在當前索引上可能不存在,檢測這種情況,如果節點不存在則調整索引
      oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left
    } else if (isUndef(oldEndVnode)) {
      oldEndVnode = oldCh[--oldEndIdx]
    } else if (sameVnode(oldStartVnode, newStartVnode)) {
      // 老開始節點和新開始節點是同一個節點,執行 patch
      patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
      // patch 結束后老開始和新開始的索引分別加 1
      oldStartVnode = oldCh[++oldStartIdx]
      newStartVnode = newCh[++newStartIdx]
    } else if (sameVnode(oldEndVnode, newEndVnode)) {
      // 老結束和新結束是同一個節點,執行 patch
      patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)
      // patch 結束后老結束和新結束的索引分別減 1
      oldEndVnode = oldCh[--oldEndIdx]
      newEndVnode = newCh[--newEndIdx]
    } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right
      // 老開始和新結束是同一個節點,執行 patch
      ……
    } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left
      // 老結束和新開始是同一個節點,執行 patch
      ……
    } else {
      // 如果上面的四種假設都不成立,則通過遍歷找到新開始節點在老節點中的位置索引
      ……
        // 在老節點中找到新開始節點了
        if (sameVnode(vnodeToMove, newStartVnode)) {
          // 如果這兩個節點是同一個,則執行 patch
          patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
          // patch 結束后將該老節點置為 undefined
          oldCh[idxInOld] = undefined
          canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)
        } else {
          // 最后這種情況是,找到節點了,但是發現兩個節點不是同一個節點,則視為新元素,執行創建
          ……
        }
      // 老節點向后移動一個
      newStartVnode = newCh[++newStartIdx]
    }
  }
  // 走到這里,說明老姐節點或者新節點被遍歷完了,執行剩余節點的處理
  ……
}

(4)咱們終于來到了主角updateChildren,在updateChildren內部實作中,使用了2套指標分別指向新舊Vnode頭尾,并向中間聚攏遞回,以實作新舊資料對比重繪,

在前述資源池模型下,當查找到新舊Iframe組件時,會執行如下邏輯:

if (sameVnode(vnodeToMove, newStartVnode)) {
          // 如果這兩個節點是同一個,則執行 patch
          patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
          // patch 結束后將該老節點置為 undefined
          oldCh[idxInOld] = undefined
          canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)
}

看來出現問題的罪魁禍首是執行了nodeOps.insertBefore,在WEB的運行環境下實際上執行的是DOM的insertBefore API,那么我們移步來看看在DOM環境下,Iframe究竟是采取了何種重繪策略,

Iframe的狀態重繪機制

為了更清晰地看到DOM節點的變化情況,我們可以引入MutationObserver在最新版Chrome中來觀測DOM根節點,
首先設定容器節點下有兩個子節點:<span/><iframe/>,分別執行以下方案并記錄結果:
對比方案A:使用insertBefore在iframe節點前再插入一個新的span節點
對比方案B:使用insertBefore在iframe節點后再插入一個新的span節點
對比方案C:使用insertBefore交換span和iframe節點
對比方案D:使用insertBefore原地操作iframe自身
其結果如下:

方案名稱 Iframe是否重繪 DOM節點變化
A 新增一個子節點span
B 新增一個子節點span
C 先移除一個iframe,再插入一個iframe
D 先移除一個iframe,再插入一個iframe

實驗結果顯示,對Iframe執行insertBefore時,實際上DOM會依次執行移除、新增節點操作,導致Iframe狀態重繪,

在Vuejs Issues #9473中提到了類似的問題,一種解決方案是在Vue Patch時優先對非Iframe型別元素進行DOM操作,但是目前這個優化策略尚未被采用,在Vue 3.0版本中也依然存在這個問題,

那么在資源池模型下,如何才能保證Iframe不執行insertBefore呢?重新回到Vue Patch機制下,我們發現,只有新舊Iframe在新舊Vnode串列中的相對位置保持不變時,才會只執行patchVnode方法,而不會觸發insertBefore方法,

因此,采取的最終解決方案是,更改淘汰機制,將排序操作改為搜索操作,保證了多開視窗在Vue中的狀態保持,

作者:京東零售 陳震

內容來源:京東云開發者社區

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/553393.html

標籤:其他

上一篇:如何撰寫一個健壯的 npm 包

下一篇:返回列表

標籤雲
其他(159684) Python(38169) JavaScript(25452) Java(18129) C(15231) 區塊鏈(8268) C#(7972) AI(7469) 爪哇(7425) MySQL(7211) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5873) 数组(5741) R(5409) Linux(5341) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4576) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2434) ASP.NET(2403) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1976) 功能(1967) Web開發(1951) HtmlCss(1944) C++(1922) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1878) .NETCore(1861) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • Iframe在Vue中的狀態保持技術

    Iframe是一個歷史悠久的HTML元素,根據MDN WEB DOCS官方介紹,Iframe定義為HTML行內框架元素,表示嵌套的Browsing Context,它能夠將另一個HTML頁面嵌入到當前頁面中。Iframe可以廉價實作跨應用級的頁面共享,并且具有使用簡單、高兼容性、內容隔離等優點,因此... ......

    uj5u.com 2023-05-25 14:05:29 more
  • 如何撰寫一個健壯的 npm 包

    比如老王我,用npm init新建一個包,改把改把,然后來個npm publish,so easy ??!Too young too naive, baby ??!請容我講述一些發布程序中踩過的坑。 ......

    uj5u.com 2023-05-25 14:04:45 more
  • 現代 CSS 解決方案:CSS 原生支持的三角函式

    在 CSS 中,存在許多數學函式,這些函式能夠通過簡單的計算操作來生成某些屬性值,例如 : * calc():用于計算任意長度、百分比或數值型資料,并將其作為 CSS 屬性值。 * min() 和 max():用于比較一組數值中的最大值或最小值,也可以與任意長度、百分比或數值型資料一同使用。 * c ......

    uj5u.com 2023-05-25 14:04:28 more
  • 2D 轉換

    1.轉換 轉換(transform)是CSS3中具有顛覆性的特征之一,可以實作元素的位移、旋轉、縮放等效果。 轉換(transform)可以簡單理解為變形。 移動:translate 旋轉:rotate 縮放:scale 1.1 二維坐標系 2D轉換是改變在二維平面上的位置和形狀的一種技術。 1.2 ......

    uj5u.com 2023-05-25 14:04:12 more
  • 【一步步開發AI運動小程式】七、進行運動計時、計數

    > 隨著人工智能技術的不斷發展,阿里體育等IT大廠,推出的“樂動力”、“天天跳繩”AI運動APP,讓**云上運動會、線上運動會、健身打卡、AI體育指導**等概念空前火熱。那么,能否將這些在APP成功應用的場景搬上小程式,分享這些概念的紅利呢?本系列文章就帶您一步一步從零開始開發一個AI運動小程式,本 ......

    uj5u.com 2023-05-25 14:03:53 more
  • 封裝vue基于element的select多選時啟用滑鼠懸停折疊文字以toolti

    相信很多公司的前端開發人員都會選擇使用vue+element-ui的形式來開發公司的管理后臺系統,基于element-ui很豐富的組件生態,我們可以很快速的開發管理后臺系統的頁面(管理后臺系統的頁面也不復雜,大多都是分頁查詢類需求和增刪改查)。但一個前端框架有優點,就必然會有一些缺點或bug存在,e... ......

    uj5u.com 2023-05-25 13:50:15 more
  • 【一步步開發AI運動小程式】七、進行運動計時、計數

    > 隨著人工智能技術的不斷發展,阿里體育等IT大廠,推出的“樂動力”、“天天跳繩”AI運動APP,讓**云上運動會、線上運動會、健身打卡、AI體育指導**等概念空前火熱。那么,能否將這些在APP成功應用的場景搬上小程式,分享這些概念的紅利呢?本系列文章就帶您一步一步從零開始開發一個AI運動小程式,本 ......

    uj5u.com 2023-05-25 13:33:47 more
  • 關于如何處理httpOnly的問題?

    寫這篇的目的是,今天在重新學習javascript時發現了HttpOnly這個標簽,所以專門的mark了下。 誰在什么時候發明了HttpOnly 2002年微軟為ie6的sp1創造了HttpOnly 什么是HttpOnly HttpOnly是包含在http回傳頭Set-Cookie里面的一個附加的f ......

    uj5u.com 2023-05-25 13:32:23 more
  • 關于如何處理httpOnly的問題?

    寫這篇的目的是,今天在重新學習javascript時發現了HttpOnly這個標簽,所以專門的mark了下。 誰在什么時候發明了HttpOnly 2002年微軟為ie6的sp1創造了HttpOnly 什么是HttpOnly HttpOnly是包含在http回傳頭Set-Cookie里面的一個附加的f ......

    uj5u.com 2023-05-25 09:16:50 more
  • Three.js 進階之旅:滾動控制模型影片和相機影片 &#129442;

    本文將學習如何使用滾動控制 ScrollControls 來控制模型的的影片播放和相機影片,通過滾動滑鼠滾輪或者上下移動觸摸板,來控制模型的影片播放進度或者相機的方位視角,從而呈現出驚艷的視覺效果。通過本文的閱讀和案例頁面的實作,你將學習到的知識包括:R3F 生態中的 ScrollControls、... ......

    uj5u.com 2023-05-25 09:16:10 more