最近在 youtube 找到一個學習 webpack 很好用的教學影片,所以正努力的學習 webpack 中,一個不小心就太沉迷在新技術的練習了,導致有點忽略 Vue.js 的筆記,再次跟自己喊話,一定要做完 Vue.js 的系列(之後還有 Vuex 跟 Vuetify 呢!)。
另外說明一下,我筆記的順序是我自己認為這樣的順序比較好理解,所以會跟文件中的說明順序有差異。
在設定模組時,會透過 template
來決定模組在頁面上要顯示的內容,而我們可以在 template
中加入 <slot>
標籤來設定模組標籤內容的位置,並且在網頁顯示的時候,模組標籤的內容會取代該 <slot>
標籤。
模組標籤裡的內容可以是純文本、 HTML 標籤或其他模組等等。
<basic-slot>
Next
<i class="fas fa-angle-right"></i>
</basic-slot>
<script>
Vue.component("basic-slot", {
// <slot></slot>指定外層模組標籤內容放置的位置
// 可放入純文本、html標籤、其他模組
template: `<div>
<div>
<slot></slot>
</div>
</div>`
});
</script>
直接在 <slot>
標籤內寫定預設內容,如果模組標籤內沒有任何內容,那麼該 component 的畫面顯示就是該預設內容。
<!-- 模組標籤沒有任何內容 -->
<basic-slot></basic-slot>
<script>
Vue.component("basic-slot", {
// 在 <slot> 標籤中設定模組畫面內容的預設值
template: `<div>
<div>
<slot>
Preview
</slot>
</div>
</div>`
});
</script>
如果要將不同內容的插槽資料分類,就可以透過 <slot name="slotName">
及 v-slot:slotName
的來命名插槽及決定不同內容所放置的位置。
如果 <slot
標籤未命名的話,預設的名字是 default
,所以在模組標籤可以透過 v-slot:default
或是不寫 v-slot:default
來找到那個未命名的 <slot
標籤。
須注意 v-slot
只能添加在 <template>
上。除非是只有默認插槽的狀況才可寫在模組標籤上
<name-slot>
<!-- 為了最後顯示時不要有太多標籤,所以使用template來避免出現太多元素-->
<!-- 對應模組內名為 footer 的插槽位置-->
<template v-slot:footer>
<h4>Here is footer</h4>
</template>
<!-- 對應模組內名為 header 的插槽位置-->
<template v-slot:header>
<h4>Here is header</h4>
</template>
<!-- 對應模組內名為 container 的插槽位置-->
<template v-slot:container>
<h4>Here is container</h4>
</template>
<!-- 對應模組內未命名或名為 default 的插槽位置-->
<template>
<h4>Here is default</h4>
</template>
<!-- <template>
<h4>Here is default</h4>
</template> 跟下面意思相同-->
<template v-slot:default>
<h4>Here is default</h4>
</template>
</name-slot>
<script>
Vue.component("name-slot", {
// 在 <slot> 標籤中利用 attribute 來命名插槽,並決定不同名字的插槽內容在畫面顯示後的位置
template: `<div>
<div>
<slot name="header"></slot>
<slot name="container"></slot>
<slot name="footer"></slot>
<slot></slot>
</div>
</div>`
});
</script>
只有下面這種情況才可以將 v-slot:default
寫在模組標籤上,另外要注意也不能將 default 改成其他名字喔!
<name-slot v-slot:default>
<h4>Here is default</h4>
</name-slot>
<script>
Vue.component("default-slot", {
template: `<div>
<slot></slot>
</div>`
});
</script>
在模組標籤中的內容只能取得該層的資料,而在模組內的 <slot>
標籤也只能取得模組內的資料,簡單來說就是在哪設定要取資料就是只能取得那一層的資料。
<scoped-slot name="Michelle">
外層的 data 資料:{{ user.name }},
模組內的 props 資料: {{ name }}
模組內的 data 資料: {{ user2.name }}
</scoped-slot>
<script>
Vue.component("scoped-slot", {
template: `<div>
<div>
<slot></slot>
</div>
</div>`,
data() {
return {
user2: {
name: "Linda",
age: 9
}
};
}
});
const vm = new Vue({
el: "#vm",
data: {
user: {
name: "Celeste",
age: 18
}
}
});
</script>
以上面為例,會發現只要是想取得模組內的 data
或是 props
都會是失敗的(會出錯),但是取得外層(模組標籤那一層)資料是成功的。
<scoped-slot name="Michelle"></scoped-slot>
<script>
Vue.component("scoped-slot", {
props:[ 'name' ],
template: `<div>
<div>
<slot>
模組內的 props 資料: {{ name }},
模組內的 data 資料: {{ user2.name }},
外層的 data 資料:{{ user.name }}
</slot>
</div>
</div>`,
data() {
return {
user2: {
name: "Linda",
age: 9
}
};
}
});
const vm = new Vue({
el: "#vm",
data: {
user: {
name: "Celeste",
age: 18
}
}
});
</script>
如果是在 <slot>
中設定預設值,會發現在 <slot>
內只能取得該模組內的資料(如 data
或是 props
),如果想要取得外層的 data
資料會出錯。
如上面所說,一般情況下外層無法取得內層的資料,但是可以透過在 <slot name="slotName" v-bind:slotPropName="slotPropValue">
標籤上設定 Attribute 來動態綁定值以將內層的值往外傳到外層去,而外層透過 v-slot:slotName="customName"
或是 ES6語法 v-slot:slotName="{ slotPropName }"
來決定適用哪一個作用域。
使用 v-slot:slotName="customName"
設定 customName
來接收從 <slot>
接收的動態綁定值,像是將所有動態綁定的值放入 customName
這個物件中。
<!-- 用 slotProps 來接收從 slot 傳進來的值 -->
<v-bind-slot v-slot:default="customName">
姓{{ slotProps.user.firstName }},
年齡{{ slotProps.user.age }}
喜歡水果{{ slotProps.fruit[0] }}
</v-bind-slot>
<script>
Vue.component("v-bind-slot", {
template: `<div>
<div>
<slot :user="user" :fruit="fruit">
{{ user.lastName }}
</slot>
</div>
</div>`,
data() {
return {
user: {
lastName: "Linda",
firstName: "KUO",
age: 9
},
fruit: ["apple", "banana", "orange"]
};
}
});
</script>
使用 v-slot:slotName="{ slotPropName }"
透過 ES6 解構語法來將動態綁定值一一傳入。
<!-- 用解構語法來接收從 slot 傳進來的值 -->
<v-bind-slot v-slot:default="{ user, fruit }">
姓{{ slotProps.user.firstName }},
年齡{{ slotProps.user.age }}
喜歡水果{{ slotProps.fruit[0] }}
</v-bind-slot>
<script>
Vue.component("v-bind-slot", {
template: `<div>
<div>
<slot :user="user" :fruit="fruit">
{{ user.lastName }}
</slot>
</div>
</div>`,
data() {
return {
user: {
lastName: "Linda",
firstName: "KUO",
age: 9
},
fruit: ["apple", "banana", "orange"]
};
}
});
</script>
只有在自己那個具名插槽中有綁定的 Attribute 才可以在外層相同名字的 v-slot
作用域中使用。
<v-bind-slot>
<template v-slot:default="{ user, fruit }">
姓{{ user.firstName }},
年齡{{ user.age }}
喜歡水果{{ fruit[1] }}
<br/>
</template>
<template v-slot:cellphone="{ cellphone, fruit }">
<!-- user未在名為 cellphone 的插槽中綁定,所以不可以在這個作用域使用 -->
姓{{ user.firstName }},
手機{{ cellphone[2] }}
喜歡水果{{ fruit[1] }}
</template>
</v-bind-slot>
<script>
Vue.component("v-bind-slot", {
template: `<div>
<div>
<slot name="default" :user="user" :fruit="fruit"></slot>
<slot name="cellphone" :cellphone="cellphone" :fruit="fruit"></slot>
</div>
</div>`,
data() {
return {
user: {
lastName: "Linda",
firstName: "KUO",
age: 9
},
fruit: ["apple", "banana", "orange"],
cellphone: ["iphone", "samsung", "oppo"]
};
}
});
</script>
可以透過 v-slot:[動態插槽名]
來動態可以模組標籤內容的位置。
另須注意動態插槽名需全為小寫,因為 Vue 會自動將該動態插槽名轉為小寫,而導致在外層找不到該筆資料( dynamicslotname
!==dynamicSlotName
)。
<dynamic-slot>
<!--動態插槽名要全為小寫-->
<!--error-->
<template v-slot:[dynamicSlotName]>
I am {{dynamicSlotName}}
</template>
<template v-slot:[lowercaseslotname]>
I am {{lowercaseslotname}}
</template>
</dynamic-slot>
<script>
Vue.component("dynamic-slot", {
template: `<div>
<slot name="header">Here is header</slot>
<br/>
<slot name="container">Here is container</slot>
<br/>
<slot name="footer">Here is footer</slot>
<slot></slot>
</div>`
});
const vm = new Vue({
el: "#vm",
data: {
user: {
name: "Celeste"
},
dynamicSlotName: "header",
lowercaseslotname: "footer"
}
});
</script>
v-slot
縮寫可以將以 #
來縮寫 v-slot
,像是 v-slot:header
可以縮寫成 #header
,v-slot:footer={ user }
可以縮寫成 #footer={ user }
,另外要注意使用縮寫字符時一定要有參數,例如 #footer={ user }
, footer
即為參數,不可以只有 #={ user }
,這樣會出錯,如果是預設值的狀況可以寫成 #default={ user }
。
Demo:DAY14 | 跟 Vue.js 認識的30天 - Vue 模組插槽(slot
)
參考資料: