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