上节中我们介绍了渲染的声明,但是我们在实际应用中我们需要拆分vue的功能使项目的结构更清晰以便于后续维护。于是我们需要使用组件,让一个功能分拆成多个组件,帮助我们细化功能让组件的功能更单一,并使相同功能在项目中能够复用。
学习本节你需要了解的内容:
- 箭头函数 arrow function
组件是vue中最为重要和强大的功能,我们可以把vue的应用想象成组件的集合。通过不同组件之间的组装和通信生成最终的应用。
组件也遵循vue的生命周期,我们可以通过在组件中监听对应生命周期事件来灵活控制组件,如:
<template>
<div>{{hello}}</div>
</template>
<script>
var data = { hello: 'Hello World' }
setTimeout(function(){
console.log('更新data值');
data.hello='Hello New One';
},1000)
module.exports = {
data(){
return data
},
beforeUpdate(){
console.log('显示新值',this.hello);
}
}
</script>
如上组件,我们通过监听beforeUpdate来查看改变的hello值。
打印数据依次为:
更新data值
显示新值 Hello New One
- 了解更详细的生命周期钩子
在vue 2.0中提供了两种方式进行组件的交互:1.属性,2.事件,两者的关系如下图所示:
Vue.component('child', {
// declare the props
props: ['message'],
// just like data, the prop can be used inside templates
// and is also made available in the vm as this.message
template: '<span>{{ message }}</span>'
})
<child message="hello!"></child>
如上方官方文档所示,组件可以接收静态数据并展示,如下的形式也可以接收动态数据:
<child :my-message="parentMsg"></child>
此时child接收的属性名是myMessage
在组件内还可以定义属性验证,来严格规范组件传入属性。
Vue.component('example', {
props: {
// basic type check (`null` means accept any type)
propA: Number,
// multiple possible types
propB: [String, Number],
// a required string
propC: {
type: String,
required: true
},
// a number with default value
propD: {
type: Number,
default: 100
},
// object/array defaults should be returned from a
// factory function
propE: {
type: Object,
default: function () {
return { message: 'hello' }
}
},
// custom validator function
propF: {
validator: function (value) {
return value > 10
}
}
}
})
- 属性遵循单向数据流模式,当父组件的值变更,则子组件的值也会对应变更,但子组件data的变更并不会影响父组件。
- 请查看官方文档了解更多。
在组件中,我们也可以使用v-on
传递一个自定义事件,并使用 this.$emit
发起事件,第一个参数时事件名,后续参数同步传输到自定义事件的参数中,如下的官方DEMO:
<div id="counter-event-example">
<p>{{ total }}</p>
<button-counter v-on:increment="incrementTotal"></button-counter>
<button-counter v-on:increment="incrementTotal"></button-counter>
</div>
Vue.component('button-counter', {
template: '<button v-on:click="increment">{{ counter }}</button>',
data: function () {
return {
counter: 0
}
},
methods: {
increment: function () {
this.counter += 1
this.$emit('increment')
}
},
})
new Vue({
el: '#counter-event-example',
data: {
total: 0
},
methods: {
incrementTotal: function () {
this.total += 1
}
}
})
所有传入的数据不仅可以在数据绑定时用过滤器,还可以使用计算函数动态计算值,如下所示:
var vm = new Vue({
data: { a: 1 },
computed: {
// get only, just need a function
aDouble: function () {
return this.a * 2
},
// both get and set
aPlus: {
get: function () {
return this.a + 1
},
set: function (v) {
this.a = v - 1
}
}
}
})
vm.aPlus // -> 2
vm.aPlus = 3
vm.a // -> 2
vm.aDouble // -> 4
- 注意,这里的计算属性如果使用复杂结构的object数据有可能产生内存泄露,建议使用Object.assign深复制。
- 计算属性如果动态计算列表时,注意列表的处理。参见“列表渲染”部分内容。
同时,还可以使用watch来监听函数的变化,处理对应的操作(比如移动端经常通过检测列表变动来更新Iscroll的高度),如下:
var vm = new Vue({
data: {
a: 1,
b: 2,
c: 3
},
watch: {
a: function (val, oldVal) {
console.log('new: %s, old: %s', val, oldVal)
},
// string method name
b: 'someMethod',
// deep watcher
c: {
handler: function (val, oldVal) { /* ... */ },
deep: true
}
}
})
vm.a = 2 // -> new: 2, old: 1
为了保证项目的可维护性,我们引入了vue在webpack的加载器vue-loader
(之前在webpack中已声明),通过vue-loader
我们可以解析.vue
的文件为vue组件,一个完整的vue组件由HTML和js两部分构成,如下:
<template>
<div>
<template v-if="hello">{{hello}}</template>
<p><a @click.prevent="msgBack">Send a msg</a></p>
</div>
</template>
<script>
var data = { hello: 'Hello World' }
module.exports = {
data(){
return data
},
methods:{
msgBack:function(){
this.hello='I get your msg';
}
}
}
</script>
和nodejs的模块原理一样,每个vue组件都会被打包为一个模块。现在我们来改造一下app.js载入插件。
import Vue from 'Vue'; //载入vue
import Hello from './components/hello.vue'; //载入hello组件
var vm = new Vue({
el:'#render', //根节点
render:createElement=>createElement(Hello)
});
现在webpack重新生成,就可以使用我们更加健壮的项目了。
在后续的示例中,我们通过根组件引入子组件来扩展更多的应用场景,这也是vue-router
的开发思路。