vue插槽解決的問題:引入的子組件標(biāo)簽中間不允許寫內(nèi)容的。插槽(Slot)是vue為組件的封裝者提供的能力;允許開發(fā)者在封裝組件時(shí),把不確定的、希望由用戶指定的部分定義為插槽;可以把插槽認(rèn)為是組件封裝期間,為用戶預(yù)留的內(nèi)容的占位符。
本教程操作環(huán)境:windows7系統(tǒng)、vue3版,DELL G3電腦。
什么是插槽?
我們知道,在vue中,引入的子組件標(biāo)簽中間是不允許寫內(nèi)容的。為了解決這個(gè)問題,官方引入了插槽(slot)的概念。
插槽,其實(shí)就相當(dāng)于占位符。它在組件中給你的HTML模板占了一個(gè)位置,讓你來傳入一些東西。插槽又分為匿名插槽、具名插槽以及作用域插槽。
你可能不太明白,為什么我要給子組件中傳入HTML,而不直接寫在子組件中呢?答案是這樣的。你可以想象一個(gè)場景,你有五個(gè)頁面,這五個(gè)頁面中只有一個(gè)區(qū)域的內(nèi)容不一樣,你會怎么去寫這五個(gè)頁面呢?復(fù)制粘貼是一種辦法,但在vue中,插槽(slot)是更好的做法。
匿名插槽
匿名插槽,我們又可以叫它單個(gè)插槽或者默認(rèn)插槽。與具名插槽相對,它不需要設(shè)置name屬性。(它隱藏的name屬性為default。)
例子:
文件目錄如下,Home組件是HelloWorld的父組件。
- 在HelloWorld中寫一個(gè)匿名插槽
<template> <div class="hello"> Helloworld組件 <div class = 'slotTxt'> <slot></slot> </div> </div> </template> <script> export default { } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped lang="less"> .hello{ width:100%; height:300px; background:#ccc; margin-top:50px; .slotTxt{ width:500px; height:200px; margin:30px auto; background:red; } } </style>
- 在Home組件中引入子組件,并在子組件標(biāo)簽中寫入內(nèi)容
<template> <div class="home"> 我是Home父組件 <HelloWorld> <!-- 沒有插槽,這里的內(nèi)容不顯示 --> <h1>我是helloworld中的插槽啊</h1> </HelloWorld> </div> </template> <script> import HelloWorld from '@/components/HelloWorld.vue' export default { name: 'home', components: { HelloWorld } } </script>
效果
不難看出,HelloWorld標(biāo)簽中的內(nèi)容(紅色部分)已經(jīng)顯示出來了。
具名插槽
上面已經(jīng)說過,插槽有一個(gè)name屬性。與匿名插槽相對,加了name屬性的匿名插槽就是具名插槽。
- HelloWorld組件中寫入name屬性分別為left和right的插槽
<template> <div class="hello"> Helloworld組件 <div class = 'slotLeft'> <slot name='left'></slot> </div> <div class = 'slotRight'> <slot name='right'></slot> </div> </div> </template> <script> export default { } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped lang="less"> .hello{ width:700px; height:300px; background:#ccc; margin: 0 auto; margin-top:50px; .slotLeft{ width:300px; height:200px; float:left; background:red; } .slotRight{ width:300px; height:200px; float:right; background:pink; } } </style>
- Home組件通過在template上寫v-slot:name來使用具名插槽
<template> <div class="home"> 我是Home父組件 <HelloWorld> <template v-slot:left> <h1>name屬性為left</h1> </template> <template v-slot:right> <h1>name屬性為right</h1> </template> </HelloWorld> </div> </template> <script> import HelloWorld from '@/components/HelloWorld.vue' export default { name: 'home', components: { HelloWorld } } </script> <style lang="less" scoped> .home{ width:900px; margin:0 auto; background:yellow; padding-bottom:100px; } </style>
注意 v-slot 只能添加在template標(biāo)簽上 (只有一種例外情況)。
-
效果
- 例外情況(被廢棄的slot=‘name’)
帶slot屬性的具名插槽自 2.6.0 起被廢棄,vue3.x被完全廢棄。只有vue3之前的cli可以使用。
<template> <div class="home"> 我是Home父組件 <HelloWorld> <h1 slot='left'>name屬性為left</h1> <h1 slot='right'>name屬性為right</h1> </HelloWorld> </div> </template> <script> import HelloWorld from '@/components/HelloWorld.vue' export default { name: 'home', components: { HelloWorld } } </script> <style lang="less" scoped> .home{ width:900px; margin:0 auto; background:yellow; padding-bottom:100px; } </style>
效果同上。
- 具名插槽的小知識點(diǎn)
跟 v-on 和 v-bind 一樣,v-slot 也有縮寫,即把參數(shù)之前的所有內(nèi)容 (v-slot:) 替換為字符 #。例如 v-slot:header 可以被重寫為 #header。
作用域插槽
作用域插槽其實(shí)就是可以傳遞數(shù)據(jù)的插槽。子組件中的一些數(shù)據(jù)想在父組件中使用,必須通過規(guī)定的方法來傳遞。在官方文檔中提出了一條規(guī)則,**父級模板里的所有內(nèi)容都是在父級作用域中編譯的。子模板里的所有內(nèi)容都是在子作用域中編譯的。**如果你在父組件直接使用子組件中的值,是會報(bào)錯(cuò)的。
匿名插槽的作用域插槽
為了讓 子組件中的數(shù)據(jù) 在父級的插槽內(nèi)容中可用,我們可以將 數(shù)據(jù) 作為 元素的一個(gè)特性綁定上去:
語法:v-bind:users="user"
- 子組件HelloWorld代碼
<template> <div class="hello"> Helloworld組件 <div class='slotLeft'> <slot v-bind:users="user"></slot> </div> </div> </template> <script> export default { data(){ return{ user:{ name:'oralinge', age:18 } } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped lang="less"> .hello{ width:700px; height:300px; background:#ccc; margin: 0 auto; margin-top:50px; .slotLeft{ width:300px; height:200px; // float:left; background:red; margin:20px auto } .slotRight{ width:300px; height:200px; float:right; background:pink; } } </style>
綁定在 元素上的特性(v-bind:users=“user”)被稱為插槽 prop?,F(xiàn)在在父級作用域中,我們可以使用帶值的 v-slot 來定義我們提供的插槽 prop 的名字。
語法:v-slot:default="隨意取的名字" // default可省略,簡寫為v-slot="隨意取的名字"
- 父組件Home代碼
<template> <div class="home"> 我是Home父組件 <HelloWorld> <template v-slot:default="slotProps"> <h1>{{slotProps.users.name}}</h1> </template> </HelloWorld> </div> </template> <script> import HelloWorld from '@/components/HelloWorld.vue' export default { name: 'home', components: { HelloWorld } } </script> <style lang="less" scoped> .home{ width:900px; margin:0 auto; background:yellow; padding-bottom:100px; } </style>
注意:
父組件中的slotProps可以是隨意取的。
子組件中users是隨意取的,與之對應(yīng)的是父組件中的users。
子組件中的user為數(shù)據(jù)。
效果
具名插槽的作用域插槽
與匿名插槽同理,只需要把default替換成插槽的name值即可。
- 子組件HelloWorld代碼
<template> <div class="hello"> Helloworld組件 <div class='slotLeft'> <slot name='helloWorld' v-bind:users="user"></slot> </div> </div> </template> <script> export default { data(){ return{ user:{ name:'hello world', age:18 } } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped lang="less"> .hello{ width:700px; height:300px; background:#ccc; margin: 0 auto; margin-top:50px; .slotLeft{ width:300px; height:200px; // float:left; background:red; margin:20px auto } .slotRight{ width:300px; height:200px; float:right; background:pink; } } </style>
- 父組件Home代碼
<template> <div class="home"> 我是Home父組件 <HelloWorld> <template v-slot:helloWorld="slotProps"> <h1>{{slotProps.users.name}}</h1> </template> </HelloWorld> </div> </template> <script> import HelloWorld from '@/components/HelloWorld.vue' export default { name: 'home', components: { HelloWorld } } </script> <style lang="less" scoped> .home{ width:900px; margin:0 auto; background:yellow; padding-bottom:100px; } </style>
效果
注意:
默認(rèn)插槽的縮寫語法不能和具名插槽混用,因?yàn)樗鼤?dǎo)致作用域不明確。
另,slot-scope寫法在2.6之后已廢棄,作用與上面相同,在此不做解釋。
上面的寫法是不是覺得有些麻煩?別著急,我們來看一看解構(gòu)插槽 Prop。
解構(gòu)插槽 Prop
作用域插槽的內(nèi)部工作原理是將你的插槽內(nèi)容包括在一個(gè)傳入單個(gè)參數(shù)的函數(shù)里:
function (slotProps) { // 插槽內(nèi)容 }
這意味著 v-slot 的值實(shí)際上可以是任何能夠作為函數(shù)定義中的參數(shù)的 JavaScript 表達(dá)式。所以在支持的環(huán)境下 (單文件組件或現(xiàn)代瀏覽器),你也可以使用 ES2015 解構(gòu)來傳入具體的插槽 prop。
語法:v-slot="{ users }"
- HelloWold組件
<template> <div class="hello"> Helloworld組件 <div class='slotLeft'> <slot v-bind:users="user"></slot> </div> </div> </template> <script> export default { data(){ return{ user:{ name:'hello world', age:18 } } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped lang="less"> .hello{ width:700px; height:300px; background:#ccc; margin: 0 auto; margin-top:50px; .slotLeft{ width:300px; height:200px; // float:left; background:red; margin:20px auto } .slotRight{ width:300px; height:200px; float:right; background:pink; } } </style>
- Home組件
<template> <div class="home"> 我是Home父組件 <HelloWorld> <template v-slot="{ users }"> <h1>{{users.name}}</h1> </template> </HelloWorld> </div> </template> <script> import HelloWorld from '@/components/HelloWorld.vue' export default { name: 'home', components: { HelloWorld } } </script> <style lang="less" scoped> .home{ width:900px; margin:0 auto; background:yellow; padding-bottom:100px; } </style>
-
效果
- 重命名—-更改users這個(gè)名字
<template> <div class="home"> 我是Home父組件 <HelloWorld> <template v-slot="{ users:person }"> <h1>{{person.name}}</h1> </template> </HelloWorld> </div> </template> <script> import HelloWorld from '@/components/HelloWorld.vue' export default { name: 'home', components: { HelloWorld } } </script> <style lang="less" scoped> .home{ width:900px; margin:0 auto; background:yellow; padding-bottom:100px; } </style>
效果如上圖。
- 定義后備內(nèi)容,用于插槽 prop 是 undefined 的情形
此處按照官方文檔的寫法會出現(xiàn)語法報(bào)錯(cuò),后期應(yīng)該會修復(fù)(有知道的麻煩通知一聲)。
<template> <div class="home"> 我是Home父組件 <HelloWorld> <template > <h1 v-slot="{ users = { name: '1111' } }">{{users.name}}</h1> </template> </HelloWorld> </div> </template> <script> import HelloWorld from '@/components/HelloWorld.vue' export default { name: 'home', components: { HelloWorld } } </script> <style lang="less" scoped> .home{ width:900px; margin:0 auto; background:yellow; padding-bottom:100px; } </style>
使用場景
- 復(fù)用公共組件
代碼示例如下:
<template> <div> <div class="title-box"> <span class="title">{{title}}</span> <div class="right"> <slot name="right"></slot> </div> </div> <div class="content-box"> <slot></slot> </div> </div> </template> <script> export default { data () { return { } }, props: { title: { type: String, required: true } } } </script> <style lang="scss" scoped> .title-box { padding: 16px 0; border-bottom: 1px solid #eff1f5; .title { font-family: MicrosoftYaHei; font-size: 24px; color: #283039; letter-spacing: 0; line-height: 24px; &::before { width: 4px; margin-right: 20px; content: ""; background-color: #5da1ff; display: inline-block; height: 20px; vertical-align: middle; } } .right { float: right; margin-right: 20px; } } </style>
使用的ui框架為ivew。