【15】声明周期钩子
(1)简单介绍组件
- 在Vue中,组件是可复用的Vue实例,拥有与Vue实例相似的选项,例如
data
、computed
、watch
、methods
以及生命周期钩子等。 - 组件是构建大型应用的基础。通过使用组件,我们可以将UI拆分为独立、可复用的部分,并使整个应用更容易管理和维护。
template
: 定义组件的HTML模板。data
: 返回一个对象,表示组件的初始状态。在这里,组件有一个title
属性,其初始值为'首页'
。methods
: 定义组件的方法。handleClick
方法会在按钮被点击时调用,并弹出一个警告框显示title
的值。
(2)vue2生命周期钩子
- 官网:[Vue 实例 — Vue.js (vuejs.org)](https://cn.vuejs.org/guide/essentials/lifecycle.html)
-
创建阶段
beforeCreate
:在实例初始化之后,数据观测(data observer) 和事件/监听器配置之前被调用。在这个阶段,Vue实例的属性和方法尚未初始化, 此时template和data属性都为空。created
:在实例创建完成后被立即调用。在这个阶段,Vue实例已经初始化,可以访问到data
、props
、methods
和computed
等属性,此时data属性有值,但是template属性没有值。
-
挂载阶段
beforeMount
:在挂载开始之前被调用。 在这个阶段,模板编译已完成,但尚未将组件挂载到DOM中,此时template属性为空。mounted
:在实例被挂载到DOM上后调用。在这个阶段,组件已经被挂载到DOM中,可以操作DOM元素。
-
更新阶段
beforeUpdate
:在数据改变后,DOM更新之前被调用。在这个阶段,组件的数据已经被修改,但DOM尚未更新updated
:在DOM更新完成后被调用。在这个阶段,组件的数据已经更新,并且DOM也已经重新渲染。
-
销毁阶段
beforeDestroy
:在实例销毁之前调用。在这个阶段,实例仍然可以访问data
、props
、methods
和computed
等属性,但已经无法访问到DOM元素。destroyed
:在Vue实例销毁后被调用。在这个阶段,Vue实例已经被销毁,所有的事件监听和子组件也都已经被移除。
<body>
<div id="app">
<Header1 v-if="isShow"></Header1>
<button @click="handleClick">销毁或加载</button>
</div>
</body>
<script>
Vue.component('Header1', {
template: `
<div>
<button @click="handleClick">{{ info }}</button>
</div>`,
data() {
return {
info: '后台管理'
}
},
methods: {
handleClick() {
this.info = '前台管理'
}
},
beforeDestroy() {
console.log("销毁之前")
},
destroyed() {
console.log("销毁以后")
},
// beforeUpdate() {
// console.log("挂载之前")
// },
// updated() {
// console.log("挂载以后")
// },
// beforeMount() {
// console.log("挂载之前")
// console.log(this.$el)
// },
// mounted() {
// console.log("挂载以后")
// console.log(this.$el)
// }
// beforeCreate() {
// console.log("创建之前")
// console.log(this.info)
// console.log(this.$el)
// },
// created() {
// console.log("创建以后")
// console.log(this.info)
// console.log(this.$el)
// }
});
var vm = new Vue({
el: "#app",
data: {
isShow: true
},
methods: {
handleClick() {
this.isShow = !this.isShow
}
}
})
</script>
(3)使用案例
- 子组件在多种场景中可能需要使用定时器,例如定期获取数据或执行某个周期性任务。在这种情况下,正确管理定时器至关重要,以避免资源泄漏和不必要的性能开销。
- 在Vue中,我们可以利用生命周期钩子来实现定时器的创建和销毁。具体来说,当子组件创建时(
created
钩子),我们可以启动定时器;而当子组件销毁时(beforeDestroy
钩子),我们则需要清除定时器。
<body>
<div id="app">
<Child v-if="isShow"></Child>
<button @click="handleClick">销毁或加载</button>
</div>
</body>
<script>
Vue.component('Child', {
template: `
<div>
<h2 v-text="info"></h2>
</div>`,
data() {
return {
info: '后台管理',
t: null,
}
},
methods: {
autoAdd() {
this.info = this.info + '.'
console.log(this.info)
}
},
beforeDestroy() {
clearInterval(this.t)
t = null
},
created() {
this.t = setInterval(this.autoAdd, 1000)
},
});
var vm = new Vue({
el: "#app",
data: {
isShow: true
},
methods: {
handleClick() {
this.isShow = !this.isShow
}
}
})
</script>
【16】组件使用
(1)组件的分类
-
全局组件:
- 全局组件是在应用的根组件中注册,因此可以在整个应用的任何组件中使用的组件。
- 它们具有全局可用性,意味着无需在每个组件中单独引入或注册,就可以直接在需要的地方使用。
- 语法:
- 使用
Vue.component(tagName, options)
方法定义。 tagName
是组件的标签名。options
是一个对象,包含组件的选项,如template
、data
、methods
等。- 全局组件定义后,可以在任何 Vue 实例中使用,无需额外注册。
- 使用
-
局部组件:
- 与全局组件不同,局部组件只能在定义它们的当前组件内部使用。
- 这种组件的创建通常是为了实现特定组件的特定功能或界面,因此具有更好的封装性,减少了与其他组件之间的耦合。
- 语法:
components:{tagName, options}
- 先定义一个包含组件选项的对象。
- 在特定的 Vue 实例或组件的
components
选项中注册这个对象,并给它一个标签名。 - 局部组件只能在它被定义的那个 Vue 实例或组件内部使用。
<body>
<div id="app">
<h1 v-text="info"></h1>
<hr>
<Child2></Child2>
<hr>
<Child1></Child1>
</div>
</body>
<script>
Vue.component('Child2', { // 全局组件
template: `
<div>
<h2 v-text="info"></h2>
</div>`,
data() {
return {
info: '全局组件',
}
},
});
let childComponent = { // 局部组件
template: `
<div><h2 v-text="info"></h2></div>`,
data() {
return {
info: '局部组件',
}
},
}
var vm = new Vue({
el: "#app",
data: {
info: '根组件',
},
components: {
'Child1': childComponent // 局部组件在components中注册
},
})
</script>
(2)组件中的data
-
当定义组件时,
data
应该是一个返回对象的函数,而不是直接返回一个对象。这样做的原因是,当Vue实例化一个组件时,它会调用这个data
函数,从而确保每个实例都有它自己的数据状态。 -
举个例子,如果我们不将
data
定义为返回对象的函数,而是直接返回一个对象,那么多个组件实例将会共享这个对象,它们的状态将会相互影响:
Vue.component('MyComponent', {
// 错误的data定义方式
data: {
message: 'Hello'
},
template: '<div>{{ message }}</div>'
});
-
在上面的例子中,如果页面上有多个
MyComponent
的实例,那么它们会共享同一个data
对象,这意味着改变一个实例的message
属性也会影响其他实例。 -
正确的做法是将
data
定义为一个函数,这样每个组件实例都会返回一个新的数据对象:
Vue.component('MyComponent', {
// 正确的data定义方式
data: function() {
return {
message: 'Hello'
}
},
template: '<div>{{ message }}</div>'
});
- 无论是全局组件还是局部组件,这个原则都是适用的。每个组件实例都需要有它自己的数据状态,因此
data
必须是一个返回对象的函数,而不是直接返回的对象。
【17】父子通信
(1)父传子–自定义属性props
- 在Vue中,父组件向子组件传递数据通常是通过自定义属性(props)来实现的。
- 父组件可以在子组件的标签上通过属性绑定(使用v-bind指令或简写为冒号:)来传递数据给子组件。
- 子组件通过声明props来接收这些数据,并在组件内部使用。
<body>
<div id="app">
<child1 :message="messageParent"></child1>
</div>
</body>
<script>
Vue.component('Child1', { // 全局组件
template: `
<div>
<h2 v-text="info"></h2>
<button @click="handleChange">切换显示父组件信息</button>
</div>`,
data() {
return {
info: '子组件信息',
}
},
props: {
message: {
type: String,
required: true
}
},
methods: {
handleChange() {
this.info = this.message
}
}
});
var vm = new Vue({
el: "#app",
data: {
messageParent: "父组件信息"
},
})
</script>
- 在上面的例子中:
- 父组件通过
:message="messageParent"
将messageParent
变量的值传递给子组件的message
属性。 - 子组件通过声明
props: {message{ }}
来接收这个属性,并在模板中使用{{ message }}
来显示传递过来的数据。 - 需要注意的是,子组件中声明的props应该明确类型,也可以指定是否必须传递(required),以及是否允许默认值(default)。
- 父组件通过
(2)子传父–自定义事件+$emit
- 在Vue中,子组件向父组件传递数据通常是通过触发自定义事件来实现的。子组件可以使用
$emit
方法来触发一个事件,并传递数据给父组件。父组件则可以在子组件的标签上使用@
或v-on
指令来监听这个事件,并定义处理函数来接收子组件传递的数据。
<body>
<div id="app">
<child1 @event="handleShow"></child1>
<h3>子组件信息>>>:{{info}}</h3>
</div>
</body>
<script>
Vue.component('Child1', { // 全局组件
template: `
<div>
<button @click="handleSend">发信息到父组件</button>
</div>`,
data() {
return {
messageChild: "子组件信息"
}
},
methods: {
handleSend() {
this.$emit('event', this.messageChild)
}
}
});
var vm = new Vue({
el: "#app",
data: {
info: ""
},
methods: {
handleShow(messageChild) {
this.info = messageChild
}
}
})
</script>
-
子组件的模板包含一个按钮,当按钮被点击时,会触发
handleSend
方法。这个方法内部使用this.$emit('event', this.messageChild)
来触发一个名为event
的自定义事件,并将子组件内部的messageChild
数据作为参数传递。 -
父组件在模板中通过
<child1 @event="handleShow"></child1>
的方式使用子组件,并使用@event
来监听子组件触发的event
事件。当这个事件被触发时,父组件的handleShow
方法会被调用,并且子组件传递过来的messageChild
数据会作为参数传递给这个方法。
【18】ref属性
(1)介绍
-
ref属性用于给元素或子组件注册引用信息。
- 引用信息会被注册在父组件的$refs对象上。
- 通过ref属性,我们可以方便地获取到子组件或DOM元素的引用,从而对其进行操作或访问其属性和方法。
-
语法
-
ref属性的语法相对简单,只需在需要注册的元素或组件标签上添加ref属性,并为其指定一个唯一的名称即可。
-
<!-- 在普通元素上使用ref --> <div ref="myDiv">普通标签</div> <!-- 在子组件上使用ref --> <child1 ref="myChild"></child1>
-
-
(2)示例
<body>
<div id="app">
<child1 ref="myChild"></child1>
<h3>子组件信息>>>:{{info}}</h3>
<button @click="handleShow">显示并操作ref内容</button>
</div>
</body>
<script>
Vue.component('Child1', { // 全局组件
template: `
<div>
<P>子组件信息:{{ messageChild }}</P>
</div>`,
data() {
return {
messageChild: "子组件信息"
}
},
});
var vm = new Vue({
el: "#app",
data: {
info: ""
},
methods: {
handleShow(messageChild) {
this.info = this.$refs.myChild.messageChild + '-----'
this.$refs.myChild.messageChild = this.info
}
}
})
</script>
【19】动态组件
(1)component标签
-
<component :is="组件名"></component>
-
<component>
是一个特殊的Vue内置元素,它可以根据一个动态的组件名来渲染一个组件。 -
我们可以通过
is
属性来动态地改变要渲染的组件。
-
<body>
<div id="app">
<button @click="isShow='goods'">显示商品信息</button>
<button @click="isShow='order'">显示订单信息</button>
<button @click="isShow='backend'">显示后台信息</button>
<hr>
<component :is="isShow"></component>
</div>
</body>
<script>
let goods = {template: `<h2>商品信息</h2>`}
let order = {template: `<h2>订单信息</h2>`}
let backend = {template: `<h2>后台信息</h2>`}
var vm = new Vue({
el: "#app",
data: {
isShow: "goods"
},
components: {
goods, order, backend
}
})
</script>
(2)keep-alive组件
<keep-alive>
是一个包裹组件,它可以使被包含的组件保持状态,避免重新渲染。这对于性能优化和用户体验的提升非常有用,特别是在组件切换频繁的场景下。- 在使用
keep-alive
时,需要注意以下几点:- 包裹单个组件:
keep-alive
通常直接包裹一个动态组件<component :is="...">
,以缓存该动态组件的不同状态。 - include 和 exclude:通过
include
属性,你可以指定哪些组件名需要被缓存;通过exclude
属性,你可以指定哪些组件名不需要被缓存。这两个属性都接受一个字符串或正则表达式数组。 - max 属性:通过
max
属性,你可以限制同时被缓存的组件实例的最大数量。当这个数量达到上限时,新创建的组件实例会替换掉最不常用的组件实例。
- 包裹单个组件:
<body>
<div id="app">
<button @click="isShow='goods'">显示商品信息</button>
<button @click="isShow='order'">显示订单信息</button>
<button @click="isShow='backend'">显示后台信息</button>
<hr>
<keep-alive include="">
<component :is="isShow"></component>
</keep-alive>
</div>
</body>
<script>
let goods = {template: `
<div>
<h2>商品信息</h2>
<input type="text">
</div>`}
let order = {template: `<h2>订单信息</h2>`}
let backend = {template: `<h2>后台信息</h2>`}
var vm = new Vue({
el: "#app",
data: {
isShow: "goods"
},
components: {
goods, order, backend
}
})
</script>
【20】插槽slot
(1)介绍
-
插槽(Slot)允许开发者将内容插入到指定的位置,从而使模板分块,具有模块化的特质和更大的重用性。
- 插槽的显示与否以及如何显示是由父组件来控制的
- 插槽在哪里显示则由子组件来决定
-
分类:
-
匿名插槽:
- 概念:没有为插槽指定名称的插槽。
- 使用方法:通过
<slot></slot>
标签可以在子组件中定义匿名插槽。在父组件中使用子组件时,组件中的内容会填充到所有匿名插槽的位置。
-
具名插槽:
-
概念:为插槽设置名字的插槽,用于接收特定名称的内容。
-
使用方法:在定义具名插槽时,需要给插槽一个名字,如
<slot name="header"></slot>
。在父组件中,使用v-slot
指令来传递内容到指定的具名插槽,例如v-slot:header
或者简写为#header
。
-
-
(2)使用
- 匿名插槽
- 具名插槽
<body>
<div id="app">
<h2>匿名插槽</h2>
<goods>
<div>头部信息</div>
<div>尾部信息</div>
</goods>
<hr>
<h2>具名插槽</h2>
<order>
<div slot="foot">尾部信息</div>
<div slot="head">头部信息</div>
</order>
</div>
</body>
<script>
let goods = {
template: `
<div>
<slot></slot>
<h3>商品信息</h3>
<slot></slot>
</div>`
}
let order = {
template: `
<div>
<slot name="head"></slot>
<h3>订单信息</h3>
<slot name="foot"></slot>
</div>`
}
var vm = new Vue({
el: "#app",
components: {
goods, order
}
})
</script>