推荐:Vue汇总
Vue - Vue组件基础
为了介绍Vue的组件,先用Vue的组件来改造之前实现的
TodoList
:
- Vue - 用Vue写一个TodoList小栗子
代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>TodoList</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="div">
<input v-model.trim="inputValue" type="text">
<button @click.stop="bntClick">提交</button>
<ul>
<todo-item v-for="item in list" v-bind:plan="item.value" :key="item.id"></todo-item>
</ul>
</div>
</body>
</html>
<script>
Vue.component("todo-item" , {
props: ['plan'],
template: "<li>{{plan}}</li>"
});
var vue = new Vue({
el: '#div',
data: {
inputValue: '',
id: 1,
list: []
},
methods: {
bntClick(){
if(this.inputValue !== ''){
this.list.unshift({
id: this.id++,
value: this.inputValue
});
this.inputValue = '';
}
else{
alert('写点东西再交');
}
}
}
})
</script>
这里用
<todo-item v-for="item in list" v-bind:plan="item.value" :key="item.id"></todo-item>
代替原来
TodoList
里的
<li v-for="item in list" :key="item.id">{{item.value}}</li>
。
组件全局注册
Vue.component("todo-item" , {
props: ['plan'],
template: "<li>{{plan}}</li>"
});
效果和之前的
TodoList
是一样的。
组件局部注册
代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>TodoList</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="div">
<input v-model.trim="inputValue" type="text">
<button @click.stop="bntClick">提交</button>
<ul>
<todo-item v-for="item in list" v-bind:plan="item.value" :key="item.id"></todo-item>
</ul>
</div>
</body>
</html>
<script>
var TodoItem = {
props: ['plan'],
template: "<li>{{plan}}</li>"
};
var vue = new Vue({
el: '#div',
components: {
TodoItem: TodoItem
},
data: {
inputValue: '',
id: 1,
list: []
},
methods: {
bntClick(){
if(this.inputValue !== ''){
this.list.unshift({
id: this.id++,
value: this.inputValue
});
this.inputValue = '';
}
else{
alert('写点东西再交');
}
}
}
})
</script>
组件局部注册:
var TodoItem = {
props: ['plan'],
template: "<li>{{plan}}</li>"
};
需要使用时(局部作用域),在
components
中指定即可。
components: {
TodoItem: TodoItem
}
效果也是一样的。
组件基础介绍
组件是可复用的Vue实例,且带有一个名字:在上面例子中是
<todo-item>
。我们可以在一个通过 new Vue创建的Vue根实例中,把这个组件作为自定义元素来使用:
<ul>
<todo-item v-for="item in list" v-bind:plan="item.value" :key="item.id"></todo-item>
</ul>
因为组件是可复用的Vue实例,所以它们与new Vue接收相同的选项,例如
data
、
computed
、
watch
、
methods
以及生命周期钩子等。仅有的例外是像
el
这样根实例特有的选项。
组件的复用
你可以将组件进行任意次数的复用:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>TodoList</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="div">
<input v-model.trim="inputValue" type="text">
<button @click.stop="bntClick">提交</button>
<ul>
<todo-item v-for="item in list" v-bind:plan="item.value" :key="item.id"></todo-item>
<todo-item v-for="item in list" v-bind:plan="item.id" :key="item.id"></todo-item>
</ul>
</div>
</body>
</html>
<script>
var TodoItem = {
props: ['plan'],
template: "<li>{{plan}}</li>"
};
var vue = new Vue({
el: '#div',
components: {
TodoItem: TodoItem
},
data: {
inputValue: '',
id: 1,
list: []
},
methods: {
bntClick(){
if(this.inputValue !== ''){
this.list.unshift({
id: this.id++,
value: this.inputValue
});
this.inputValue = '';
}
else{
alert('写点东西再交');
}
}
}
})
</script>
效果:
每个组件都会各自独立维护它的
plan
。因为你每用一次组件,就会有一个它的新实例被创建(上面例子中被创建了6次,通过
v-for
)。
data 必须是一个函数
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>count</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="div">
<count></count>
<count></count>
<count></count>
</div>
</body>
</html>
<script>
Vue.component('count',{
data: function () {
return {
cnt: 1
}
},
template: '<button @click="cnt++">点击{{cnt}}</button>'
});
var vue = new Vue({
el: '#div'
})
</script>
效果:
这样点击一个组件,不会影响其他组件,因为组件的
data
选项是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝。
如果
data
不是一个函数呢?我的Vue版本(
v2.6.10
)是用不了的,和官网有点不同。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>count</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="div">
<count></count>
<count></count>
<count></count>
</div>
</body>
</html>
<script>
Vue.component('count',{
data:{
cnt: 1
},
template: '<button @click="cnt++">点击{{cnt}}</button>'
});
var vue = new Vue({
el: '#div'
})
</script>
效果:
没有数字。
在官网文档中是可以使用的,可能是因为版本不同,官网文档大概的意思是:如果
data
不是一个函数,组件复用就可能会存在问题,因为所有实例的
data
实际上是指向同一个
data
,某一个实例改变了
data
的值,其他实例的
data
同样也就改变了。
组件的组织通常一个应用会以一棵嵌套的组件树的形式来组织:
例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接之类的组件。
为了能在模板中使用,这些组件必须先注册,以便Vue能够识别。有两种组件注册方式:全局注册和局部注册,在前面也都介绍过了。全局注册的组件可以用在其被注册之后的任何 (通过new Vue) 新创建的Vue根实例,也包括其组件树中的所有子组件的模板中。
通过 Prop 向子组件传递数据
在第一个用Vue组件改造
TodoList
的例子中就使用过
Prop
向子组件传递数据,
Prop
是你可以在组件上注册的一些自定义
attribute
。当一个值传递给一个
prop attribute
的时候,它就变成了那个组件实例的一个
property
。为了给
todo-item
组件传递
plan
,我们可以用一个
props
选项将其包含在该组件可接受的
prop
列表中:
Vue.component("todo-item" , {
props: ['plan'],
template: "<li>{{plan}}</li>"
});
一个组件默认可以拥有任意数量的
prop
,任何值都可以传递给任何
prop
。在上述模板中,你会发现我们能够在组件实例中访问这个值,就像访问
data
中的值一样。
例子:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>TodoList</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="div">
<input v-model.trim="inputValue" type="text">
<button @click.stop="bntClick">提交</button>
<ul>
<todo-item v-for="item in list" :plan="item.value" :key="item.id"></todo-item>
<todo-item v-for="item in list" plan="+1" :key="item.id"></todo-item>
</ul>
</div>
</body>
</html>
<script>
Vue.component("todo-item" , {
props: ['plan'],
template: "<li>{{plan}}</li>"
});
var vue = new Vue({
el: '#div',
data: {
inputValue: '',
id: 1,
list: []
},
methods: {
bntClick(){
if(this.inputValue !== ''){
this.list.unshift({
id: this.id++,
value: this.inputValue
});
this.inputValue = '';
}
else{
alert('写点东西再交');
}
}
}
})
</script>
效果:
通过
v-bind
可以传递动态的值,通过变量名直接传递静态的值。
<todo-item v-for="item in list" :plan="item.value" :key="item.id"></todo-item>
<todo-item v-for="item in list" plan="+1" :key="item.id"></todo-item>
监听子组件事件
代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>count</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="div">
<count :count="count" v-on:bnt-click="bntClick"></count>
</div>
</body>
</html>
<script>
Vue.component("count" , {
props: ['count'],
template: `
<button v-on:click="$emit('bnt-click')">{{count}}</button>
`
});
var vue = new Vue({
el: '#div',
data: {
count: 1
},
methods: {
bntClick(){
this.count++;
}
}
})
</script>
效果:
当点击这个按钮时,我们需要告诉父级组件将
count
加一。Vue实例提供了一个自定义事件的系统来解决这个问题。父级组件可以像处理native DOM(如
click
)事件一样通过
v-on
监听子组件实例的任意事件:
<count :count="count" v-on:bnt-click="bntClick"></count>
同时子组件可以通过调用内建的
$emit
方法并传入事件名称来触发一个事件:
Vue.component("count" , {
props: ['count'],
template: `
<button v-on:click="$emit('bnt-click')">{{count}}</button>
`
});
事件名称要用
bnt-click
这种格式,不要用
bntClick
这种格式,不然就不起作用(我这个版本是这样的)。
使用事件抛出一个值
代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>count</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="div">
<count :count="count" v-on:bnt-click="bntClick"></count>
</div>
</body>
</html>
<script>
Vue.component("count" , {
props: ['count'],
template: `
<button v-on:click="$emit('bnt-click' , 5)">{{count}}</button>
`
});
var vue = new Vue({
el: '#div',
data: {
count: 1
},
methods: {
bntClick(value){
this.count += value;
}
}
})
</script>
点四下:
当在父级组件监听这个事件的时候,我们可以通过
$event
访问到被抛出的这个值。
代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>count</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="div">
<count :count="count" v-on:bnt-click="count += $event"></count>
</div>
</body>
</html>
<script>
Vue.component("count" , {
props: ['count'],
template: `
<button v-on:click="$emit('bnt-click' , 5)">{{count}}</button>
`
});
var vue = new Vue({
el: '#div',
data: {
count: 1
}
})
</script>
效果是一样的。
在组件上使用 v-model
自定义事件也可以用于创建支持
v-model
的自定义输入组件。记住:
<input v-model="searchText">
等价于:
<input
v-bind:value="searchText"
v-on:input="searchText = $event.target.value"
>
当用在组件上时,
v-model
则会这样:
<custom-input
v-bind:value="searchText"
v-on:input="searchText = $event"
></custom-input>
为了让它正常工作,这个组件内的
<input>
必须:
- 将其
绑定到一个名叫 value attribute
的 value
上prop
- 在其
事件被触发时,将新的值通过自定义的 input
事件抛出。input
写成代码之后是这样的:
Vue.component('custom-input', {
props: ['value'],
template: `
<input
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
`
})
现在
v-model
就应该可以在这个组件上完美地工作起来了:
<custom-input v-model="searchText"></custom-input>
通过插槽分发内容
和HTML元素一样,我们经常需要向一个组件传递内容,Vue自定义的
<slot>
元素让这变得非常简单:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>count</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="div">
<slot-demo>kaven</slot-demo>
</div>
</body>
</html>
<script>
Vue.component("slot-demo" , {
template: `
<div>
<p>welcome </p>
<slot></slot>
</div>
`
});
var vue = new Vue({
el: '#div'
})
</script>
效果:
动态组件
有的时候,在不同组件之间进行动态切换是非常有用的。
可以通过 Vue的
<component>
元素加一个特殊的
is attribute
来实现:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>count</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="div">
<component v-bind:is="currentComponent">kaven</component>
<button @click="click">切换</button>
</div>
</body>
</html>
<script>
Vue.component("slot-welcome" , {
template: `
<div>
<p>welcome </p>
<slot></slot>
</div>
`
});
Vue.component("slot-hello" , {
template: `
<div>
<p>hello </p>
<slot></slot>
</div>
`
});
var vue = new Vue({
el: '#div',
data: {
currentComponent: 'slot-hello'
},
methods: {
click(){
if(this.currentComponent === 'slot-hello'){
this.currentComponent = 'slot-welcome'
}
else{
this.currentComponent = 'slot-hello'
}
}
}
})
</script>
效果:
点击按钮。