實際應用項目:http://github.crmeb.net/u/long
主要内容:
1. 元件的基本使用
2. 全局元件和局部元件
3. 父元件和子元件
4. 元件文法糖的寫法
5. 元件data關聯的寫法
6. 父子元件的通信
元件系統是 Vue 的一個重要概念,因為它是一種抽象,允許我們使用小型、獨立和通常可複用的元件建構大型應用。幾乎任意類型的應用界面都可以抽象為一個元件樹:
例如上面頁面, 頁面整體分為三個部分. 我們可以将每一個部分設計為一個元件. 然後将三個元件組成一個頁面. 每一個元件又是由多個小元件構成的.
元件可以讓子產品可複用性提高. 是一種提倡的用法
一. 元件的基本使用
建構一個元件分為三個部分:
- 定義元件
- 注冊元件
- 使用元件
下面, 我們就從這三個部分來定義一個元件
1. 定義元件
文法:
Vue.extend({
template: ""
})
定義元件使用Vue.extend({})然後在裡面定義一個template, 我們看到template的内容是html内容,
為了讓html能夠按照格式顯示, 我們使用``将内容框起來
下面我們來注冊一個元件
const cpnC = Vue.extend({
template: `<div>
<h2>aaa</h2>
<p>元件内容1</p>
<p>元件内容2</p>
</div>`
})
2. 注冊元件
文法:
Vue.component("元件名稱", 元件内容)
我們将上面定義的元件進行注冊
// 2. 注冊元件
Vue.component('my-first-comp', cpnC)
3. 使用元件
<div id="app">
<my-first-comp></my-first-comp>
</div>
直接使用元件名即可
完整源碼:
<!DOCTYPE html>
<html >
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--
1. 注冊元件的基本步驟
-->
<div id="app">
<my-first-comp></my-first-comp>
</div>
<script src="../js/vue.js"></script>
<script>
// 1. 建立元件構造器對象
/**
* 調用vue.extend()建立一個元件
* 傳入的template代表我們自定義元件的模闆
* vue2.x以後基本看不到這種寫法, 會直接使用文法糖建立, 但原理還是這個.
*
* @type {VueComponent|void}
*/
const cpnC = Vue.extend({
template: `<div>
<h2>aaa</h2>
<p>元件内容1</p>
<p>元件内容2</p>
</div>`
})
// 2. 注冊元件
Vue.component('my-first-comp', cpnC)
var app = new Vue({
el: "#app",
data: {
message: "hello"
}
});
</script>
</body>
</html>
二. 全局元件和局部元件
1. 全局元件
元件有全局元件和局部元件的概念, 如果将一個元件定義在外部就是全局元件
// 定義一個元件
const myComp = Vue.extend({
template: `
<div>
<p>你好!!</p>
</div>
`
})
// 注冊元件(這種方式注冊的元件是全局元件)
Vue.component('my-comp', myComp)
let app = new Vue({
el: "#app",
data: {
message: "hello"
}
});
第一步: 定義了一個元件
第二步: 注冊元件, 在new Vue({})外面注冊的, 是全局元件.
第三步: 調用元件
<div id="app2">
<my-comp></my-comp>
</div>
以上定義的就是一個全局元件. 全局元件什麼概念呢? 也就是說任何一個Vue執行個體對象都可以使用.
下面定義了兩個Vue對象
let app = new Vue({
el: "#app",
data: {
message: "hello"
},
components:{
app1Comp: app1Comp
}
});
let app2 = new Vue({
el: "#app2"
})
第一個作用對象是id="app"的div, 第二個作用對象是id="app2"的div
<div id="app">
<my-comp></my-comp>
<app1-comp></app1-comp>
</div>
<div id="app2">
<my-comp></my-comp>
<app1-comp></app1-comp>
</div>
我們在裡面調用元件, 都可以成功.
全局元件, 一次注冊, 多處調用
2. 局部元件
const app1Comp = Vue.extend({
template: `
<div>
<p>隻有app1才能使用的元件</p>
</div>
`
})
let app = new Vue({
el: "#app",
data: {
message: "hello"
},
components:{
app1Comp: app1Comp
}
});
我們定義了一個appComp的元件, 但是注冊的時候, 隻注冊到了app這個Vue對象裡, 那麼就隻有app能使用,其他vue對象不能使用, 這樣的元件就是局部元件.
局部元件, 哪裡注冊, 哪裡調用
三. 父元件和子元件
像這種有嵌套關系的元件, 就是父子元件.
那麼父元件和子元件如何定義呢?
首先, 定義了一個元件1--comp1
// 定義元件1
const comp1 = Vue.extend({
template: `
<div>
<p>元件1</p>
</div>
`
})
然後定義了一個元件2---comp2
// 定義元件2
const comp2 = Vue.extend({
template: `
<div>
<h2>元件2</h2>
<comp1></comp1>
</div>
`,
components:{ // 局部元件
comp1: comp1
}
})
我們發現, 在元件comp2中注冊了comp1元件, 這裡comp2是父元件, comp1是子元件.
最後我們可以把comp2注冊到Vue對象上, 在頁面就可以調用了
// vue也是一個元件, 是一個root根元件
var app = new Vue({
el: "#app",
data: {
message: "hello"
},
components:{
comp2: comp2
}
});
vue也是一個元件, 是一個root根元件
四. 元件文法糖的寫法
在vue2之後, 就很少看到Vue.extend({})的寫法了, 而是使用文法糖
// 文法糖的寫法
Vue.component('comp2', {
template: '<div><h2>你好, 文法糖寫法!</h2></div>'
})
直接注冊Vue元件
但是, 這麼寫會将html代碼群組件紐在一起, 下面就說說如何将元件和模闆分開
五. 模闆群組件分離
我們有單獨的方式定義模闆代碼. 有兩種方法
第一種: script寫法
<!-- 第一種方式: 使用script -->
<script type="text/x-template" id="comp2">
<div>
<h2>元件和模闆分離的寫法1</h2>
</div>
</script>
使用script, 需要将type設定為text/x-template. 然後給模闆設定一個id, 就代表一個模闆了
然後, 注冊模闆
Vue.component('comp2', {
template: comp2
})
接下來就可以調用元件了
<div id="app">
<comp2></comp2>
</div>
第二種: template寫法
推薦使用第二種寫法
<template id="comp3">
<div>
<h3>元件模闆分離的第二種寫法</h3>
</div>
</template>
使用template标簽, 并為其定義一個id, 元件的定義是一樣的
Vue.component('comp3', {
template: comp3
})
五. 元件data關聯的寫法
元件中如果有變量, 怎麼辦呢? 我們知道在vue執行個體中, 變量可以定義在data中, 在元件中也有data屬性, 但這個data屬性是一個方法
例如: 我們定義了一個元件, 其中有一個變量title
<template id="comp1">
<div>
<h2>這是一個元件:{{title}}</h2>
</div>
</template>
我們在注冊元件的時候, 可以定義一個data函數, 并在傳回值輸出title屬性
Vue.component('comp1', {
template: comp1,
data() {
return {
title: 'vue元件'
}
}
})
這樣就可以拿到屬性的值了. data()方法裡面定義一個return傳回值, 傳回值是一個對象.
這樣寫有些奇怪是不是? 那為什麼要寫成方法呢?
協程元件, 我們的目的是複用, 在多處使用, 如果定義成一個變量值, 在一處修改, 其他調用的的地方也會跟着修改, 這不是我們希望看到的.
而方法是有作用域的, 每一個匿名方法都有自己的位址空間, 是以, 變量是不共享. 達到了互相隔離的目的.
那麼, 如果就想共享怎麼辦呢? , 我們可以将變量提取出來. 如下寫法:
// 如何讓所有元件共享變量呢
let shareData = {
title: "元件共有的title"
}
Vue.component('comp2', {
template: comp2,
data(){
return shareData
}
})
這樣每次傳回的都是一個位址, 是以, 變量之間是共享的.
六. 父子元件的通信
什麼是父子通訊呢? 我們來看看京東官網
可以吧這個頁面看成是大元件, 裡面有4個子元件構成: 上面是導航, 左邊是欄目導航, 點選欄目導航右側跟着變化.
我們來分析一下:
資料是在最外層的data裡面, 然後循環周遊擷取左側導航, 當點選左側導航的時候, 需要将參數傳遞給父元件, 然後發起新的請求, 在渲染到子元件中.
這就是父子通訊.
父子通訊分為父傳子和子傳父兩種方式
1. 父傳子元件的通訊
父子通訊有兩種方式: 一種是數組, 一種是對象
我們在vue對象中定義了兩個屬性: message和languages
let app = new Vue({
el: "#app",
data: {
message: "父元素傳遞值給子元素111",
languages:["go", "php", "python", "java", "c語言"]
}
});
然後要在模闆中使用這兩個屬性, 要怎麼樣才能拿到屬性呢?
在模闆中使用props來接收屬性, 使用props接收屬性有兩種方式:
1) 父子通訊方式---數組方式
第一種是使用數組的方式. 我們在數組中定義兩個變量來接收Vue對象中的兩個屬性.
Vue.component("comp1", {
template: "#comp1",
props:["clanguages", "cmessage"]
})
然後, 在模闆裡怎麼寫呢? 如下:
<template id="comp1">
<div>
<p>{{cmessage}}</p>
<ul>
<li v-for="item in clanguages">{{item}}</li>
</ul>
</div>
</template>
接下來綁定元件變量和vue對象變量的關系, 在哪裡調用元件, 就在哪裡綁定
<div id="app">
<comp1 :clanguages="languages" :cmessage="message"></comp1>
</div>
綁定的時候其實使用的是v-bind. 将元件的屬性clanguage綁定到vue對象, 可以這麼寫:
:clanguages="languages"
這樣就完成了綁定
其實總結有三步驟:
1. 在vue對象中定義屬性
2. 在模闆元件中定義與vue屬性接收的變量
3. 在模闆中綁定他們之間的關系
2) 父子通訊方式---對象方式
除了使用數組的方式來接收, 還可以使用對象的方式來接收
// props的對象寫法
Vue.component('comp2', {
template: "#comp2",
props:{
clanguages: {
type: Array, // 設定傳值的類型必須是數組類型
default: [], // 預設值是空數組
required: true // 如果設定為true, 這個值必須傳, 如果不傳将報錯
},
cmessage: {
type: String,
default: "aa",
required: true
}
}
})
props接收的是一個對象, clanguages對象裡面可以定義接收資料有三種
- 類型type,
- 預設值default
- 是否是必須有這個屬性required: 這個屬性的含義是, 調用了元件必須要使用這個屬性.
其他使用方法可以參考文章: https://www.cnblogs.com/em2464/p/10418820.html
2. 子傳父自定義事件
父傳子使用的是定義屬性接收, 而子傳父使用的是定義事件的方式.
就使用上面的例子, 點選類型傳參給父對象.
Vue.component('comp1', {
template: "#comp1",
data() {
return {
"types":[
{id:1, name:"手機類"},
{id:2, name:"日用品"},
{id:3, name:"空調類"},
{id:4, name:"電腦裝置"},
{id:5, name:"家用電器"},
]
}
}
}
上面定義了一個元件, 元件定義了商城産品的類型
template id="comp1">
<div>
<button v-for="item in types" @click="clicktype(item)">{{item.name}}</button>
</div>
</template>
定義一個元件模闆, 循環周遊商品類型, 并定義了一個點選事件. clicktype(item)
Vue.component('comp1', {
template: "#comp1",
data() {
return {
"types":[
{id:1, name:"手機類"},
{id:2, name:"日用品"},
{id:3, name:"空調類"},
{id:4, name:"電腦裝置"},
{id:5, name:"家用電器"},
]
}
},
methods:{
clicktype(item) {
this.$emit('itemclick',item)
console.log("點選類型", item)
}
}
})
在點選事件中, 我們使用this.$emit('itemclick', item)定義了一個事件, 并将元素對象item傳遞給了事件.
那麼父元件如何接受這個事件呢?
父元件需要定義這個事件的監聽. 通常我們都是監聽點選事件click, 按鍵事件input等自帶事件, 這裡需要監聽的是自定義事件
<div id="app">
<comp1 @itemclick="itemClick"></comp1>
</div>
監聽事件使用v-on:事件名稱, 簡寫為@itemclick. 然後在父元件定義時間itemClick
var app = new Vue({
el: "#app",
data: {
message: "hello"
},
methods:{
itemClick(item) {
console.log("傳遞事件到父元件", item)
}
}
});
這樣就可以接收到子元件傳遞過來的資料了.
總結一下:
1. 在模闆中定義一個事件, 調用this.$emit('事件名稱', 傳遞參數....)
2. 在模闆調用的時候監聽事件. @事件名稱="方法名()"
3. 在父元件中定義方法來接收事件監聽.
案例源碼:
<!DOCTYPE html>
<html >
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 2. 綁定事件 -->
<comp1 @itemclick="itemClick"></comp1>
</div>
<template id="comp1">
<div>
<button v-for="item in types" @click="clicktype(item)">{{item.name}}</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
Vue.component('comp1', {
template: "#comp1",
data() {
return {
"types":[
{id:1, name:"手機類"},
{id:2, name:"日用品"},
{id:3, name:"空調類"},
{id:4, name:"電腦裝置"},
{id:5, name:"家用電器"},
]
}
},
methods:{
clicktype(item) {
// 1. 注冊事件
this.$emit('itemclick',item)
console.log("點選類型", item)
}
}
})
var app = new Vue({
el: "#app",
data: {
message: "hello"
},
methods:{
// 3.接收事件
itemClick(item) {
console.log("傳遞事件到父元件", item)
}
}
});
</script>
</body>
</html>
效果如下圖