【Vue學習(二)元件和插槽】
元件
元件是 Vue 強大的功能之一
Vue元件具有封裝可複用的特點,能夠讓你在複雜的應用中拆分成獨立模組使用
在開發中,我們可以將重複使用的功能封裝為元件,方便開發提高效率
因為元件是可複用的 Vue 例項,所以它們與 new Vue
接收相同的選項
例如 data
、computed
、watch
、methods
以及生命週期鉤子等。僅有的例外是像 el
這樣根例項特有的選項。
為什麼需要元件
- 提高複用性
- 解耦
- 提升開發效率
建立元件
Vue.js 使用component();
函式來建立元件,該函式中可以傳入兩個引數 分別為元件名、以物件的形式定義(描述)元件。
我們來看一下如何定義一個簡單的元件
<body>
<div id="app">
<!--
定義一個 sc標籤,該標籤沒有任何功能
甚至可以說該標籤在 html中不合法,
我們可以 以定義元件的方式使其擁有功能且合法
-->
<sc></sc>
</div>
<!--引入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
//定義元件: 第一個引數為元件名,第二個引數為定義元件
Vue.component("sc",{
data: function () {
return {
x: 0
}
},
//template屬性的值就是元件的模板,這裡我們定義了一個點選會 +1的小按鈕
template: '<div><button v-on:click="x++">點我+1s</button><span>--->{{x}}s</span></div>'
});
//元件是可複用的 Vue 例項,且帶有一個名字,在這個例子中是 <sc>。
// 我們可以在一個通過 new Vue 建立的 Vue 根例項中,把這個元件作為自定義元素來使用
var vm = new Vue({
el: "#app"
});
</script>
</body>
這裡就不測試了,感興趣可以自己拷過去玩一下。
元件的複用
元件是可複用的,且每個元件中資料是封裝在元件內部的,相同元件之間資料互不影響。
我們可以將上述元件多複製幾個來測試一下
<div id="app">
<!--將 sc元件複製四份在網頁中觀察元件中的資料是否獨立-->
<sc></sc>
<sc></sc>
<sc></sc>
<sc></sc>
</div>
沒有問題,x的值是獨立的
當我們定義這個 <sc>
元件時,你可能會發現它的 data
並不是像這樣直接提供一個物件:
data: {
x: 0
}
取而代之的是,一個元件的 data
選項必須是一個函式,因此每個例項可以維護一份被返回物件的獨立的拷貝:
data: function () {
return {
count: 0
}
}
如果 Vue 沒有這條規則,那我們就沒辦法做到元件之間的資料相互獨立。
元件的組織
通常一個應用會以一棵巢狀的元件樹的形式來組織:
例如,你可能會有頁頭、側邊欄、內容區等元件,每個元件又包含了其它的像導航連結、博文之類的元件。
為了能在模板中使用,這些元件必須先註冊以便 Vue 能夠識別。這裡有兩種元件的註冊型別:全域性註冊和區域性註冊。
以 Vue.component();函式定義的元件為全域性註冊的元件
全域性註冊的元件可以用在其被註冊之後的任何 (通過 new Vue
) 新建立的 Vue 根例項,也包括其元件樹中的所有子元件的模板中。
Vue.component('component-a', { /* ... */ })
Vue.component('component-b', { /* ... */ })
Vue.component('component-c', { /* ... */ })
new Vue({ el: '#app' })
<div id="app">
<component-a></component-a>
<component-b></component-b>
<component-c></component-c>
</div>
在所有子元件中也是如此,也就是說這三個元件在各自內部也都可以相互使用
區域性註冊
全域性註冊往往是不夠理想的。比如,如果你使用一個像 webpack 這樣的構建系統,全域性註冊所有的元件意味著即便你已經不再使用一個元件了,它仍然會被包含在你最終的構建結果中。這造成了使用者下載的 JavaScript 的無謂的增加。
在這些情況下,你可以通過一個普通的 JavaScript物件來定義元件,並使用 Vue例項中的 components屬性來新增一個元件作為該例項中的區域性元件
<body>
<div id="app">
<sc></sc>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
// 使用普通 JavaScript物件的形式定義一個元件例項
var sc = {
data(){
return{
x: 0
}
},
template:'<div><button v-on:click="x++">點我+1s</button><span>--->{{x}}s</span></div>'
}
var vm = new Vue({
el: "#app",
// 使用 components屬性區域性註冊到該 Vue例項中
components: {
// key為 元件名,value為元件例項
'sc':sc
}
});
</script>
</body>
注意:區域性註冊的元件在其子元件中不可用。
例如,如果你希望 ComponentA
在 ComponentB
中可用,則你需要這樣寫:
var ComponentA = { /* ... */ }
var ComponentB = {
components: {
'component-a': ComponentA
},
// ...
}
Prop
每個Vue元件例項都有獨立範圍的作用域的,這意味著不能並且不應該在子元件的模板內直接引用父元件的資料。
但 Vue.js可以通過使用 props引數實現父元件向子元件傳遞資料這一操作。
示例:
為了便於理解,你可以將這個Vue例項看作<lc>
的父元件。
如果我們想使父元件的資料,則必須先在子元件中定義props屬性,否則無法拿到父元件中的資料。
<body>
<div id="app">
<!--但實際上並拿不到-->
<lc v-for="item in la"></lc>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('lc',{
// 元件定義沒有任何問題,按理應該可以拿到陣列中遍歷的每一項
template: '<li>{{item}}</li>'
});
new Vue({
el: "#app",
data: {
la: ["js","java","c#","go"]
}
});
</script>
</body>
那麼父元件如何動態的傳遞資料給子元件呢?
還記得v-bind指令的作用嗎,其作用是用於動態繫結 html屬性或者是元件的 props值
,所以應該使用v-bind指令來動態傳遞資料。
<body>
<div id="app">
<!--新增 v-bind:將遍歷出來的每一項繫結到 test中-->
<lc v-for="item in la" v-bind:test="item"></lc>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('lc',{
// 使用 props屬性拿到上面 v-bind指令中繫結的test
props:['test'],
// 在模板中取出props拿到的 test
template: '<li>{{test}}</li>'
});
new Vue({
el: "#app",
data: {
la: ["js","java","c#","go"]
}
});
</script>
</body>
關於Prop如果存在疑問請移步 Vue官網
template標籤
在上文中我們使用 component();函式建立元件,該方式屬於一種註冊語法糖。
儘管語法糖簡化了元件註冊,但在 template屬性中拼接 HTML元素是比較麻煩的。
這也導致了 HTML和 JavaScript的高耦合性,慶幸的是,Vue.js提供了兩種方式將定義在 JavaScript中的 HTML模板分離出來。
一種是使用 script標籤,但這種方式仍不是最優解 就不提了。
我們直接快進到 <template></template>
<body>
<div id="app">
<!-- 3、使用該元件-->
<mc></mc>
</div>
<!-- 1、使用 template標籤,在標籤體中定義元件的內容-->
<template id="myComponent">
<div>
<strong>在這裡定義元件模板</strong>
<p>的話就不需要</p>
<span>做一些拼接 Html標籤的操作了</span>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
// 2、註冊元件
Vue.component("mc",{
// 選中 template標籤中的 id
template: "#myComponent"
});
new Vue({
el: "#app"
});
</script>
</body>
如上
我建議使用
<script>
或<template>
標籤來定義元件的 HTML模板。
這使得 HTML程式碼和 JavaScript程式碼是分離的,便於閱讀和維護。
另外,在Vue.js中,可建立.vue字尾的檔案,在.vue檔案中定義元件,這個內容我會在後面的文章介紹。
插槽
為了讓元件可以組合,我們需要一種方式來混合父元件的內容與子元件自己的模板。
這個處理稱為內容分發,Vue.js實現了一個內容分發 API,使用特殊的 <slot>
元素作為原始內容的插槽。
插槽簡單示例
如下,定義了一個父元件和一個子元件。
在父元件中使用了子元件,並嘗試為子元件分發文字內容。
<body>
<div id="app">
<!--使用父元件-->
<parent></parent>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
// 註冊父元件
Vue.component("parent",{
// 在父元件的模板中使用子元件
template: '<div><p>我是父元件</p><child><p>使用子元件,看看能不能在渲染時顯示這行字</p></child></div>'
});
// 註冊子元件
Vue.component("child",{
template: '<div><p>我是子元件</p></div>'
});
new Vue({
el: "#app"
});
</script>
</body>
測試:
結果是父元件分發的文字內容 並沒有顯示出來。
怎麼讓父元件分發的內容顯示出來呢?
很簡單,在子元件中為父元件留一個插槽即可 也就是使用<solt></solt>
標籤
使用<solt></solt>
標籤後,如果父元件在使用子元件時 為子元件分發了內容,則顯示父元件分發的內容。
反之未分發內容 則會顯示子元件<solt></solt>
標籤中的內容
// 註冊子元件
Vue.component("child",{
// 在子元件模板中留一個插槽
template: '<div><slot>我是子元件中的 slot</slot></div>'
});
使用該標籤後,再進行測試
再將父元件中分發的內容注掉
// 註冊父元件
Vue.component("parent",{
// 在父元件的模板中使用子元件,註釋掉父標籤分發的內容
template: '<div><p>我是父元件</p><child><!--<p>使用子元件,看看能不能在渲染時顯示這行字</p>--></child></div>'
});
官方文件對於插槽的應用場景是這樣描述的:
我們經常需要向一個元件傳遞內容
Vue 自定義的
<slot>
元素讓這變得非常簡單只要在需要的地方加入插槽就行了——就這麼簡單!
結合上面的例子來理解就是這樣的:
1.父元件在引用子元件時希望向子元件傳遞模板內容
<p>使用子元件,看看能不能在渲染時顯示這行字</p>
2.子元件讓父元件傳過來的模板內容在所在的位置顯示
3.子元件中的
<slot>
就是一個槽,可以接收父元件傳過來的模板內容,<slot>
元素自身將被替換4.
<child></child>
元件沒有包含一個<slot>
元素,則該元件起始標籤和結束標籤之間的任何內容都會被拋棄
插槽也有分類,像上面使用的這種就是預設插槽,它是一個匿名slot,它只能表示一個插槽。
除了預設插槽還有具名插槽以及作用域插槽。
具名插槽
顧名思義,具名插槽 就是存在名字的插槽,也就是擁有 name屬性的插槽。
為插槽定義 name自然是為了能夠更準確的將資料插入到指定的插槽中。
我們來看一下示例:
下列程式碼和預設插槽示的例程式碼沒有太大區別,無非是多了個插槽 且都加上了 name屬性。
可以預見的 網頁中會渲染出子元件中預設的標題和文字內容。
<body>
<div id="app">
<!-- 3、使用父元件,不進行任何操作-->
<page></page>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('page',{
// 2、在父元件的模板中呼叫子元件,不進行任何額外操作。
template: "<div><ct></ct></div>"
});
// 1、註冊子元件
Vue.component('ct',{
// 1.1、在子元件的模板中定義兩個插槽,併為這兩個個插槽分別 新增 name屬性
template: "<div><h1><slot name='pageTitle'>預設標題</slot></h1><p><slot name='pageText'>預設文字內容</slot></p></div>"
});
new Vue({
el: "#app",
});
</script>
</body>
網頁中的顯示:
我們在父元件的模板中做一些操作
Vue.component('page',{
// 對模板進行一些改動,在子元件中新增 span標籤,為該 span標籤新增 slot屬性,值為'pageText'
template: "<div><ct><span slot='pageText'>新文字內容</span></ct></div>"
});
在模板中 往呼叫的子元件裡 寫一個 span標籤,併為其新增 slot屬性。
該屬性的值為:子元件中定義插槽 name屬性的值
<slot name='pageText'>預設文字內容</slot>
我們再來看看網頁會如何渲染
子元件中 name為 ‘pageText’ 插槽的文字內容,被替換為了 span標籤中的文字內容。
那我們再將該 span標籤的 slot屬性值改為 ‘pageTitle’,是不是就意味著能夠替換掉 name為 ‘pageTitle’ 插槽的文字內容呢?
Vue.component('page',{
// 對模板進行一些改動,在子元件中新增 span標籤,為該 p標籤新增 slot屬性,值為'pageTitle'
template: "<div><ct><span slot='pageTitle'>新文字內容</span></ct></div>"
});
確實是如此
也就是說為插槽設定 name後,父元件想要使用哪個插槽 指定該插槽的 name即可。
當然了,該用法早已廢棄。❌(雖然被廢棄,但仍可繼續使用)
新用法
我們再看看2.6.0之後的具名插槽如何使用
在2.6.0後,v-slot
指令替代了 slot屬性。
需要注意的是,v-slot
指令只能使用在 template標籤中,這一點和已經廢棄的 slot屬性有所不同。
我們在一個 <template>
元素上使用 v-slot
指令,並傳入引數的形式提供其名稱:
<body>
<div id="app">
<!--3、使用父元件-->
<page>
</page>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
// 1、註冊子元件
Vue.component("ct",{
// 1.1、在模板中使用 slot標籤並新增對應的 name屬性
template: `<div>
<slot name="pageTitle">預設標籤</slot>
<slot>預設插槽的內容</slot>
<slot name="pageText">預設內容</slot>
/div>`
});
// 2、註冊父元件
Vue.component("page",{
template: `<div>
// 2.1、在父元件模板中呼叫子元件標籤
<ct>
// 2.2、在子元件標籤中書寫 template標籤,並使用 v-slot指令,指令的引數為插槽 name的值
<template v-slot:pageTitle>
<h1>
<span>新標籤</span>
</h1></template>
// 2.3、該 template標籤不使用 v-slot指令
<template>
<p>我會替換掉預設插槽的內容</p>
</template>
<template v-slot:pageText>
</template>
</ct>
</div>`
});
</script>
</body>
<template>
元素中的所有內容都將會被傳入相應的插槽。
任何沒有被包裹在帶有 v-slot
的 <template>
中的內容都會被視為預設插槽的內容。
測試:
一個未新增 name
屬性 的 <slot>
標籤 ,會預設攜帶隱藏的 name屬性值 “default” 。
也就是說我們可以在 template標籤中使用 v-slot
指令,傳入 ‘default’ 引數使其包裹的內容改變為預設插槽的內容。
template: `<div>
// 2.1、在父元件模板中呼叫子元件標籤
<ct>
// 2.2、在子元件標籤中使用 template標籤,並使用 v-slot指令,指令的引數為插槽 name的值
<template v-slot:pageTitle>
<h1>
<span>新標籤</span>
</h1></template>
// 2.3、該 template標籤不使用 v-slot指令
<template>
<p>我會替換掉預設插槽的內容</p>
</template>
/*
2.4、為該template標籤的 v-slot指令傳入引數 default,
使其覆蓋掉第二個 template標籤中預設插槽的內容。
*/
<template v-slot:default>
<p>嘗試再次替換預設插槽的內容</p>
</template>
</ct>
</div>`
測試:
作用域插槽
在上述兩種插槽中,都是子元件接收父元件傳遞的資料。
而作用域插槽則是用來,向父元件中傳入子元件的資料
可能有些人就要問了,我們直接在父元件呼叫子元件時不分發內容,使用子元件插槽中的內容不就實現了該操作嗎?
匿名插槽和具名插槽中,插槽上不繫結資料,所以父元件提供的模板既要包括樣式又要包括資料。
而作用域插槽是子元件提供資料,父元件只需要提供一套樣式即可。
使用作用域插槽比較簡單,無非兩步:
- 將子元件資料繫結給
slot
上的屬性 - 父元件模板中通過
slot-scope
拿到slot
物件,並進行屬性訪問。
以下為示例:
<body>
<div id="app">
<!-- 2、為子元件中拿到的資料新增不同的樣式-->
<ct>
<!--
2.1、呼叫子元件標籤後書寫 template標籤,並使用 slot-scope拿到子元件插槽中的資料,
將該資料命名為 sc(這個可以隨意),我們可以通過該名稱訪問到子元件中的資料
-->
<template slot-scope="sc">
<!--ul li的形式輸出-->
<ul>
<li v-for="item in sc.test">{{item}}</li>
</ul>
</template>
</ct>
<ct>
<template slot-scope="sc">
<!--以加粗的形式輸出,每個值之間使用 "--》"字串分隔 -->
<strong>{{sc.test.join('-->' )}}</strong>
</template>
</ct>
<ct>
<template slot-scope="sc">
<!--正常輸出,每個值之間使用 " / "分隔-->
<p>{{sc.test.join(' / ')}}</p>
</template>
</ct>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
// 1、註冊子元件
Vue.component("ct",{
// 1.1、在模板中 通過:test="name" ,將子元件的 name繫結到 slot的 test屬性上
template: '<div><slot :test="names"></slot></div>',
data(){
return{
names: ["molu","qiu","lin","zhang"]
}
}
});
new Vue({
el: "#app"
});
</script>
</body>
程式碼塊比較長,但都是一些很基礎的東西,對著註解順序應該很容易看懂
測試:
同樣的,作用域插槽在 2.6.0之後的版本也有新的寫法,上面這種寫法也已被廢棄。
新用法
新寫法沒什麼講究也沒什麼需要注意的,將 slot-scope
修改為 v-slot
指令即可,如下:
<ct>
<!--將 slot-scope 修改為 v-slot-->
<template v-slot:default="sc">
<ul>
<li v-for="item in sc.test">{{item}}</li>
</ul>
</template>
</ct>
關於插槽更多的可以移步Vue.js官網
作者本人實際上是寫後端的(從程式碼風格應該也能看出端倪),對這些東西研究尚淺 就不發表什麼高論了。
也查閱了很多,發現作用域插槽好像能做不少文章 不是我一個業餘前端能說明白的,感興趣可以自行搜尋其他文章。
寫的比較匆忙也比較亂,應該有不少錯誤的地方吧。
醒過來再檢查吧…現在的時間是凌晨三點半,寫到昏厥
放鬆一下眼睛
原圖P站地址
畫師主頁
相關文章
- Vue元件、元件傳值和元件插槽Vue元件
- Vue 插槽之插槽內容學習總結Vue
- vue基礎-元件&插槽Vue元件
- Vue元件深入理解--插槽Vue元件
- vue 元件中solt 插槽使用Vue元件
- Vue 元件化開發之插槽Vue元件化
- 8.Vue元件三---slot插槽Vue元件
- 158-vue04-元件&元件間通訊&插槽Vue元件
- 總結Vue第二天:自定義子元件、父子元件通訊、插槽Vue元件
- Vue插槽與作用域插槽Vue
- vue學習筆記(六) ----- vue元件Vue筆記元件
- Flutter元件學習(二)—— ImageFlutter元件
- VUE 插槽 slotVue
- vue。js插槽VueJS
- vue插槽slotVue
- Vue學習(四)元件(參考)Vue元件
- Vue學習筆記(二)------axios學習Vue筆記iOS
- Vue 學習記錄二Vue
- vue學習筆記二Vue筆記
- Vue全家桶學習(二)Vue
- Vue 匿名、具名和作用域插槽的使用Vue
- Vue中使用插槽Vue
- Vue原始碼學習(十八):實現元件註冊(一)Vue.component()和Vue.extend()Vue原始碼元件
- Vue學習(二)自定義指令Vue
- 「Vue原始碼學習」你真的知道插槽Slot是怎麼“插”的嗎Vue原始碼
- Vue 開發之插槽(slot)的理解和使用Vue
- 關於Vue中插槽的理解和總結Vue
- Angular學習(二):元件-生命週期Angular元件
- 06 Vue3插槽Vue
- Vue 作用域插槽slotVue
- 會vue,學習react元件父子通訊VueReact元件
- 【Vue】計算屬性 監聽屬性 元件通訊 動態元件 插槽 vue-cli腳手架Vue元件
- vue系列元件篇(二)Vue元件
- flutter學習日記(二)————flutter的佈局和頁面元件Flutter元件
- Vue原始碼學習(二)——從巨集觀看VueVue原始碼
- Vue原始碼學習二 ———— Vue原型物件包裝Vue原始碼原型物件
- FlatList元件學習和封裝元件封裝
- Vue.js 學習筆記之四:Vue 元件基礎Vue.js筆記元件