>[success] # key 作用
1. 在`v-for` 時候經常 會給節點加上 `key` 屬性對應值可以是`number | string | symbol`,這個`key` 是 特殊 `attribute `主要用做 `Vue `的虛擬 `DOM `算法的提示,以在比對新舊節點組時辨識 `VNodes`
* **不使用** `key`,`Vue `會使用一種算法來最小化元素的移動并且盡可能嘗試就地**修改/復用相同類型元素**。
* **使用** `key `時,它會基于 `key `的順序變化重新排列元素,并且那些使用了已經不存在的 `key `的元素將會被**移除/銷毀**
[官方文檔解釋](https://cn.vuejs.org/api/built-in-special-attributes.html#key)
>[danger] ##### 額外補充關于Vnode
1. **VNode**的全稱是`Virtual Node`,也就是**虛擬節點**,`VNode`的本質是一個`JavaScript`的對象(`template =》vnode =》 真實DOM`)
2. 同過比較 新老的 `vnode` 可以判斷出新老變化中沒有改變的**節點**,將這些節點的在**老Vonde 渲染的真實dom 對象直接服用給新Vnode 的真實dom對象**
[具體可參考](http://www.hmoore.net/cyyspring/webpack/3079650)
>[info] ## 源碼位置
1. 通過[**源碼位置**](https://github1s.com/vuejs/core/blob/main/packages/runtime-core/src/renderer.ts)代碼可以發現`Vue`事實上會對于**有key和沒有key會調用兩個不同的方法**;
* **有`key`**,使用 `patchKeyedChildren `方法;
* **沒有`key`**,使用 `patchUnkeyedChildren`方法;
>[danger] ##### 簡單來看patchUnkeyedChildren -- 沒key 情況
* 舉個例子現在 有個展示 `a b c d` 經過展示后 需要變成 `a b f c d`,但在循環的過程中并沒有綁定`key`,形成的代碼如下
~~~
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<button @click="add">新增</button>
<div v-for="item in list">{{item}}</div>
</div>
<!-- 通過cdn 引入 -->
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
// data: option api
data() {
return {
list: ['a', 'b', 'c', 'd'],
}
},
methods: {
add() {
this.list.splice(2, 0, 'f')
},
},
})
app.mount('#app')
</script>
</body>
</html>
~~~
* 上面代碼整體執行后效果在源碼中過程
1. 獲取到舊vnode和新vnode**判斷兩者哪個數組長度較小(用較小的數組patch較大的數組, 才不會引起數組越界)**
2. 進入`patch`, 分兩種情況 **舊vnode < 新vnode** ,如果新的多創建多出的部分。**舊vnode > 新vnode** ,如果老的多 **(則卸載舊vnode節點)直接刪除比新的多出的部分**
3. 在這個過程中如下圖,在比較過程中發現 `a b` 節點并沒有發生改變, 直接就可以將 `a,b 老的vnode 映射的真實dom` 直接 賦值給 `a,b 新Vonde的真實節點`,其他節點怎**C更新成F,D更新成C,E更新成D,最后再插入F**,整體看只有前兩個節點進行了真實dom 復用,后面的節點都是進行了變更

* 代碼位置

>[danger] ##### patchKeyedChildren - 有key
1. **有key 情況會進行進行diff算法的時候,并且會復用相同節點**

>[info] ## 注意點key 不要使用index
* 使用index 作為`key`第一個數據可以復用之前的之外,另外三條數據都需要重新渲染;因為`key`雖然相同但節點內容不同
~~~
之前的數據 之后的數據
key: 0 index: 0 name: test1 key: 0 index: 0 name: test1
key: 1 index: 1 name: test2 key: 1 index: 1 name: 我是插隊的那條數據
key: 2 index: 2 name: test3 key: 2 index: 2 name: test2
key: 3 index: 3 name: test3
~~~
* 使用唯一元素例如`id`現在對比發現只有一條數據變化了,就是id為4的那條數據,因此只要新渲這一條數據就可以了,其他都是就復用之前的;
~~~
之前的數據 之后的數據
key: 1 id: 1 index: 0 name: test1 key: 1 id: 1 index: 0 name: test1
key: 2 id: 2 index: 1 name: test2 key: 4 id: 4 index: 1 name: 我是插隊的那條數據
key: 3 id: 3 index: 2 name: test3 key: 2 id: 2 index: 2 name: test2
key: 3 id: 3 index: 3 name: test3
~~~
>[info] # 重新整理這個
http://www.hmoore.net/cyyspring/vuejs/1129266#_key_276
>[info] ## 參考文章
[深入解析vue中的key,看看key到底能拿來干嘛!](https://www.php.cn/vuejs/486467.html)
- 官網給的工具
- 聲明vue2 和 vue3
- 指令速覽
- Mustache -- 語法
- v-once -- 只渲染一次
- v-text -- 插入文本
- v-html -- 渲染html
- v-pre -- 顯示原始的Mustache標簽
- v-cloak -- 遮蓋
- v-memo(新)-- 緩存指定值
- v-if/v-show -- 條件渲染
- v-for -- 循環
- v-bind -- 知識
- v-bind -- 修飾符
- v-on -- 點擊事件
- v-model -- 雙向綁定
- 其他基礎知識速覽
- 快速使用
- 常識知識點
- key -- 作用 (后續要更新)
- computed -- 計算屬性
- watch -- 偵聽
- 防抖和節流
- vue3 -- 生命周期
- vue-cli 和 vite 項目搭建方法
- vite -- 導入動態圖片
- 組件
- 單文件組件 -- SFC
- 組件通信 -- porp
- 組件通信 -- $emit
- 組件通信 -- Provide / Inject
- 組件通信 -- 全局事件總線mitt庫
- 插槽 -- slot
- 整體使用案例
- 動態組件 -- is
- keep-alive
- 分包 -- 異步組價
- mixin -- 混入
- v-model-- 組件
- 使用計算屬性
- v-model -- 自定義修飾符
- Suspense -- 實驗屬性
- Teleport -- 指定掛載
- 組件實例 -- $ 屬性
- Option API VS Composition API
- Setup -- 組合API 入口
- api -- reactive
- api -- ref
- 使用ref 和 reactive 場景
- api -- toRefs 和 toRef
- api -- readonly
- 判斷性 -- API
- 功能性 -- API
- api -- computed
- api -- $ref 使用
- api -- 生命周期
- Provide 和 Inject
- watch
- watchEffect
- watch vs. watchEffect
- 簡單使用composition Api
- 響應性語法糖
- css -- 功能
- 修改css -- :deep() 和 var
- Vue3.2 -- 語法
- ts -- vscode 配置
- attrs/emit/props/expose/slots -- 使用
- props -- defineProps
- props -- defineProps Ts
- emit -- defineEmits
- emit -- defineEmits Ts
- $ref -- defineExpose
- slots/attrs -- useSlots() 和 useAttrs()
- 自定義指令
- Vue -- 插件
- Vue2.x 和 Vue3.x 不同點
- $children -- 移除
- v-for 和 ref
- attribute 強制行為
- 按鍵修飾符
- v-if 和 v-for 優先級
- 組件使用 v-model -- 非兼容
- 組件
- h -- 函數
- jsx -- 編寫
- Vue -- Router
- 了解路由和vue搭配
- vueRouter -- 簡單實現
- 安裝即使用
- 路由懶加載
- router-view
- router-link
- 路由匹配規則
- 404 頁面配置
- 路由嵌套
- 路由組件傳參
- 路由重定向和別名
- 路由跳轉方法
- 命名路由
- 命名視圖
- Composition API
- 路由守衛
- 路由元信息
- 路由其他方法 -- 添加/刪除/獲取
- 服務器配置映射
- 其他
- Vuex -- 狀態管理
- Option Api -- VUEX
- composition API -- VUEX
- module -- VUEX
- 刷新后vuex 數據同步
- 小技巧
- Pinia -- 狀態管理
- 開始使用
- pinia -- state
- pinia -- getter
- pinia -- action
- pinia -- 插件 ??
- Vue 源碼解讀
- 開發感悟
- 練手項目