在大型应用开发的时候,页面可以划分成很多部分。往往不同的页面,也会有相同的部分。例如可能会有相同的头部导航。
但是如果每个页面都独自开发,这无疑增加了开发的成本。所以会把页面的不同部分拆分成独立的组件,然后在不同页面就可以共享这些组件,避免重复开发。
1、组件也是一个Vue实例,因此它在定义时也会接收:data、methods、生命周期函数等
2、不同的是组件不会与页面的元素绑定,否则就无法复用了,因此没有el属性。
3、但是组件渲染需要html模板,所以增加了template属性,值就是HTML模板。
4、data的定义方式比较特殊,必须是一个函数。
组件的复用:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>组件化</title>
<script src="../assets/vue-2.6.12/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!--组件复用-->
<counter></counter>
<counter></counter>
<counter></counter>
</div>
<script>
//定义组件
const counter1 = {
template:"<button @click='num++'>你点击了{{num}}次</button>",
data(){
return {
num:0
}
}
}
var app = new Vue({
el:"#app",
//局部注册组件
components:{
counter: counter1
}
});
</script>
</body>
</html>
效果图:
你会发现每个组件互不干扰,都有自己的值。这是怎么实现的?
因为组件的data属性必须是函数!
当定义组件时,它的data 并不是像这样直接提供一个对象:
data: {
num: 0
}
取而代之的是,一个组件的data选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:
data: function () {
return {
num: 0
}
}
如果 Vue 没有这条规则,点击一个按钮就会影响到其它所有实例!
组件局部注册:
一旦全局注册,就意味着即便不使用这个组件时,它依然会随着Vue的加载而加载。
因此,对于一些并不频繁使用的组件,会采用局部注册。
<div id="app">
<!--使用定义好的全局组件-->
<counter></counter>
<counter></counter>
<counter></counter>
</div>
<script>
//定义组件
const counter = {
template: "<button @click='num++'>你点击了{{num}}次;我记住了</button>",
data(){
return {num: 0}
}
};
var app = new Vue({
el:"#app",
//局部注册组件
components:{
//counter: counter
//组件名称与定义组件对象名称一致可简写为:
counter
}
});
</script>
components就是当前vue对象子组件集合,key就是子组件名称,值就是组件对象属性。
注意:局部注册的组件只能在当前的Vue实例中使用,比如加上
<div id="app111">
<counter></counter>
</div>
该div是无法使用该组件的(不会报错),但如果还想实例化这个Vue实例,就必须注册组件 或 把没定义的组件删掉,不然会引起报错!如:
var app1 = new Vue({
el:"#app1"
});
正确写法:
var app1 = new Vue({
el:"#app1",
//局部注册组件
components:{
counter: counter1
}
});
组件全局注册:
全局组件定义完毕,任何vue实例都可以直接在HTML中通过组件名称来使用组件了。
比如刚刚的代码,当用了全局注册组件就不用每个vue实例都去注册了
<div id="app">
<counter></counter>
<counter></counter>
<counter></counter>
</div>
<br>
<div id="app1">
<counter></counter>
<counter></counter>
<counter></counter>
</div>
<script>
//定义组件
const counter1 = {
template:"<button @click='num++'>你点击了{{num}}次</button>",
data(){
return {num:0}
}
}
//全局注册组件:在所有的vue实例中都可以使用组件
//参数1:组件名称,参数2:具体的组件
Vue.component("counter", counter1);
var app = new Vue({
el:"#app",
/*components:{
counter: counter1
}*/
});
var app1 = new Vue({
el:"#app1",
});
</script>
效果图:
同理我们可以利用全局注册组件,对组件进行封装,以达到在不同页面使用该组件的效果。
定义一个名为components的js文件,在里面写需要封装的组件
//定义组件
const counter1 = {
template:"<button @click='num++'>你点击了{{num}}次</button>",
data(){
return {num:0}
}
}
//全局注册组件
Vue.component("counter", counter1);
需要使用时,引进该js并把使用了该组件的vue实例实例化后即可使用:
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<script src="../assets/vue-2.6.12/dist/vue.js"></script>
<script src="../assets/js/components.js"></script>
</head>
<body>
<div id="app">
<counter></counter>
<counter></counter>
<counter></counter>
</div>
<div id="app1">
<counter></counter>
<counter></counter>
<counter></counter>
</div>
<script>
var app = new Vue({
el:"#app"
});
var app1 = new Vue({
el:"#app1",
});
</script>
</body>
</html>
组件通信:
各个组件之间以嵌套的关系组合在一起,那么这个时候不可避免的会有组件间通信的需求。
1、父组件向子组件通信:
比如有一个子组件
Vue.component("introduce",{
// 直接使用props接收到的属性来渲染页面
template:'<h3>{{title}}</h3>',
props:[title] // 通过props来接收一个父组件传递的属性
})
这个子组件中要使用title属性渲染页面,但是自己并没有title属性
通过props来接收父组件属性,名为title
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>父组件向子组件通信</title>
<script src="../assets/vue-2.6.12/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!--使用组件-->
<introduce :title="mag"></introduce>
</div>
<script>
//定义组件
const introduce1 = {
//使用props属性title的值渲染模版
template:"<h2>{{title}}</h2>",
//定义接收父组件的属性
props:['title'],
}
//全局注册组件:参数1:组件名称,参数2:组件
Vue.component("introduce", introduce1);
var app = new Vue({
el:"#app",
data:{
mag:"父组件的msg属性数据内容"
}
});
</script>
</body>
</html>
效果图:
也可以传递一些复杂的数据,如数组对象等:
定义一个子组件可以对 items 进行迭代,并输出到页面,但是组件中并未定义items属性,通过props来定义需要从父组件中接收的属性
items:是要接收的属性名称
type:限定父组件传递来数据类型;type的值可以是Array或者Object(传递对象的
时候使用)
default:默认值,
<div id="app">
<!--使用组件(不能写成myList因为有大写字母需改成小写并在它前面加-)-->
<my-list :items="lessons"></my-list>
</div>
<script>
//定义组件
const myList = {
//可以使用双引号、单引号或者如下使用的 ` 飘号
template:`
<ul>
<li v-for="item in items" :key="item.id">{{item.id}}--{{item.name}}</li>
</ul>
`,
//定义接收父组件的属性
props: {
//定义模版中使用的属性
items:{
type:Array,//数据类型,如果是数组则是Array,如果是对象则是Object
default:[],//默认值
//default(){return[];}
}
}
}
var app = new Vue({
el:"#app",
data:{
mag:"父组件的msg属性数据内容",
lessons:[
{"id":1,"name":"Java"},
{"id":2,"name":"C#"},
{"id":3,"name":"C++"},
],
},
//局部注册组件
components:{
myList
}
});
</script>
效果图:
2、子组件向父组件通信:
<div id="app">
<h2>num = {{num}}</h2>
<!--点击按钮是在子组件中,那就是说需要子组件来调用父组件的函数;可以通过v-on指令(简写'@')将父组件的函数绑定到子组件上-->
<counters @plus="numPlus" @reduce="numReduce"></counters>
</div>
<script>
//定义组件
const counter2 = {
//可以使用双引号、单引号或者如下使用的 ` 飘号
//组件只能是一个元素里面包裹其他元素;如下面,一个div包含两个按钮
template:`
<div>
<button @click='incrNum'>+</button>
<button @click='decrNum'>-</button>
</div>
`,
methods: {
//点击按钮调用自身点击事件,递增
incrNum(){
return this.$emit("plus");//调用到父组件的方法
},
//递减
decrNum(){
return this.$emit("reduce");
},
}
}
var app = new Vue({
el:"#app",
components:{ counters: counter2 },//局部注册组件
data:{ num:0 },
methods:{
//父组件中定义操作num的方法
numPlus(){
this.num++;
},
numReduce(){
this.num--;
}
}
});
</script>
效果图:
总结:
组件通信意义:父子组件之间数据的交换,能够及时更新组件内容。
父向子传递:子组件使用props属性来定义需要从父组件中接收的数据。
子向父传递:子组件使用$emit来调用父组件的方法。