切换组件案例:
比如我们现在想要实现了一个功能:
这个案例我们可以通过两种不同的实现思路来实现:
方式一
:通过v-if来判断,显示不同的组件;方式二
:动态组件的方式;方式一代码示例:
<template>
<div class="app">
<div class="tabs">
<template v-for="(item, index) in tabs" :key="item">
<button @click="changeColor(index)" :class="{ active: index === currentIndex }">
{{ 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 About from "./views/About.vue";
import Category from "./views/Category.vue";
import Home from "./views/Home.vue";
export default {
components: {
Home,
Category,
About
},
data() {
return {
tabs: ["home", "about", "category"],
currentIndex: 0
}
},
methods: {
changeColor(index) {
this.currentIndex = index
}
}
}
script>
动态组件方式实现
动态组件是使用component组件
,通过一个特殊的attribute is
来实现:
<template>
<div class="app">
<div class="tabs">
<template v-for="(tab, index) in tabs" :key="item">
<button @click="changeColor(tab)" :class="{ active: currentTab === tab }">
{{ tab }}
button>
template>
div>
<div class="view">
<component name="sevgilid" :age="19" :is="currentTab">component>
div>
div>
template>
在上面的演示中,我们可以看到:
使用了component组件,is:组件名
,来展示对应的组件;点击按钮时获取对应的tab来作为组件名实现动态组件
的展示(动态组件了解即可,后边会讲解路由,使用路由切换更为简洁遍历)
在 Vue.js 中, keep-alive 内置组件可以缓存不活动的组件实例,它通过包裹动态组件来实现缓存
,避免了组件的频繁销毁和重建,从而提升了页面性能
。
我们先对之前的案例中Home组件进行改造:
比如我们将counter点到10,那么在切换到about再切换回来Home时,状态是否可以保持呢?
切换组件后,Home组件会被销毁掉
,再次回来时会重新创建组件
;但是,在开发中某些情况我们希望继续保持组件的状态,而不是销毁掉,这个时候我们就可以使用一个内置组件: keep-alive
使用 keep-alive 内置组件很简单,只需要将需要缓存的组件用
即可。例如在刚才的案例中:
像上面这样包裹后,我们在点击递增按钮到一定数值后来回切换发现已经数值不会再被重置;
但通过再各个组件内部使用created和unmounted钩子函数发现都没有回调
,这说明里面被包裹的所有组件都被缓存了
;
如果想要精准控制组件的缓存
和对保持keep-alive组件监听有没有切换
该怎么办呢?
(1)精准控制组件缓存
keep-alive有一些属性:
include
- string | RegExp |Array。只有名称匹配的组件会被缓存;exclude
- string | RegExp | Array。任何名称匹配的组件都不会被缓存;max
- number | string。最多可以缓存多少组件实例,一旦达到这个数字,那么缓存组件中最近没有被访问的实例会被销毁;include和exclude prop允许组件有条件地缓存:
用逗号分隔字符串、正则表达式或一个数组
来表示;name选项
;使用示例:
Home组件中:
<template>
<h2>home组件h2>
<h2>计数器:{{ counter }}h2>
<button @click="counter++">+1button>
template>
<script>
export default {
// 此处的name提供给Keep-Alive中的include属性使用的
name: "home",
data() {
return {
counter: 0
}
}
}
script>
App根组件中:
(2)缓存组件的生命周期
对于缓存的组件来说,再次进入时,我们是不会执行created或者mounted
等生命周期函数的:
activated和deactivated
这两个生命周期钩子函数来监听;需要注意的是,由于 keep-alive 是内置组件,所以不支持动态组件模式下的异步组件
:
需要缓存异步组件
,可以使用工厂函数与异步组件创建一个高阶组件
,再将其包裹在 keep-alive 内;在学习异步组件使用前,先来了解一下webpack的分包处理,因为异步组件打包时会进行分包的处理
默认的打包过程:
通过模块化直接依赖的
,那么webpack在打包时就会将组件模块打包到一起
(比如一个app.js文件中);项目的不断庞大,app.js文件的内容过大
,会造成首屏的渲染速度变慢
;打包时,代码的分包:
不需要立即使用的组件
,我们可以单独对它们进行拆分
,拆分成一些小的代码块chunk.js
;从服务器加载下来
,并且运行代码
,显示对应的内容;那么webpack中如何可以对代码进行分包呢?
如果我们的项目过大了,对于某些组件
我们希望通过异步的方式来进行加载
(目的是可以对其进行分包处理),那么Vue中给我们提供了一个函数: defineAsyncComponent
defineAsyncComponent接受两种类型的参数:
类型一
:工厂函数,该工厂函数需要返回一个Promise对象;类型二
:接受一个对象类型,对异步函数进行配置;工厂函数类型一的写法:
<script>
import { defineAsyncComponent } from "vue";
// import Category from "./views/Category.vue";
// 异步组件写法一(了解):工厂函数:返回的是一个promise对象
const AsyncCategory = defineAsyncComponent(() => import("./views/Category.vue"))
export default {
components: {
Home,
// 异步组件:打包的时候会进行分包处理
Category: AsyncCategory,
}
}
script>
异步组件写法二(了解):
(异步组件的使用仅作了解即可,因为实际开发中路由懒加载的写法会更多)
前面我们在input
中可以使用v-model
来完成双向绑定:
v-bind:value的数据绑定
和@input的事件监听
;如果我们现在封装了一个组件,其他地方在使用这个组件时,是否也可以使用v-model来同时完成这两个功能呢?
在组件上使用v-model
;当我们在组件上使用的时候,等价于如下的操作:
那么,为了我们的counter组件可以正常的工作,这个组件内的click必须:
modelValue 的prop
上;自定义的update:modelValue事件
抛出;Counter组件代码如下:
<template>
<h2>counter:{{ modelValue }}h2>
<button @click="changeCounter">修改counterbutton>
template>
<script>
export default{
props:{
modelValue:{
type:Number,
default:0
}
},
emits:["update:modelValue"],
methods:{
changeCounter(){
// this.$emit("update:modelValue", 1000)
this.$emit("update:modelValue", 999)
}
}
}
script>
v-model默认绑定的参数名和事件名是modelValue;
此处的@click绑定click事件;换成@input则表示的是绑定的是input事件,当input表有input输入时触发事件
绑定多个属性(高级用法):
我们现在通过v-model是直接绑定了一个属性,如果我们希望绑定多个属性呢?
一个组件上使用多个v-model
是否可以实现呢?默认情况
下的v-model其实是绑定了modelValue属性
和@update:modelValue
的事件;给v-model传入一个参数
,那么这个参数的名称就是我们绑定属性的名称
;默认绑定一个属性和绑定多个自定义属性的代码对比:
(1)子组件部分:
总结:
绑定了title属性;
监听了@update:title的事件;
Mixin主要在Vue2中使用,在Vue3中基本不用
虽然mixin在Vue中给我们带来了很多的代码复用和逻辑复用的好处,但是也存在一些弊端:
- 命名冲突:mixin可以将通用的逻辑和代码分离出来,但是它也可能导致命名冲突。如果多个Mixin中有同名的选项,或者Mixin中的选项与组件中的选项重名,就会导致选项的合并冲突。而且如果有多个mixin混合使用可能会更加复杂,因为难以判断选项的来源和优先级。为了避免命名冲突,我们需要在编写Mixin时格外小心,同时在开发过程中也需要保持清晰的组织结构。
- 耦合性高:mixin虽然可以让我们方便地复用代码和逻辑,但是也可能导致组件与Mixin之间的耦合性增加。因为mixin需要了解组件的内部实现细节和数据结构,从而才能正确地暴露需要的数据和方法。这就导致了Mixin与组件之间的耦合度会比较高,一旦需要修改Mixins,可能会对组件产生意外的影响。
在Vue 3中,Composition API的出现主要是为了解决mixin中所存在的问题和局限性。Composition API可以更好地实现逻辑复用,并且可以更好地组织组件代码。通过Composition API,我们可以将逻辑功能组织成一组Hooks(钩子函数)并进行复用。
目前我们是使用组件化的方式在开发整个Vue的应用程序,但是组件和组件之间有时候会存在相同的代码逻辑,我们希望对相同的代码逻辑进行抽取。
在Vue2和Vue3中都支持的一种方式就是使用Mixin来完成:
分发Vue组件中的可复用功能
;任何组件选项
;Mixin对象的选项将被混合进入该组件本身的选项
中;(1)基本使用:
Vue中的Mixin分为全局Mixin与组件级Mixin。下面我们以组件级Mixin为例讲解其基本使用方法。
首先,定义一个名为myMixin的mixin:
var myMixin = {
data: function () {
return {
message: 'Hello world!'
}
},
methods: {
greet: function () {
console.log(this.message)
}
}
}
然后,可以在Vue组件中使用这个mixin了。我们需要在组件中使用mixins选项来将Mixin混入(mixin)到组件中。
Vue.component('my-component', {
mixins: [myMixin],
template: '{{ message }}',
created: function () {
this.greet() // 执行myMixin中的方法greet
}
})
在上面的代码中,我们将myMixin混入到了my-component组件中。所以在my-component组件中,我们可以使用myMixin中定义的message实例属性和greet方法。
(2)mixin的合并规则
当多个Mixin共同作用于一个组件的时候,Vue中的Mixin会按照一定的规则进行合并;如果多个Mixin有相同的选项
,这些选项会被合并到一个选项
中,合并规则如下:
props、computed
等,都会以和方法相同的方式进行合并。(3)全局混入mixin
全局混入Mixin的主要作用是向所有的Vue实例添加一些全局的逻辑或方法。这样一来,我们就可以在所有Vue实例内使用这些全局的逻辑或方法。
比如,我们可以在全局Mixin中添加一个$myMethod
方法:
Vue.mixin({
methods: {
$myMethod: function () {
console.log('This is a global method.')
}
}
})
通过上面的代码,我们就向所有的Vue实例中添加了一个全局方法$myMethod
,可以在任何Vue组件内使用。
new Vue({
created: function () {
this.$myMethod() //这里就可以调用到已经全局混合的$myMethod方法了
}
})
总结: