前面的话
组件是Vue.js最核心的功能,也是整个框架设计最精彩的地方。组件可以扩展HTML元素,封装可重用的代码。根据项目需求,抽象出一些组件,每个组件里包含了展现、功能和样式。每个页面,根据自己所需,使用不同的组件来拼接页面。这种模式使前端页面易于扩展,且灵活性高,而且组件之间也实现了解耦。
概述
<div id="app">
<xiao-qi></xiao-qi>
</div>
上面这个例子中的自定义标签< xiao-qi>< / xiao-qi>就是一个组件。组件是一个自定义元素或称为一个模块。
组件注册
注册组件包括全局注册和局部注册
[全局注册]
全局注册后,任何Vue实例都可以使用。要注册全局组件可以使用Vue.component(‘tagName’,{})的语法形式。
Vue.component('my-component',{
// 选项
});
my-component就是注册的组件自定义标签名称,推荐使用小写加减号分割的形式命名。
注意: 要想在父实例中使用这个组件,必须要在实例创建前注册,之后就可以使用 的形式来使用组件。
<div id="app1">
<my-component></my-component>
</div>
<script>
// 全局注册
Vue.component('my-component',{
template: '<p> 这是组件的内容 </p>'
});
// 创建实例
var app1 = new Vue({
el: '#app1'
})
</script>
在组件选项中添加template选项就可以显示组件的内容。template中的DOM结构必须被一个元素包含,如果直接写成“这是组件的内容”不带“< div>< /div>” 是无法渲染的。
[局部注册]
在Vue实例中使用components选项可以局部注册一个组件,注册后的组件只有在该实例作用域下有效。
<div id="app2">
<my-component></my-component>
</div>
<script>
var Child = {
template: '<p>这是一个局部组件</p>'
}
var app2 = new Vue({
el: "#app2",
components: {
'my-component': Child
}
});
</script>
[组件嵌套]
组件中还可以使用components选项嵌套组件
<div id="app3">
<my-component></my-component>
</div>
<scirpt>
var headTitel = {
template:'<p>我是一个标题</p>'
};
var headText = {
template: '<p>我是内容</p>'
};
var header = {
template: `<div>
<headTitel></headTitel>
<headText></headText>
</div>`,
// 组件内使用components注册组件
components: {
'headTitel': headTitel,
'headText': headText
}
}
var app3 = new Vue({
el: '#app3',
components: {
'my-component': header
}
})
</script>
模板分离
在template选项中拼接HTML元素比较麻烦,也导致了HTML和JS的高耦合性。庆幸的是Vue提供两种方式将定义在js中的HTML模板分离出来。
[script]
在script标签里使用text/x-template类型,并且指定一个id 。
<script type="text/x-template" id="hello-vue-template">
<p>Hello Vue</p>
</script>
Vue.complate('hello-world',{
template: '#hello-vue-template'
})
相当于:
Vue.complate('hello-world',{
template: '<p>Hello Vue</p>'
})
示例:
<div id="app4">
<my-component></my-component>
</div>
<script type="text/x-template" id="example-template">
<p>小示例</p>
</script>
<script>
var app4 = new Vue({
el: '#app4',
//局部注册
components: {
'my-component': {
template: '#example-template'
}
}
})
</script>
[template]
如果使用标签,则不需要指定type属性
<div id="app5">
<my-component></my-component>
</div>
<template id="hello-template">
<div>hello Vue.js</div>
</template>
<script>
var app5 = new Vue({
el: "#app5",
components: {
'my-component': {
template: "#hello-template"
}
}
})
</script>
命名约定
对于组件的命名,W3C规范是字母小写且包含一个中划线(-),虽然Vue没有强制要求,但最好遵循规范。
在HTMl模板中始终使用 kebab-cased-component
<kebab-cased-component></kebab-cased-component>
<camel-cased-component></camel-cased-component>
<pascal-cased-component></pascal-cased-component>
在注册组件时,使用中划线、小驼峰、大驼峰三种任意一种
components: {
// 使用 中划线形式注册
'kebab-cased-complate':{},
//使用 小驼峰形式注册
'camelCasedComponent':{},
// 使用 大驼峰形式注册
'PascalCasedComponent':{}
}
模板限制
Vue组件的模板在某些情况在会受到HTML的限制,比如< table>内规定只允许
是< tr>、< td>、< th>等这些表格元素,所以在< table>内直接使用组件时无效的. 这种情况下可以使用is属性来挂载组件。
[is属性]
<div id="app6">
<table>
<tbody is="my-component1"></tbody>
</table>
</div>
<template id="g-template">
<p>这是挂载的组件</p>
</template>
<script>
Vue.component('my-component1',{
template: "#g-template"
});
var app6 = new Vue({
el:"#app6",
})
</script>
tbody在渲染时,会被替代为组件的内容。常见的限制元素还有ol、ul、select
注意:如果使用的是字符串模板,是不受限制的。
根元素
Vue强制要求每个Vue实例(组件本质就是一个Vue实例)需要有一个根元素.否则会报错。
<div id="app7">
<my-component2></my-component2>
</div>
<script>
Vue.component('my-component2',{
template: `
<p>第一段</p>
<p>第二段</p>
`
});
var app7 = new Vue({
el:"#app7",
})
</script>
需要改写为:
Vue.component('my-component2',{
template: `
<div>
<p>第一段</p>
<p>第二段</p>
</div>
`
});
data函数
除了template选项外,组件中还可以使用项Vue实例那样其他的选项,比如data、computed、 methods等。但是在使用data时,和实例稍微有点区别,data必须是函数,然后将数据return出去。
<div id="app8">
<my-component3></my-component3>
</div>
<script>
Vue.component('my-component3',{
template: '<div>{{ message }}</div>',
data() {
return {
message: '组件内容'
}
}
})
var app8 = new Vue({
el:"#app8",
})
</script>
注意:
js对象是引用关系,所以如果return出的对象引用了外部的一个对象,那么这个对象就是共享的,任何一方修改都会同步。
例如:
<div id="app9">
<my-component4></my-component4>
<my-component4></my-component4>
<my-component4></my-component4>
</div>
<script>
var data = {
counter: 0
};
var app9 = new Vue({
el: '#app9',
components: {
'my-component4':{
template: '<button @click="counter++">{{ counter }}</button>',
data(){
return data
}
}
}
})
</script>
上面这个示例的data对象就是共享的。组件使用了3次,但是点检的任意一个button,3个的数字都会加1 就是因为组件的data引用的是外部对象。
这肯定不是我们所希望的,可以给组件返回一个新的data对象来 独立。
将上面的组件改为:
components: {
'my-component4':{
template: '<button @click="counter++">{{ counter }}</button>',
data(){
return {
counter: 0
}
}
}
}
这样3个按钮就不会相互影响了
原生事件
有时可能我们想在组件的根元素上监听一个事件。直接使用v-on指令时不生效的。
<div id="app10">
<my-component @click="doSomeThing"></my-component>
<p>{{ message}}</p>
</div>
<script>
var app10 = new Vue({
el:"#app10",
components: {
'my-component': {
template: '<button>按钮</button>'
}
},
data: {
message: 0
},
methods: {
doSomeThing: function() {
this.message++;
return this.message;
}
}
})
</script>
无论怎么点击都没有反应
可以使用 .native 修饰 v-on指令即可
<div id="app10">
<my-component @click.native="doSomeThing"></my-component>
<p>{{ message}}</p>
</div>
<script>
var app10 = new Vue({
el:"#app10",
components: {
'my-component': {
template: '<button>按钮</button>'
}
},
data: {
message: 0
},
methods: {
doSomeThing: function() {
this.message++;
return this.message;
}
}
})
</script>