Vue對于插槽有兩個專門的APIvm.$slots
和vm.$scopedSlots
,分別是普通插槽和作用域插槽,使用JSX語法或渲染函數的時候,定義插槽將使用上述兩個API。
渲染函數createElement
普通插槽和作用域插槽在定義上相差不大,但是在使用方法上略微有點區別,詳見渲染函數>數據對象
普通插槽,插槽內容以children子節點
形式,然后在數據對象中指定插槽名
// 定義帶插槽的組件,`$slots.default`為匿名插槽,其余的則是具名插槽,匿名插槽的插槽名可以省略
const MySlot = {render (h) {return h('div', [h('header', [this.$slots.header]),h('main', [this.$slots.header]),h('footer', [this.$slots.footer])])}
}// 在`children子節點`中指定插槽名以使用具名插槽,未指定插槽名的則放入匿名插槽中
export default {components: { MySlot },render (h) {return h('MySlot', [h('template', { slot: 'header' }, 'hello world'),'children node',h('div', { slot: 'footer' }, 'this is footer')])}
}
作用域插槽,與普通插槽不同,作用域插槽的內容直接放入渲染函數的數據對象中的
// 定義作用域插槽
const MySlot = {data () {return { user: 'John', content: 'vue', copytight: 'CopyRight' }},render (h) {return h('div', [h('header', [this.$scopedSlots.header({ user: this.user })]),h('main', [this.$scopedSlots.default({ content: this.content })]),h('footer', [this.$scopedSlots.footer({ copytight: this.copytight })])])}
}// 要使用作用域插槽的數據內容,則插槽必須在組件的數據對象`scopedSlots`中使用,如`header`所示
// 作用域插槽也可以當作普通插槽使用,如`default`和`footer`
export default {components: { MySlot },render (h) {return h('MySlot', {scopedSlots: {header: props => `hello, ${props.user}`}}, ['children node',h('div', { slot: 'footer' }, 'this is footer')])}
}
關于靜態插槽和作用域插槽:
你可以通過
this.$slots
訪問靜態插槽的內容,每個插槽都是一個 VNode 數組
也可以通過this.$scopedSlots
訪問作用域插槽,每個作用域插槽都是一個返回若干 VNode 的函數
this.$slots
返回的是數組,this.$scopedSlots
返回的是函數,這里踩一個坑,使用 this.$scopedSlots
定義的插槽如果未被使用則會報錯
例如刪除或注釋掉具名插槽footer內容后,控制臺報錯
[Vue warn]: Error in render: "TypeError: this.$scopedSlots.footer is not a function"
如何避免這個問題暫時沒有找到解決方案,研究還不夠深入
2021年1月25日補充:
使用
this.$scopedSlots
定義的插槽如果未被使用則會報錯
原因: 以footer插槽為例,其實不用想的太復雜,作用域插槽this.$scopedSlots.footer()
就是一個函數,MySlot
組件使用了這個函數,但是父組件卻沒有傳入此函數的定義,所以MySlot
再調用這個函數的時候發生報錯。
解決辦法: 在MySlot
調用this.$scopedSlots.footer()
前進行一次判斷此函數是否存在即可。
JSX語法
JSX是createElement
的語法糖,在用法上沒有什么區別,對照著上面的內容稍微改一改就好了
靜態插槽
const MySlot = {render (h) {return (<div><header>{this.$slots.header}</header><main>{this.$slots.default}</main><footer>{this.$slots.footer}</footer></div>)}
}export default {render (h) {return (<MySlot><template slot='header'>hello world</template>children node<div slot='footer'>this is footer</div></MySlot>)}
}
作用域插槽
const MySlot = {data () {return { user: 'John', content: 'vue', copytight: 'CopyRight' }},render (h) {return (<div><header>{this.$scopedSlots.header({ user: this.user })}</header><main>{this.$scopedSlots.default({ content: this.content })}</main><footer>{this.$scopedSlots.footer({ copytight: this.copytight })}</footer></div>)}
}export default {render (h) {return (<MySlotscopedSlots={{header: props => `hello, ${props.user}`}}>children node<div slot='footer'>this is footer</div></MySlot>)}
}