三、組件化開發
- 1.1 組件化的實作和使用步驟
- 組件注冊步驟決議
- 1.2 全域組件和區域組件
- 1.3 父組件和子組件
- 1.4 注冊組件語法糖
- 1.5 組件模板抽離的寫法
- 1.6 組件資料存放
- 1.7 父子組件通信
- 父組件向子組件傳遞資料
- 子組件向父組件傳遞資料
- 1.8 組件訪問
- 父組件訪問子組件
- children
- refs
- 子組件訪問父組件
- 1.9 插槽
- slot
- 具名插槽
- 作用域插槽
人面對復雜問題的處理方式:
- 任何一個人處理資訊的邏輯能力都是有限的
- 所以,當面對一個非常復雜的問題時,我們不太可能—次性搞定一大堆的內容,
- 但是,我們人有一種天生的能力,就是將問題進行拆解,如果將一個復雜的問題,拆分成很多個可以處理的小問題,再將其放在整體當中,你會發現大的問題也會迎刃而解,
組件化也是類似的思想:
- 如果我們將一個頁面中所有的處理邏輯全部放在一起,處理起來就會變得非常復雜,而且不利于后續的管理以及擴展,
- 但如果,我們講一個頁面拆分成一個個小的功能塊,每個功能塊完成屬于自己這部分獨立的功能,那么之后整個頁面的管理和維護就變得非常容易了,
組件化是Vue.js中的重要思想:
- 它提供了一種抽象,讓我們可以開發出一個個獨立可復用的小組件來構造我們的應用,
- 任何的應用都會被抽象成一顆組件樹,

組件化思想的應用:
- 有了組件化的思想,我們在之后的開發中就要充分的利用它,
- 盡可能的將頁面拆分成一個個小的、可復用的組件,
- 這樣讓我們的代碼更加方便組織和管理,并且擴展性也更強,
1.1 組件化的實作和使用步驟
組件的使用分成三個步驟:
- 創建組件構造器
- 注冊組件
- 使用組件

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 3、使用組件 -->
<my-cpn></my-cpn>
<my-cpn></my-cpn>
</div>
<script src="../js/vue.js"></script>
<script>
// 1、創建組件構造器
const cpnC = Vue.extend({
template: `
<div>
<h2>我是標題</h2>
<p>我是內容,哈哈哈哈</p>
<p>我是內容,呵呵呵呵</p>
</div>`,
});
// 2、注冊組件
Vue.component('my-cpn', cpnC);
const app = new Vue({
el: '#app',
data: {
}
})
</script>
</body>
</html>

組件注冊步驟決議
-
Vue.extend():- 呼叫
Vue.extend()創建的是一個組件構造器, - 通常在創建組件構造器時,傳入
template代表我們自定義組件的模板,該模板就是在使用到組件的地方,要顯示的HTML代碼, - 事實上,這種寫法在
Vue2.x的檔案中幾乎已經看不到了,它會直接使用下面我們會講到的語法糖,但是在很多資料還是會提到這種方式,而且這種方式是學習后面方式的基礎,
- 呼叫
-
Vue.component():- 呼叫
Vue.component()是將剛才的組件構造器注冊為一個組件,并且給它起一個組件的標簽名稱, - 所以需要傳遞兩個引數:
1、注冊組件的標簽名,2、組件構造器
- 呼叫
-
組件必須掛在在某個
Vue實體下,否則它不會生效- 下面的實體中,我們使用了三次
<my-cpn></my-spn> - 但是第三次并沒有生效
- 下面的實體中,我們使用了三次

1.2 全域組件和區域組件
- 全域組件:在注冊之后可以用在任何新創建的
Vue根實體 (new Vue) 的模板中, - 區域組件:區域組件只有在所注冊的
Vue實體中才能使用,并不能在其他未進行注冊的Vue實體中使用,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 3、使用組件 -->
<my-cpn></my-cpn>
<cpn></cpn>
</div>
<hr>
<div id="app2">
<my-cpn></my-cpn>
<cpn></cpn>
</div>
<script src="../js/vue.js"></script>
<script>
// 1、創建組件構造器
const cpnC = Vue.extend({
template: `
<div>
<h2>我是標題</h2>
<p>我是內容,哈哈哈哈</p>
<p>我是內容,呵呵呵呵</p>
</div>`,
});
// 2、注冊組件(全域組件,意味著可以在多個 Vue 實體下面使用)
Vue.component('my-cpn', cpnC);
const app = new Vue({
el: '#app',
data: {
},
// 區域組件
components: {
cpn: cpnC
}
})
const app2 = new Vue({
el: '#app2',
data: {
}
})
</script>
</body>
</html>
在上面的代碼中,我們創建了兩個Vue實體物件app和app2,同時我們使用注冊了全域組件my-cpn以及在app內注冊了區域組件cpn,我們在app和app2中使用上面的兩個組件,可以看到區域組件cpn并沒有在app2中生效,符合我們的預期,

