coderwhy Vue3+Ts笔记(十)组件化知识补充(动态组件,keep-alive,异步组件,生命周期)
切换组件案例:
<template>
<div>
<button v-for="tab in tabs"
:key="tab"
:class="{active: currentTab === tab}"
@click="tabClick(tab)">
{{tab}}
button>
<template v-if="currentTab === 'home'">
<home>home>
template>
<template v-else-if="currentTab === 'about'">
<about>about>
template>
<template v-else>
<category>category>
template>
div>
template>
<script>
import Home from "./pages/Home.vue";
import About from "./pages/About.vue";
import Category from "./pages/Category.vue";
export default {
components: {
Home, About, Category
},
data() {
return {
tabs: ["home", "about", "category"],
currentTab: "home"
}
},
methods: {
tabClick(tab) {
this.currentTab = tab;
}
}
}
script>
<style scoped>
.active {
color: red;
}
style>
动态组件是使用 component
组件,通过一个特殊的attribute is
来实现:
<template>
<div>
<button v-for="tab in tabs"
:key="tab"
:class="{active: currentTab === tab}"
@click="tabClick(tab)">
{{tab}}
button>
<component :is="currentTab">component>
div>
template>
App.vue的代码如下:
<template>
<div>
<button v-for="tab in tabs"
:key="tab"
:class="{active: currentTab === tab}"
@click="tabClick(tab)">
{{tab}}
button>
<component name="why"
:age="18"
@pageClick="pageClick"
:is="currentTab"/>
div>
template>
<script>
import Home from "./pages/Home.vue";
import About from "./pages/About.vue";
import Category from "./pages/Category.vue";
export default {
components: {
Home, About, Category
},
data() {
return {
tabs: ["home", "about", "category"],
currentTab: "home"
}
},
methods: {
tabClick(tab) {
this.currentTab = tab;
},
pageClick(payload) {
console.log("pageClick", payload);
}
}
}
script>
<style scoped>
.active {
color: red;
}
style>
Home.vue中的代码如下:
<template>
<div @click="pageClick">
Home组件: {{name}}-{{age}}
div>
template>
<script>
export default {
props: {
name: String,
age: Number
},
emits: ["pageClick"],
methods: {
pageClick() {
this.$emit("pageClick", "Home组件");
}
}
}
script>
keep-alive
。对于生命周期的知识下面的四有讲解,因为这部分知识和keep-alive联系紧密,所以放到了这里。
大家可以等学习了生命周期后,再回头看这部分的内容。
import {sum} from './utils/math';
console.log(sum(20, 30));
import("./utils/math").then(({ sum }) => {
console.log(sum(20, 30));
});
defineAsyncComponent
。defineAsyncComponent
接受两种类型的参数:
<script>
import { defineAsyncComponent } from 'vue';
const AsyncHome = defineAsyncComponent(() => import("./AsyncHome.vue"));
export default {
components: {
AsyncHome
}
}
</script>
<script>
import { defineAsyncComponent } from "vue";
// const AsyncHome = defineAsyncComponent(() => import("./AsyncHome.vue"));
import Loading from "./Loading.vue";
import Error from "./Error.vue";
const AsyncHome = defineAsyncComponent({
// 工厂函数
loader: () => import("./AsyncHome.vue"),
// 加载过程中显示的组件
loadingComponent: Loading,
// 加载失败时显示的组件
errorComponent: Error,
// 在显示 loadingComponent 之前的延迟 | 默认值:200(单位 ms)
delay: 200,
// 如果提供了 timeout ,并且加载组件的时间超过了设定值,将显示错误组件
// 默认值:Infinity(即永不超时,单位 ms)
timeout: 3000,
// 定义组件是否可挂起 | 默认值:true
suspensible: false,
/**
*
* @param {*} error 错误信息对象
* @param {*} retry 一个函数,用于指示当 promise 加载器 reject 时,加载器是否应该重试
* @param {*} fail 一个函数,指示加载程序结束退出
* @param {*} attempts 允许的最大重试次数
*/
onError(error, retry, fail, attempts) {
if (error.message.match(/fetch/) && attempts <= 3) {
// 请求发生错误时重试,最多可尝试 3 次
retry();
} else {
// 注意,retry/fail 就像 promise 的 resolve/reject 一样:
// 必须调用其中一个才能继续错误处理。
fail();
}
},
});
export default {
components: {
AsyncHome,
},
};
</script>
注意:目前(2021-06-08)Suspense显示的是一个实验性的特性,API随时可能会修改。
default
:如果default可以显示,那么显示default的内容;fallback
:如果default无法显示,那么会显示fallback插槽的内容;<template>
<div>
<suspense>
<template #default>
<async-home>async-home>
template>
<template #fallback>
<loading/>
template>
suspense>
div>
template>
ref
attribute 的所有 DOM 元素和组件实例。<template>
<div>
<h2 ref="title">哈哈哈h2>
<hello-world ref="helloCpn">hello-world>
<button @click="visitElement">访问元素或者组件button>
div>
template>
<script>
import HelloWorld from './HelloWorld.vue';
export default {
components: {
HelloWorld
},
methods: {
visitElement() {
// 访问元素
console.log(this.$refs.title);
// 访问组件实例
this.$refs.helloCpn.showMessage();
}
}
}
script>
HelloWorld.vue实现:
<template>
<div>
div>
template>
<script>
export default {
methods: {
showMessage() {
console.log("我是HelloWorld组件的showMessage方法");
}
}
}
script>
<template>
<div>
<button @click="visitParent">访问父组件button>
div>
template>
<script>
export default {
methods: {
showMessage() {
console.log("我是HelloWorld组件的showMessage方法");
},
visitParent() {
console.log(this.$parent.message);
}
}
}
script>
注意:在Vue3中已经移除了$children的属性,所以不可以使用了。
App.vue组件对象:
<template>
<div>
<button @click="toggle">切换button>
<div v-if="isShow">
<home>home>
div>
div>
template>
<script>
import Home from './Home.vue';
export default {
components: {
Home
},
data() {
return {
isShow: true
}
},
methods: {
toggle() {
this.isShow = !this.isShow;
console.log(this.isShow);
}
}
}
script>
Home.vue组件对象:
<template>
<div>
<button @click="changeMessage">修改messagebutton>
<h2 ref="titleRef">{{message}}h2>
div>
template>
<script>
export default {
data() {
return {
message: "Hello World"
}
},
methods: {
changeMessage() {
this.message = "你好啊,李银河";
}
},
beforeUpdate() {
console.log("beforeUpdate");
console.log(this.$refs.titleRef.innerHTML);
},
updated() {
console.log("updated");
console.log(this.$refs.titleRef.innerHTML);
},
beforeCreate() {
console.log("beforeCreate");
},
created() {
console.log("created");
},
beforeMount() {
console.log("beforeMount");
},
mounted() {
console.log("mounted");
},
beforeUnmount() {
console.log("beforeUnmount");
},
unmounted() {
console.log("unmounted");
}
}
script>
input
中可以使用v-model
来完成双向绑定:
v-model
默认帮助我们完成了两件事;v-bind:value的数据绑定
和@input的事件监听
;在组件上使用v-model
;
必须:
MyInput.vue
的组件代码如下:<template>
<div>
<input :value="modelValue" @input="inputChange">
div>
template>
<script>
export default {
props: ["modelValue"],
emits: ["update:modelValue"],
methods: {
inputChange(event) {
this.$emit("update:modelValue", event.target.value);
}
}
}
script>
<template>
<div>
<my-input v-model="message"/>
<button @click="changeMessage">修改messagebutton>
div>
template>
<script>
import MyInput from './MyInput.vue';
export default {
components: {
MyInput
},
data() {
return {
message: ""
}
},
methods: {
changeMessage() {
this.message = "Hello World"
}
}
}
script>
<template>
<div>
<input v-model="modelValue">
div>
template>
<script>
export default {
props: ["modelValue"]
}
script>
我们依然希望在组件内部按照双向绑定的做法去完成,应该如何操作呢?我们可以使用计算属性的setter
和getter
来完成。
<template>
<div>
<input v-model="value">
div>
template>
<script>
export default {
props: ["modelValue"],
emits: ["update:modelValue"],
computed: {
value: {
get() {
return this.modelValue;
},
set(value) {
this.$emit("update:modelValue", value)
}
}
}
}
script>
我们先看一下在App.vue中我是如何使用的:
<template>
<div>
<my-input v-model="message" v-model:title="title"/>
<h2>{{message}}h2>
<button @click="changeMessage">修改messagebutton>
<hr>
<h2>{{title}}h2>
<button @click="changeTitle">修改titlebutton>
div>
template>
<script>
import MyInput from './MyInput.vue';
export default {
components: {
MyInput
},
data() {
return {
message: "",
title: ""
}
},
methods: {
changeMessage() {
this.message = "Hello World"
},
changeTitle() {
this.title = "Hello Title"
}
}
}
script>
<my-input v-model="message" v-model:title="title"/>
v-model:title
相当于做了两件事:
title
属性;@update:title
的事件;所以,我们MyInput中的实现如下:
<template>
<div>
<input :value="modelValue" @input="input1Change">
<input :value="title" @input="input2Change">
</div>
</template>
<script>
export default {
props: ["modelValue", "title"],
emits: ["update:modelValue", "update:title"],
methods: {
input1Change(event) {
this.$emit("update:modelValue", event.target.value);
},
input2Change(event) {
this.$emit("update:title", event.target.value);
}
}
}
</script>