插槽做爲vue重要內容分發手段,不少同窗對它的原理比較感興趣,下面咱們來探究一下。html
<!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出現,並無什麼特別vue
(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
組件實例化時會怎麼作,看一下相關代碼:node
// core/instance/render.js vm.$slots = resolveSlots(options._renderChildren, renderContext) 複製代碼
resolveSlots()函數從父組件中獲取渲染結果VNode,它們被存入default,這就是默認插槽的內容。這裏的renderContext
就是父組件實例,顯然若是有動態內容要從它裏面獲取。 markdown
這告訴咱們爲何咱們能在render函數中訪問
this.$slots.default
獲取默認插槽內容app
那麼誰在使用$slots中的內容,顯然是comp1
組件的渲染函數,輸出看一下:函數
(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指令時,this
<comp> <template v-slot:default>abc</template> <template v-slot:foo="ctx">{{ctx.abc}}</template> </comp> 複製代碼
編譯結果將成爲做用域插槽形式:spa
(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
,結構以下: code
咱們知道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把做用域插槽轉換爲函數形式在子組件中調用了。
你們平時使用插槽時常常會記不住不一樣插槽的用法,通過原理分析後咱們可以更清楚的瞭解到,其實無論匿名插槽、具名插槽仍是做用域插槽,最終的編譯結果是一致的。既然如此徹底能夠寫成相一樣子:
<comp1> <template v-slot:default>abc</template> <template v-slot:foo>foo</template> </comp1> 複製代碼
而後只需記住若是我要的數據是父組件的仍是子組件的,若是是後者就給v-slot
設置一個屬性對象值:
<comp1> <!--foo1來自承載comp1的父組件--> <template v-slot:foo>{{foo1}}</template> <!--bar1來自comp1--> <template v-slot:bar="{bar1}">{{bar1}}</template> </comp1> 複製代碼
還有哪些疑問沒有解答,歡迎你們留言。