繼前幾天學習了指令,屬性學習後,這兩天又深刻學習了組件。每次學習事後發一篇文章,本身對知識點的記憶也更加深入了,同時也但願本身的分享可以幫助到其餘人html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>組件中的細節點</title> <script src="../vue.js"></script> </head> <body> <div id="root"> <table> <tbody> <row></row> <row></row> <row></row> </tbody> </table> </div> <script> Vue.component('row', { template:'<tr><td>this is a row</td></tr>' }) var vm = new Vue({ el:'#root' }) </script> </body> </html> 複製代碼
上述代碼將this is a row封裝成一個名爲row的Vue全局組件,並在tbody中引用了row組件,使用時徹底符合html5頁面規範table裏面有tbody,tbody裏面有tr,頁面顯示出來的結果以下,看起來也沒有任何問題vue
<tbody> <tr is="row"></tr> <tr is="row"></tr> <tr is="row"></tr> </tbody> 複製代碼
這個時候在tr後面加一個is屬性讓它等於row就行了,意思就是雖然我是tr可是我實際上是一個row組件,這樣既可以保證tr裏面顯示的是咱們的row組件又保證咱們符合h5的編碼規範,程序不會有BUG。而且建議像ul、ol、select下面也不要直接使用row這個組件,由於有些瀏覽器會顯示bug,因此就用Vue提供的is屬性就行了html5
在組件中使用data以下vuex
Vue.component('row', { data:{ content:'this is arow' }, template: '<tr><td>{{content}}</td></tr>' }) 複製代碼
頁面渲染報錯數組
data() { return { content: 'this is a row' } }, 複製代碼
Vue不建議咱們在代碼裏面去操做dom,可是在處理一些及其複雜的動做效果時,你不操做dom光靠Vue的數據綁定,有的時候處理不了這樣的狀況,因此在必要狀況下須要操做dom。那麼咱們如何操做dom呢?咱們須要經過ref這種引用的形式獲取到進行dom的操做。瀏覽器
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>組件中的細節點</title> <script src="../vue.js"></script> </head> <body> <div id="root"> <div ref='hello' @click="handleClick">hello world </div> </div> <script> var vm = new Vue({ el: '#root', methods: { handleClick: function() { console.log(this.$refs.hello) } }, }) </script> </body> </html> 複製代碼
咱們在div上添加了一個ref屬性並命名爲hello,而且綁定了一個名爲handleClick的點擊方法,在Vue實例的methods方法中打印了一串代碼。bash
this.$refs.hello 複製代碼
這個代碼的意思就是整個Vue實例裏面全部的引用裏面的名爲hello的引用,這個引用只的就是被修飾的div的DOM結點,因此下面的this.$refs.hello指向的就是相對應的div的dom結點,打印結果以下markdown
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>組件中的細節點</title> <script src="../vue.js"></script> </head> <body> <div id="root"> <!-- 當子組件觸發change事件時,父組件能監聽到這個事件並執行handleChange這個方法 --> <counter ref="one" @change="handleChange"></counter> <counter ref="two" @change="handleChange"></counter> <div>{{total}}</div> </div> <script> Vue.component('counter', { template: '<div @click="handleClick">{{number}}</div>', data() { return { number: 0 } }, methods: { handleClick: function() { this.number ++ // 子組件向父組件傳值,這裏的意思是當子組件觸發事件時同時告訴外面觸發一個事件 this.$emit('change') } }, }) var vm = new Vue({ el: '#root', data:{ total: 0 }, methods:{ handleChange: function() { this.total = this.$refs.one.number + this.$refs.two.number } } }) </script> </body> </html> 複製代碼
上述代碼中封裝了一個counter組件實現了一個點擊實現num++的功能,在html引用了兩次counter組件。咱們須要實現的是在父組件中計算兩個counter組件中值的和。這時就須要在子組件上添加ref屬性。而後再父組件中用 this.$refs.(ref名稱)獲取組件並經過.number獲取每一個引用組件各自的number值並相加實現求total的功能。app
父組件如何向子組件傳值呢? Vue中父組件向子組件傳都是經過屬性的形式向子組件傳值的框架
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>父子組件傳值</title> <script src="../vue.js"></script> </head> <body> <div id="root"> <!-- count前面加了冒號,傳遞給子組件的就是數組,不加冒號傳遞給子組件的就是字符串,由於加了冒號後,後面的雙引號的內容其實是一個js表達式了就不是一個字符串了 --> <counter :count="0"></counter> <counter :count="1"></counter> </div> <script> var counter = { // 在子組件中寫一個props(用來接收父組件傳遞過來的內容),接收完之後就能夠在子組件中直接使用了 props:['count'], template: '<div>{{count}}</div>' } var vm = new Vue({ el: '#root', components:{ counter } }) </script> </body> </html> 複製代碼
上面栗子,聲明瞭一個局部counter組件並掛載到了vm根實例中並在頁面引用了counter組件,其中父組件經過在counter組件中添加屬性:count="0/1"(count前面加了冒號,傳遞給子組件的就是數組,不加冒號傳遞給子組件的就是字符串,由於加了冒號後,後面的雙引號的內容其實是一個js表達式了就不是一個字符串了)。那麼子組件如何接收父組件的傳值呢?子組件須要在內部寫一個props用來接收父組件傳遞過來的內容。這樣就能在子組件內部使用啦,這時候咱們再修改一會兒組件中的代碼
var counter = { // 在子組件中寫一個props(用來接收父組件傳遞過來的內容),接收完之後就能夠在子組件中直接使用了 props:['count'], template: '<div @click="handleClick">{{count}}</div>', methods: { handleClick: function() { this.count ++ } }, } 複製代碼
在子組件中添加一個方法實現點擊+1的效果。你會發現頁面上實現正確,可是打開控制檯就有報錯信息了
var counter = { // 在子組件中寫一個props(用來接收父組件傳遞過來的內容),接收完之後就能夠在子組件中直接使用了 props:['count'], data() { return { // 複製了一份count放到子組件本身的data中 number:this.count } }, template: '<div @click="handleClick">{{number}}</div>', methods: { handleClick: function() { this.number ++ } }, } 複製代碼
咱們須要在子組件的data中自定義一個number複製一份父組件傳遞過來的count,這樣就能夠在組件中使用子組件本身number了。這樣控制檯就不會報錯啦。
子組件向父組件傳值是經過**$emit()**方法來實現對外暴露的
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>父子組件傳值</title> <script src="../vue.js"></script> </head> <body> <div id="root"> <counter :count="3" @inc="handleIncrease"></counter> <counter :count="2" @inc="handleIncrease"></counter> <div>{{total}}</div> </div> <script> var counter = { props:['count'], data() { return { // 複製了一份count放到子組件本身的data中 number:this.count } }, template: '<div @click="handleClick">{{number}}</div>', methods: { handleClick: function() { this.number = this.number + 2 // 每次觸發事件時都告訴外部觸發了一個inc事件,而且攜帶了一個參數(能夠攜帶多個參數) this.$emit('inc',2) } }, } var vm = new Vue({ el: '#root', data:{ total: 5 }, components:{ counter }, methods:{ // 子組件觸發inc事件時傳遞了一個參數,這時就能夠在父組件中的方法中接收這個參數了 handleIncrease:function(num) { this.total+=num } } }) </script> </body> </html> 複製代碼
代碼中在子組件counter中的handleClick方法中經過this.$emit(inc,2)向父組件暴露了一個inc事件,而且攜帶了一個參數2(不只僅能攜帶一個參數,它能夠攜帶多個參數)。而在父組件中
<counter :count="3" @inc="handleIncrease"></counter> <counter :count="2" @inc="handleIncrease"></counter> 複製代碼
經過handleIncrease來監聽inc事件,在vm根實例中的methods方法中定義handleIncrease方法,因爲子組件向父組件傳遞了參數,因此handleIncrease能夠在方法中接收這個參數並使用。
什麼是組件的參數校驗? 父組件向子組件傳遞了內容,那麼子組件有權對這些內容進行約束,這些約束就是參數的校驗。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>組件參數校驗與非Props特性</title> <script src="../vue.js"></script> </head> <body> <div id="root"> <child content="hello world"></child> </div> <script> Vue.component('child', { props: { // content:String,//子組件接收到的content必須是String類型的,若是不是String類型那麼當子組件用了content值得時候控制檯將會發出警告。 // 若是須要給定多個類型則須要把類型放入數組中 // content:[ Number, String ], // 固然這些約束還能夠更加複雜,實際上還能夠跟一個對象 content:{ type:String, //type規定類型 required:true, //required則表示這個參數是必傳的,若是父組件不傳這個屬性則會報錯 default:'default value', //當required爲false時(接收到的屬性值不是必傳的),設置default則會在父組件沒傳值的時候用默認值(default value)代替 validator:function(value) { return (value.length > 5 ) } } }, template:`<div> <p>{{content}}</p> </div>` }) var vm = new Vue({ el: '#root' }) </script> </body> </html> 複製代碼
上面的代碼中父組件向子組件傳遞了一個content屬性。那麼咱們如何在子組件中對它進行約束呢?首先咱們須要把prop定義成一個對象。最簡單的就是隻約束類型而且類型只有一個了。下面的這段代碼的意思是子組件接收到的content必須是String類型的,若是不是String類型那麼當子組件用了content值得時候控制檯將會發出警告。
props: {
conten:String
}
複製代碼
若是要給content規定多個類型呢?只須要在後面使用數組就好了
content:[ Number, String ]
複製代碼
固然這些約束還能夠更加複雜,實際上還能夠跟一個對象。能夠看下面這段代碼以及其中的註釋
content:{ type:String, //type規定類型 required:true, //required則表示這個參數是必傳的,若是父組件不傳這個屬性則會報錯 default:'default value', //當required爲false時(接收到的屬性值不是必傳的),設置default則會在父組件沒傳值的時候用默認值(default value)代替 validator:function(value) { return (value.length > 5 ) } } }, 複製代碼
什麼叫作非Pops特性和Props特性呢? Props特性指的是當你的父組件使用子組件的時候,經過屬性屬性向子組件傳值的時候剛好子組件裏面聲明瞭對父組件傳遞過來的屬性的接收。非Pops特性值的時父組件向子組件傳遞了一個屬性,可是子組件並無聲明接收父組件傳遞過來的內容。
先看一段代碼
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>給子組件綁定原生事件</title> <script src="../vue.js"></script> </head> <body> <div id="root"> <child @click="handleClick"></child> </div> <script> Vue.component('child', { template:` <div> Child </div> ` }) var vm = new Vue({ el:'#root', methods: { handleClick: function() { alert('點擊了') } }, }) </script> </body> </html> 複製代碼
上面的代碼乍一眼看當咱們點擊child組件的dom時,會觸發父組件上的點擊事件方法。可是運行上面的代碼時會發如今父組件中定義的點擊事件是沒有用的。這是怎麼一回事呢?當給一個組件綁定一個事件的時候,實際上這個事件綁定的時一個自定義的事件,也就是說若是你的鼠標點擊觸發的事件,並非綁定的click事件,若是想要觸發自定義的click事件,必須在子組件裏面對須要觸發事件的dom元素進行事件的綁定,這個事件纔是真正的原生事件。添加下面部分代碼
Vue.component('child', { template:` <div @click="handleChildClick"> Child </div> `, methods:{ handleChildClick: function() { alert("child click") } } }) 複製代碼
點擊dom元素時child click 被打印出來了子組件的原生事件被觸發了,而父組件的事件沒有被觸發
this.$emit('click') 複製代碼
只須要在子組件methods中調用$click(由於自定義事件的名稱爲click,這裏的名字必須與自定義事件名稱保持一致),這樣父組件中的事件就能被調用了,這樣當點擊child組件綁定了handleChildClick事件的dom元素就會再打印下面這條消息了
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>給子組件綁定原生事件</title> <script src="../vue.js"></script> </head> <body> <div id="root"> <!-- 在組件上加一個native修飾符,表示監聽的是原生的點擊事件 --> <child @click.native="handleClick"></child> </div> <script> Vue.component('child', { template:` <div @click="handleChildClick"> Child </div> ` }) var vm = new Vue({ el:'#root', methods: { handleClick: function() { alert('點擊了') } }, }) </script> </body> </html> 複製代碼
在代碼中的自定義事件上加上**.native**修飾就表示觸發的是原生的點擊事件了,這就是給組件綁定原生事件的方法了
<child @click.native="handleClick"></child> 複製代碼
一個網頁能夠拆分爲不少部分,每個部分就是代碼中的組件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>非父子組件間的傳值(Bus/總線/發佈訂閱模式/觀察者模型)</title> <script src="../vue.js"></script> </head> <body> <div id="app"> <child content='Yu'></child> <child content='Han'></child> </div> <script> // 在Vue的prototype上掛載了一個名字叫作bus的屬性,而且這個屬性指向Vue()的實例,只要咱們以後去調用new.vue或者建立組件的時候,每一個組件都會有bus這個屬性 // 由於每一個組件或者每一個Vue實例都是經過類來建立的,而我在vue的prototype上掛載了一個bus屬性 Vue.prototype.bus = new Vue() Vue.component('child',{ props:{ content:String }, data() { return { selfcontent:this.content } }, template:'<div @click="handleClick">{{selfcontent}}</div>', methods: { handleClick:function() { this.bus.$emit('change',this.selfcontent) //由於bus是vue的實例,因此可使用$emit } }, // 聲明一個mounted生命鉤子函數 mounted:function() { // 在鉤子函數中this指向會發生改變因此須要作如下工做 var this_ = this this.bus.$on('change',function(msg) { this_.selfcontent = msg // })//由於bus是個實例因此有$on的方法,它就能監聽到bus上面觸發出來的事件(change),而且能接收帶過來的參數 }, }) var vm = new Vue({ el: '#app' }) </script> </body> </html> 複製代碼
this.bus.$emit('change',this.selfcontent) 複製代碼
this.bus.$on('change',function(msg) { this_.selfcontent = msg // })//由於bus是個實例因此有$on的方法,它就能監聽到bus上面觸發出來的事件(change),而且能接收帶過來的參數 複製代碼
當使用組件時須要往裏面添加一些內容,不只能夠用父組件傳值的方法,還能夠利用插槽來實現
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Vue中的插槽(slot)</title> <script src="../vue.js"></script> </head> <body> <div id="app"> <child> <p>Yu</p> </child> </div> <script> Vue.component('child',{ template:`<div> <P>HELLO</P> <slot>默認內容</slot> </div>` }) var vm = new Vue({ el: '#app' }) </script> </body> </html> 複製代碼
上面的代碼在child組件中插入了p標籤和Yu內容,如何在組件中接收這個內容呢只須要在模板中使用slot就好了。而且能夠在slot標籤中寫上默認內容,當不傳遞插槽內容的時候則會顯示默認內容。再來看一個
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Vue中的插槽(slot)</title> <script src="../vue.js"></script> </head> <body> <div id="app"> <contentx> <div class="header">header</div> <div class="footer">footer</div> </contentx> </div> <script> Vue.component('contentx',{ template:`<div> <slot></slot> <div class='content'>content</div> <slot></slot> </div>` }) var vm = new Vue({ el: '#app' }) </script> </body> </html> 複製代碼
上面這段代碼的目的是在頁面上顯示出一個header、一個content、一個footer。結果倒是
<div id="app"> <contentx> <div class="header" slot="header">header</div> <div class="footer" slot="footer">footer</div> </contentx> </div> <script> Vue.component('contentx',{ template:`<div> <slot name='header></slot> <div class='content'>content</div> <slot name='footer'></slot> </div>` }) var vm = new Vue({ el: '#app' }) 複製代碼
當一個組件中某些標籤元素是經過循環自身data中的某個數組來進行渲染的時候,而這個組件又須要給其餘組件使用,這時這個組件中的模板樣式就不能被寫死,由於每一個組件須要循環的次數是不同的。或者組件中的某一部分須要經過外部傳遞進來的時候,這時候就須要用到做用域插槽了
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Vue中的做用域插槽</title> <script src="../vue.js"></script> </head> <body> <div id="app"> <child> <!-- 必定要在最外層套用一個template,這時固定寫法,同時要寫一個slot-scope="自定義名字" 它的意思是當子組件用slot的時候會往slot裏面傳遞一個item數據,這樣就可使用item了,而item是放在slot-scope="自定義名字"當中--> <!-- 這個時候每一項應該顯示成什麼,就不禁子組件決定了,而是父組件調子組件的時候給子組件去傳遞對應的模板 --> <template slot-scope="props"> <li>{{props.item}} - hello</li> </template> </child> </div> <script> Vue.component('child',{ data() { return { list:[1,2,3,4] } }, template:`<div> <ul> <slot v-for="item of list" :item=item></slot> </ul> </div>` }) var vm = new Vue({ el: '#app' }) </script> </body> </html> 複製代碼
但願這篇文章能對您有所幫助,太困了晚安