vue插槽究竟是如何運做的

Vue插槽原理

插槽做爲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指令時

若是使用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把做用域插槽轉換爲函數形式在子組件中調用了。

還有哪些疑問沒有解答,歡迎你們留言。

相關文章
相關標籤/搜索