一.組件化開發
- 組件 (Component) 是 Vue.js 強大的功能之一。組件可以擴展 HTML 元素,封裝可重用的代 碼。在較高層面上,組件是自定義元素,Vue.js 的編譯器為它添加特殊功能。
- 在vue中都是組件化開發的,組件化開發就是把一個完整的頁面分割成一個一個的小組件
- 組件化開發 容易維護 可以復用
1.1.定義簡單的組件
1.1.1語法:
Vue.component()里面有兩個參數:
- 組件名稱
- 是個對象 對組件的設置 包括html模板 數據偵聽器等
Vue.component('first-component',{ template:'<h1>我愛我的祖國</h1>'})
1.1.2組件的使用:
<div id="app"> <first-component></first-component>
</div>
組件不能寫到#app的元素外面
1.1.3組件的重復使用
組件就是一個可以重復引用的Vue實例 可以在頁面中引用多次
<div id="app">
<first-component></first-component>
<first-component></first-component>
<first-component></first-component>
<first-component></first-component>
<first-component></first-component>
</div>
1.1.4定義的組件只能有一個根元素
案例:
下面定義組件中兩個h1都輸入根元素
Vue.component('aa',{
template:'<h1>愛我中華</h1><h1>建設我們的國家</h1>'
})
可以改寫成如下:
Vue.component('aa',{
template:'
<div><h1>愛我中華</h1><h1>建設我們的國家</h1>
</div>
'
})
1.2.組件中的事件
組件就是可以重復使用的Vue實例 所以組件中也有Vue中的事件
data computed watch methods 以及生命周期鉤子函數等 但是組件中沒有el
組件中也可以為元素添加事件:
Vue.component('aa', {template: `<div class="bottom"><button @click="turnOn">我是測試按鈕</button></div>`,methods: {turnOn(){console.log('我是組件中的事件')}}})
1.3.組件中的數據
組件中的data數據 與new Vue中的不同 為了保證組件中的數據是私有的所以組件中的data數據是一個返回函數 組件中的數據都在函數的作用域中 保證組件中的數據互不感染
data() {return {msg: '愛我中華'}}
二.組件通訊
因為組件對封閉的 但是在實際的開發中 組件之間的數據是相互依賴 需要相互傳遞的 具體來說組件通訊分為三大類:
- 父組件為子組件傳遞數據
- 子組件為父組件傳遞數據
- 非父子組件之間傳遞數據
2.1 父傳子
父組件向子組件傳遞數據
- 在子組件中定義props屬性 值為數組類似于data 但data中的數據來自本身 而props中的數據來自父組件
- 子組件使用模板中使用props中的屬性和data中的用法相同
- 父組件通過props傳值給子組件
輸出結果為:
說明:
- 創建Vue實例 data中的數據msg為一個數組
- 創建組件 在整個項目中 2組件相對就是1的子組件
- 通過3方式前者msg為props值中的數據 后者msg為newVue中data中的數據
- 最后正是props中的屬性也有data中的使用方法 將數據進行遍歷在頁面中
**注意:props負責獲取父組件的傳遞過來的,props中的值是只讀的,不允許修改 **
2.2 子傳父
原理
- 父組件使用子組件時 在其中定義一個自定義事件 并且綁定父組件中的一個自定義函數 當事件被調用時執行自定義函數
- 子組件通過this$emit執行自定義事件
最終輸出結果為3
- 在子組件中定義一個點擊事件 觸發時執行子組件中的dian函數 并且將參數傳入函數中
- 在上面的函數中通過this.$emit(‘事件名稱’,參數)調用3中的a自定義事件并且將參數傳過去
- 當a事件被觸發時 會執行4中的aaa自定義函數 同時獲取參數 最終實現子組件向父組件傳數據
實例改造
根據父子組件之間的數據傳遞實現產品列表的組件化開發
代碼如下
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>div {width: 1000px;margin: 100px auto;text-align: center;}table {width: 900px;margin: 50px auto;border-collapse: collapse;}table,th,td {border: 1px solid rgb(218, 124, 17);}.color {background-color: rgb(26, 172, 152);}</style>
</head><body><div id="app"><atitle @tian="tian" :aatitle="aupda" @cha="cha"></atitle><acontent :content="newcontent" @del="del" @upda="upda"></acontent></div><script src="./node_modules/vue/dist/vue.js"></script><script>Vue.component('atitle', {props: ['aatitle'],data() {return {keyword: ''}},template: `<div>型號 <input type="text" v-model=aatitle.name>價格 <input type="text" v-model=aatitle.may @keyup.enter="tian"><button @click="tian">錄入</button><br><input type="text" v-model="keyword"></div>`,methods: {tian() {this.$emit('tian', this.aatitle.name, this.aatitle.may)this.aatitle.name = ''this.aatitle.may = ''}},watch: {keyword: {handler(newvalue) {this.$emit('cha', newvalue)},immediate: true}}})Vue.component('acontent', {props: ['content'],template: `<table><tr><th>編號</th><th>機型</th><th>價格</th><th>時間</th><th>操作</th></tr><tr v-show="this.content.length==0"><td colspan="5">沒有任何發布任何產品</td></tr><tr v-for="(item,index) in content"><td>{{item.id}}</td><td>{{item.name}}</td><td>{{item.may}}</td><td>{{item.time}}</td><td><a href="#"" @click.prevent="del(item.id)">刪除</a><a href="#"" @click.prevent='upda(item.id)'>編輯</a></td></tr></table>`,methods: {del(id) {this.$emit('del', id)},upda(id) {this.$emit('upda', id)}}})const app = new Vue({el: '#app',data: {newcontent: [],aupda: {},isup: false,upid: '',content: [{id: 1,name: '華為',may: 5000,time: Date.now()},{id: 2,name: '小米',may: 6000,time: Date.now()},{id: 3,name: '蘋果',may: 4500,time: Date.now()},{id: 4,name: '1+',may: 3000,time: Date.now()},{id: 5,name: 'oppo',may: 2000,time: Date.now()},{id: 6,name: '1+2',may: 8000,time: Date.now()},{id: 7,name: '1+3',may: 12000,time: Date.now()}]},methods: {del(id) {let index = this.content.findIndex(item => {return item.id == id})this.content.splice(index, 1)this.newcontent.splice(index, 1)},tian(name, may) {if (this.isup) {let a = this.content.find(item => {return item.id == this.upid})a.name = namea.may = may} else {let id = this.content.length - 1 < 0 ? 1 : this.content[this.content.length - 1].id + 1let content = {id: id,name: name,may: may,time: Date.now()}this.content.push(content)this.newcontent.push(content)}},cha(value) {this.newcontent = this.content.filter(item => {return item.name.includes(value)})},upda(id) {let a = this.content.find(item => {return item.id == id})this.aupda = {name: a.name,may: a.may}this.isup = truethis.upid = id}}})</script>
</body></html>
2.3 非父子之間的組件通訊
原理:
通過一個空的Vue實例來傳遞數據
const bus =new Vue()
核心邏輯:
組件A給組件B傳值:
- 組件A給bus注冊一個事件,監聽事件的處理程序
- 組件B觸發bus上對應的事件,把 值當成參數來傳遞
- 組件A通過事件處理程序獲取數據
最終點擊h2控制臺會輸出2
- 創建1和2兩個非父子組件以及3空vue實例bus
- 在1組件中 鉤子函數created中通過bus.$on為bus自定義一個事件aa
- 在2組件中 當點擊h2元素時觸發dian函數 并且將值出過去
- 在2組件的dian函數中通過bus.$emit方觸發1中的aa事件 并傳參過去
- 當1中的aa事件被觸發時會執行其中的函數并獲取參數
實例:
通過非父子組件 實現開關燈案例
- 關閉狀態:
開啟狀態:
代碼如下
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>#app {width: 500px;height: 500px;margin: 100px auto;}.box {height: 200px;width: 200px;margin: 0 auto;background-color: black;border-radius: 50%;}.below {height: 200px;width: 400px;margin: 50px auto;}button {margin-left: 66px;width: 100px;height: 40px;}.on {background-color: rgb(160, 184, 25);}</style>
</head><body><div id="app"><zss></zss><sgy></sgy></div><script src="./node_modules/vue/dist/vue.js"></script><script>const bus = new Vue()Vue.component('zss', {data() {return {attribute: "on",state: false}},created() {bus.$on('lamp', result => {this.state = result})},template: `<div class="box" :class="state?attribute:''"></div>`})Vue.component('sgy', {template: `<div class="below"><button @click="on">開燈</button><button @click="off">關閉</button></div>`,methods: {on() {bus.$emit('lamp', true)},off() {bus.$emit('lamp', false)}}})const app = new Vue({el: '#app',data: {}})</script>
</body></html>