slot多作用域 vue_vue 深度长文之slot 篇
今天我們將分析我們經常使用的 vue 功能 slot 是如何設計和實現的,本文將圍繞 普通插槽 和 作用域插槽 以及 vue 2.6.x 版本的 v-slot 展開對該話題的討論。當然還不懂用法的同學建議官網先看看相關 API 先。接下來,我們直接進入正文吧
普通插槽
首先我們看一個我們對于 slot 最常用的例子
然后我們直接使用,頁面則正常顯示一下內容
然后,這個時候我們使用的時候,對 slot 內容進行覆蓋
this is slot custom content.內容則變成下圖所示
對于此,大家可能都能清楚的知道會是這種情況。今天我就將帶領大家直接看看 vue 底層對 slot 插槽的具體實現。
vm.$slots
我們開始前,先看看 vue 的 Component 接口上對 $slots 屬性的定義
$slots: { [key: string]: Array };多的咱不說,咱直接 console 一下上面例子中的 $slots
剩下的篇幅將講解 slot 內容如何進行渲染以及如何轉換成上圖內容
renderSlot
看完了具體實例中 slot 渲染后的 vm.$slots 對象,這一小篇我們直接看看 renderSlot 這塊的邏輯,首先我們先看看 renderSlot 函數的幾個參數都有哪些
這里我們先不看 scoped-slot 的邏輯,我們只看普通 slot 的邏輯。
const slotNodes = this.$slots[name]nodes = slotNodes || fallbackreturn nodes這里直接先取值 this.$slots[name] ,若存在則直接返回其對其的 vnode 數組,否則返回 fallback。看到這,很多人可能不知道 this.$slots 在哪定義的。解釋這個之前我們直接往后看另外一個方法
看完 resolveSlots 的參數后我們接著往后過其中具體的邏輯。如果 children 參數不存在,直接返回一個空對象
const slots = {}if (!children) { return slots}如果存在,則直接對 children 進行遍歷操作
slots 獲取到值后,則進行一些過濾操作,然后直接返回有用的 slots
我們從上面已經知道了 vue 對 slots 是如何進行賦值保存數據的。而在 src/core/instance/render.js 的 initRender 方法中則是對 vm.$slots 進行了初始化的賦值。
了解了是 vm.$slots 這塊邏輯后,肯定有人會問:你這不就只是拿到了一個對象么,怎么把其中的內容給搞出來呢?別急,我們接著就來講一下對于 slot 這塊 vue 是如何進行編譯的。這里咱就把 slot generate 相關邏輯過上一過,話不多說,咱直接上代碼
注:上面的 slotName 在 src/compiler/parser/index.js 的 processSlot() 函數中進行了賦值,并且 父組件編譯階段用到的 slotTarget 也在這里進行了處理
隨即在 genData() 中使用 slotTarget 進行 data 的數據拼接
if (el.slotTarget && !el.slotScope) { data += `slot:${el.slotTarget},`}此時父組件將生成以下代碼
然后當 el.tag 為 slot 的情況,則直接執行 genSlot()
else if (el.tag === 'slot') { return genSlot(el, state)}按照我們舉出的例子,則子組件最終會生成以下代碼
作用域插槽
上面我們已經了解到 vue 對于普通的 slot 標簽是如何進行處理和轉換的。接下來我們來分析下作用域插槽的實現邏輯。
1、vm.$scopedSlots
了解之前還是老規矩,先看看 vue 的 Component 接口上對 $scopedSlots 屬性的定義
$scopedSlots: { [key: string]: () => VNodeChildren };其中的 VNodeChildren 定義如下:
declare type VNodeChildren = Array<?VNode | string | VNodeChildren> | string;先來個相關的例子
然后進行使用
效果如下
從使用層面我們能看出來,子組件的 slot 標簽上綁定了一個 text 以及 :msg 屬性。然后父組件在使用插槽使用了 slot-scope 屬性去讀取插槽帶的屬性對應的值
注:提及一下 processSlot() 對于 slot-scope 的處理邏輯
從上面的代碼我們能看出,vue 對于這塊直接讀取 slot-scope 屬性并賦值給 AST 抽象語法樹的 slotScope 屬性上。而擁有 slotScope 屬性的節點,會直接以 **插槽名稱 name 為 key、本身為 value **的對象形式掛載在父節點的 scopedSlots 屬性上
然后在 src/core/instance/render.js 的 renderMixin 方法中對 vm.$scopedSlots 則是進行了如下賦值:
if (_parentVnode) { vm.$scopedSlots = _parentVnode.data.scopedSlots || emptyObject}然后 genData() 里會進行以下邏輯處理
if (el.scopedSlots) { data += `${genScopedSlots(el, el.scopedSlots, state)},`}緊接著我們來看看 genScopedSlots 中的邏輯
然后我們再來看看 genScopedSlot 是如何生成 render function 字符串的
我們把上面例子的 $scopedSlots 打印一下,結果如下
然后上面例子中父組件最終會生成如下代碼
renderSlot(slot-scope)
上面我們提及對于插槽 render 邏輯的時候忽略了 slot-scope 的相關邏輯,這里我們來看看這部分內容
這里我們看看 renderHelps 里面的 _u ,即 resolveScopedSlots,其邏輯如下
這塊會對 attrs 和 v-bind 進行,對于這塊內容上面我已經提過了,要看請往上翻閱。結合我們的例子,子組件則會生成以下代碼
v-slot
1、基本用法
vue 2.6.x 已經出來有一段時間了,其中對于插槽這塊則是放棄了 slot-scope 作用域插槽推薦寫法,直接改成了 v-slot 指令形式的推薦寫法(當然這只是個語法糖而已)。下面我們將仔細談談 v-slot 這塊的內容。
在看具體實現邏輯前,我們先通過一個例子來先了解下其基本用法
然后進行使用
頁面展示效果如下
相同與區別
接下來,咱來會會這個新特性
round 1. $slots & $scopedSlots
$slots 這塊邏輯沒變,還是沿用的以前的代碼
// $slotsconst options = vm.$optionsconst parentVnode = vm.$vnode = options._parentVnodeconst renderContext = parentVnode && parentVnode.contextvm.$slots = resolveSlots(options._renderChildren, renderContext)$scopedSlots 這塊則進行了改造,執行了 normalizeScopedSlots() 并接收其返回值為 $scopedSlots 的值
接著,我們來會一會 normalizeScopedSlots ,首先我們先看看它的幾個參數
- 首先,如果 slots 參數不存在,則直接返回一個空對象 {}
 
- 若 prevSlots 存在,且滿足系列條件的情況,則直接返回 prevSlots
 
注:這里的 $key , $hasNormal , $stable 是直接使用 vue 內部對 Object.defineProperty 封裝好的 def() 方法進行賦值的
def(res, '$stable', isStable)def(res, '$key', key)def(res, '$hasNormal', hasNormalSlots)復制代碼- 否則,則對 slots 對象進行遍歷,操作 normalSlots ,賦值給 key 為 key,value 為 normalizeScopedSlot 返回的函數 的對象 res
 
- 隨后再次對 normalSlots 進行遍歷,若 normalSlots 中的 key 在 res 找不到對應的 key,則直接進行 proxyNormalSlot 代理操作,將 normalSlots 中的 slot 掛載到 res對象上
 - 接著,我們看看 normalizeScopedSlot() 都做了些什么事情。該方法接收三個參數,第一個參數為 normalSlots,第二個參數為 key,第三個參數為 fn
 
參考文章:
https://juejin.im/post/5cced0096fb9a032426510ad
總結
以上是生活随笔為你收集整理的slot多作用域 vue_vue 深度长文之slot 篇的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 建行卡号有重复的吗?
 - 下一篇: 微信不绑定卡怎么收钱?