1.3 父組件和子組件
在前面我們看到了組件樹:
- 組件和組件之間存在層級關系
- 而其中一種非常重要的關系就是父子組件的關系
我們來通過代碼看這種層級關系:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn2></cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
// 1、創建第一個組件構造器(子組件)
const cpnC1 = Vue.extend({
template: `
<div>
<h2>我是標題1</h2>
<p>我是內容,哈哈哈哈</p>
</div>`,
});
// 2、創建第二個組件構造器(父組件)
const cpnC2 = Vue.extend({
template: `
<div>
<h2>我是標題2</h2>
<p>我是內容,呵呵呵呵</p>
<cpn1></cpn1>
</div>`,
components: {
cpn1: cpnC1,
},
});
const app = new Vue({
el: '#app',
data: {
},
components: {
cpn2: cpnC2,
}
})
</script>
</body>
</html>


1.4 注冊組件語法糖
通過語法糖的方式,我們可以跳過Vue.extend()方法,直接通過Vue.component()方法實作組件的注冊,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 3、使用組件 -->
<cpn1></cpn1>
<cpn2></cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
// 1、全域組件注冊語法糖
// 1.1 創建組件構造器
// const cpn1 = Vue.extend({
// template: `
// <div>
// <h2>我是標題</h2>
// <p>我是內容,哈哈哈哈</p>
// <p>我是內容,呵呵呵呵</p>
// </div>`,
// });
// 1.2 注冊組件
// Vue.component('cpn1', cpn1);
Vue.component('cpn1', {
template: `
<div>
<h2>我是標題</h2>
<p>我是內容,哈哈哈哈</p>
<p>我是內容,呵呵呵呵</p>
</div>`,
});
const app = new Vue({
el: '#app',
data: {
},
components: {
cpn2: {
template: `
<div>
<h2>我是標題</h2>
<p>我是內容,哈哈哈哈</p>
<p>我是內容,呵呵呵呵</p>
</div>`,
}
}
})
</script>
</body>
</html>

1.5 組件模板抽離的寫法
在IDE中,寫組件的template時,由于其是一個字串,所以沒有代碼提示,寫起來非常不方便,并且template的代碼直接耦合在Vue的代碼中,非常的凌亂,為此,我們可以將組件的模板抽離出來,有兩種抽離的方式,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn1></cpn1>
<hr>
<cpn2></cpn2>
</div>
<!-- 1、第一種寫法 -->
<script type="text/x-template" id="cpn">
<div>
<h2>我是標題</h2>
<p>我是內容,哈哈哈哈</p>
</div>
</script>
<!-- 2、第二種寫法 -->
<template id="cpn2">
<div>
<h2>我是標題</h2>
<p>我是內容,哈哈哈哈</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
Vue.component('cpn1', {
template: '#cpn',
});
Vue.component('cpn2', {
template: '#cpn2',
});
const app = new Vue({
el: '#app',
data: {
},
})
</script>
</body>
</html>

通常,我們采用的是方式二,可以看到,組件模板抽離后的代碼看起來非常簡潔,
1.6 組件資料存放
組件是一個單獨功能模塊的封裝:
這個模塊有自己的 HTML 模板,也應該有屬于自己的資料data
組件中的資料是保存在哪里呢?頂層的Vue實體中嗎?
我們可以通過下面的代碼測驗,組件中能不能直接訪問Vue實體中的data
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn></cpn>
</div>
<template id="a">
<div>
<h2>{{ message}}</h2>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
Vue.component('cpn', {
template: '#a',
});
const app = new Vue({
el: '#app',
data: {
message: 'Hello'
},
})
</script>
</body>
</html>

