學(xué)習(xí)心得,
組件(二),
直接上代碼了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Prop</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app-1">
<div is="child1" message="HI~~Vue.js"></div>
</div>
<hr>
<div id="app-2">
<!-- 在 HTML 中使用 kebab-case -->
<div is="child2" my-message="Hello~~2"></div>
</div>
<hr>
<div id="app-3">
<input v-model="parentMsg">
<br>
<div is="child3" v-bind:my-message="parentMsg" v-bind="todo"></div>
</div>
<hr>
<!--字面量語法 vs 動態(tài)語法-->
<!--初學(xué)者常犯的一個(gè)錯(cuò)誤是使用字面量語法傳遞數(shù)值:-->
<!--<!– 傳遞了一個(gè)字符串 "1" –>-->
<!--<comp some-prop="1"></comp>-->
<!--因?yàn)樗且粋€(gè)字面量 prop伍纫,它的值是字符串 "1" 而不是一個(gè)數(shù)值贯涎。如果想傳遞一個(gè)真正的 JavaScript 數(shù)值,則需要使用 v-bind索绪,從而讓它的值被當(dāng)作 JavaScript 表達(dá)式計(jì)算:-->
<!--<!– 傳遞真正的數(shù)值 –>-->
<!--<comp v-bind:some-prop="1"></comp>-->
<!--非 Prop 特性-->
<!--所謂非 prop 特性,就是指它可以直接傳入組件裆针,而不需要定義相應(yīng)的 prop帐萎。-->
<!--盡管為組件定義明確的 prop 是推薦的傳參方式,組件的作者卻并不總能預(yù)見到組件被使用的場景弥锄。-->
<!--所以,組件可以接收任意傳入的特性蟆沫,這些特性都會被添加到組件的根元素上籽暇。-->
<!--例如,假設(shè)我們使用了第三方組件 bs-date-input饭庞,它包含一個(gè) Bootstrap 插件戒悠,該插件需要在 input 上添加 data-3d-date-picker 這個(gè)特性。-->
<!--這時(shí)可以把特性直接添加到組件上 (不需要事先定義 prop):-->
<!--<bs-date-input data-3d-date-picker="true"></bs-date-input>-->
<!--添加屬性 data-3d-date-picker="true" 之后舟山,它會被自動添加到 bs-date-input 的根元素上救崔。-->
<div id="app-4">
<span is="bs-date-input" data-3d-date-picker="true" class="date-picker-theme-dark"></span>
</div>
<hr>
<div id="app-5">
<p>{{totals}}</p>
<button-counter v-on:increments="incrementTotals"></button-counter>
<button-counter v-on:increments="incrementTotals"></button-counter>
</div>
<hr>
<!--.sync 修飾符-->
<!--<comp :foo.sync="bar"></comp>-->
<!--使用自定義事件的表單輸入組件-->
<!--自定義事件可以用來創(chuàng)建自定義的表單輸入組件,使用 v-model 來進(jìn)行數(shù)據(jù)雙向綁定捏顺。要牢記:-->
<!--<input v-model="something">-->
<!--這不過是以下示例的語法糖:-->
<!--<input-->
<!--v-bind:value="something"-->
<!--v-on:input="something = $event.target.value">-->
<!--所以在組件中使用時(shí),它相當(dāng)于下面的簡寫:-->
<!--<custom-input-->
<!--v-bind:value="something"-->
<!--v-on:input="something = arguments[0]">-->
<!--</custom-input>-->
<div id="app-6">
<currency-input v-model="price"></currency-input>
</div>
<hr>
<div id="app-7">
<my-checkbox v-model="foo" value="some value"></my-checkbox>
</div>
<hr>
<script>
//使用Prop傳遞數(shù)據(jù)
//組件實(shí)例的作用域是孤立的纬黎。
//這意味著不能 (也不應(yīng)該) 在子組件的模板內(nèi)直接引用父組件的數(shù)據(jù)幅骄。
//父組件的數(shù)據(jù)需要通過 prop 才能下發(fā)到子組件中。
//子組件要顯式的用props選項(xiàng)聲明它預(yù)期的數(shù)據(jù):
Vue.component('child1',{
//聲明 props
props:['message'],
//就像data一樣,prop也可以用在模板中使用
//同樣也可以在 vm 實(shí)例中通過 this.message 來使用
template:'<span>{{message}}</span>'
});
var app1 = new Vue({
el:"#app-1"
});
// camelCase vs. kebab-case
//
// HTML 特性是不區(qū)分大小寫的本今。
// 所以拆座,當(dāng)使用的不是字符串模板時(shí),camelCase (駝峰式命名) 的 prop 需要轉(zhuǎn)換為相對應(yīng)的 kebab-case (短橫線分隔式命名):
Vue.component('child2',{
// 在 JavaScript 中使用 camelCase
props:['myMessage'],
template:'<span>{{myMessage}}</span>'
});
var app2 = new Vue({
el:"#app-2"
});
//動態(tài)Prop
//與綁定到任何普通的 HTML 特性相類似冠息,
// 我們可以用 v-bind 來動態(tài)地將 prop 綁定到父組件的數(shù)據(jù)挪凑。
// 每當(dāng)父組件的數(shù)據(jù)變化時(shí),該變化也會傳導(dǎo)給子組件:
// 如果你想把一個(gè)對象的所有屬性作為 prop 進(jìn)行傳遞逛艰,
// 可以使用不帶任何參數(shù)的 v-bind (即用 v-bind 而不是 v-bind:prop-name)躏碳。
// 例如,已知一個(gè) todo 對象:
var app3 = new Vue({
el:"#app-3",
data:{
parentMsg:'',
todo:{
text:'Learn Vue',
isComplete:false
}
},
components:{
child3:{
props:['myMessage','text'],
template:'<span>{{myMessage}},{{text}}</span>'
}
}
});
//單項(xiàng)數(shù)據(jù)流
// Prop 是單向綁定的:當(dāng)父組件的屬性變化時(shí)散怖,將傳導(dǎo)給子組件菇绵,但是反過來不會。
// 這是為了防止子組件無意間修改了父組件的狀態(tài)镇眷,來避免應(yīng)用的數(shù)據(jù)流變得難以理解咬最。
//另外,每次父組件更新時(shí)欠动,子組件的所有 prop 都會更新為最新值永乌。
// 這意味著你不應(yīng)該在子組件內(nèi)部改變 prop。如果你這么做了,Vue 會在控制臺給出警告翅雏。
//在兩種情況下圈驼,我們很容易忍不住想去修改 prop 中數(shù)據(jù):
//Prop 作為初始值傳入后,子組件想把它當(dāng)作局部數(shù)據(jù)來用枚荣;
//Prop 作為原始數(shù)據(jù)傳入碗脊,由子組件處理成其它數(shù)據(jù)輸出啦逆。
//對這兩種情況顾画,正確的應(yīng)對方式是:
//1.定義一個(gè)局部變量,并用 prop 的值初始化它:
// props: ['initialCounter'],
// data: function () {
// return { counter: this.initialCounter }
// }
// 2.定義一個(gè)計(jì)算屬性叨咖,處理 prop 的值并返回:
//props: ['size'],
// computed: {
// normalizedSize: function () {
// return this.size.trim().toLowerCase()
// }
// }
// 注意在 JavaScript 中對象和數(shù)組是引用類型害碾,指向同一個(gè)內(nèi)存空間矢劲,
// 如果 prop 是一個(gè)對象或數(shù)組,在子組件內(nèi)部改變它會影響父組件的狀態(tài)慌随。
//Prop驗(yàn)證
//我們可以為組件的 prop 指定驗(yàn)證規(guī)則芬沉。
//如果傳入的數(shù)據(jù)不符合要求,Vue 會發(fā)出警告阁猜。
//這對于開發(fā)給他人使用的組件非常有用丸逸。
//要指定驗(yàn)證規(guī)則,需要用對象的形式來定義 prop剃袍,而不能用字符串?dāng)?shù)組:
Vue.component('child3',{
props:{
//基礎(chǔ)類型檢測('null',指允許任何類型)
propA: Number,
//可能是多種類型
propB: [String, Number],
//必傳且是字符串
propC:{
type:String,
required:true
},
//數(shù)值且有默認(rèn)值
propD:{
type:'Number',
default:100
},
//數(shù)組/對象的默認(rèn)值應(yīng)當(dāng)由一個(gè)工廠函數(shù)返回
propE:{
type:'Object',
defalut:function(){
return {message:'hello'}
}
},
//自定義驗(yàn)證函數(shù)
propF:{
validator:function(value){
return value > 10
}
}
}
});
// type 可以是下面原生構(gòu)造器:
// String
// Number
// Boolean
// Function
// Object
// Array
// Symbol
// type 也可以是一個(gè)自定義構(gòu)造器函數(shù)黄刚,使用 instanceof 檢測。
// 當(dāng) prop 驗(yàn)證失敗民效,Vue 會拋出警告 (如果使用的是開發(fā)版本)憔维。
// 注意 prop 會在組件實(shí)例創(chuàng)建之前進(jìn)行校驗(yàn),
// 所以在 default 或 validator 函數(shù)里畏邢,諸如 data业扒、computed 或 methods 等實(shí)例屬性還無法使用。
//非Prop特性
//替換/合并現(xiàn)有的特性
Vue.component('bs-date-input',{
template:'<input type="date" class="form-control">'
});
var app4 = new Vue({
el:"#app-4"
});
//自定義事件
// 我們知道舒萎,父組件使用 prop 傳遞數(shù)據(jù)給子組件程储。但子組件怎么跟父組件通信呢?這個(gè)時(shí)候 Vue 的自定義事件系統(tǒng)就派得上用場了臂寝。
// 使用 v-on 綁定自定義事件
//
// 每個(gè) Vue 實(shí)例都實(shí)現(xiàn)了事件接口虱肄,即:
// 使用 $on(eventName) 監(jiān)聽事件
// 使用 $emit(eventName) 觸發(fā)事件
// Vue 的事件系統(tǒng)與瀏覽器的 EventTarget API 有所不同。
// 盡管它們的運(yùn)行起來類似交煞,但是 $on 和 $emit 并不是addEventListener 和 dispatchEvent 的別名咏窿。
//
// 另外,父組件可以在使用子組件的地方直接用 v-on 來監(jiān)聽子組件觸發(fā)的事件素征。
// 不能用 $on 偵聽子組件釋放的事件集嵌,而必須在模板里直接用 v-on 綁定萝挤,參見下面的例子。
//聲明是子組件,調(diào)用是父組件
Vue.component('button-counter',{
template:'<button v-on:click="incrementTotals">{{counter}}</button>',
data:function(){
return {
counter:0
}
},
methods:{
incrementTotals:function () {
this.counter += 1;
this.$emit("increments");
}
}
});
var app5 = new Vue({
el:"#app-5",
data:{
totals:0
},
methods:{
incrementTotals:function () {
this.totals +=1 ;
}
}
});
// 給組件綁定原生事件
//
// 有時(shí)候根欧,你可能想在某個(gè)組件的根元素上監(jiān)聽一個(gè)原生事件怜珍。
// 可以使用 v-on 的修飾符 .native。例如:
//所以要讓組件的 v-model 生效凤粗,它應(yīng)該 (從 2.2.0 起是可配置的):
//接受一個(gè) value prop
// 在有新的值時(shí)觸發(fā) input 事件并將新值作為參數(shù)
// 我們來看一個(gè)非常簡單的貨幣輸入的自定義控件:
Vue.component('currency-input',{
template:'<span>$' +
'<input ref="inputr" v-bind:value="value" v-on:input="updateValue($event.target.value)">' +
'</span>',
props:['value'],
methods:{
//不是直接更新,而是使用此方法來對輸入值進(jìn)行改格式化和位數(shù)限制
updateValue:function(value){
var formattedValue = value
//刪除兩側(cè)的空格符
.trim()
//保留2位小數(shù)
.slice(
0,
value.indexOf('.') === -1
? value.length
:value.indexOf('.') + 3
)
//如果值上不合規(guī),則手動覆蓋為合規(guī)的值
if(formattedValue !== value){
this.$refs.inputr.value = formattedValue;
}
//通過input 事件帶出數(shù)值
this.$emit('input', Number(formattedValue));
}
}
});
var app6 = new Vue({
el:'#app-6',
data:{
price:''
}
//為什么price寫在這里,哪里用到了v-model="price" ??
});
// 自定義組件的 v-model
// 默認(rèn)情況下酥泛,一個(gè)組件的 v-model 會使用 value prop 和 input 事件。
// 但是諸如單選框嫌拣、復(fù)選框之類的輸入類型可能把 value 用作了別的目的柔袁。
// model 選項(xiàng)可以避免這樣的沖突:
Vue.component('my-checkbox',{
template:'<input type="checkbox">',
model:{
prop:'checked',
event:'checked'
},
props:{
checked:Boolean,
value:String
}
});
var app7 = new Vue({
el:'#app-7',
data:{
foo:''
}
// 沒調(diào)通,看不明白
});
// 非父子組件的通信
//
// 有時(shí)候,非父子關(guān)系的兩個(gè)組件之間也需要通信异逐。
// 在簡單的場景下捶索,可以使用一個(gè)空的 Vue 實(shí)例作為事件總線:
var bus = new Vue();
//觸發(fā)組件A中的事件
bus.$emit('id-selected',1);
//在組件B創(chuàng)建的鉤子中監(jiān)聽事件
bus.$on('id-selected',function(id){
//...
})
// 使用插槽分發(fā)內(nèi)容
</script>
</body>
</html>