[TOC]
>[success] # 了解虛擬dom
~~~
1.vue 無論是模板通過AST 轉換成render形式也好,或者直接使用render的形式也罷,其實都是為
接下來轉換'虛擬DOM' 做準備,在這之前需要了解什么是虛擬dom
~~~
>[info] ## 虛擬dom 是什么
~~~
1.是由普通的 JS 對象來描述 DOM 對象,因為不是真實的 DOM 對象,所以叫 Virtual DOM
~~~
>[danger] ##### 真實dom是什么
~~~
1.既然有了虛擬dom,那么真實dom是什么?把下面的代碼在瀏覽器中運行
let div = document.createElement('div')
let str = ''
for (const key in div) {
str += key + ''
}
console.log(str)
2.上面代碼運行后,可以發現輸出了很多的屬性,可以發現'一個真正的 DOM 元素是非常龐大',這些對象
屬性并不是實際開發過程都會用上的如果有一個用js表現的dom的結構,再將這個結構渲染會成為dom可以
讓開發階段更關心數據
~~~
>[danger] ##### 為什么使用虛擬dom
~~~
1.Vue是數據驅動視圖的,數據發生變化視圖就要隨之更新,在更新視圖的時候難免要操作DOM,
而操作真實DOM又是非常耗費性能的
2.手動操作DOM時代 ,即使有了jq幫助我們對dom做了簡化的操作,但隨著項目的復雜DOM操作也變得
復雜起來
3.后來也出現了一種產物模板方式進行dom操作,就例如'art-template',但依舊不能解決跟蹤狀態變化
4.開始思考,想跟蹤變化,就需要進行比較,比較后知道哪里改變了,去改變對應位置思路就誕生了,但是
如果說維護兩個dom,每次兩個dom進行比較,這原本一個dom就相對比較浪費,現在又用兩個,這個肯定
不行,如果我們用js形式記錄兩個數組,每次比較兩個數組那個位置變化了,再去操作對應變化的局部的dom
是不是會更好(兩個dom 比較不好的主要原因通過打印dom 屬性看到的數量上,這么多屬性每次都要最對比是個開銷,并且并不是每個屬性都用了)
5.Virtual DOM 的好處是當狀態改變時不需要立即更新 DOM,只需要創建一個虛擬樹來描述
DOM, Virtual DOM 內部將弄清楚如何有效(diff)的更新 DOM
~~~
* 虛擬dom 用js表現形式
~~~
{
sel: "div",
data: {},
children: undefined,
text: "Hello Virtual DOM",
elm: undefined,
key: undefined
}
~~~
>[danger] ##### 虛擬dom 作用
~~~
1.維護視圖和狀態關系
2.復雜視圖情況下提高性能,如果說頁面只是一個很簡單的視圖狀態,額外去多維護一個虛擬dom也就是
js的話其實很不值當
3.還可以實現 SSR(Nuxt.js/Next.js)、原生應用(Weex/React Native)、小程序(mpvue/uni-app)
~~~
>[danger] ##### 常見的操作虛擬dom 的庫
~~~
1.'Snabbdom' Vue 2.x 內部使用的 Virtual DOM 就是改造的 Snabbdom
2.'virtual-dom',其實進入'virtual-dom'github在項目介紹中也有一段對虛擬dom的產生的動機描述
~~~
* 谷歌翻譯動機描述
~~~
手動DOM操作比較麻煩,并且很難跟蹤以前的DOM狀態。解決此問題的方法是編寫代碼,
就像在狀態更改時重新創建整個DOM一樣。當然,如果您每次更改應用程序狀態時實際上都重新創建
了整個DOM,則應用程序將非常緩慢,并且輸入字段將失去焦點。
virtual-dom是模塊的集合,旨在提供聲明性的方式來表示應用程序的DOM。因此,您不必在應用程序
狀態更改時更新DOM,而只需創建一個虛擬樹或VTree,它看起來像您想要的DOM狀態。virtual-dom
然后,您將了解如何有效地使DOM看起來像這樣,而無需重新創建所有DOM節點。
virtual-dom通過創建完整VTree的視圖,然后有效地修補DOM以使其完全符合您的描述,您可以在狀態
變化時更新視圖。這樣可以避免在應用程序代碼中進行手動DOM操作和先前狀態跟蹤,從而為Web應用
程序提供了清晰且可維護的呈現邏輯。
~~~
>[danger] ##### 虛擬dom 真的能提高性能么?
[本段內容來自拉勾前端高手進階](https://kaiwu.lagou.com/course/courseInfo.htm?courseId=180#/detail/pc?id=3190)
~~~
1.優化性能 。DOM 操作是比較耗時的,對于大量、頻繁的 DOM 操作,如果先在 JavaScript 中模擬進行,
然后再通過計算比對,找到真正需要更新的節點,這樣就有可能減少不必要的 DOM 操作,從而提升渲染
性能。但并不是所有的 DOM 操作都能通過虛擬 DOM 提升性能,比如單次刪除某個節點,直接操作 DOM
肯定比虛擬 DOM 計算比對之后再刪除要快。總體而言, 虛擬 DOM 提升了 DOM 操作的性能下限,降低了
DOM 操作的性能上限。 所以會看到一些對渲染性能要求比較高的場景,比如在線文檔、表格編輯,還是會
使用原生 DOM 操作。
2.跨平臺 。由于虛擬 DOM 以 JavaScript 對象為基礎,所以可根據不同的運行環境進行代碼轉換
(比如瀏覽器、服務端、原生應用等),這使得它具有了跨平臺的能力。
~~~
>[danger] ##### 總結
~~~
1.要分清虛擬dom 和直接操作dom本質上的區別,在'jq'的時代需要開發既要關心數據,又要對對應的'dom'進行
操作,例如新增數據的時候需要開發對所在新增數據'dom'區域進行新增數據的'dom'創建并且插入。那是否可以
讓'jq' 也變成只關心數據'dom'直接改變?這里提出一個假設將對應的數據要渲染的'js模板'寫好,當數據變化的時候
整個該數據地方的dom全部重新渲染。和之前相比原本100條數據新增一條我們只需要創建一條新增數據的dom
插入這100條即可,現在為了減少dom操作需要重新渲染101條數據'dom'并且重新插入,如果現在用虛擬dom來解決
這個問題我們每次會存儲新老的dom結構,注意這里存儲的不是webapi 提供的dom對象結構,而是將其抽象化
用js數據格式表現的數據結構成為虛擬dom,每次數據改變生成的新虛擬dom 結構和老的虛擬dom 結構進行比較
哪里不一樣重新渲染哪里(這是理想狀態下)
2.通過用 JavaScript 對象來表示 DOM 元素的方式,該對象僅包括常用的這些屬性方法和節點關系,這樣就可以大大降低對象內存、虛擬 DOM 差異對比的計算量等
~~~
* jq直接操作數據
~~~
1.正常的jq代碼以我的理解我是不會去像下面那么寫,更多的是新增創建一個對應的新增dom,插入到他的父節點
,下面案例站在的角度是vue這類不操組dom 直接數據變化影響視圖用jq強行實現,就出現了已經被渲染的dom
還需要重新創建重新渲染,虛擬dom出現就是既可以實現數據操作又滿足不創建之前存在的dom節點
~~~
~~~
let heroes = [
{ name: "劍圣", age: 80, offsetTop: 0, elmHeight: 20 },
{ name: "盲僧", age: 30, offsetTop: 0, elmHeight: 20 },
{ name: "暗夜獵手", age: 50, offsetTop: 0, elmHeight: 20 },
{ name: "寒冰射手", age: 20, offsetTop: 0, elmHeight: 20 },
{ name: "賞金獵人", age: 40, offsetTop: 0, elmHeight: 20 }
];
function render() {
let html = "";
heroes.forEach(hero => {
html += `
<li class="hero" style="opacity: 0; transform: translateX(0px) translateY(${
hero.offsetTop
}px)">
<div>
<span class="left">姓名:${hero.name}</span>
<span class="left l30">年齡:${hero.age}</span>
<span class="right close">x</span>
</div>
</li>
`;
});
$(".content > ul").html(html);
$(".content > ul > li").each((index, li) => {
heroes[index].elmHeight = $(li).height();
});
heroes = heroes.reduce((arr, hero) => {
let last = arr[arr.length - 1];
hero.offsetTop = last ? last.elmHeight + last.offsetTop + 10 : 10;
return arr.concat(hero);
}, []);
~~~
[jq案例鏈接](https://codesandbox.io/s/jq-demo-5i7qp?file=/src/index.js:25-1034)
[虛擬dom鏈接](https://codesandbox.io/s/snabbdom-demo-forked-8lsvb?file=/src/index.js:769-944)
>[danger] ##### 文章參考來源
[參考內容來源](https://github.com/lagoufed/vuejs-enhancement)
- Vue--基礎篇章
- Vue -- 介紹
- Vue -- MVVM
- Vue -- 創建Vue實例
- Vue -- 模板語法
- Vue -- 指令用法
- v-cloak -- 遮蓋
- v-bind -- 標簽屬性動態綁定
- v-on -- 綁定事件
- v-model -- 雙向數據綁定
- v-for -- 只是循環沒那么簡單
- 小知識點 -- 計劃內屬性
- key -- 屬性為什么要加
- 案例說明
- v-if/v-show -- 顯示隱藏
- v-for 和 v-if 同時使用
- v-pre -- 不渲染大大胡語法
- v-once -- 只渲染一次
- Vue -- class和style綁定
- Vue -- filter 過濾器
- Vue--watch/computed/fun
- watch -- 巧妙利用watch思想
- Vue -- 自定義指令
- Vue -- $方法
- Vue--生命周期
- Vue -- 專屬ajax
- Vue -- transition過渡動畫
- 前面章節的案例
- 案例 -- 跑馬燈效果
- 案例 -- 選項卡內容切換
- 案例-- 篩選商品
- 案例 -- 搜索/刪除/更改
- 案例 -- 用computed做多選
- 案例 -- checked 多選
- Vue--組件篇章
- component -- 介紹
- component -- 使用全局組件
- component -- 使用局部組件
- component -- 組件深入
- component -- 組件傳值父傳子
- component -- 組件傳值子傳父
- component -- 子傳父語法糖拆解
- component -- 父組件操作子組件
- component -- is 動態切換組件
- component -- 用v-if/v-show控制子組件
- component -- 組件切換的動畫效果
- component -- slot 插槽
- component -- 插槽2.6
- component -- 組件的生命周期
- component -- 基礎組件全局注冊
- VueRouter--獲取路由參數
- VueRouter -- 介紹路由
- VueRouter -- 安裝
- VueRouter -- 使用
- VueRouter--router-link簡單參數
- VueRouter--router-link樣式問題
- VueRouter--router-view動畫效果
- VueRouter -- 匹配優先級
- vueRouter -- 動態路由
- VueRouter -- 命名路由
- VueRouter -- 命名視圖
- VueRouter--$router 獲取函數
- VueRouter--$route獲取參數
- VueRouter--路由嵌套
- VueRouter -- 導航守衛
- VueRouter -- 寫在最后
- Vue--模塊化方式結構
- webpack--自定義配置
- webpack -- 自定義Vue操作
- VueCli -- 3.0可視化配置
- VueCli -- 3.0 項目目錄
- Vue -- 組件升級篇
- Vue -- 組件種類與組件組成
- Vue -- 組件prop、event、slot 技巧
- Vue -- 組件通信(一)
- Vue -- 組件通信(二)
- Vue -- 組件通信(三)
- Vue -- 組件通信(四)
- Vue -- 組件通信(五)
- Vue -- 組件通信(六)
- Vue -- bus非父子組件通信
- Vue -- 封裝js插件成vue組件
- vue組件分裝 -- 進階篇
- Vue -- 組件封裝splitpane(分割面板)
- UI -- 正式封裝
- Vue -- iview 可編輯表格案例
- Ui -- iview 可以同時編輯多行
- Vue -- 了解遞歸組件
- UI -- 正式使用遞歸菜單
- Vue -- iview Tree組件
- Vue -- 利用通信仿寫一個form驗證
- Vue -- 使用自己的Form
- Vue -- Checkbox 組件
- Vue -- CheckboxGroup.vue
- Vue -- Alert 組件
- Vue -- 手動掛載組件
- Vue -- Alert開始封裝
- Vue -- 動態表單組件
- Vue -- Vuex組件的狀態管理
- Vuex -- 參數使用理解
- Vuex -- state擴展
- Vuex -- getters擴展
- Vuex--mutations擴展
- Vuex -- Action 異步
- Vuex -- plugins插件
- Vuex -- v-model寫法
- Vuex -- 更多
- VueCli -- 技巧總結篇
- CLI -- 路由基礎
- CLI -- 路由升級篇
- CLI --異步axios
- axios -- 封裝axios
- CLI -- 登錄寫法
- CLI -- 權限
- CLI -- 簡單權限
- CLI -- 動態路由加載
- CLI -- 數據性能優化
- ES6 -- 類的概念
- ES6類 -- 基礎
- ES6 -- 繼承
- ES6 -- 工作實戰用類數據管理
- JS -- 適配器模式
- ES7 -- 裝飾器(Decorator)
- 裝飾器 -- 裝飾器修飾類
- 裝飾器--修飾類方法(知識擴展)
- 裝飾器 -- 裝飾器修飾類中的方法
- 裝飾器 -- 執行順序
- Reflect -- es6 自帶版本
- Reflect -- reflect-metadata 版本
- 實戰 -- 驗證篇章(基礎)
- 驗證篇章 -- 搭建和目錄
- 驗證篇章 -- 創建基本模板
- 驗證篇章 -- 使用
- 實戰 -- 更新模型(為了迎合ui升級)
- 實戰 -- 模型與接口對接
- TypeSprict -- 基礎篇章
- TS-- 搭建(一)webpack版本
- TS -- 搭建(二)直接使用
- TS -- 基礎類型
- TS -- 枚舉類型
- TS -- Symbol
- TS -- interface 接口
- TS -- 函數
- TS -- 泛型
- TS -- 類
- TS -- 類型推論和兼容
- TS -- 高級類型(一)
- TS -- 高級類型(二)
- TS -- 關于模塊解析
- TS -- 聲明合并
- TS -- 混入
- Vue -- TS項目模擬
- TS -- vue和以前代碼對比
- TS -- vue簡單案例上手
- Vue -- 簡單弄懂VueRouter過程
- VueRouter -- 實現簡單Router
- Vue-- 原理2.x源碼簡單理解
- 了解 -- 簡單的響應式工作原理
- 準備工作 -- 了解發布訂閱和觀察者模式
- 了解 -- 響應式工作原理(一)
- 了解 -- 響應式工作原理(二)
- 手寫 -- 簡單的vue數據響應(一)
- 手寫 -- 簡單的vue數據響應(二)
- 模板引擎可以做的
- 了解 -- 虛擬DOM
- 虛擬dom -- 使用Snabbdom
- 閱讀 -- Snabbdom
- 分析snabbdom源碼 -- h函數
- 分析snabbdom -- init 方法
- init 方法 -- patch方法分析(一)
- init 方法 -- patch方法分析(二)
- init方法 -- patch方法分析(三)
- 手寫 -- 簡單的虛擬dom渲染
- 函數表達解析 - h 和 create-element
- dom操作 -- patch.js
- Vue -- 完成一個minVue
- minVue -- 打包入口
- Vue -- new實例做了什么
- Vue -- $mount 模板編譯階段
- 模板編譯 -- 分析入口
- 模板編譯 -- 分析模板轉譯
- Vue -- mountComponent 掛載階段
- 掛載階段 -- vm._render()
- 掛載階段 -- vnode
- 備份章節
- Vue -- Nuxt.js
- Vue3 -- 學習
- Vue3.x --基本功能快速預覽
- Vue3.x -- createApp
- Vue3.x -- 生命周期
- Vue3.x -- 組件
- vue3.x -- 異步組件???
- vue3.x -- Teleport???
- vue3.x -- 動畫章節 ??
- vue3.x -- 自定義指令 ???
- 深入響應性原理 ???
- vue3.x -- Option API VS Composition API
- Vue3.x -- 使用set up
- Vue3.x -- 響應性API
- 其他 Api 使用
- 計算屬性和監聽屬性
- 生命周期
- 小的案例(一)
- 小的案例(二)-- 泛型
- Vue2.x => Vue3.x 導讀
- v-for 中的 Ref 數組 -- 非兼容
- 異步組件
- attribute 強制行為 -- 非兼容
- $attrs 包括 class & style -- 非兼容
- $children -- 移除
- 自定義指令 -- 非兼容
- 自定義元素交互 -- 非兼容
- Data選項 -- 非兼容
- emits Option -- 新增
- 事件 API -- 非兼容
- 過濾器 -- 移除
- 片段 -- 新增
- 函數式組件 -- 非兼容
- 全局 API -- 非兼容
- 全局 API Treeshaking -- 非兼容
- 內聯模板 Attribute -- 非兼容
- key attribute -- 非兼容
- 按鍵修飾符 -- 非兼容
- 移除 $listeners 和 v-on.native -- 非兼容
- 在 prop 的默認函數中訪問 this -- ??
- 組件使用 v-model -- 非兼容
- 渲染函數 API -- ??
- Slot 統一 ??
- 過渡的 class 名更改 ???
- Transition Group 根元素 -- ??
- v-if 與 v-for 的優先級對比 -- 非兼容
- v-bind 合并行為 非兼容
- 監聽數組 -- 非兼容