主要內容:
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 lang="en"> <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 lang="en"> <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>
效果如下圖