全局组件
所有的vue实例里面都可以使用
注意: 2.0之后template最外层只有一个结点
Component template should contain exactly one root element. If you are using v-if on multiple elements, use v-else-if to chain them instead
注意: data必须是函数
//正确
template="<div><div></div><div></div></div>"
//错误(2.0之前也可以)
template="<div></div><div></div>"
组件写法1
Vue.component('my-component', {
template: '<div @click="num++"></div>',
data() {
return {
num: 0
}
}
});
组件写法2
var myComponent = Vue.extend({
template: '<div @click="num++"></div>',
data() {
return {
num: 0
}
}
});
Vue.component('my-component', myComponent);
组件使用模版
在组件里面用template: '<div @click="num++"></div>'是不利于书写的
方式1
<script type="x-template" id="myComponent">
<div @click="num++"></div>
</script>
template: '#myComponent'
方式2
<template id="myComponent">
<div @click="num++"></div>
</template>
template: '#myComponent'
动态组件
<div id="app">
<input type="button" @click="cpName='my-component-1'" value="使用my-component-1渲染">
<input type="button" @click="cpName='my-component-2'" value="使用my-component-2渲染">
<component :is="cpName"></component>
</div>
<script>
Vue.component('my-component-1', {
template: '<div>我是组件1</div>'
});
Vue.component('my-component-2', {
template: '<div>我是组件2</div>'
});
var vm = new Vue({
el: '#app',
data: {
cpName: 'my-component-1'
}
});
</script>
注意,组件切换是是重新渲染,数据不会保留
如果希望保留数据,可以使用keep-alive
<keep-alive>
<component :is="currentView">
<!-- 非活动组件将被缓存! -->
</component>
</keep-alive>
父子组件
注意子组件必须在父组件的模板中使用而不是嵌套
//错误写法
<father><son></son></father>
//正确写法
父组件的模板
<div>
//...
<son></son>
</div>
<div id="app">
<father></father>
</div>
<script>
Vue.component('father', {
template: '<div>我是父组件<son></son></div>',
components: {
'son': {
template: '<div>我是子组件</div>'
}
}
});
var vm = new Vue({
el: '#app'
});
</script>
父子组建通信
组件实例的作用域是孤立的。这意味着不能 (也不应该) 在子组件的模板内直接引用父组件的数据。父组件的数据需要通过 prop 才能下发到子组件中
点击父组件,子组件绑定了父组件的数据也会随之改变
但是点击子组件,子组件改变,父组件不改变,点击一下父组件,子组件会和父组件同步。这样试图用子组件来改变父组件的数据是不行的,子组件的数据虽然变化了,但是会被父组件的变化给覆盖,而且控制台还会有警告子组件的值有会被覆盖的风险