我們發現并不能訪問,而且即使可以訪問,如果將所有的資料都放在Vue實體中,Vue實體會變的非常臃腫,
結論:Vue 組件應該有自己保存資料地方,
組件自己的資料存放在哪里呢?
- 組件物件也有一個
data屬性(也可以有methods屬性) - 只是這個
data屬性必須是一個函式 - 而且這個函式回傳一個物件,物件內部保存著資料
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn></cpn>
</div>
<template id="a">
<div>
<h2>{{ title }}</h2>
<p>我是內容,哈哈哈哈</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
Vue.component('cpn', {
template: '#a',
data() {
return {
title: 'Hello'
}
}
});
const app = new Vue({
el: '#app',
data: {
},
})
</script>
</body>
</html>

1.7 父子組件通信
子組件是不能參考父組件或者Vue實體的資料的,
但是,在開發中,往往一些資料確實需要從上層產地到下層:
- 比如在一個頁面中,我們從服務器請求到了很多的資料
- 其中一部分資料,并非是我們整個頁面的大組件來展示的,而是需要下面的子組件進行展示
- 這個時候,并不會讓子組件再次發送要給網路請求,而是直接讓大組件(父組件)將資料傳遞給小組件(子組件),
如果進行父子組件間的通信呢?官方提到
- 通過
props向子組件傳遞資料 - 通過事件向父組件發送訊息

Vue實體同樣是組件,所以Vue實體與子組件通信和父組件與子組件通信的程序是一樣的,
父組件向子組件傳遞資料
在子組件中,使用選項props來宣告需要從父組件接收到的資料,
props的值有兩種方式:
- 方式一:字串陣列,陣列中的字串就是傳遞時的名稱,
- 方式二:物件,物件可以設定傳遞時的型別,也可以設定默認值等,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn :cmovies="movies" :cmessage="message"></cpn>
<!-- 如果要傳遞的是 data 屬性,則必須用 v-bind -->
<cpn cmovies="movies" cmessage="message"></cpn>
</div>
<template id="a">
<div>
{{cmessage}}
<ul>
<li v-for="movie in cmovies">{{movie}}</li>
</ul>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: '#a',
props: ['cmovies', 'cmessage'],
}
const app = new Vue({
el: '#app',
data: {
message: 'Hello',
movies: ['a', 'b', 'c', 'd']
},
components: {
cpn
}
})
</script>
</body>
</html>

在上面,我們的props選項是使用一個陣列,
我們說過,處理陣列之外,我們也可以使用物件,當需要對props進行型別等驗證時,就需要物件寫法了,
驗證都支持如下資料型別:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn :cmovies="movies" ></cpn>
<!-- 錯誤語法:必須用 v-bind -->
<!-- <cpn cmovies="movies" cmessage="message"></cpn> -->
</div>
<template id="a">
<div>
{{cmessage}}
<ul>
<li v-for="movie in cmovies">{{movie}}</li>
</ul>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: '#a',
// props: ['cmovies', 'cmessage'],
props: {
// 1、型別檢查
// cmovies: Array,
// cmessage: String
// 2、提供一些默認值
cmessage: {
type: String,
default: 'aaaa',
required: false
},
cmovies: {
type: Array,
}
}
}
const app = new Vue({
el: '#app',
data: {
message: 'Hello',
movies: ['a', 'b', 'c', 'd']
},
components: {
cpn
}
})
</script>
</body>
</html>
子組件向父組件傳遞資料
props用于父組件向子組件傳遞資料,還有一種比較常見的是子組件傳遞資料或事件到父組件中,
我們應該如何處理呢?這個時候,我們需要使用自定義事件來完成,
什么時候需要自定義事件呢?
- 當子組件需要向父組件傳遞資料時,就要用到自定義事件了,
- 我們之前學習的
v-on不僅僅可以用于監聽DOM事件,也可以用于組件間的自定義事件,
自定義事件的流程︰
- 在子組件中,通過
$emit('事件名', [引數])來觸發事件, - 在父組件中,通過
v-on來監聽子組件事件,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 父組件模板 -->
<div id="app">
<cpn v-on:itemclick="cpnClick"></cpn>
</div>
<!-- 子組件模板 -->
<template id="a">
<div>
<button v-for="item in categories" @click="btnClick(item)">
{{item.name}}
</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: '#a',
data() {
return {
categories: [
{id: 1, name: '熱門推薦'},
{id: 2, name: '手機數碼'},
{id: 3, name: '家用家電'},
{id: 4, name: '電腦辦公'},
]
}
},
methods: {
btnClick(item) {
// 產生事件:自定義事件
this.$emit('itemclick', item);
}
}
}
const app = new Vue({
el: '#app',
data: {
},
components: {
cpn
},
methods: {
cpnClick(item) {
console.log(item)
}
}
})
</script>
</body>
</html>
上面代碼的含義如下:
- 父組件監聽
itemclick事件,該事件產生時,執行cpnClick方法 - 點擊子組件內部的按鈕時,執行
btnClick方法 btnClick方法內部,產生itemclick事件- 由此便完成了子組件向父組件的資料傳遞

