vue學習筆記(八)元件校驗&通訊
前言
在上一章部落格的內容中vue學習筆記(七)元件我們初步的認識了元件,並學會了如何定義區域性元件和全域性元件,上一篇內容僅僅只是對元件一個簡單的入門,並沒有深入的瞭解元件當中的其它機制,本篇部落格將會帶大家深入瞭解元件的其它知識,元件的校驗,元件的通訊等等。
本章目標
- 學會元件簡單的校驗
- 學會父元件向子元件傳遞資料
- 學會子元件向父元件傳遞資料
父元件向子元件傳遞資料
父元件向子元件傳遞資料實現的方式特別簡單,只用使用props進行資料傳遞就可以了。
語法:props['屬性1',‘屬性2’,...]
我找了一張圖給大家參考一下
在 Vue.js 中,父子元件的關係可以總結為 props down, events up 。父元件通過 props 向下傳遞資料給子元件,子元件通過 events 給父元件傳送訊息
(1)簡單的父元件向子元件傳遞資訊
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>父元件向子元件傳遞資訊</title> </head> <body> <div id="app"> <my-content :title='title' :content='content'></my-content> </div> <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> let vm=new Vue({ el:'#app', data:{ title:'標題', content:'內容' }, methods:{ }, computed:{ }, components:{ 'myContent':{ props:['title','content'], template:`<div> <h1>{{title}}</h1> <p>{{content}}</p> </div>` } } }) </script> </body> </html>
結果:顯示標題和內容,最簡單的父元件向子元件傳遞資訊我們就實現了,但是裡面還有其它的小知識點我們沒有講解到,而且後期開發都是使用vue-cli來實現父元件向子元件傳遞資料的,所以這個知識點下一篇部落格會講解到,不會搭建vue-cli專案的朋友可以參考這篇部落格使用webstorm搭建vue-cli專案後續的許多文章都會使用vue-cli中的元件進行講解,而不是通過簡單的引入vue.js檔案了,所以強烈推薦大家一定要學會搭建vue-cli專案。
總結:父元件向子元件傳遞資料使用props
(2)props傳遞整個物件
假設父元件中的物件含有多個屬性,我們每一個屬性都需要進行傳遞,那麼是不是需要繫結每一個屬性呢?
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>props傳遞多個屬性</title> </head> <body> <div id="app"> <my-content :title="attr.title" :content1="attr.content1" :content2="attr.content2"></my-content> </div> <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> let vm=new Vue({ el:'#app', data:{ attr:{ title:'新聞主題', content1:'新聞內容1', content2:'新聞內容2', } }, methods:{ }, computed:{ }, components:{ 'myContent':{ props:['title','content1','content2'], template:'<div><h4>{{title}}</h4><span>{{content1}}</span><span>{{content2}}</span></div>' } } }) </script> </body> </html>
現在這個例項所表現出來的就是一個物件裡面有很多個屬性,現在僅僅只有三個屬性而已,我們的父元件繫結屬性的時候就需要繫結三個屬性,如果是100個或者1000個那麼繫結元件的那個標籤不是很長嗎?和同事一起開發的話,你的同事看到那麼長的程式碼肯定會懷疑人生的,所以為了解決這個問題我們將程式碼改寫下面那樣。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>props傳遞多個屬性</title> </head> <body> <div id="app"> <my-content v-bind="attr"></my-content> </div> <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> let vm=new Vue({ el:'#app', data:{ attr:{ title:'新聞主題', content1:'新聞內容1', content2:'新聞內容2', } }, methods:{ }, computed:{ }, components:{ 'myContent':{ props:['title','content1','content2'], template:'<div><h4>{{title}}</h4><span>{{content1}}</span><span>{{content2}}</span></div>' } } }) </script> </body> </html>
在這裡我們使用v-bind將整個物件打包傳遞過去,這樣一來就大大的減少了程式碼的冗餘度了,那麼你的同事肯定誇你小夥子不錯,你就等著被領導表揚吧!(自己腦補出來的)
data必須是函式
為什麼說data必須是一個函式呢?這個知識點在上一篇部落格中沒有提及到,現在的話我們來討論一下,這個案例就足夠說明了。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>data必須是函式</title> </head> <body> <div id="app"> <my-content></my-content> </div> <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> Vue.component('myContent',{ data:{ msg:'hello world' }, template:'<span>{{msg}}</span>', }); let vm=new Vue({ el:'#app' }) </script> </body> </html>
結果:
控制檯顯示vue.js給出的警告也是提出data必須是一個函式,好的既然規定data必須是一個函式,那我們就按照它說的來做。看看結果如何
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>data必須是函式</title> </head> <body> <div id="app"> <my-content></my-content> <my-content></my-content> <my-content></my-content> </div> <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> var data={ count:1 } Vue.component('myContent',{ data:function(){ return data; }, template:'<button @click="count+=1">{{count}}</button>', }); let vm=new Vue({ el:'#app' }) </script> </body> </html>
現在的話,我們的data是一個函式,但是解決一個問題的同時新的問題又出現了,點選任意一個按鈕的時候發現其它按鈕的值都會發生改變,這是因為我們引用了同一個物件,我們知道物件是引用傳遞的,所以為了改變這一種情況,我們讓每個元件內部都有自己的狀態而不會去幹涉其它元件。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>data必須是函式</title> </head> <body> <div id="app"> <my-content></my-content> <my-content></my-content> <my-content></my-content> </div> <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> Vue.component('myContent',{ data(){ return{ count:1 } }, template:'<button @click="count+=1">{{count}}</button>', }); let vm=new Vue({ el:'#app' }) </script> </body> </html>
結果:
我們在data中使用return返回新的物件,這樣一來每一個元件都有自己本身的狀態了,從而不會去影響其它的元件。
元件的校驗
元件的校驗從字面上看就是對父元件向子元件傳遞的資訊中,子元件對父元件傳遞過來的資訊進行驗證,一方面是為了資料的安全性,另一方面給他人使用的時候也可以限制他人傳遞過來的資料的驗證。
Vue.component('example', { props: { // 基礎型別檢測 (`null` 意思是任何型別都可以) propA: Number, // 多種型別 propB: [String, Number], // 必傳且是字串 propC: { type: String, required: true }, // 數字,有預設值 propD: { type: Number, default: 100 }, // 陣列/物件的預設值應當由一個工廠函式返回 propE: { type: Object, default: function () { return { message: 'hello' } } }, // 自定義驗證函式 propF: { validator: function (value) { return value > 10 } } } })
type可選:
- Sting
- Number
- Boolean
- Function
- Object
- Array
(1)示例一
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>元件的校驗示例一</title> </head> <body> <div id="app"> <my-content :name="name" :age="age" :sex="sex"></my-content> </div> <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> let vm=new Vue({ el:'#app', data:{ name:'小明', age:18, sex:'男' }, methods:{ }, computed:{ }, components:{ 'myContent':{ props:{ name:{ type:Number, required:true, }, age:{ type:Number, default:20, }, sex:{ type:String, default:'女' } }, template:'<div><span>{{name}}</span><span>{{age}}</span><span>{{sex}}</span></div>' } } }) </script> </body> </html>
結果:
我們對name做了驗證規定,name的型別必須是Number,而傳遞過來的卻是String,所以vue.js給出了警告,對name屬性也要求是必須傳遞的引數,required:true,對sex給定預設值,當我們沒有傳遞sex的時候,子元件中的sex預設值是女的,所以有了元件的這一校驗,大大提高的資料的安全性。
(2)示例二
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>元件的校驗示例二</title> </head> <body> <div id="app"> <my-content name="喜馬拉雅" :size="8848" :is-boy="false" address="中國西藏"></my-content> </div> <script type="text/template" id="template1"> <div>姓名:{{name}}身高:{{size}}是否是男生:{{isBoy}}位置:{{address}}重量:{{weight.ton}}億噸</div> </script> <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> Vue.component('myContent',{ template:'#template1', props:{ name:{ type:String, //型別 required:true, //規定必填 }, size:Number, //規定整形 isBoy:Boolean, //規定布林值 age:[Number,String],// 多種型別 address:{ type:String, default:'中國', validator:function(value){ return value.indexOf('中國')>=0 } }, weight:{ type:Object, default:function(){ return {ton:999999999} } }, } }) let vm=new Vue({ el:'#app', data:{ }, methods:{ }, computed:{ } }) </script> </body> </html>
結果:
子元件向父元件傳遞資料
我們知道,父元件是使用 props 傳遞資料給子元件,但如果子元件要把資料傳遞回去,應該怎樣做?那就是自定義事件!每個 Vue 例項都實現了事件介面(Events interface),即:
- 使用
$on(eventName)
監聽事件 - 使用
$emit(eventName)
觸發事件
接下來我們一步一步對子元件向父元件傳遞資料進行講解,先來看下一個特別簡單的示例,這個需求是這樣的,當我們改變子元件的count的時候,父元件的count也需要改變
(1)子元件實現功能
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>子元件向父元件傳遞資料</title> </head> <body> <div id="app"> <my-content :count="count"></my-content> 父元件的count:<span>{{count}}</span> </div> <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> let vm=new Vue({ el:'#app', data:{ count:1 }, methods:{ }, computed:{ }, components:{ 'myContent':{ data(){ return{ ownCount:this.count } }, props:['count'], template:'<div>子元件的count:<span>{{ownCount}}</span><button @click="handleClick">add</button></div>', methods:{ handleClick(){ this.ownCount++; } } } } }) </script> </body> </html>
我們通過父元件向子元件傳遞了count值,然後子元件中的count值在點選之後會發生改變,但是這僅僅實現了子元件的值改變,並沒有實現父元件中的count改變
(2)向父元件傳遞資訊
需要向父元件傳遞資訊的話,我們就需要子元件通知父元件,然後父元件在做相應的處理,而需要通知父元件我們就i需要用到this.$emit('事件名稱',值)。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>子元件向父元件傳遞資料</title> </head> <body> <div id="app"> <my-content :count="count" @add="handleAdd"></my-content> 父元件的count:<span>{{count}}</span> </div> <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> let vm=new Vue({ el:'#app', data:{ count:1 }, methods:{ handleAdd(count){ this.count=count; } }, computed:{ }, components:{ 'myContent':{ data(){ return{ ownCount:this.count } }, props:['count'], template:'<div>子元件的count:<span>{{ownCount}}</span><button @click="handleClick">add</button></div>', methods:{ handleClick(){ this.ownCount+=10; this.$emit('add',this.ownCount); } } } } }) </script> </body> </html>
結果:
我們在子元件點選按鈕的時候添加了this.$emit()來通知父元件,然後將需要註冊的事件和每次改變的值傳遞了過去,這時父元件註冊子元件傳遞過來的事件和值,然後將父元件中的值替換子元件傳遞過來的就可以了。
(3)優化子元件向父元件傳遞資訊
其實我們可以做到讓父元件來控制子元件中count值的變化,假設我們每次讓子元件增加10,我們先來一個小案例
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>子元件向父元件傳遞資料</title> </head> <body> <div id="app"> <my-content :count="count" @add="handleAdd" v-bind="info"></my-content> 父元件的count:<span>{{count}}</span> <button @click="handleClick">點選</button> </div> <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> let vm=new Vue({ el:'#app', data:{ count:1, info:{ name:'男', age:18 } }, methods:{ handleAdd(count){ this.count=count; }, handleClick(){ this.info.name='女' } }, computed:{ }, components:{ 'myContent':{ data(){ return{ ownCount:this.count } }, props:['count','name','age'], template:'<div><span>姓名:{{name}}</span><br/>子元件的count:<span>{{ownCount}}</span><button @click="handleClick">add</button></div>', methods:{ handleClick(){ this.ownCount+=10; this.$emit('add',this.ownCount); } } } } }) </script> </body> </html>
結果:
當我們點選按鈕的時候發現父元件傳遞給子元件的姓名發生了改變,那麼我們只要改變父元件中的count的值,那麼子元件中count的值也會發生改變
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>子元件向父元件傳遞資料</title> </head> <body> <div id="app"> <my-content :count="count" @add="hanldeAdd"></my-content> 父元件中的count<span>{{count}}</span> </div> <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> let vm=new Vue({ el:'#app', data:{ count:1 }, methods:{ hanldeAdd(count){ this.count+=count; } }, computed:{ }, components:{ 'myContent':{ data(){ return{ //ownCount=this.count; } }, props:['count'], template:'<div>子元件中的count<span>{{count}}</span><button @click="handleClick">add</button></div>', methods:{ handleClick(){ this.$emit('add',10) } } } } }) </script> </body> </html>
這樣一來父子元件的簡單通訊就全部講完了,接下來為了鞏固一下父子元件之前通訊的知識,我們來做一個任務清單
總結:子元件向父元件傳遞資訊this.$emit('事件名稱',值)
(4)任務清單
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>任務清單</title> </head> <body> <div id="app"> 任務:<input type="text" v-model="newTask" @keyup.enter="addNew" placeholder="請輸入您要完成的任務" /> <ul> <li is="todoImte" v-for="(item,index) of tasks" :title="item" @remove="removeItem(index)"></li> </ul> </div> <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> Vue.component('todoImte',{ props:['title'], template:"<li>{{title}}<button @click='$emit(\"remove\")'>X</button></li>" }); let vm=new Vue({ el:'#app', data:{ newTask:'', tasks:['買一本書','寫一次部落格','和朋友一起吃飯'] }, methods:{ addNew(){ this.tasks.unshift(this.newTask); this.newTask=''; }, removeItem(index){ if(confirm('確定要刪除嗎?')){ this.tasks.splice(index); } } }, computed:{ } }) </script> </body> </html>
結果:
任務清單中,首先有個新增任務的框,然後通過addNew方法將任務新增到任務清單中,子元件中通過this.$emit()告訴父元件執行相應的方法,任務清單中,我們可以新增任務清單也可以刪除任務清單,新增任務清單的話,沒有涉及元件通訊,二刪除任務清單的時候,子元件通知父元件需要刪除那個任務,然後將需要刪除任務的索引傳遞過去,父元件根據傳遞過來的index進行刪除。
總結
本篇部落格主要講了三個知識點,元件的校驗,父元件向子元件傳遞資訊(通過props),子元件向父元件傳遞資訊(通過this.$emit),本篇部落格講解的內容也比較簡單,但是我認為這僅僅也還是元件的開始,下一遍部落格我們將會講解vue-cli中的元件通訊,畢竟這個才是重點,現在的元件通訊也才是入門。歡迎大家在部落格下方評論,我們一起交