什么是生命周期呢?
创建
、挂载
、更新
、卸载
等一系列的过程;生命周期函数
;生命周期函数:
<template>
<h2>message: {{message}}-{{counter}}h2>
<button @click="message = 'Hello World'">修改messagebutton>
<button @click="counter++">+1button>
<div>
<button @click="isShowHome = !isShowHome">显示Homebutton>
<home v-if="isShowHome">home>
div>
template>
<script>
import Home from "./Home.vue"
export default {
components: {
Home
},
data() {
return {
message: "Hello App",
counter: 0,
isShowHome: true
}
},
// 1.组件被创建之前
beforeCreate() {
console.log("beforeCreate")
},
// 2.组件被创建完成
created() {
console.log("created")
console.log("1.发送网络请求, 请求数据")
console.log("2.监听eventbus事件")
console.log("3.监听watch数据")
},
// 3.组件template准备被挂载
beforeMount() {
console.log("beforeMount")
},
// 4.组件template被挂载: 虚拟DOM -> 真实DOM
mounted() {
console.log("mounted")
console.log("1.获取DOM")
console.log("2.使用DOM")
},
// 5.数据发生改变
// 5.1. 准备更新DOM
beforeUpdate() {
console.log("beforeUpdate")
},
// 5.2. 更新DOM
updated() {
console.log("updated")
},
// 6.卸载VNode -> DOM元素
// 6.1.卸载之前
beforeUnmount() {
console.log("beforeUnmount")
},
// 6.2.DOM元素被卸载完成
unmounted() {
console.log("unmounted")
}
}
script>
<style scoped>
style>
某些情况下,我们在组件中想要直接获取到元素对象或者子组件实例:
不推荐
进行DOM操作的;ref
的attribute属性;组件实例有一个$refs属性:
<template>
<div class="app">
<h2 ref="title" class="title" :style="{ color: titleColor }">{{ message }}h2>
<button ref="btn" @click="changeTitle">修改titlebutton>
<banner ref="banner"/>
div>
template>
<script>
import Banner from "./Banner.vue"
export default {
components: {
Banner
},
data() {
return {
message: "Hello World",
titleColor: "red"
}
},
methods: {
changeTitle() {
// 1.不要主动的去获取DOM, 并且修改DOM内容
// this.message = "你好啊, 李银河!"
// this.titleColor = "blue"
// 2.获取h2/button元素
console.log(this.$refs.title)
console.log(this.$refs.btn)
// 3.获取banner组件: 组件实例
console.log(this.$refs.banner)
// 3.1.在父组件中可以主动的调用子组件的对象方法
this.$refs.banner.bannerClick()
// 3.2.获取banner组件实例, 获取banner中的元素
console.log(this.$refs.banner.$el)
// 3.3.如果banner template是多个根, 拿到的是第一个node节点
// 注意: 开发中不推荐一个组件的template中有多个根元素
// console.log(this.$refs.banner.$el.nextElementSibling)
// 4.组件实例还有两个属性(了解):
console.log(this.$parent) // 获取父组件
console.log(this.$root) // 获取根组件
}
}
}
script>
<style scoped>
style>
我们可以通过$parent
来访问父元素。
HelloWorld.vue的实现:
console.log('parent', this.$parent.$el)
console.log('root', this.$root.$data)
注意:在Vue3中已经移除了$children
的属性,所以不可以使用了。
比如我们现在想要实现了一个功能:
这个案例我们可以通过两种不同的实现思路来实现:
我们可以先通过v-if来判断显示不同的组件,这个可以使用我们之前讲过的知识来实现:
<template>
<div class="app">
<div class="tabs">
<template v-for="(item, index) in tabs" :key="item">
<button :class="{ active: currentTab === item }"
@click="itemClick(index)">
{{ item }}
button>
template>
div>
<div class="view">
<template v-if="currentIndex === 0">
<home>home>
template>
<template v-else-if="currentIndex === 1">
<about>about>
template>
<template v-else-if="currentIndex === 2">
<category>category>
template>
div>
div>
template>
<script>
import Home from './views/Home.vue'
import About from './views/About.vue'
import Category from './views/Category.vue'
export default {
components: {
Home,
About,
Category
},
data() {
return {
tabs: ["home", "about", "category"],
currentIndex: 0
}
},
methods: {
itemClick(tab) {
this.currentIndex = tab
},
homeClick(payload) {
console.log("homeClick:", payload)
}
}
}
script>
<style scoped>
.active {
color: red;
}
style>
动态组件是使用 component
组件,通过一个特殊的attribute is
来实现:
<template>
<div class="app">
<div class="tabs">
<template v-for="(item, index) in tabs" :key="item">
<button :class="{ active: currentTab === item }"
@click="itemClick(item)">
{{ item }}
button>
template>
div>
<div class="view">
<component name="why"
:age="18"
@homeClick="homeClick"
:is="currentTab">
component>
div>
div>
template>
<script>
import Home from './views/Home.vue'
import About from './views/About.vue'
import Category from './views/Category.vue'
export default {
components: {
Home,
About,
Category
},
data() {
return {
tabs: ["home", "about", "category"],
// currentIndex: 0
currentTab: "home"
}
},
methods: {
itemClick(tab) {
this.currentTab = tab
},
homeClick(payload) {
console.log("homeClick:", payload)
}
}
}
script>
<style scoped>
.active {
color: red;
}
style>
这个currentTab的值需要是什么内容呢?
如果是动态组件我们可以给它们传值和监听事件吗?
<component name="zhd" :age="18" @pageClick="click" :is="currentTab">component>
我们先对之前的案例中About组件进行改造:
比如我们将counter点到10,那么在切换到home再切换回来about时,状态是否可以保持呢?
否定
的;被销毁
掉,再次回来时会重新创建
组件;但是,在开发中某些情况我们希望继续保持组件的状态
,而不是销毁掉,这个时候我们就可以使用一个内置组件:keep-alive
。
<keep-alive>
<component name="zhd" :age="18" @pageClick="click" :is="currentTab">component>
keep-alive>
keep-alive有一些属性:
include 和 exclude prop 允许组件有条件地缓存:
组件自身的 name 选项
;
<keep-alive include="a,b">
<component :is="view">component>
keep-alive>
<keep-alive :include="/a|b/">
<component :is="view">component>
keep-alive>
<keep-alive :include="['a', 'b']">
<component :is="view">component>
keep-alive>
对于缓存的组件来说,再次进入时,我们是不会执行created或者mounted等生命周期函数的:
activated
和 deactivated
这两个生命周期钩子函数来监听;<script>
export default {
name: "home",
data() {
return {
counter: 0
}
},
created() {
console.log("home created")
},
unmounted() {
console.log("home unmounted")
},
// 对于保持keep-alive组件, 监听有没有进行切换
// keep-alive组件进入活跃状态
activated() {
console.log("home activated")
},
deactivated() {
console.log("home deactivated")
}
}
script>
默认的打包过程:
打包到一起
(比如一个app.js文件中);渲染速度变慢
;打包时,代码的分包:
单独对它们进行拆分
,拆分成一些小的代码块chunk.js
;那么webpack中如何可以对代码进行分包呢?
如果我们的项目过大了,对于某些组件我们希望通过异步
的方式来进行加载(目的是可以对其进行分包处理),那么Vue中给我们提供了一个函数:defineAsyncComponent
。
defineAsyncComponent接受两种类型的参数:
工厂函数类型一的写法:
<script>
import { defineAsyncComponent } from 'vue'
import Home from './views/Home.vue'
import About from './views/About.vue'
// import Category from './views/Category.vue'
// const Category = import("./views/Category.vue")
const AsyncCategory = defineAsyncComponent(() => import("./views/Category.vue"))
export default {
components: {
Home,
About,
Category: AsyncCategory
},
data() {
return {
tabs: ["home", "about", "category"],
// currentIndex: 0
currentTab: "home"
}
},
methods: {
itemClick(tab) {
this.currentTab = tab
},
homeClick(payload) {
console.log("homeClick:", payload)
}
}
}
script>
类型二的写法:
前面我们在input中可以使用v-model来完成双向绑定
:
如果我们现在封装了一个组件,其他地方在使用这个组件时,是否也可以使用v-model来同时完成这两个功能呢?
当我们在组件上使用的时候,等价于如下的操作:
<counter v-model="appCounter">counter>
<counter :modelValue="appCounter" @update:modelValue="appCounter = $event">counter>
那么,为了我们的MyInput组件可以正常的工作,这个组件内的必须:
update:modelValue
事件抛出;MyInput.vue的组件代码如下:
<template>
<div class="myform">
用户名:<input type="text" :value="modelValue" @change="changeValue">
div>
template>
<script>
export default {
data() {
return {
}
},
props: {
modelValue: {
type: String,
default: '我是默认值'
}
},
methods: {
changeValue(event) {
this.$emit('update:modelValue', event.target.value)
}
},
emits: ['update:modelValue']
}
script>
我们现在通过v-model是直接绑定了一个属性,如果我们希望绑定多个属性呢?
注意:这里我是绑定了两个属性的
<my-input v-model="message" v-model:title="title">my-input>
v-model:title相当于做了两件事:
export default {
props: ['modelValue', 'title'],
emits: ['update:modelValue', 'update:title'],
methods: {
inputChange(event) {
this.$emit('update:modelValue', event.target.value)
},
input2Change(event) {
this.$emit('update:title', event.target.value)
}
}
}
目前我们是使用组件化的方式在开发整个Vue的应用程序,但是组件和组件之间有时候会存在相同的代码逻辑,我们希望对相同的代码逻辑进行抽取
。
在Vue2和Vue3中都支持的一种方式就是使用Mixin来完成:
<template>
<div class="app">
<h2>{{ title }}h2>
<home>home>
div>
template>
<script>
import sayHelloMixin from './mixins/sayHello'
import Home from './components/Home.vue'
export default {
name: 'app.vue',
mixins: [sayHelloMixin],
components: {
Home
},
data() {
return {
title: 'mixin'
}
}
}
script>
sayHello
export default {
created() {
this.sayHello()
},
methods: {
sayHello() {
console.log("hello mixin")
}
},
mounted() {
console.log(this.$options.name + "加载完成")
}
}
如果Mixin对象中的选项和组件对象中的选项发生了冲突,那么Vue会如何操作呢?
情况一:如果是data函数的返回值对象
合并
;保留组件自身的数据
;情况二:如何生命周期钩子函数
合并
到数组中,都会被调用;情况三:值为对象的选项,例如 methods、components 和 directives,将被合并为同一个对象。
取组件对象的键值对
;如果组件中的某些选项,是所有的组件都需要拥有的,那么这个时候我们可以使用全局的mixin:
全局混入的选项将会影响每一个组件
;const app = createApp(App)
app.mixin({
create() {
console.log('global mixin created')
}
})
app.mount("#app");