1.8 組件訪問
有時候,我們需要父組件直接訪問子組件,子組件直接訪問父組件,或者是子組件訪問根組件,
- 父組件訪問子組件:使用
$children或$refs - 子組件訪問父組件:使用
$parent
父組件訪問子組件
children
this.$children是一個陣列型別,它包含所有子組件物件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn></cpn>
<button @click="btnClick">點擊</button>
</div>
<template id="cpn">
<div>
<h2>我是子組件</h2>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
},
methods: {
btnClick() {
console.log(this.$children);
this.$children[0].showMessage();
}
},
components: {
cpn: {
template: '#cpn',
methods: {
showMessage() {
console.log("showMessage")
}
}
}
},
})
</script>
</body>
</html>

可以看到,我們通過this.$children成功訪問了cpn組件,并且呼叫了cpn所擁有的methods,當然也可以訪問cpn的其他屬性,
refs
通過this.$children存在這樣一個問題,便是我們要訪問一個組件時,可能并不知道其索引,需要進行遍歷查找,
而this.$refs的作用相當于給組件一個id,這樣我們便能根據這個id直接找到對應的組件了,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn ref="aaa"></cpn>
<button @click="btnClick">點擊</button>
</div>
<template id="cpn">
<div>
<h2>我是子組件</h2>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
},
methods: {
btnClick() {
console.log(this.$refs);
console.log(this.$refs.aaa);
this.$refs.aaa.showMessage();
}
},
components: {
cpn: {
template: '#cpn',
methods: {
showMessage() {
console.log("showMessage")
}
}
}
},
})
</script>
</body>
</html>

子組件訪問父組件
this.$parent獲得父組件this.$root獲得根組件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>我是子組件</h2>
<button @click="btnClick">點擊</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
},
methods: {
showMessage() {
console.log("showMessage")
}
},
components: {
cpn: {
template: '#cpn',
methods: {
btnClick() {
console.log(this.$parent);
this.$parent.showMessage();
// 訪問根組件
console.log(this.$root);
}
}
}
},
})
</script>
</body>
</html>

1.9 插槽
slot翻譯為插槽:
- 在生活中很多地方都有插槽,電腦的USB插槽,插板當中的電源插槽,口插槽的目的是讓我們原來的設備具備更多的擴展性,
- 比如電腦的USB我們可以插入U盤、硬碟、手機、音響、鍵盤、滑鼠等等,
組件的插槽︰
- 組件的插槽也是為了讓我們封裝的組件更加具有擴展性,
- 讓使用者可以決定組件內部的一些內容到底展示什么,
例子∶移動網站中的導航欄,
- 移動開發中,幾乎每個頁面都有導航欄,
- 導航欄我們必然會封裝成一個插件,比如
nav-bar組件,一旦有了這個組件,我們就可以在多個頁面中復用了,
如何去封裝這類的組件呢?
- 它們也很多區別,但是也有很多共性,
- 如果,我們每一個單獨去封裝一個組件,顯然不合適∶比如每個頁面都回傳,這部分內容我們就要重復去封裝,
- 但是,如果我們封裝成一個,好像也不合理∶有些左側是選單,有些是回傳,有些中間是搜索,有些是文字,等等,
如何封裝合適呢?抽取共性,保留不同,
- 最好的封裝方式就是將共性抽取到組件中,將不同暴露為插槽,
- 一旦我們預留了插槽,就可以讓使用者根據自己的需求,決定插槽中插入什么內容,
- 是搜索框,還是文字,還是選單,由呼叫者自己來決定,
這就是為什么我們要學習組件中的插槽slot的原因,
slot
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!--
1、插槽的基本使用 <slot></slot>
2、插槽的默認值 <slot>button</slot>
3、如果有多個值,同時放入到組件進行替換時,一起作為替換元素
-->
<div id="app">
<cpn><button>按鈕</button></cpn>
<cpn><div>哈哈哈</div></cpn>
<cpn><a href="">百度</a></cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>我是組件</h2>
<div>我是組件,哈哈哈</div>
<slot></slot>
<!-- 可以給 slot 默認值,如果父組件沒有插入,則使用默認值 -->
<!-- <slot><button>按鈕</button></slot> -->
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
},
components: {
cpn: {
template: '#cpn',
}
}
})
</script>
</body>
</html>