Vue.component('father', {
data: function () {
return {
fNum: 1
}
},
template: '<div><div @click="fNum++">我是父组件</div><son :sNum="fNum"></son></div>',
components: {
'son': {
props: ['sNum'],
template: '<div @click="sNum++">我是子组件</div>',
}
}
});
有时候,子组件只是需要父组件给他一个初始值,不想让父组件来改变他,可以定义一个局部变量,并用 prop 的值初始化它
或者需要数据进行处理,然后跟随父组件变化,可以使用计算属性
Vue.component('father', {
data: function () {
return {
fNum: 1
}
},
template: '<div><div @click="fNum++">我是父组件</div><son :sNum="fNum"></son></div>',
components: {
'son': {
props: ['sNum'],
data() {
return {
sNumCopy: this.sNum
}
},
computed: {
fNumM2() {
return this.sNum * 2;
}
},
template: '<div @click="sNumCopy++">我是子组件,</div>',
}
}
});
prop数据的绑定和固定数据
<son :sNum="fNum" sNum2="fNum"></son>
props: ['sNum',sNum2]
sNum是父组件数据的绑定
sNum2只是获得了一个字符传,里面的东西统统都是字符串,而不是表达式,可以理解为组件的静态属性吧
上面说到子组件无法直接改变父组件的值,有时候又确实要改变的可以使用emit来通知父组件自己改变了,同时传递给父组件自己改变后的值,然后父组件可以接受这个值,然后进行改变
子组件已经和它外部完全解耦了。它所做的只是报告自己的内部事件,因为父组件可能会关心这些事件
Vue.component('father', {
data: function () {
return {
fNum: 1
}
},
methods: {
fFunc(a) {
this.fNum = a;
}
},
template: '<div><div @click="fNum++">我是父组件</div><son @notice="fFunc" :sNum="fNum"></son></div>',
components: {
'son': {
props: ['sNum'],
data() {
return {
sNumCopy: this.sNum
}
},
template: '<div @click="sClick">我是子组件</div>',
methods: {
sClick() {
this.sNumCopy++;
this.$emit('notice', this.sNumCopy);
}
},
}
}
});
sync语法糖
有时候只是简单的实现下数据同步,并不对数据做处理,而父组件还要为了这个么简单的一个功能写一个函数来处理,实在是太麻烦
其实是有sync这个语法糖的,同时很直观的能看到这个数据是双向绑定的
<comp :foo.sync="bar"></comp>
会被vue自动扩展为
<comp :foo="bar" @update:foo="val => bar = val"></comp>
当子组件需要更新 foo 的值时,它需要显式地触发一个更新事件
this.$emit('update:foo', newValue)
案例如下
Vue.component('father', {
data: function () {
return {
fNum: 1
}
},
template: '<div><div @click="fNum++">我是父组件</div><son :sNum.sync="fNum"></son></div>',
components: {
'son': {
props: ['sNum'],
template: '<div @click="sUpdate">我是子组件</div>',
methods: {
sUpdate() {
this.sNum++;
this.$emit('update:sNum', this.sNum)
}
}
}
}
});
不同组件之间通信
不同组件之间的数据共享还好办,data返回同一个Object就行了
之间通信的话就需要借助一个中间人,
<div id="app">
<comp1></comp1>
<comp2></comp2>
</div>
<script>
var hub = new Vue(); //创建事件中心
Vue.component('comp1', {
data() {
return {
num: 1
}
},
methods: {
comp2Change() {
hub.$emit('comp2Change');
}
},
mounted: function () {
hub.$on('comp1Change', function () {
this.num++;
}.bind(this))
},
template: '<div @click="comp2Change"></div>'
});
Vue.component('comp2', {
data() {
return {
num: 1
}
},
methods: {
comp1Change() {
hub.$emit('comp1Change');
}
},
mounted: function () {
hub.$on('comp1Change', function () {
this.num++;
}.bind(this))
},
mounted: function () {
hub.$on('comp2Change', function () {
this.num++;
}.bind(this))
},
template: '<div @click="comp1Change"></div>'
});
var vm = new Vue({
el: '#app'
});
</script>
插槽
引用组件时候,在组件标签内写的内容是会被覆盖的,即无效
<div id="app">
<comp>
<span>World</span>
</comp>
</div>
<script>
Vue.component('comp', {
template: '<div>Hello</div>'
});
var vm = new Vue({
el: '#app'
});
</script>
可以通过slot来将标签内的内容传递到组件模板中
<div id="app">
<comp>
<span>World</span>
</comp>
</div>
<script>
Vue.component('comp', {
template: '<div>Hello<slot></slot></div>'
});
var vm = new Vue({
el: '#app'
});
</script>
当然,把标签内的作为一个整体作为一个slot还是不太自由,可以为slot起名字,根据名字引用
直接slot代表的是所有没有名字的slot
<div id="app">
<comp>
<span slot='s1'>World</span>
<span slot='s2'>!!!</span>
</comp>
</div>
<script>
Vue.component('comp', {
data() {
return {
name: " Ming"
}
},
template: '<div>Hello<slot name="s2"></slot><slot name="s1"></slot></div>'
});
var vm = new Vue({
el: '#app'
});
</script>
林秀栋的技术博客