最近看了下Vue組件的相關知識,除了官網也推薦這篇博客www.cnblogs.com/keepfool/p/…,可是這篇博客中用的是v1.0.25,我就用最新的v2.4.4來模仿下文中的例子,也一塊兒談一談下面幾個方面:javascript
- 組件編譯做用域
- 父子組件傳遞數據
- 非父子組件通訊
- 過濾器
先看下最終的實現效果: html
先看下總體代碼,首先是html部分, 分紅三部分vue
1.id="app"的container部分 2.id="grid-template"的表格部分 3.id="dialog-template"的對話框部分 總共涉及兩個自定義組件,一個父組件
<grid-template></grid-template>
;一個子組件<modal-dialog></<modal-dialog>
.java
<body>
<!-- container -->
<div id="app">
<div class="container">
<div class="form-group">
<label>
Search
</label>
<input type="text" v-model="searchQuery" />
</div>
</div>
<div class="container">
<grid-template :column="columns" :search-key="searchQuery" :data-list="people"></grid-template>
</div>
</div>
<!-- 表格 -->
<template id="grid-template">
<div>
<table>
<thead>
<th v-for="headName in column">
{{headName.name | capitalize}}
</th>
<th>
Delete
</th>
</thead>
<tbody class="text-center">
<tr v-for="(entry, index) in filt(dataList, searchKey)">
<td v-for="col in column">
<span v-if="col.isKey">
<a href="javascript:void(0)" @click="openEditItemDialog(index, 'Edit item ' + entry[col.name])">{{entry[col.name]}}</a>
</span>
<span v-else>{{entry[col.name]}}</span>
</td>
<td>
<button @click="deleteItem(index)">Delete</button>
</td>
</tr>
</tbody>
</table>
<div class="container">
<button class="button" v-on:click="openNewItemDialog('Create new item')">Create</button>
</div>
<modal-dialog :show="show" :mode="mode" :title="title" :fields="column" :item="item" @on-show-change="handleShow" @add-item="addData"
@update-item="updateData"></modal-dialog>
</div>
</template>
<!-- 對話框 -->
<template id="dialog-template">
<div class="dialogs">
<div class="dialog" v-bind:class="{'dialog-active': myShow}">
<div class="dialog-content">
<header class="dialog-header">
<h1 class="dialog-title">{{title}}</h1>
</header>
<div v-for="field in fields" class="form-group">
<label>{{field.name}}</label>
<select v-if="field.dataSource" v-model="item[field.name]" :disabled="mode === 2 && field.isKey">
<option v-for="opt in field.dataSource" :value="opt">
{{opt}}
</option>
</select>
<input v-else type="text" v-model="item[field.name]" :disabled="mode === 2 && field.isKey" />
</div>
<footer class="dialog-footer">
<div class="form-group">
<label></label>
<button v-on:click="save">Save</button>
<button v-on:click="close">Close</button>
</div>
</footer>
</div>
</div>
<div class="dialog-overlay"></div>
</div>
</template>
</body>
複製代碼
先看下第一部分的代碼,編程
<div id="app">
<div class="container">
<div class="form-group">
<label>
Search
</label>
<input type="text" v-model="searchQuery" />
</div>
</div>
<div class="container">
<grid-template :column="columns" :search-key="searchQuery" :data-list="people"></grid-template>
</div>
</div>
複製代碼
上面代碼中[column, search-key, data-list]
都是組件grid-template的屬性,[columns,searchQuery,people ]
就是使用container
傳給grid-template
組件的數據。 定義全局組件方式Vue.component(name, {})
,在父組件grid-template
中經過屬性components
定義子組件modal-dialog
,這也是比較經常使用的定義全局組件和局部組件的方式。api
Vue.component('grid-template', {
template: "#grid-template",
props: [
'dataList', 'column', 'searchKey'
],
components: {
'modal-dialog': {
template: '#dialog-template',
data: function () {
return {
myShow: this.show
}
},
props: [
'mode',
'title',
'fields',
'item',
'show'
]
}
}
}
複製代碼
html中用kebab-case (短橫線分隔命名),JS中採用camelCase (駝峯式命名),這個Vue會自動進行識別,咱們按各自習慣進行編碼就行。bash
就跟編程語言中的函數有做用域一說,Vue中組件也有做用域,而且做用域只在組件的模板中。 看一個官方的例子:app
<child-component>
{{ message }}
</child-component>
複製代碼
這裏message用的是父組件(父組件就是使用這個組件的組件)的數據,仍是子組件的數據?答案是父組件。 官方對做用域的定義:編程語言
父組件模板的內容在父組件做用域內編譯;子組件模板的內容在子組件做用域內編譯。函數
再看一個例子:
<!-- 無效 -->
<child-component v-show="someChildProperty"></child-component>
複製代碼
假定someChildProperty
是子組件的屬性,上面代碼就會出問題,由於這裏是父組件的做用域,也就是說someChildProperty
應該是父組件裏面定義的數據。若是要綁定子組件做用域內的指令到一個組件的根節點,須要在子組件的模板裏作:
Vue.component('child-component', {
// 有效,由於是在正確的做用域內
template: '<div v-show="someChildProperty">Child</div>',
data: function () {
return {
someChildProperty: true
}
}
})
複製代碼
來仔細看下上面代碼的第二部分-表格部分,看看父組件使用子組件的狀況:
<!-- 表格 -->
<template id="grid-template">
<div>
<modal-dialog :show="show" :mode="mode" :title="title" :fields="column" :item="item" @on-show-change="handleShow" @add-item="addData"
@update-item="updateData"></modal-dialog>
</div>
</template>
複製代碼
其中,[show, mode, title, fields, item]
是modal-dialog
組件本身定義的屬性,@是v-on:
的簡寫,method [handleShow, addData]
是父組件id="grid-template"做用域中的函數。
Vue.component('grid-template', {
template: "#grid-template",
methods: {
handleShow: function (val) {
this.show = val
},
addData: function (item) {
if (this.itemExists(item)) {
alert('Item ' + item[this.keyColumn] + " is already exists");
this.show = true;
return;
}
this.dataList.push(item)
this.item = {}
this.show = false
},
updateData: function (item) {
var keyColumn = this.keyColumn
for (var i = 0; i < this.dataList.length; i++) {
if (this.dataList[i][keyColumn] === item[keyColumn]) {
for (var j in item) {
this.dataList[i][j] = item[j]
}
break;
}
}
item = {}
}
},
components: {
'modal-dialog': {
template: '#dialog-template',
......
}
}
})
複製代碼
組件實例的做用域是孤立的。這意味着不能 (也不該該) 在子組件的模板內直接引用父組件的數據。父組件的數據須要經過 prop 才能下發到子組件中。 子組件要顯式地用[props
]聲明它預期的數據:
Vue.component('child', {
// 聲明 props
props: ['message'],
// 就像 data 同樣,prop 也能夠在模板中使用
// 一樣也能夠在 vm 實例中經過 this.message 來使用
template: '<span>{{ message }}</span>'
})
複製代碼
而後咱們能夠這樣向它傳入一個普通字符串: 結果就是 hello!
也能夠採用動態props,也就是v-bind指令。v-bind 動態地將 prop 綁定到父組件的數據。每當父組件的數據變化時,該變化也會傳導給子組件:
<div id="app">
<div class="container">
<div class="form-group">
<label>
Search
</label>
<input type="text" v-model="searchQuery" />
</div>
</div>
<div class="container">
<grid-template :column="columns" :search-key="searchQuery" :data-list="people"></grid-template>
</div>
</div>
複製代碼
子組件怎麼和父組件通訊呢?這個時候 Vue 的自定義事件系統就派得上用場了。
使用 $on(eventName) 監聽事件 使用 $emit(eventName) 觸發事件
看看咱們這個例子中,對話框中的添加了數據須要通知父組件<grid-template></grid-template>
,能夠這樣操做: 在子組件對話框中<modal-dialog></<modal-dialog>
<footer class="dialog-footer">
<div class="form-group">
<label></label>
<button v-on:click="save">Save</button>
</div>
</footer>
methods: {
save: function () {
this.$emit('add-item', this.item)
}
},
複製代碼
Vue監聽到了'add-item事件,Vue就會調用父組件
`中的addData方法:
<modal-dialog @add-item="addData" ></modal-dialog>
methods: {
addData: function (item) {
if (this.itemExists(item)) {
alert('Item ' + item[this.keyColumn] + " is already exists");
this.show = true;
return;
}
this.dataList.push(item)
this.item = {}
this.show = false
}
複製代碼
有時候,非父子關係的兩個組件之間也須要通訊。可使用一個空的 Vue 實例做爲事件總線:
var bus = new Vue()
// 觸發組件 A 中的事件
bus.$emit('id-selected', 1)
// 在組件 B 建立的鉤子中監聽事件
bus.$on('id-selected', function (id) {
// ...
})
複製代碼
在咱們這個例子中,對話框的顯示須要父子組件進行通訊,點擊Create
按鈕,須要控制show的true或false,從而控制對話框的顯示與否。有的小夥伴可能立馬想到props的方法,可是這樣就會有一個問題:
經過父組件把show的值傳給子組件對話框做爲屬性,是能夠達到控制對話框的顯示或隱藏,可是在對話框中點擊save或者close按鈕時須要改變這個show的值,這時候就至關於子組件改變了父組件傳過來的屬性props了,這是比較不和諧的,Vue推薦props傳到子組件的時候是隻讀的,也就是子組件不要改變父組件傳過來的porps。
<template id="dialog-template">
<div class="dialogs">
<div class="dialog" v-bind:class="{'dialog-active': show}">
</div>
</template>
複製代碼
這時候就能夠經過事件的方式來達到這個目的,show這個狀態只做爲子組件對話框本身的data,父組件不知道這個屬性,要控制對話框的狀態,須要給子組件發送事件:
<div class="container">
<button class="button" v-on:click="openNewItemDialog('Create new item')">Create</button>
</div>
Vue.component('grid-template', {
template: "#grid-template",
methods: {
openNewItemDialog: function (title) {
this.title = title
this.mode = 1
this.item = {}
bus.$emit('dialog-show', true) //經過總線發送dialog-show事件
}
addData: function (item) {
this.dataList.push(item)
},
},
components: {
'modal-dialog': {
template: '#dialog-template',
mounted: function () {
bus.$on('dialog-show', function (show) {
this.show = show
}.bind(this))
}
}
}
})
複製代碼
1.點擊Create按鈕,經過bus.$emit('dialog-show', true) //經過總線發送dialog-show事件 2.子組件在mounted生命週期鉤子中監聽事件 mounted: function () { bus.$on('dialog-show', function (show) { this.show = show }.bind(this)) }
若是必定要經過屬性props的方式呢?能夠這樣,
components: {
'modal-dialog': {
template: '#dialog-template',
data: function () {
return {
myShow: this.show
}
},
methods: {
close: function () {
this.myShow= false
}
},
watch: {
show(val) {
this.myShow = val;
},
myShow(val) {
this.$emit("on-show-change", val);
}
},
}
}
複製代碼
1.子組件中不要直接修改父組件傳遞過來的props,會報錯: 在子組件中直接修改props-show close: function () { this.show = false } Error: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. 2.經過計算屬性,在watch中父組件show改變的時候改變子組件的myShow屬性;同理,子組件myShow屬性改變的時候經過emit發射事件通知父組件改變show屬性 <modal-dialog @on-show-change="handleShow" > 在父組件中定義方法 handleShow: function (val) { this.show = val }
Vue.js 容許你自定義過濾器,可被用於一些常見的文本格式化。過濾器能夠用在兩個地方:雙花括號插值和 v-bind 表達式 (後者從 2.1.0+ 開始支持)。過濾器應該被添加在 JavaScript 表達式的尾部,由「管道」符號指示:
<!-- 在雙花括號中 -->
{{ message | capitalize }}
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>
複製代碼
有兩種定義方式:在組件選項中經過filters定義本地的過濾器;經過Vue.filter定義全局的過濾器
// 首字母大寫的過濾器
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
複製代碼
像下面這樣使用,表格標題首字母大寫
<th v-for="headName in column">
{{headName.name | capitalize}}
</th>
複製代碼
本文例子來源於www.cnblogs.com/keepfool/p/…,一方面是自身學習,另一方面用V2.4.4重寫了,對一些細節進行了擴展解釋,但願對你們有點幫助哈,謝謝!