在上面的代碼中,我們在組件中使用<slot></slot>創建插槽,當我們使用該組件時,組件標簽內的內容會自動替換掉<slot></slot>,
具名插槽
當子組件的功能復雜時,子組件的插槽可能并非是一個,
- 比如我們封裝一個導航欄的子組件,可能就需要三個插槽,分別代表左邊、中間、右邊,
- 那么,外面在給插槽插入內容時,如何區分插入的是哪—個呢?
- 這個時候,我們就需要給插槽起一個名字
如何使用具名插槽呢?
- 非常簡單,只要給
slot元素一個name屬性即可<slot name='myslot'> </slot>
我們來給出一個案例︰
- 這里我們先不對導航組件做非常復雜的封裝,先了解具名插槽的用法,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn>
<span slot="center">標題</span>
</cpn>
</div>
<template id="cpn">
<div>
<slot name="left"><span>左邊</span></slot>
<slot name="center"><span>中間</span></slot>
<slot name="right"><span>右邊</span></slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
},
components: {
cpn: {
template: '#cpn',
}
}
})
</script>
</body>
</html>

在上面的代碼中,我們在組件中創建了三個具名插槽,分別為left、center、right.
我們使用該組件時,通過slot="center"指定要將內容插入到center的插槽,
作用域插槽
在了解作用域插槽之前,我們需要西安了解一個概念:編譯作用域,
官方對于編譯的作用域決議比較簡單,我們自己來通過一個例子來理解這個概念:
我們來考慮下面的代碼是否最終是可以渲染出來的︰
<my-cpn v-show="isShow"></my-cpn>中,我們使用了isShow屬性,isShow屬性包含在組件中,也包含在Vue實體中,
答案︰最終可以渲染出來,也就是使用的是Vue實體的屬性,
為什么呢?
- 官方給出了一條準則∶父組件模板的所有東西都會在父級作用域內編譯;子組件模板的所有東西都會在子級作用域內編譯,
- 而我們在使用
<my-cpn v-show="isShow"></my-cpn>的時候,整個組件的使用程序是相當于在父組件中出現的, - 那么他的作用域就是父組件,使用的屬性也是屬于父組件的屬性,
- 因此,
isShow使用的是Vue實體中的屬性,而不是子組件的屬性,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn v-show="isShow"></cpn>
</div>
<template id="cpn">
<div>
<h2>我是組件</h2>
<div>我是組件,哈哈哈</div>
<button v-show="isShow">按鈕</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
isShow: true
},
components: {
cpn: {
template: '#cpn',
data() {
return {
isShow: false
}
}
}
}
})
</script>
</body>
</html>

作用域插槽是slot—個比較難理解的點,而且官方檔案說的又有點不清晰,
這里,我們用一句話對其做一個總結,然后我們在后續的案例中來體會︰
- 父組件替換插槽的標簽,但是內容由子組件來提供,
我們先提一個需求︰
- 子組件中包括一組資料:比如:
pLanguages: ['JavaScript', 'Python', 'Swift', 'Go','C++'] - 需要在多個界面進行展示:
- 某些界面是以水平方向展示的
- 某些界面是以串列形式展示的
- 某些界面直接展示一個陣列
- 內容在子組件,希望父組件告訴我們如何展示,怎么辦呢?
- 利用slot作用域插槽就可以了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<cpn></cpn>
<cpn>
<!-- 目的是獲取子組件中的 pLanguages -->
<template slot-scope="slot">
<span v-for="item in slot.data">{{item}} - </span>
</template>
</cpn>
</div>
<template id="cpn">
<div>
<h2>我是組件</h2>
<slot :data="pLanguages">
<ul>
<li v-for="item in pLanguages">
{{item}}
</li>
</ul>
</slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
},
components: {
cpn: {
template: '#cpn',
data() {
return {
pLanguages: ['Python', 'Java', 'Go', 'C++']
}
}
}
}
})
</script>
</body>
</html>

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/279229.html
標籤:其他
上一篇:谷粒商城專案簡介
