组件
组件是用来提高代码的复用性,可以分为局部组件和全局组件。在使用的时候可以认为他就是个自定义的标签,渲染的结果是template中的内容,Vue实例(new Vue)是根组件。
全局组件
注册全局组件: Vue.component("组件名",配置对象)
<div id="app">
<!-- 使用组件 -->
<v-tab></v-tab>
<v-tab></v-tab>
</div>
<div id="app2">
<!-- 使用组件 -->
<v-tab></v-tab>
<v-tab></v-tab>
</div>
<template id="first-template">
<!-- 此处必须得有一个元素节点包裹我们要复用的内容 -->
<div>
<h3>标题</h3>
<p>文章的段落</p>
</div>
</template>
<script>
// 注册全局组件
Vue.component("v-tab",
{
//配置组件,封装的html结构写在template属性中
template: "#first-template"
})
//创建Vue实例1,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {}
});
//创建Vue实例2,得到 ViewModel
var vm = new Vue({
el: '#app2',
});
</script>
从渲染的结果我们可以发现实例1和实例2都可以使用注册的这个全局组件
局部组件
部组件只能用在定义的组件中。组件中可以使用 data 和 methods ,不过data不能直接写一个对象,而是要写成一个函数,并返回一个新的对象(为什么呢?)。 -->
<body>
<div id="app">
<v-tab></v-tab>
<!-- 以下内容并不会渲染 -->
<z-tab></z-tab>
</div>
<div id="app2">
<!-- 以下内容并不会渲染 -->
<v-tab></v-tab>
<z-tab></z-tab>
</div>
<!-- template标签是占位符,并不会渲染到页面上 -->
<template id="feature-template">
<!-- 根元素有且只能有一个 -->
<div>
<h1>hello,vue</h1>
<p>{{msg}}</p>
<button @click="sumbit">提交</button>
<!-- VM实例中的子组件 -->
<z-tab></z-tab>
</div>
</template>
<script>
//创建Vue实例1
var vm = new Vue({
el: '#app',
data: {},
//组件定义在这个属性中,这里定义的组件只能在当前的vm中使用。
components: {
// Vm实例中首组件
"v-tab": {
template: '#feature-template',
data() {
return {
msg: "2022加油"
}
},
methods: {
sumbit() {
alert("提交按钮")
}
},
//VM实例中的子组件
components: {
"z-tab": {
template: "<p>hello,2022</p>"
}
}
}
}
});
//创建Vue实例2
var vm2 = new Vue({
el: '#app2',
data: {}
});
</script>
</body>
为什么组件中的data是一个函数?(重点)
组件是可复用的,为了防止组件中的数据彼此影响,每个组件的数据应该互相隔离,也就是说,每个组件应该都有自己的数据。设置成函数之后,在函数中返回一个数据对象。组件每复用一次,data数据就会被复制一次。
动态组件
我们可以通过v-if、v-show来动态的控制组件的显示和隐藏。利用component标签的is属性来控制组件的显示和隐藏。
格式: <component :is="显示的组件名(可以是data中的数据)"></component>
<div id="app">
<button @click="which='index'">首页</button>
<button @click="which='car'">购物车</button>
<button @click="which='user'">用户</button>
<keep-alive>
<component :is="which"></component>
</keep-alive>
</div>
<template id="tmp">
<div>这是购物车组件 <input type="text"></div>
</template>
<script>
//创建Vue实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
which: "car"
},
methods: {},
components: {
"index": {
template: '<div>这是首页组件</div>'
},
"car": {
template: '#tmp'
},
"user": {
template: '<div>这是用户组件</div>'
}
},
});
</script>
Tips
- component实际上是在用v-if来动态渲染组件,切换组件时,不渲染的组件实际上是被销毁了。
- 我们想要component标签来保存组件的状态,可以配合
keep-alive
来使用
格式:
<keep-alive>
<component :is="which"></component>
</keep-alive>
生命周期
- Vue组件从创建到销毁的一系列过程。
- Vue的生命周期分为三大阶段:创建阶段、运行阶段、销毁阶段;每个阶段都有对应的函数,叫做钩子,又被称为钩子函数。
- 创建阶段的钩子只会在创建的时候执行一次,之后就再也不会执行了
数据传递
组件在注册的时候作用域是孤立的,如果A组件想要使用B组件的数据,我们必须通过一定的方法来实现。
父→子传递
实现步骤:
- 创建Vue实例,用定义局部组件的方式定义son组件,使两个组件为父子关系
- 在father使用时,在son中自定义一个属性来接收father的数据
- 在子组件注册中使用props,使得son拿到father的数据,这样son就可以使用了
<!-- vue是最大的根组件,其实我们这里的father是vue的子组件,son是father的子组件 -->
<div id="app">
<father></father>
</div>
<!-- 父组件模版内容 -->
<template id="father">
<div>
父组件:<input type="text" v-model="msg">
<hr>
<!-- info是我们自定义的属性名加上数据绑定,就可以接收到父组件inp框输入的数据了 -->
<son :info="msg"></son>
</div>
</template>
<!-- 子组件模版内容 -->
<template id="son">
<div>
<!-- 渲染接收到的数据info -->
<p>子组件得到父组件的数据是:{{ info }}</p>
</div>
</template>
<script>
Vue.component('father', {
//父组件
template: '#father',
data() {
return {
msg: "",
}
},
//定义局部组件的方式定义子组件
components: {
//子组件
son: {
template: "#son",
//props中的元素名必须和我们在父组件中自定义的属性名一致
props: ["info"]
}
}
});
new Vue({
el: "#app",
})
</script>
子→父传递
原理:让子组件调用父组件的方法,通过方法传参的方式传递数据
实现步骤:
- 在father使用时,给son自定义一个事件A,这个事件的值为father的methods中的方法名A-m
- 在子组件的methods中定义一个名为B-m的方法,在methods中 通过 this.$emit("A-m",子组件的数据)来触发A-m方法,并通过参数的方式传递数据给father
<div id="app">
<father></father>
</div>
<!-- 父组件 -->
<template id="father">
<div>
<p>父组件接收到的数据是:{{ msg }}</p>
<hr>
<!-- 自定义一个事件accept,当触发的时候就可以拿到传过来的数据 -->
<son @accept="accept" :msg="msg"></son>
</div>
</template>
<!-- 子组件 -->
<template id="son">
<div>
子组件:<input type="text" v-model="msg" placeholder="请输入你要发送的内容">
<!-- 点击按钮,发送子组件的数据 -->
<button @click="sendOut">发送数据</button>
</div>
</template>
Vue.component('father', {
template: '#father',
data() {
return {
msg: "",
}
},
methods: {
accept(value) {
this.msg = value;
}
},
components: {
son: {
template: "#son",
data() {
return {
msg: this.msg
}
},
methods: {
sendOut() {
// 通过this.$emit来触发accept事件,并将数据以参数(this.msg)的形式传给father
this.$emit('accept', this.msg);
},
},
}
}
});
new Vue({
el: "#app",
})
</script>
非父子传递
本质上是通过事件触发一个非本身的自定义的事件处理函数,然后通过传参的方式实现通信。过程需要一个媒介也就是空的Vue实例对象,需要调用$on
和$emit
方法来实现监听和传递数据。
实现步骤:
- 第一步,创建一个空的中央处理组件bus,空的实例
- 第二步,A组件使用中定义一个事件,并在A组件中的methods中使用这个事件(这里我们用的事件类型是click名字是sendtoB)
- 第三步,A组件 mounted钩子函数中使用bus.$on("Aincident",传递数据的变量=>{....代码块....})来监听一个自定义类型的事件函数
- 第四步,B组件的mehonds中使用:bus.$emit("Aincident", this.Binfo)来触发Aincident事件处理函数。
- 第五步,当我们在使用了B的methods中的方法时,就会将Aincident激活,并将this.Binfo(B要给A的消息)传递出去,同时A的Aincident事件激活,并对A发来的消息处理。
- 第六步,以上第二至第四步再配置一下B组件就OK了。 例子:
<!-- 要求是:A和B两个朋友间的通信 -->
<!-- 实例Vm -->
<div id="app">
<!-- A组件 -->
<Ampp></Ampp>
<hr>
<!-- B组件 -->
<Bmpp></Bmpp>
</div>
<!-- 使用A组件 -->
<template id="Amodel">
<div>
<h1>A</h1>
<h3>A要发送给B的消息内容:{{Ainfo}}</h3>
<input type="text" v-model="Ainfo" placeholder="请输入消息">
<button @click="sendtoB">发送</button>
<h2>接收到的消息:{{remsgB}}</h2>
</div>
</template>
<!-- 使用B组件 -->
<template id="Bmodel">
<div>
<h1>B</h1>
<h3>B要发送给A的消息内容:{{Binfo}}</h3>
<input type="text" v-model="Binfo" placeholder="请输入消息">
<button @click="sendtoA">发送</button>
<h2>接收到的消息:{{remsgA}}</h2>
</div>
</template>
<script>
let bus = new Vue();
// 创建一个vue实例
var Vm = new Vue({
el: "#app",
data: {},
methods: {},
components: {
// A组件
Ampp: {
template: '#Amodel',
// 存储组件数据
data() {
return {
// 发送给B的消息内容
Ainfo: '',
// 接收储存B消息的变量
remsgB: ""
}
},
methods: {
sendtoB() {
// console.log("要给B发消息了");
bus.$emit("Bincident", this.Ainfo)
}
},
mounted() {
bus.$on("Aincident", value => {
this.remsgB = value;
})
},
},
// B组件
Bmpp: {
template: '#Bmodel',
// 存储数据
data() {
return {
// 发送给A的消息内容
Binfo: '',
// 接收储存A消息的变量
remsgA: ""
}
},
methods: {
sendtoA() {
// console.log("要给A发消息了");
bus.$emit("Aincident", this.Binfo)
}
},
mounted() {
bus.$on("Bincident", value => {
this.remsgA = value;
})
},
}
},
})
</script>
单向数据流
上面的例子:父子通信的时候我们发现,数据有父→子 之后,子无法改变数据内容,如果改变就会报错,这就是单向数据流的问题。那我们应该怎么解决呢? 这里我们可以使用变量来接收父组件传来的数据,然后在子组件中使用watch监听的方法来监听值的变化。