插槽做爲vue重要內容分發手段,不少同窗對它的原理比較感興趣,下面咱們來探究一下。javascript
<!DOCTYPE html>
<html>
<head>
<title>Vue事件處理</title>
<script src="../../../dist/vue.js"></script>
</head>
<body>
<div id="demo">
<h1>插槽處理機制</h1>
<comp1>
<span>abc</span>
</comp1>
</div>
<script> // 聲明自定義組件 Vue.component('comp1', { template: '<div><slot></slot></div>' }) // 建立實例 const app = new Vue({ el: '#demo' }); console.log(app.$options.render); </script>
</body>
</html>
複製代碼
輸出結果以下:可見只是做爲span的children出現,並無什麼特別html
(function anonymous() {
with(this){return _c('div',{attrs:{"id":"demo"}},[
_c('h1',[_v("插槽處理機制")]),_v(" "),
_c('comp1',[_c('span',[_v("abc")])]),
})
複製代碼
沒有使用v-slot指令,此時組件包含內容做爲父組件的children出現,內部有沒有slot對編譯結果沒有影響。下一步就是comp1
組件實例化時會怎麼作,看一下相關代碼:vue
// core/instance/render.js
vm.$slots = resolveSlots(options._renderChildren, renderContext)
複製代碼
resolveSlots()函數從父組件中獲取渲染結果VNode,它們被存入default,這就是默認插槽的內容。這裏的renderContext
就是父組件實例,顯然若是有動態內容要從它裏面獲取。 java
這告訴咱們爲何咱們能在render函數中訪問
this.$slots.default
獲取默認插槽內容node
那麼誰在使用$slots中的內容,顯然是comp1
組件的渲染函數,輸出看一下:app
(function anonymous( ) {
with(this){return _c('div',[_t("default"),_v(" "),
_t("foo",null,{"abc":"abc from comp"})],2)}
})
複製代碼
這裏的_t就是renderSlot()的別名,它會用到$slots或$scopedSlots的內容函數
//src/core/instance/render-helpers/render-slot.js
const scopedSlotFn = this.$scopedSlots[name]
let nodes
if (scopedSlotFn) {
// ...
}
else {
nodes = this.$slots[name] || fallback
}
複製代碼
若是使用v-slot指令時,測試
<comp>
<template v-slot:default>abc</template>
<template v-slot:foo="ctx">{{ctx.abc}}</template>
</comp>
複製代碼
編譯結果將成爲做用域插槽形式:ui
(function anonymous( ) {
with(this){return _c('div',{attrs:{"id":"demo"}},[
_c('h1',[_v("attr update")]),
_c('comp',{scopedSlots:_u([
{key:"default",fn:function(){return [_v("abc")]},proxy:true},
{key:"foo",fn:function(ctx){return [_v(_s(ctx.abc))]}}])})],1)}
})
複製代碼
上面的_u是resolveScopedSlots()
的別名,v-slot的參數會做爲key,值會做爲fn函數的參數,好比上面的ctx。根組件首次渲染時會調用該函數,返回做用域插槽描述對象$scopedSlots
,結構以下: this
咱們知道ctx
來自於子組件,它是怎麼傳進來的呢?先看一下comp1
的渲染函數:
//...
_t("foo",null,{"abc":"abc from comp"})
複製代碼
comp1
組件的渲染函數調用_t即renderSlot()時會將屬性對象做爲參數3傳遞,它們都來自comp1
中名稱爲foo的具名插槽。因此最後返回的結果是renderSlot()的返回值,也就是前面fn
的執行結果:
//src/core/instance/render-helpers/render-slot.js
const scopedSlotFn = this.$scopedSlots[name]
nodes = scopedSlotFn(props)
複製代碼
這就解答了爲何做用域插槽可以使用子組件中的數據,由於vue把做用域插槽轉換爲函數形式在子組件中調用了。
還有哪些疑問沒有解答,歡迎你們留言。