內(nèi)容包含:v-model / component(全局注冊(cè)/局部注冊(cè)/父子組件之間傳遞數(shù)據(jù)/slot/動(dòng)態(tài)組件等..)
哈哈,又是 demo 和基礎(chǔ)概念理解。
邊寫 demo 邊理解總結(jié),花費(fèi)兩天終于完成,我要緩一天去學(xué)學(xué) webpack ,接著回來再戰(zhàn) Vue。
這邊博文會(huì)略長,因?yàn)橛嘘P(guān)于組件的各個(gè)例子的 demo~全部敲一遍才能理解得更快嘛。
你可以用 v-model 指令在表單<input>、<textarea> 及 <select> 元素上創(chuàng)建雙向數(shù)據(jù)綁定。它會(huì)根據(jù)控件類型自動(dòng)選取正確的方法來更新元素。
Vue 的官方文檔:v-model
v-model 的 demo :JSbin
Vue 關(guān)于 組件的官方文檔:Component
定義:組件是可復(fù)用的 Vue 實(shí)例
作用:提高代碼的復(fù)用性
關(guān)于 component 的 demo:JSbin
<div id="app"> <!-- 2.引入組件 --> <div-component></div-component></div>
// 1.注冊(cè)組件Vue.component('div-component',{ template:'<div>I\'m component</div>'})var app = new Vue({ el:'#app', data:{}})
優(yōu)點(diǎn):所有的 Vue實(shí)例都可以使用
缺點(diǎn):權(quán)限太大,容錯(cuò)率低
<div id="app"> <!-- 引入局部組件 --> <app-component></app-component></div>
var app = new Vue({ el:'#app', data:{}, // 注冊(cè)局部組件 components:{ 'app-component':{ template:'<div>I\'m 局部 component</div>' } }})
在 app實(shí)例 中創(chuàng)建的組件 只有在 app 掛載的 HTML 中才可以使用。
注意 HTML 中的某些標(biāo)簽受限:
比如 table 標(biāo)簽中嵌入組件的話將不會(huì)生效,因?yàn)樗锩嬷挥?tr/td/tbody 等屬性,除非使用 is 將組件傳遞進(jìn)去:
<div id="app"> <!-- 引入局部組件 --> <table> <tbody :is='app-component'></tbody> </table></div>
1.命名規(guī)則:推薦使用小寫加 - 的形式命名
2.template 中的內(nèi)容必須要用一個(gè) DOM 元素包裹,也可以嵌套
3.在組件的定義中,還可以使用除了 template 之外其他的屬性,如 data / computed / methods 等
4.一個(gè)組件中的 data 必須是一個(gè)函數(shù),返回一個(gè)對(duì)象(每個(gè)實(shí)例可以維護(hù)一份被返回對(duì)象的獨(dú)立的拷貝)
如:
<div id="app"> <!-- 引入局部組件 --> <app-component></app-component> <app-component></app-component></div>
var app = new Vue({ el:'#app', data:{}, // 注冊(cè)局部組件 components:{ 'app-component':{ template:`<div><div>I\'m 局部 component</div><button @click='countNum'>已點(diǎn)擊{{ count }}次</button></div>`, data:function(){ return { msg:'component 中的 屬性', count:0 } }, methods:{ countNum:function(){ this.count = 1 } } } }})
兩個(gè)按鈕每次點(diǎn)擊,都會(huì)維護(hù)各自的 count,而不會(huì)影響到其他所有實(shí)例。
demo1:
<div id="app"> <!-- 引入局部組件 --> <!-- 父組件傳遞數(shù)據(jù) --> <app-component msg='這是父組件給子組件傳遞的信息'></app-component> <app-component msg='msg'></app-component></div>
var app = new Vue({ el:'#app', data:{}, // 注冊(cè)局部組件 components:{ 'app-component':{ // 在子組件頁面中渲染數(shù)據(jù) template:`<div><div>I\'m 局部 component</div>{{ msg }}</div>`, // 子組件通過 props 接收參數(shù) props:[ 'msg' ] data:function(){ return { msg:'component 中的 屬性', count:0 } }, } }})
1.在組件中使用props來從父組件接收參數(shù),注意,在props中定義的屬性,都可以在組件中直接使用
2.propps來自父級(jí),而組件中data return的數(shù)據(jù)就是組件自己的數(shù)據(jù),兩種情況作用域就是組件本身,可以在template,computed,methods中直接使用
3.props的值有兩種,一種是字符串?dāng)?shù)組,一種是對(duì)象
4.可以使用v--bind動(dòng)態(tài)綁定父組件來的內(nèi)容:
// 1.創(chuàng)建 Vue 實(shí)例var app = new Vue({ el:'#app', data:{ posts: [ { id: 1, title: 'My journey with Vue' }, { id: 2, title: 'Blogging with Vue' }, { id: 3, title: 'Why Vue is so fun' } ] }, // 2.注冊(cè)子組件 components:{ 'app-component':{ // 3.子組件通過 props 接收參數(shù) props:[ 'id', 'titel' ], // 4.創(chuàng)建模板,渲染組件 template:`<div>{{ id }} - {{ title }}</div>` } }})
<div id='app'> <!-- 5.在父組件中引入子組件 --> <!-- 6.動(dòng)態(tài)綁定屬性,傳遞到子組件 --> <app-component v-for='item in posts' v-bind:id='item.id' v-bind:title='item.title'></app-component></div>
解釋 : 通過 props 傳遞數(shù)據(jù) 是單向的了, 也就是父組件數(shù)據(jù)變化時(shí)會(huì)傳遞給子組件,但是反過來不行。
目的 :是盡可能將父子組件解耦,避免子組件無意中修改了父組件的狀態(tài)。
應(yīng)用場(chǎng)景: 業(yè)務(wù)中會(huì)經(jīng)常遇到兩種需要改變 prop 的情況
父組件傳遞初始值進(jìn)來,子組件將它作為初始值保存起來,在自己的作用域下可以隨意使用和修改。這種情況可以在組件 data 內(nèi)再聲明一個(gè)數(shù)據(jù),引用父組件的 prop
步驟一:注冊(cè)組件
步驟二:將父組件的數(shù)據(jù)傳遞進(jìn)來,并在子組件中用props接收
步驟三:將傳遞進(jìn)來的數(shù)據(jù)通過 初始值 保存起來:
components:{ 'app-component':{ props:['msg'], data:function(){ return { Message:this.msg } }, template:`<div>{{ Message }}</div>` }}
prop 作為需要被轉(zhuǎn)變的原始值傳入。這種情況用計(jì)算屬性就可以了
步驟一:注冊(cè)組件
步驟二:將父組件的數(shù)據(jù)傳遞進(jìn)來,并在子組件中用props接收
步驟三:將傳遞進(jìn)來的數(shù)據(jù)通過 計(jì)算屬性 進(jìn)行重新計(jì)算
<div id='app'> <input text='text' v-model='width'> <app-component :width='width'></app-component></div>
var app = new Vue({ el:'#app', data:{ width:0 }, components:{ 'app-component':{ props:['width'], template:`<div :style='style'></div>`, computed:{ style:function(){ return { width:this.width 'px', background:'yellow', height:'20px' } } } } }})
因?yàn)楦附M件中的 input 用 v-model 綁定了父組件 data 中的 width,所以當(dāng)在 input 中輸入數(shù)字時(shí),width 也會(huì)跟著變化;而子組件中使用 :width 傳遞著父組件的 width,template 中使用了動(dòng)態(tài)綁定 :style 更新 div 的樣式,所以當(dāng) width 變化時(shí),計(jì)算屬性 style 開始計(jì)算,返回一個(gè)對(duì)象,重新渲染 div 的寬度 width。
vue組件中camelCased (駝峰式) 命名與 kebab-case(短橫線命名)
在html中, myMessage 和 mymessage 是一致的,因此在組件中的html中使用必須使用kebab-case(短橫線)命名方式。在html中不允許使用駝峰?。?/p>
<div id='app'> <app-component my-msg='xxx'></app-component></div>
在組件中, 父組件給子組件傳遞數(shù)據(jù)必須用短橫線。在template中,必須使用駝峰命名方式,若為短橫線的命名方式。則會(huì)直接保錯(cuò)。
components:{ props:['myMsg'], template:`<div>{{ myMsg }}</div>`}
在組件的data中,用this.XXX引用時(shí),只能是駝峰命名方式。若為短橫線的命名方式,則會(huì)報(bào)錯(cuò)。
components:{ props:['myMsg'], data:function(){ return { name = this.myMsg } }, template:`<div>{{ myMsg }}</div>`}
驗(yàn)證父組件傳遞給子組件的數(shù)據(jù)的類型:
String / Number / Boolean / Object / Array / Function
<div id='app'> <app-component :a='a' :b='b' :c='c' :d='d' :e='e' :f='f'></app-component></div>
var app = new Vue({ el:'#app', data:{ a:'1', b:1, c:true, d:{'name':'sgt'}, e:[], f:console.log(), g:67, }, components:{ 'app-component':{ // required 必傳 / default 默認(rèn) / type 類型 // 如果父組件沒有向子組件 傳遞數(shù)據(jù),則使用 default 值 props:{ // 傳入的 a 的值需要是 String/Number 類型,如果不是,則報(bào)錯(cuò) a:[String,Number] // 必須傳入 b 且 b 的值是 Number類型,如果不傳入(即沒有 :b='b'),則報(bào)錯(cuò);如果傳入是其他類型,則報(bào)錯(cuò) b:{type:Number,required:true}, // 傳入的值需為 Boolean 類型,如果不傳入(即沒有 :c='c'),則使用 default 值,true;如果傳入值不為 Boolean,則報(bào)錯(cuò) c:{type:Boolean,default:true}, // 傳入的值需為 Object 類型,如果不傳入(即沒有 :d='d'),則使用 default 值;如果傳入值不為 Object,則報(bào)錯(cuò) d:{type:Object,defult:function(){return {'name':'xxx'}}}, // 傳入的值需為 Array 類型,如果不傳入(即沒有 :e='e'),則使用 default 值;如果傳入值不為 Array,則報(bào)錯(cuò) e:{type:Array,defult:function(){return [666]}}, // 傳入的值需為 Function 類型 f:{type:Function} // 自定義驗(yàn)證函數(shù) g:{validator:function(value){return value>10}}, }, template:`<div>{{a}}-{}-{{c}}-{fu8ihs5fyo3}-{{e}}-{{f}}-{{g}}</div>` } }})
利用自定義事件,子組件傳遞數(shù)據(jù)給父組件
子組件用$emit()來觸發(fā)事件 ,父組件用$on()來監(jiān)昕子組件的事件
<div id="app"> <!-- 引入局部組件 --> <!-- 自定義事件 --> <app-component @change='handleTotal'></app-component></div>
var app = new Vue({ el:'#app', data:{ // 父組件中的數(shù)據(jù) total:1000 }, methods:{ handleTotal:function(value){ this.total = value } }, components:{ 'app-component':{ template:`<div> <button @click='handleCrease'>點(diǎn)擊 1000</button> <button @click='handleReduce'>點(diǎn)擊 -1000</button> </div>`, data:function(){ return { count:1000 } }, methods:{ handleCrease:function(){ this.count = 1000 this.$emit('change',this.count) }, handleReduce:function(){ this.count -= 1000 this.$emit('change',this.count) } } } }})
過程解析:在子組件中定義了一個(gè)數(shù)據(jù) count 和兩個(gè)監(jiān)聽事件 handleCrease / handleReduce,每當(dāng)點(diǎn)擊按鈕,觸發(fā)監(jiān)聽事件,則改變 count 的值,并且把改變后的 count 作為參數(shù)通過 $emit 傳給自定義事件 change,change 綁定的是父組件中的方法 handleTotal,所以他執(zhí)行了父組件中的方法 handleTotal,把父組件中的 total 值修改成通過自定義事件傳遞進(jìn)來的參數(shù) count,實(shí)現(xiàn)子組件向父組件傳遞信息。
其實(shí)使用 v-model 可以用更少的代碼實(shí)現(xiàn)上面的功能,而且不需要自定義事件:
<div id="app"> <!-- 引入局部組件 --> <!-- 自定義事件 --> <app-component v-model='total'></app-component></div>
var app = new Vue({ el:'#app', data:{ // 父組件中的數(shù)據(jù) total:1000 }, components:{ 'app-component':{ template:`<div> <button @click='handleCrease'>點(diǎn)擊 1000</button> <button @click='handleReduce'>點(diǎn)擊 -1000</button> </div>`, data:function(){ return { count:1000 } }, methods:{ handleCrease:function(){ this.count = 1000 this.$emit('input',this.count) }, handleReduce:function(){ this.count -= 1000 this.$emit('input',this.count) } } } }})
$emit的代碼,實(shí)際上會(huì)觸發(fā)一個(gè)input事件, ‘input’后的參數(shù)就是傳遞給v--model綁定的屬性的值
v--model 其實(shí)是一個(gè)語法糖,這背后其實(shí)做了兩個(gè)操作:
1.v--bind 綁定一個(gè) value 屬性
2.v--on 指令給當(dāng)前元素綁定 input 事件
<input v-model="searchText"><!-- 等價(jià)于 --><input v-bind:value="searchText" v-on:input="searchText = $event.target.value">
要使用v--model,要做到:
接收一個(gè) value 屬性。
在有新的 value 時(shí)觸發(fā) input 事件
有時(shí)候兩個(gè)組件也需要通信(非父子關(guān)系),在簡(jiǎn)單的場(chǎng)景下,可以使用一個(gè)空的Vue實(shí)例作為中央事件總線
<div id="app"> <app-component></app-component> <bpp-component></bpp-component></div>
var app = new Vue({ el:'#app', data:{ // 1.創(chuàng)建空的 Vue 實(shí)例,this.$root 代表著當(dāng)前組件樹的根 Vue 實(shí)例。如果當(dāng)前實(shí)例沒有父實(shí)例,此實(shí)例將會(huì)是其自己。 bus:new Vue() } components:{ 'app-component':{ data:function(){ return { a:'通訊信息 SOS' } }, template:`<div><button @click='handleA2B'>點(diǎn)擊我從 A 組件發(fā)送到 B 組件</button></div>`, methods:{ // 2.點(diǎn)擊執(zhí)行函數(shù),觸發(fā) $emit ,把 this.a 當(dāng)成參數(shù)傳遞給 aEvent事件 handleA2B:function(){ this.$root.bus.$emit('aEvent',this.a) } } }, 'bpp-component':{ template:`<div>我是 B 組件</div>` // 3.B 組件在實(shí)例創(chuàng)建的時(shí)候就監(jiān)聽 aEvent 事件,一旦監(jiān)聽到變化,即值傳入函數(shù)進(jìn)行處理 created:function(){ this.$root.bus.$on('aEvent',function(value){ alert('我是 B 組件,已接收到 A 組件發(fā)出的信息:' value) }) } } }})
父實(shí)例,如果當(dāng)前實(shí)例有的話。子組件可以拿到父組件中的內(nèi)容
<div id="app"> {{ msg }} <app-component></app-component></div>
var app = new Vue({ el:'#app', data:{ msg:'我是父組件中的 msg' }, components:{ 'app-component':{ template:`<div><button @click='changeFather'>點(diǎn)擊設(shè)置父組件中的 msg </button></div>`, methods:{ changeFather:function(){ this.$parent.msg = '哈哈,我已經(jīng)改變了這個(gè) msg' } } } }})
提供了為子組件提供索引的方法,用特殊的屬性ref為其增加一個(gè)索引,拿到子組件中的內(nèi)容
<div id="app"> {{ msg }} <button @click='changeChild'>點(diǎn)擊拿到子組件中的 msg</button> <app-component ref='a'></app-component> <bpp-component ref='b'></bpp-component></div>
var app = new Vue({ el:'#app', data:{ msg:'這是父組件原始的 msg' } methods:{ changeChild:function(){ // 這里是 refs,而不是 ref this.msg = this.$refs.a.msg } }, components:{ 'app-component':{ data:function(){ return { msg:'這是子組件中的 msg' } }, } }})
為了讓組件可以組合,我們需要一種方式來混合父組件的內(nèi)容與子組件自己的模板。這個(gè)過程被稱為 內(nèi)容分發(fā)。Vue.js 實(shí)現(xiàn)了一個(gè)內(nèi)容分發(fā) API,用特殊的 ‘slot’ 元素作為原始內(nèi)容的插槽。
父組件的內(nèi)容與子組件相混合,從而彌補(bǔ)了視圖的不足。
插槽嘛,顧名思義,就是在子組件中的 template 中開一個(gè)槽,槽里面可以不放數(shù)據(jù),也可以用放默認(rèn)的數(shù)據(jù);當(dāng)父組件往子組件里面插數(shù)據(jù)的時(shí)候,這個(gè)數(shù)據(jù)就會(huì)安放在槽里;如果父組件沒有網(wǎng)子組件里面插數(shù)據(jù),則顯示槽里面的默認(rèn)內(nèi)容。
<div id="app"> <app-component> <span>這是父組件安插在子組件中的內(nèi)容</span> </app-component></div>
var app = new Vue({ el:'#app', components:{ 'app-component':{ template:`<div>我是組件 A <slot>如果父組件沒有插入內(nèi)容,則顯示此條消息</slot></div>` } }})
<div id="app"> <app-component> <h2 slot='header'>具名插槽</h2> <span>這是父組件安插在子組件中的內(nèi)容</span> <div slot='footer'>插槽尾部</div> </app-component></div>
var app = new Vue({ el:'#app', components:{ 'app-component':{ template:` <div>我是組件 A <div class='header'> <slot name='header'></slot> </div> <div class='container'> <slot>如果父組件沒有插入內(nèi)容,則顯示此條消息</slot> </div> <div class='footer'> <slot name='footer'></slot> </div> </div>` } }})
作用域插槽是一種特殊的slot,使用一個(gè)可以復(fù)用的模板來替換已經(jīng)渲染的元素 - 從子組件獲取數(shù)據(jù)
template模板是不會(huì)被渲染的,在 Vue 2.5 之后可以直接用 標(biāo)簽 slot-scope
獲取 slot 中的數(shù)組,而不是指定的 template 模板了
<div id="app"> <app-component> <p slot='abc' slot-scope='temp'> 這是 slot 中的內(nèi)容 {{ temp.text }} {{ temp.prop }} </p> </app-component></div>
var app = new Vue({ el:'#app', components:{ 'app-component':{ template:` <div> <slot name='abc' text='這是 slot 中的 text' prop='這是 slot 中的 prop'></slot> </div>` } }})
通過this.$slots.(插槽名稱)可以訪問 slot
<div id="app"> <app-component> <p slot='abc'> slot is ready. </p> </app-component></div>
var app = new Vue({ el:'#app', components:{ 'app-component':{ template:` <div> <slot name='abc'></slot> </div>`, // 在 Vue 實(shí)例掛載后 mounted:function(){ let abc = this.$slots.abc // 獲取了一個(gè) Vue 所特有的 VNODE 節(jié)點(diǎn) let text = abc[0].ele.innerHTML // 根據(jù)節(jié)點(diǎn) API 獲取內(nèi)容 console.log(text) // slot is ready. } } }})
Vue 給我們提供 了一個(gè)元素叫 component
作用是: 用來動(dòng)態(tài)的掛載不同的組件
實(shí)現(xiàn):使用is特性來進(jìn)行實(shí)現(xiàn)的
<div id="app"> <!-- :is 綁定的是組件名 --> <component :is='thisView'></component> <button @click='changeTo("A")'>點(diǎn)擊跳到組件A</button> <button @click='changeTo("B")'>點(diǎn)擊跳到組件B</button></div>
var app = new Vue({ el:'#app', data:{ // 默認(rèn)顯示的組件 thisView:'componentA' }, methods:{ // 點(diǎn)擊后顯示改變后的組件 changeTo:function(value){ this.thisView = 'component' value } }, components:{ 'componentA':{ template:` <div> <slot name='abc'>這是組件A</slot> </div>`, }, 'componentB':{ template:` <div> <slot name='cba'>這是組件B</slot> </div>`, } }})
關(guān)于 v-model 和 component 的內(nèi)容就到這里啦~有更深入的理解的話會(huì)回來更新的~
來源:http://www.icode9.com/content-1-37301.html聯(lián)系客服