組件
組件是什麼:組件就是自定義標籤,他是vue中核心功能之一,是把頁面當中的可以重複使用的內容進行封裝,方便調用
全局組件
作用域:在所有實例中都可以使用
語法:Vue.component:{"組件名不能喝現有的html標籤重名",{
template:"<div></div>"
}
基本使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>全局組件基本使用</title>
</head>
<body>
<div id="app">
<!-- 2. 使用組件 -->
<!-- <demo></demo> -->
<!--
4.使用駝峯法調用 必須大寫轉小寫 前面加個-
例 demoElOne 調用 demo-el-one
-->
<demo-test></demo-test>
</div>
</body>
<script src="./node_modules/vue/dist/vue.min.js"></script>
<script>
// 1.創建組件
// Vue.component('demo',{
// template:"<div>我是組件一</div>"
// })
// 3.如果有個多個標籤 必須要用父元素包裹
Vue.component('demo',{
template:"<div><p>組件1</p><p>組件2</p></div>"
})
// 4.組件名使用駝峯法
Vue.component('demoTest',{
template:'<div>我是組件一</div>'
})
const vm=new Vue({
el:'#app',
data:{
}
})
</script>
</html>
外部模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>全局組件外部模板</title>
</head>
<body>
<div id="app">
<!-- 2. 使用組件 -->
<demo></demo>
</div>
</body>
<script src="./node_modules/vue/dist/vue.min.js"></script>
<!-- 3.定義外部模板 -->
<template id="text">
<div>
<p>我是組件1</p>
<p>我是組件2</p>
</div>
</template>
<script>
<!-- 1.創建組件 -->
Vue.component('demo',{
template:"#text"
})
const vm=new Vue({
el:'#app',
data:{
}
})
</script>
</html>
自定義數據和方法
如果組件中需要數據和方法,那麼必須把數據定義在當前組件中
注意:組件中定義數據的方法是用函數的方式,return 一個對象,自定義方法使用對象的方式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>全局組件自定義數據和方法</title>
</head>
<body>
<div id="app">
<!-- 2. 使用組件 -->
<demo></demo>
</div>
</body>
<script src="./node_modules/vue/dist/vue.min.js"></script>
<!-- 定義外部模板 -->
<template id="ext">
<div>
<p>{{text}}</p>
<p @click="fun()">點擊顯示自定義方法</p>
</div>
</template>
<script>
Vue.component('demo',{
template:"#ext",
data(){
return {
text:"我是自定義數據"
}
},
methods:{
fun(){
console.log("我是自定義方法");
}
}
})
const vm=new Vue({
el:'#app',
data:{
}
})
</script>
</html>
局部組件
作用域:定義在指定的vue實例當中 與el data methods等這些同級
components:{
"組件名":{
template:" ",
data(){
return { }
},
methods:{ }
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<demo></demo>
</div>
</body>
<script src="./node_modules/vue/dist/vue.min.js"></script>
<template id="exl">
<div>
<h1>{{text}}</h1>
<h1>{{text1}}</h1>
<h1>333</h1>
</div>
</template>
<script>
const vm = new Vue({
el: '#app',
components: {
"demo": {
template: "#exl",
data() {
return {
text:"111",
text1:"222"
}
}
}
}
})
</script>
</html>
爲什麼組件中的data是一個函數?
在vue中,每個vue的組件都是一個實例,通過new Vue()來進行實例化,引用同一個對象。
如果data直接是一個對象的話,那麼其中一個組件的數據修改,其他位置上的相同組件也會被修改;
如果data對應一個函數的話就解決了這個問題,因爲vue組件的data都是函數,就相當於有了自己的作用域,這樣一來,組件被多次調用修改數據,他們也互不干涉
總結
- 組件中的數據定義的時候需用函數返回值的方式來進行模型數據的定義
- 組件的本質就是自定義標籤
父子組件
父子組件==》組件中再嵌套一個組件 他的語法其實和傳統寫組件的時候是一模一樣的,只是套在一起
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<parent></parent>
</div>
</body>
<script src="./node_modules/vue/dist/vue.min.js"></script>
<!-- 組件模塊 -->
<template id="fuTem">
<div>
<h1>我是父組件===={{text}}</h1>
<!-- 調用子組件必須在父組件模板中調用 -->
<son></son>
</div>
</template>
<template id="sonTem">
<div>
<h1>我是子組件===={{text1}}</h1>
</div>
</template>
<script>
new Vue({
el: '#app',
data: {},
components: {
"parent": {
template: "#fuTem",
data() {
return {
text: '我是父組件的數據'
}
},
// 創建子組件
// 子組件是創建在父組件中的
components: {
"son": {
template: '#sonTem',
data() {
return {
text1: '我是子組件的數據'
}
}
}
}
}
}
})
</script>
</html>
父組件能使用子組件的data數據嗎? 不能
子組件能使用父組件的data數據嗎? 不能
注意:無論是哪種類型的組件 每個組件都相當於是一個完整的vue實例。組件的作用域是完全獨立的(組件與組件之間的數據不互通)。父子組件之間的作用域是完全完全完全獨立的
如何進行父子組件的通信?
通過props向子組件傳遞數據
通過事件向父組件發送數據
props
作用:可以讓組件接受外部傳遞進來的數據
子組件要使用父組件的數據(默認是不行的 使用props就可以進行傳遞)
props的使用:
需要在子組件中定義props,其實就是一個變量(可以接受外部傳遞進來的數據)
基本用法:
1.定義 在子組件中與data template methods 等同級創建一個props屬性
2.使用props變量
3.傳遞數據 在調用當前組件的使用 以屬性的方式傳遞進
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<parent></parent>
</div>
</body>
<script src="./node_modules/vue/dist/vue.js"></script>
<!-- 外部模塊 -->
<template id="fu">
<div>
<h1>京東秒殺</h1>
<div>
<!-- 3.調用數據 調用的是死數據-->
<!-- <son message1="圖片1" message2="價格1" message3="商品描述1"></son>
<son message1="圖片2" message2="價格2" message3="商品描述2"></son>
<son message1="圖片3" message2="價格3" message3="商品描述3"></son>
<son message1="圖片4" message2="價格4" message3="商品描述4"></son> -->
<!-- 3.調用父組件中的單個數據 -->
<!-- <son :message1="img" :message2="price" v-bind:message3="text"></son> -->
<!-- 3.調用父組件中的多個數據 -->
<son v-for="(item,index) in arr" :message1="item.img" :message2="item.price" v-bind:message3="item.text"></son>
</div>
</div>
</template>
<template id="zi">
<div>
<!-- 2.父傳子使用props -->
<p>{{message1}}</p>
<p>{{message2}}</p>
<p>{{message3}}</p>
</div>
</template>
<script>
const vm=new Vue({
el:'#app',
data:{},
components:{
"parent":{
template:"#fu",
data(){
return {
// img:"圖片1",
// price:"價格1",
// text:"商品描述1",
// 多個數據循環
arr:[
{img:"圖片1",price:"價格1",text:"商品描述1"},
{img:"圖片2",price:"價格2",text:"商品描述2"},
{img:"圖片3",price:"價格3",text:"商品描述3"},
{img:"圖片4",price:"價格4",text:"商品描述4"},
]
}
},
components:{
"son":{
template:"#zi",
data(){
return {
}
},
// 1.定義props屬性 message1用來接受外部傳來的圖片的數據,message2用來接受外部傳來的價格,message3用來接受外部傳來的商品描述
props:["message1","message2","message3"]
}
}
}
}
})
</script>
</html>
子級向父級傳遞
使用自定義事件來完成(必須必須必須要通過事件來觸發才能進行傳遞)
拋出自定義事件監聽 語法:this.$emit( “自定義的事件名”,你要傳遞的數據 )
在當前子組件被調用的時候接收自定義事件的監聽
什麼時候需要自定義事件呢?
當子組件需要向父組件傳遞數據時,就要用到自定義事件了。
v-on不僅僅可以用於監聽DOM事件,也可以用於組件間的自定義事件。
自定義事件的流程:
在子組件中,通過$emit()來觸發事件。
在父組件中,通過v-on來監聽子組件事件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<parent></parent>
</div>
</body>
<script src="./node_modules/vue/dist/vue.js"></script>
<!-- 外部模塊 -->
<template id="fu">
<div>
點擊次數:{{total}}
<son @add='changtotal' @minus='changtotal'></son>
</div>
</template>
<template id="zi">
<div>
<button @click='add'>+1</button>
<button @click='minus'>-1</button>
</div>
</template>
<script>
const vm=new Vue({
el:'#app',
data:{},
components:{
"parent":{
template:"#fu",
data(){
return {
total:0
}
},
methods: {
changtotal(count){
this.total=count
}
},
components:{
"son":{
template:"#zi",
data(){
return {
count:0
}
},
methods: {
add(){
this.count++
// console.log(this.count);
this.$emit('add',this.count)
},
minus(){
this.count--
this.$emit('minus',this.count)
}
},
}
}
}
}
})
</script>
</html>
父子組件的訪問方式
父組件訪問子組件:使用$children或$refs
子組件訪問父組件:使用$parent
$children的缺陷:
通過$children訪問子組件時,是一個數組類型,訪問其中的子組件必須通過索引值。但是當子組件過多,我們需要拿到其中一個時,往往不能確定它的索引值,甚至還可能會發生變化。有時候,我們想明確獲取其中一個特定的組件,這個時候就可以使用$refs。所有$children不過多介紹。
父子組件的訪問方式: $refs
$refs和ref指令通常是一起使用的。首先,我們通過ref給某一個子組件綁定一個特定的ID。其次,通過this.$refs.ID就可以訪問到該組件了。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<parent></parent>
</div>
</body>
<script src="./node_modules/vue/dist/vue.js"></script>
<!-- 外部模塊 -->
<template id="fu">
<div>
<son></son>
<son></son>
<son ref='aaa'></son>
<button @click='btnClick()'>按鈕</button>
</div>
</template>
<template id="zi">
<div>
我是子組件
</div>
</template>
<script>
const vm = new Vue({
el: '#app',
data: {},
components: {
"parent": {
template: "#fu",
data() {
return {
}
},
methods: {
btnClick(){
console.log(this.$refs.aaa.text)
}
},
components: {
"son": {
template: '#zi',
data() {
return {
text:'子組件文本'
}
}
}
}
},
}
})
</script>
</html>
子組件中直接訪問父組件,可以通過$parent
注意事項:
儘管在Vue開發中,允許通過$parent來訪問父組件,但是在真實開發中儘量不要這樣做。
子組件應該儘量避免直接訪問父組件的數據,因爲這樣耦合度太高了。
如果將子組件放在另外一個組件之內,很可能該父組件沒有對應的屬性,往往會引起問題。
另外,更不好做的是通過$parent直接修改父組件的狀態,那麼父組件中的狀態將變得飄忽不定,很不利於調試和維護。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<parent></parent>
</div>
</body>
<script src="./node_modules/vue/dist/vue.js"></script>
<!-- 外部模塊 -->
<template id="fu">
<div>
我是父組件
<son></son>
</div>
</template>
<template id="zi">
<div>
<h2>我是子組件</h2>
<button @click='btnclick'>按鈕</button>
</div>
</template>
<script>
const vm = new Vue({
el: '#app',
data: {
text:'我是根組件'
},
components: {
"parent": {
template: "#fu",
data() {
return {
name:'我是父組件的名字'
}
},
components: {
"son": {
template: '#zi',
data() {
return {
}
},
methods: {
btnclick(){
// 1.訪問父組件$parent
console.log(this.$parent.name);
// 2.訪問跟組件$root
console.log(this.$root.text);
}
},
}
}
},
}
})
</script>
</html>
Slot槽口/插槽
組件的插槽:
組件的插槽也是爲了讓我們封裝的組件更加具有擴展性。讓使用者可以決定組件內部的一些內容到底展示什麼。
可以在組件調用的時候在開標籤和關標籤中傳入數據 進行展示。官方描述:用來混合父組件的內容與子組件自己的模版
栗子:移動網站中的導航欄
移動開發中,幾乎每個頁面都有導航欄。導航欄我們必然會封裝成一個插件,比如nav-bar組件。一旦有了這個組件,我們就可以在多個頁面中複用了。
如何封裝
最好的封裝方式就是將共性抽取到組件中,將不同暴露爲插槽。一旦我們預留了插槽,就可以讓使用者根據自己的需求,決定插槽中插入什麼內容。是搜索框,還是文字,還是菜單,由調用者自己來決定。
slot基本使用
1.插槽的基本使用<slot></slot>
2.插槽的默認值<slot>button</slot>
3.如果有多個值,同時放入到組件進行替換時,一起作爲替換元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<parent></parent>
<parent>
<h2>我是插槽的h2替換內容</h2>
<span>我是插槽的span替換內容</span >
</parent>
<parent>
<p>我也是插槽的替換內容</p>
</parent>
<parent></parent>
<parent></parent>
</div>
</body>
<script src="./node_modules/vue/dist/vue.js"></script>
<!-- 外部模塊 -->
<template id="fu">
<div>
<h1>組件1</h1>
<p>我是組件</p>
<slot><button>按鈕</button></slot>
</div>
</template>
<script>
const vm=new Vue({
el:'#app',
data:{},
components:{
"parent":{
template:"#fu",
data(){
return {
}
},
methods: {
}
},
}
})
</script>
</html>
具名插槽
當子組件的功能複雜時,子組件的插槽可能並非是一個。比如我們封裝一個導航欄的子組件,可能就需要三個插槽,分別代表左邊、中間、右邊。這個時候,我們就需要給插槽起一個名字
如何使用具名插槽呢?
slot元素一個name屬性即可 <slot name='myslot'></slot>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 沒有傳入內容 -->
<parent></parent>
<!-- 傳入某一個值 -->
<parent><span slot="center">具名插槽</span></parent>
</div>
</body>
<script src="./node_modules/vue/dist/vue.js"></script>
<!-- 外部模塊 -->
<template id="fu">
<div>
<slot name='left'><span>left</span></slot>
<slot name='center'><span>center</span></slot>
<slot name='right'><span>right</span></slot>
</div>
</template>
<script>
const vm=new Vue({
el:'#app',
data:{},
components:{
"parent":{
template:"#fu",
data(){
return {
}
},
methods: {
}
},
}
})
</script>
</html>
作用域插槽
父組件替換插槽的標籤,但是內容由子組件來提供。
用途
需要在多個界面進行展示,某些界面是以水平方向一一展示的,某些界面是以列表形式展示的,某些界面直接展示一個數組,ü利用slot作用域插槽就可以了
在父組件使用子組件時,從子組件中拿到數據:
我們通過<template v-slot="slotProps">獲取到slotProps屬性,在通過slotProps.data就可以獲取到剛纔傳入的data了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<parent></parent>
</div>
</body>
<script src="./node_modules/vue/dist/vue.js"></script>
<!-- 外部模塊 -->
<template id="fu">
<div>
<son></son>
<son>
<template v-slot='slotProps'>
<span> {{slotProps.data.join(' * ')}}</span>
</template >
</son>
</div>
</template>
<template id="zi">
<div>
<slot :data='arr'>
<ul>
<li v-for='item in arr'>{{item}}</li>
</ul>
</slot>
</div>
</template>
<script>
const vm = new Vue({
el: '#app',
data: {},
components: {
"parent": {
template: "#fu",
data() {
return {
}
},
methods: {
},
components: {
"son": {
template: '#zi',
data() {
return {
arr: ['aarrhyj', 'bftth', 'cvvf', 'dsss']
}
}
}
}
},
}
})
</script>
</html>