最近看了下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重写了,对一些细节进行了扩展解释,但愿对你们有点帮助哈,谢谢!