就是Vuejs最強大的功能之一!!!讓HTML有更豐富並保有原生的特色,也可以使用is
擴充
如果你有物件的概念,那component
可以視為一個class
(of OO)的概念
要使用components
,就要先註冊。在這裡先介紹「全域註冊」,這種註冊方式可以提供一個元件給所有的Vue物件使用。
component
只有一個靜態字串。
< div id = "example" >
< my-component > my-component >
< /div >
Javascript
註冊元件,在HTML
有一樣名稱的tag name就可以改成template
的內容
Vue.component('my-component', {
template: '< div >A custom component!< /div >'
})
Vue物件實體,在HTML
有一樣id
,對應到Vue物件實體的el
,就會進行Vue的渲染。
也就是因為有讓Vue實體物件對應上,component
自訂義的HTML tag才會渲染成component.template
。
new Vue({
el: '#example'
})
Rendered
< div id="example" >
< div >A custom component!< /div >
< /div >
Result
區域註冊,就是「限定註冊域」,指定components給特定的Vue物件使用。
此例指定local-component
給local
component
只有一個靜態字串。
< div id="local" >
< local-component > < /local-component >
< /div >
Javascript
var Child = {
template: '< div >A custom component!< /div >'
}
new Vue({
// ...
components: {
'local-component': Child
}
})
Result
在HTML渲染中,有存在著某些tag彼此有固定的相依關係
ul
後面一定接li
ol
後面一定接li
table
裡面一定接tbody, td, tr
...等select
一定??怎麼搭配?option
外面一定接select
自訂義組件使用這些元素,會有一些問題。
這裡的實驗結果,看不出什麼太大的差別,就差別比較大
其它的差別感覺不太出來。
倒是有一種,在is
上的tag name
,當作template
的味道。
< div id="dom-templae" >
< table >< table-cell >會搬出table,沒有渲染!!< /table-cell >< /table >
< ul >< ul-cell >ul沒有渲染!!< /ul-cell >< /ul >
< ol >< ol-cell >ol沒有渲染!!< /ol-cell >< /ol >
< section >< section-cell >section沒有渲染!!< /section-cell >< /section >
< !-- 使用is的效果 -- >
< table >< tr is="table-cell" >table只是搬出來,沒有渲染!!< /tr >< /table >
< ul >< li is="ul-cell" >ul沒有渲染!!< /li >< /ul >
< ol >< li is="ol-cell" >ol沒有渲染!!< /li >< /ol >
< section >< h5 is="section-cell" >section沒有渲染!!< /h5 >< /section >
< /div >
渲染成
< div id="dom-templae" >
< tr >< td >有渲染,無is會搬出table< /td >< /tr >
< table >< /table >
< ul >< li >有渲染< /li >< /ul >
< ol >< li >有渲染< /li >< /ol >
< section >< h6 >有渲染< /h6 >< /section >
< !-- 使用is的效果 -- >
< table >< tbody >< tr >< td >有渲染,無is會搬出table< /td >< /tr >< /tbody >< /table >
< ul >< li >有渲染< /li >< /ul >
< ol >< li >有渲染< /li >< /ol >
< section >< h6 >有渲染< /h6 >< /section >
< /div >
is
的效果
is
的效果
無論is
所屬的tag name是什麼,會置換成template
的第一層tag name
應當注意,如果您使用來自以下來源之一的字符串模板,這些限制將不適用:
因此,有必要的話請使用字符串模板。
data
Must Be a Functioncomponents
的data
必須是function。
可以看一下Vue 组件data为什么必须是函数?
如果這麼做,console
會發出警告告訴你「在組件中data必須是一個函數。」
Vue.component('my-component', {
template: '< span >{ { message } }< /span >',
data: {
message: 'hello' //被警告
}
})
console錯誤訊息
vue.js:485 [Vue warn]: The "data" option should be a function that returns a per-instance value in component definitions.
理解這種規則的存在意義很有幫助,讓我們假設用如下方式來繞開Vue的警告:
HTML< div id="example-2" >
< simple-counter >< /simple-counter >
< simple-counter >< /simple-counter >
< simple-counter >< /simple-counter >
< /div >
Javascript
技術上data 的確是一個函數了,因此Vue 不會警告,
但是我們返回給每個組件的實例卻引用了同一個data 對象
var data = { counter: 0 } ---> 全域宣告data物件
Vue.component('simple-counter', {
template: '< button v-on:click="counter += 1" >{{ counter }}< /button >',
data: function () {
return data ---> 回傳 全域data物件
}
})
new Vue({
el: '#example-2'
})
Result
data: function () {
return {
counter : 0
}
}
Result
在前面components
的練習中,我們都只是在component
的template
寫靜態的字串,讓它顯示。
接下來會開始引進變數,並且介紹如何修改。
在Vue中,父子組件的關係可以總結為props down
, events up
。
props
向下傳遞數據給子組件events
給父組件發送消息接下來就來看props
和events
的同作機制!
component
實例的作用域是獨立的。
這意味著無法直接將父組件的變數傳給子組件使用。
除非,使用props
宣告一段自訂義tag name的component
< div id="props">
< child message = "hello!" > < /child >
< /div>
Javascript
Vue.component( 'child' , { //父組件
props: [ 'message' ], // 宣告props
template: '< span >{ { message } }< /span >' // 子組件!! 就可以像data 一樣,prop 可以用在模板內
})
在component中的用法,就像在vm實例中,直接呼叫data般this.message
這樣使用。
Vue.component('child', {
props: ['message'],
template: '< span v-on:click="showit">{ { message } }< /span>',
methods: {
showit: function () {
setTimeout(() => console.log(this.message), 0);
}
}
}
Result
駝峰命名法=小寫分隔線命名法
component
指的是自訂tag name的HTML
HtmlElement.attr = component.props
HTML< !-- kebab-case in HTML -- >
< child my-message="hello!" >< /child > ---> my-message(HTML) = myMessage(js): render
< child myMessage="hello!" >< /child > ---> myMessage(HTML) ≠ myMessage(js): not render
Javascript
如果你使用字符串模板,則沒有這些限制。??
Vue.component('child', {
// camelCase in JavaScript
props: ['myMessage'],
template: '< span >{ { myMessage } }< /span >'
})
new Vue({
el: '#my-message'
})
Result
把Vue物件裡的data
丟到組件裡。
:my-message="parentMsg"
中,若"parentMsg"是字串,則my-message就不用v-bind
。但是,若它是變數,要v-bind
< div id="dynamic_prop" >
< input v-model="parentMsg" >
< br >
< child :my-message="parentMsg" >< /child >
< /div >
Javascript
Vue.component('child', {
// camelCase in JavaScript
props: ['myMessage'],
template: '< span >{ { myMessage } }< /span >'
})
new Vue({
el: '#dynamic_prop',
data: {
parentMsg: 'aaaaaa'
}
})
數值傳遞圖
Result
初學常犯的問題。
< !-- this passes down a plain string "1" -- >
< comp some-prop="1" >< /comp >
誤以為這樣的1是數字,但其實是文字。要數字要使v-bind
< !-- this passes down an actual number -- >
< comp v-bind:some-prop="1" >< /comp >
props
是父層組件和子層組件的溝通管道。但是它是單向資料流。
所以,不該在子組件內部改變prop。如果你這麼做了,Vue會在控制台給出警告。
那為什麼還會想改呢?有兩個常見的原因
prop
作為初始值傳入後,子組件想把它當作區域變數來用;prop
作為初始值傳入,由子組件處理成其它資料-輸出。區域變數: component
需要data function
props: [ 'initialCounter' ],
data : function () {
return { counter : this .initialCounter }
}
初始化變數: component
需要computed function
props: [ 'size' ],
computed : {
normalizedSize : function () {
return this .size.trim().toLowerCase()
}
}
注意在JavaScript中物件和陣列是reference
,指向同一個記憶體空間,
如果prop
是一個物件或陣列,在子組件內部改變它時,會影響父組件的狀態。
components
的prop可以設計「資料驗證」。如果驗證NG,Vue會發出警告。有助於設計給別人使用時,別人可以正確的使用props
< div id="validation" >
< label for="1" >propA: < /label >
< input id="1" v-model.number="inputA" placeholder="waring: empty string or string" >< br / >
< label for="2" >propB(number): < /label >
< input id="2" v-model.number="inputB" >< br / >
< label for="3" >propB(string): < /label >
< input id="3" v-model="inputB" >< br / >
< label for="4" >propC: < /label >
< input id="4" v-model.number="inputC" placeholder="waring: no default string" >< br / >
< label for="4" >propD(number, default 100, but not show): < /label >
< input id="4" v-model.number="inputD" placeholder="waring: no warning, default 100" >< br / >
< label for="5" >propD(string): < /label >
< input id="5" v-model="inputD" placeholder="waring: string, default 100" >< br / >
< label for="6" >propE: < /label >
< input id="6" v-model="inputE.message" >< br / >
< label for="7" >propF: < /label >
< input id="7" v-model.number="inputF" type="number" placeholder="warning: string" >< br / >
< vali :prop-a="inputA"
:prop-b="inputB"
:prop-c="inputC"
:prop-d="inputD"
:prop-e="inputE"
:prop-f="inputF" >
< /vali >
< /div >
Javascript
new Vue({
el: '#validation',
data: {
inputA: 0,
inputB: 0,
inputC: 'chris',
inputD: 10,
inputE: { message: 0},
inputF: 11
},
components: {
'vali': {
props : {
propA: Number , // 基本資料型別檢查(`null` 意思是任何類型都可以)
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
}
}
},
template: `< ol >
< li >propA: {{propA}}< /li >
< li >propB: {{propB}}< /li >
< li >propC: {{propC}}< /li >
< li >propD: {{propD}}< /li >
< li >propE: {{propE}}< /li >
< li >propF: {{propF}}< /li >
< /ol >`
}
}
})
Reault
例子的練習,參考Vue学习总结笔记(二):组件
非prop
屬性的意思,指的是一般的屬性,要提供給「使用component
的開發者」
在不修改template
的情況,想修改component
渲染完的根元屬性,該如何做?
< bs-date-input id="non-prop" data-3d-date-picker="true" >< /bs-date-input >
Javascript
new Vue ({
el: '#non-prop',
components: {
'bs-date-input': {
template: `< div >component< /div >`
}
}
})
Rendered
< div id="non-prop" data-3d-date-picker="true" >component /div >
Result
在component的例子,HTML有class="date-picker-theme-dark"
,
在component
的template
有class="form-control"
< bs-date-input id="merging-attri" data-3d-date-picker="true" class="date-picker-theme-dark" >< /bs-date-input >
Javascript
new Vue ({
el: '#merging-attri',
components: {
'bs-date-input': {
template: `< input type="date" class="form-control" >`
}
}
})
渲染完會長怎樣呢?
Rendered< input type="date" class="form-control date-picker-theme-dark" id="merging-attri" data-3d-date-picker="true" >
HTML的attri會取「聯集」的結果class="form-control date-picker-theme-dark"
我們前面學了 父對子 的變數值傳遞方式,接下來要說的是 子對父 的變數值傳遞方式
讓我們從熟悉的角度切入
每一個Vue物件本身,都擁有event interface
$on(eventName)
$emit(eventName)
$on
和$emit
並不是addEventListener
和dispatchEvent
的別名。
父組件可以直接監聽在子層裡template
裡的事件
無法使用$on
直接監聽子層觸發的事件。必須直接在template
使用v-on
< div id="counter-event-example">
< p>{ { total } }< /p>
< button-counter v-on:increment="incrementTotal">< /button-counter> < -- 3. 子層的自訂義increment事件,觸發父層的incrementTotal
< button-counter v-on:increment="incrementTotal">< /button-counter>
< /div>
Javascript
Vue.component('button-counter', {
template: '< button v-on:click="incrementCounter" >{ { counter } }< /button >', //1. 子組件用v-on執行incrementCounter
data: function () {
return {
counter: 0
}
},
methods: {
incrementCounter: function () {
this.counter += 1
this.$emit('increment') //2. 自訂事件觸發$emit給父層的Method
}
},
})
new Vue({
el: '#counter-event-example',
data: {
total: 0
},
methods: {
incrementTotal: function () {
this.total += 1 //4. 執行父層函數,更新total
}
}
})
數值傳遞圖
Result
{{ total }}
如果你不要自訂,要用原生的,在template
的v-on
加上.native
的修飾字
< my-component v-on:click.native="doTheThing">< /my-component>
.sync
修飾符不同於v1.x版的雙向綁定的用途,在v2.x版.sync
當作一個自動附加父組件v-on
偵聽器的屬性。目的在於讓子組件改變父組件狀態的代碼更容易被區分。,也就只是作為一個編譯時的語法糖存在。
< comp :foo.sync="bar">< /comp>
會幫你加上
< comp :foo="bar" @update:foo="val => bar = val">< /comp>
組件需要更新foo
時,可以這樣寫
this .$emit( 'update:foo' , newValue)
一般而言,我們用雙向綁定vue實例與HTML,在HTML加上v-mode
< input v-model="something">
Vue會幫你改成這樣
< input
v-bind:value="something"
v-on:input="something = $event.target.value">
在components
使用時,需要這樣寫
< custom-input
v-bind:value="something"
v-on:input="value => { something = value }">
< /custom-input>
練習
接下來,做vue-cli
的練習