VUE3浅析---内置指令以及生命周期

news/2024/11/23 0:37:56/

VUE3的内置指令用法实例以及生命周期函数的使用和原理探索


文章目录

    • 一、内置指令
      • 1、v-text:更新元素的文本内容
      • 2、v-html:更新元素的文本内容,并且是可以识别html标签的
      • 3、v-bind:绑定某一个属性,简写为:
      • 4、v-once:只会渲染一次元素
      • 5、v-memo:一般配置v-for来使用,使用场景和v-once一样,可以灵活定义限制条件,当v-memo不传值时,作用和v-once一样。
      • 5、v-for:循环,一般在使用的时候,要指定一个key,指定key作用于后续的diff算法有关系
      • 6、v-on:监听绑定事件,也可以简写为@
      • 7、v-model:绑定模型,将数据绑定某一个组件上。
      • 8、v-pre:将标签里的文本按照Vue的模版语法显示,常见是就是显示{{}}
      • 9、v-show:显示或者隐藏组件
      • 10、v-if:显示或者隐藏
      • 11、v-solt:插槽
          • 11.1、匿名插槽
          • 11.2、具名插槽
          • 11.3、作用域插槽(插槽传值)
          • 11.4、动态插槽名
    • 二、生命周期
          • 1、生命周期钩子介绍:
          • 2、源码解读:
            • 2.1、注册生命周期函数:
            • 2.2、`createHook`函数实际上会调用`injectHook`,`injectHook`中有一个特殊的参数`target`代表的是当前组件的实例`currentInstance`,而之后,所有的生命周期函数都会被注册到当前实例`currentInstance`上:
            • 2.3、调用生命周期函数:


一、内置指令

1、v-text:更新元素的文本内容

const text = "这是测试v-text"
<h1 v-text="text"></h1>

2、v-html:更新元素的文本内容,并且是可以识别html标签的

const html = "<span style='color:red;'>这是测试v-html</span>"
<h1 v-html="html"></h1>

3、v-bind:绑定某一个属性,简写为:

  const id = "123"const  flag = trueconst style = {color: "green"}<h1 :id="id">这是v-bind绑定id</h1><h1 class="c" :class="['a','b']">这是v-bind绑定class,测试动态增加class</h1><h1 :class="[flag ? 'a' : 'b']">这是v-bind绑定class,测试表达式</h1><h1 :style="style">这是v-bind绑定style</h1><style>.a { color:red; }.b{ font-size: smaller; }.c { font-family: 'Times New Roman', Times, serif; }</style>

4、v-once:只会渲染一次元素

h1标签加了v-once之后,无论按钮点击多少下,值始终为0,不会改变。v-once的使用场景为:当多次触发某些操作时,又不想频繁更新某一个组件时,就可以使用v-once来限制对应的组件。

   import { ref } from 'vue'let count = ref<number>(0)const once = () => {count.value++;console.log(count.value)}<button @click="once" style="height: 100px; width: 100px; background: green;"></button><div><h1 v-text="count" v-once></h1> </div>

5、v-memo:一般配置v-for来使用,使用场景和v-once一样,可以灵活定义限制条件,当v-memo不传值时,作用和v-once一样。

v-memo的作用场景和v-once的使用场景相似,但是比v-once更加灵活,v-memo可以通过自定义限制条件,通过条件是否满足,进而来处理组件。以下场景:当点击了按钮之后,clicked的值变成了0,并且同时我们改变了第一条数据和第二条数据的值,但是发现,渲染的时候只改变了第一条数据。而第二条数据并没有发生变化,这就说明,v-memo中条件成立的数据才会被重新渲染。

import { ref, reactive } from 'vue'
const clicked = ref(1)
const list = reactive([{ id: 0, name: "000" },{ id: 1, name: "111" },{ id: 2, name: "222" },{ id: 3, name: "333" },
]);
const memo = () => {clicked.value = 0;list[0].name = "1"; //修改结果 1list[2].name = "2"; // 修改结果 2
};
<button @click="memo" style="height: 100px; width: 100px; background: green">memo</button>
<div v-for="(item, index) in list" v-memo="[index == clicked]" :key="index"><p>index: {{ item }} - selected: {{ clicked }}</p>
</div>

5、v-for:循环,一般在使用的时候,要指定一个key,指定key作用于后续的diff算法有关系

<div v-for="(item, index) in list" :key="index"><p> {{ item }} </p>
</div>

6、v-on:监听绑定事件,也可以简写为@

用来绑定事件,可以是一个方法名,也可以是一个表达式。对于普通函数的调用,还可以通过以下修饰符来处理自定义事件,比如使用.stop来阻止组件的事件触发。

  • .stop:阻止父组件事件的触发。也就是只执行自己的函数事件。
  • .{keyAlias} :只在某些按键下触发处理函数。
  • .prevent:阻止表单默认事件,会阻止form当中的action行为
<button v-on:click="change" style="width: 100px; height: 30px; background: green">改变值</button>
<button @click="change" style="width: 100px; height: 30px; background: green">改变值</button>// .once只会让事件触发一次
<button @click.once="change" style="width: 100px; height: 30px; background: green">改变值</button>// .stop会阻止parent事件的触发<div @click="parent"><p>{{ message }}</p><button @click.stop="change1" style="width: 100px; height: 30px; background: green">改变值</button></div>// .prevent阻止跳转到https://www.baidu.com<form action="https://www.baidu.com"><button @click.prevent="change2" style="width: 100px; height: 30px; background: green">改变值</button></form>import { ref } from 'vue'const message = ref('This is a page about stuff')
const change = () => {console.log('change被点击了')message.value = 'Hello Vue 3!'
}const change2 = () => {console.log('change2被点击了')message.value = 'Hello Vue 3!'
}

动态事件:

<button v-on:[event]="change" style="width: 100px; height: 30px; background: green">改变值</button>
<button @[event]="change" style="width: 100px; height: 30px; background: green">改变值</button>

7、v-model:绑定模型,将数据绑定某一个组件上。

<input v-model="message" />import { ref } from 'vue'
const message = ref('This is a page about stuff')

8、v-pre:将标签里的文本按照Vue的模版语法显示,常见是就是显示{{}}

<span v-pre>{{ const a = 1; const b = 2; }}</span>

9、v-show:显示或者隐藏组件

v-show的原理是在组件上增加display样式属性,来达到显示或者隐藏组件的目的。频繁的操作显示隐藏的场景时,使用v-show性能更好。

<button @click="isShow = !isShow" style="width: 100px; height: 30px; background: green">显示隐藏</button>
<span v-show="isShow">显示了</span>const isShow = ref(true)

10、v-if:显示或者隐藏

v-if的原理是通过创建或者销毁组件,来达到显示或者隐藏组件的目的,一般用在组件在第一次加载的场景时,使用v-if性能更好。

<button @click="isShow = !isShow" style="width: 100px; height: 30px; background: green">显示隐藏</button>
<span v-if="isShow">显示了</span>const isShow = ref(true)

11、v-solt:插槽

vue中的插槽使得组件设计更加灵活,可以在组件中使用插槽设计灵活的满足组件上动态的化的展示。

11.1、匿名插槽

当我们直接使用<solt></solt>定义插槽的时候,此时的插槽称为匿名插槽。也就是说没有名字的插槽,实际上VUE会这个插槽默认设置一个default的名字。

// SoltComponetents.vue
<template><div class="header"><slot>这是子组件header内容</slot></div>
</template><script setup lang="ts"></script>
<style></style>// PComponents.vue
<template><SoltComponetents><template #default></template></SoltComponetents>
</template><script setup lang="ts">
import SoltComponetents from '../solt/SoltComponetents.vue'
</script><style>
.header {background: turquoise;width: 100px;height: 100px;
}
.main {background: rgb(226, 148, 92);width: 100px;height: 100px;
}
.bottom {background: rgb(43, 31, 204);width: 100px;height: 100px;
}
</style>
11.2、具名插槽

当我们直接使用<solt name="main"></solt>定义插槽的时候,此时的插槽称为具名插槽。也就是说给插槽起了个名字叫做main。父组件在使用插槽的时候,使用<template #main></template>使名称为main的插槽生效,其中#mainv-solt:main的缩写。

// SoltComponetents.vue
<template><div class="main"><slot name="main">这是子组件main内容</slot></div>
</template><script setup lang="ts"></script>
<style></style>// PComponents.vue
<template><SoltComponetents><template #main></template> </SoltComponetents>
</template><script setup lang="ts">
import SoltComponetents from '../solt/SoltComponetents.vue'
</script><style>
.header {background: turquoise;width: 100px;height: 100px;
}
.main {background: rgb(226, 148, 92);width: 100px;height: 100px;
}
.bottom {background: rgb(43, 31, 204);width: 100px;height: 100px;
}
</style>
11.3、作用域插槽(插槽传值)

当我们直接使用<solt name="bottom"></solt>定义插槽的时候,可以在插槽内定义数据,并将数据传递到父组件使用

// SoltComponetents.vue
<template><div class="bottom"><slot name="bottom" :data="data" :count="count">{{ pData }}</slot></div>
</template><script setup lang="ts">
import { ref } from 'vue'const data = ref('这是子组件定义的数据')
const count = ref(100)
</script><style></style>// PComponents.vue
<template><SlotComponetents>// <template #bottom="pros"> {{ pros.data }} -- {{ pros.count }} </template> // 如果参数比较多,接受的时候直接使用对象接受,比如使用pros接受,使用pros.count获取对象中count属性的值// 如果参数比较少,接受的时候直接使用ES6的{}进行解构接受,这样看起来比较直观<template #bottom="{ data, count }"> {{ data }} -- {{ count }} </template> </SlotComponetents>
</template><script setup lang="ts">
import SlotComponetents from '../slot/SlotComponetents.vue'
</script><style>
.header {background: turquoise;width: 100px;height: 100px;
}
.main {background: rgb(226, 148, 92);width: 100px;height: 100px;
}
.bottom {background: rgb(43, 31, 204);width: 100px;height: 100px;
}
</style>
11.4、动态插槽名

动态插槽就是动态的设置插槽的名称,以达到动态设置插槽的目的,实际项目中经常使用,这里不做介绍。

注意:

  • 在父组件中使用插槽的时候,如果直接使用插槽组件,那么插槽组件中定义的多个插槽都可以被展示,一旦在父组件中的插槽组件中加入<template v-solt></template>或者<template></template>标签,那么匿名插槽将不会展示。
  • 在父组件中使用插槽的时候,在父组件中的插槽组件中加入<template #default></template>标签或者一旦指定了任意插槽的名字,即使当前指定的插槽的名称不是匿名插槽的名字,匿名插槽也会被展示。

二、生命周期

1、生命周期钩子介绍:

VUE3 setup语法糖模式下,主要有以下几个生命周期函数。

  • onBeforeMount:页面加载完成前执行这个生命周期函数,此时不可以获取操作DOM
  • onMounted:页面加载完成后执行这个生命周期函数,此时可以获取操作DOM
  • onBeforeUpdate:在改变变量的值之前执行这个生命周期函数
  • onUpdated:在改变变量的值之后执行这个生命周期函数
  • onBeforeUnmount:页面加载销毁前执行这个生命周期函数,此时可以获取操作DOM
  • onUnmounted:页面加载销毁后执行这个生命周期函数,此时不可以获取操作DOM
  • onActivated:使用<keep-alive></keep-alive>缓存组件时,当组件被加载后会执行这个函数
  • onDeactivated:使用<keep-alive></keep-alive>缓存组件时,当组件被销毁后会执行这个函数
// // PComponents.vue
<template><button@click="show = !show"style="width: 80px; height: 40px; background: green">删除组件</button><keep-alive><SlotComponetents v-if="show"><template #bottom="pro">{{ pro.data }} -- {{ pro.count }}</template></SlotComponetents></keep-alive>
</template><script setup lang="ts">
import SlotComponetents from '../slot/SlotComponetents.vue'
import { ref } from 'vue'
const show = ref(true)
</script><style>
.header {background: turquoise;width: 600px;height: 100px;
}
.main {background: rgb(226, 148, 92);width: 600px;height: 100px;
}
.bottom {background: rgb(43, 31, 204);width: 600px;height: 100px;
}
</style>// SoltComponetents.vue
<template><div class="bottom" ref="mainDiv"><slot name="bottom" :data="data" :count="count"></slot><button@click="add"style="width: 80px; height: 40px; background: green">1</button></div>
</template><script setup lang="ts">
import {ref,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted,onActivated,onDeactivated
} from 'vue'const data = ref('这是子组件定义的数据')
const count = ref(100)const mainDiv = ref<HTMLElement>()const add = () => {count.value = count.value + 1
}// 页面加载完成前执行这个生命周期函数,此时不可以获取操作DOM
onBeforeMount(() => console.log('before mount', '获取DOM:', mainDiv.value))// 页面加载完成后执行这个生命周期函数,此时可以获取操作DOM
onMounted(() => console.log('mounted', '获取DOM:', mainDiv.value))// 在改变变量的值之前执行这个生命周期函数
onBeforeUpdate(() => console.log('before update', '获取DOM:', mainDiv.value?.innerHTML))// 在改变变量的值之后执行这个生命周期函数
onUpdated(() => console.log('updated', '获取DOM:', mainDiv.value?.innerHTML))// 页面加载销毁前执行这个生命周期函数,此时可以获取操作DOM
onBeforeUnmount(() => console.log('before unmount', '获取DOM:', mainDiv.value))// 页面加载销毁后执行这个生命周期函数,此时不可以获取操作DOM
onUnmounted(() => console.log('unmounted', '获取DOM:', mainDiv.value))// 使用<keep-alive></keep-alive>缓存组件时,当组件被加载后会执行这个函数
onActivated(() => console.log('active', '获取DOM:', mainDiv.value?.innerHTML))// 使用<keep-alive></keep-alive>缓存组件时,当组件被销毁后会执行这个函数
onDeactivated(() => console.log('inactive', '获取DOM:', mainDiv.value?.innerHTML))
</script><style></style>
2、源码解读:
2.1、注册生命周期函数:

VUE3的生命周期函数的定义在apiLifecycle.ts这个文件中定义的,在这个文件中,通过一下方式导出了常用的生命周期函数,每一个生命周期函数都调用了createHook这个函数,参数实际上是各个生命周期函数的缩写。

export const onBeforeMount = createHook(LifecycleHooks.BEFORE_MOUNT)
export const onMounted = createHook(LifecycleHooks.MOUNTED)
export const onBeforeUpdate = createHook(LifecycleHooks.BEFORE_UPDATE)
export const onUpdated = createHook(LifecycleHooks.UPDATED)
export const onBeforeUnmount = createHook(LifecycleHooks.BEFORE_UNMOUNT)
export const onUnmounted = createHook(LifecycleHooks.UNMOUNTED)
export const onServerPrefetch = createHook(LifecycleHooks.SERVER_PREFETCH)
2.2、createHook函数实际上会调用injectHookinjectHook中有一个特殊的参数target代表的是当前组件的实例currentInstance,而之后,所有的生命周期函数都会被注册到当前实例currentInstance上:
// 主要是将生命周期函数挂载到当前实例上,以便于后期渲染页面时调用
export function injectHook(type: LifecycleHooks,hook: Function & { __weh?: Function },target: ComponentInternalInstance | null = currentInstance,prepend: boolean = false
): Function | undefined {if (target) {// 先从当前实例上获取生命周期函数,如果没有那就初始化为一个空的数组const hooks = target[type] || (target[type] = []);// cache the error handling wrapper for injected hooks so the same hook// can be properly deduped by the scheduler. "__weh" stands for "with error// handling".const wrappedHook =hook.__weh ||(hook.__weh = (...args: unknown[]) => {// 如果当前实例被销毁了,那就不用注册了,直接返回,相当于做了一个缓存if (target.isUnmounted) {return;}// disable tracking inside all lifecycle hooks// since they can potentially be called inside effects.// 这里先取消依赖收集,防止重复收集依赖,因为在组件初始化的时候,就已经进行过依赖收集了。pauseTracking();// Set currentInstance during hook invocation.// This assumes the hook does not synchronously trigger other hooks, which// can only be false when the user does something really funky.// 将target设置为当前实例setCurrentInstance(target);// 并且执行相应的生命周期函数const res = callWithAsyncErrorHandling(hook,target,type,args);// 释放当前实例unsetCurrentInstance();// 恢复依赖收集resetTracking();return res;});if (prepend) {hooks.unshift(wrappedHook);} else {hooks.push(wrappedHook);}return wrappedHook;} else if (__DEV__) {const apiName = toHandlerKey(ErrorTypeStrings[type].replace(/ hook$/, ""));warn(`${apiName} is called when there is no active component instance to be ` +`associated with. ` +`Lifecycle injection APIs can only be used during execution of setup().` +(__FEATURE_SUSPENSE__? ` If you are using async setup(), make sure to register lifecycle ` +`hooks before the first await statement.`: ``));}
}
2.3、调用生命周期函数:

生命周期函数的调用在renderer.ts中,componentUpdateFn函数中会进行函数的调用

const componentUpdateFn = () => {// 先判断下当前组件是不是已经挂载了if (!instance.isMounted) {let vnodeHook: VNodeHook | null | undefined;const { el, props } = initialVNode;const { bm, m, parent } = instance;const isAsyncWrapperVNode = isAsyncWrapper(initialVNode);toggleRecurse(instance, false);// beforeMount hook// bm就是beforeMount钩子的缩写,如果当前实例上有bm,则先执行bmif (bm) {invokeArrayFns(bm);}// ………………省略部分源码………………if (el && hydrateNode) {// vnode has adopted host node - perform hydration instead of mount.// ………………省略部分源码………………// 此时还没有DOM的化,执行下边的逻辑之后,就会将DOM挂载到VNode上,之后就存在DOM了,执行mounted操作就可以操作DOM了。} else {if (__DEV__) {startMeasure(instance, `render`);}const subTree = (instance.subTree = renderComponentRoot(instance));if (__DEV__) {endMeasure(instance, `render`);}if (__DEV__) {startMeasure(instance, `patch`);}// 将VNode装载到容器当中,此后就会有DOMpatch(null,subTree,container,anchor,instance,parentSuspense,isSVG);if (__DEV__) {endMeasure(instance, `patch`);}initialVNode.el = subTree.el; // 挂载DOM}// mounted hook// m就是mounted的缩写,这里执行onMounted生命周期函数if (m) {queuePostRenderEffect(m, parentSuspense);}// ………………省略部分源码………………} else {// ………………省略部分源码………………// beforeUpdate hook// bu就是beforeUpdate的缩写,这里开始执行onBeforeUpdate生命周期函数if (bu) {invokeArrayFns(bu);}// onVnodeBeforeUpdateif ((vnodeHook = next.props && next.props.onVnodeBeforeUpdate)) {invokeVNodeHook(vnodeHook, parent, next, vnode);}// ………………省略部分源码………………// updated hook// u就是updated,这里开始执行onUpdated生命周期函数if (u) {queuePostRenderEffect(u, parentSuspense);}// ………………省略部分源码………………}
};// unmount进行组件卸载,这里调用unmountComponent函数清空收集的所有依赖
const unmount: UnmountFn = (vnode,parentComponent,parentSuspense,doRemove = false,optimized = false
) => {// ………………省略部分源码………………// 清空收集的所有依赖unmountComponent(vnode.component!, parentSuspense, doRemove)// 清空所有的依赖之后,就会执行onBeforeUnmount生命周期函数if (shouldInvokeDirs) {invokeDirectiveHook(vnode, null, parentComponent, "beforeUnmount");}// 接着还会调用该函数删除DOM下边的所有子树unmountChildren(dynamicChildren, parentComponent, parentSuspense, false, true);// 最后执行onUnmounted函数,此时表明组件已经卸载完成invokeDirectiveHook(vnode, null, parentComponent, 'unmounted')// ………………省略部分源码………………
};

http://www.ppmy.cn/news/266087.html

相关文章

C语言趣味小游戏---利用二维数组实现三子棋游戏

学习了C语言中的二维数组&#xff0c;本照着学以致用的原则&#xff0c;现在利用对二维数组的操作来实现一个简单版的三子棋游戏。 三子棋其实我们都玩过&#xff0c;在我们这边又叫"一条龙"。就是一个九空格&#xff0c;下棋的双方依次在九个空格里面下棋&#xff0…

Java代码块和属性的赋值顺序

代码块 类的成员之四&#xff1a;代码块(初始化块)&#xff08;重要性较属性、方法、构造器差一些&#xff09; 1.代码块的作用&#xff1a;用来初始化类、对象的信息 2.分类&#xff1a;代码块要是使用修饰符&#xff0c;只能使用static 分类&#xff1a;静态代码块 vs 非静态…

第2章 存储器层次结构设计

2.1 引言 计算机先驱准确预测到程序员会希望拥有无限数量的快速存储器。满足这一愿望的一种经济型解决方法是存储器层次结构。基于局部性原理下和“在给定实现工艺和功耗预算下&#xff0c;硬件越小&#xff0c;速度越快”的指导原则&#xff0c;产生了存储器层次结构&#xf…

Nginx增添api接口记录

问题 在之前的文章中《Flask 部署项目 Nginx Gunicorn Flask》有讲解如何配置NginxGunicornFlask服务。现需求是需要增加一个接口。 方法 需要重新修改 Nginx 的配置文件&#xff08;/etc/nginx/nginx.conf 或其他自定义的配置文件&#xff09;&#xff0c; 添加一个新的 …

day12 | 239. 滑动窗口最大值、347.前 K 个高频元素、

目录&#xff1a; 链接 题目链接&#xff1a; https://leetcode.cn/problems/sliding-window-maximum/ https://leetcode.cn/problems/top-k-frequent-elements/ 解题及思路学习 239. 滑动窗口最大值 给你一个整数数组 nums&#xff0c;有一个大小为 k **的滑动窗口从数…

串口助手(布局,图标,串口号,隐藏界面,显示实时时间)

文章目录 前言一、串口助手布局二、设置软件的标题&#xff0c;图标三、显示可用串口号四、隐藏&#xff0c;显示面板五、显示实时时间总结 前言 从这篇文章开始 教大家自己制作一个串口助手软件&#xff0c;并实现基本的功能。学做一个 串口助手可以一边回顾复习 QT 的相关知…

Android处理器

全文框架按阵营分为&#xff1a; Ⅰ、ARMv4架构阵营&#xff0c;代表核心&#xff1a; ARM9核心 Ⅱ、ARMv6架构阵营&#xff0c;代表核心&#xff1a; ARM11核心 Ⅲ、ARMv7架构阵营&#xff0c;代表核心&#xff1a; ①高通Scorpion核心 ②Cortex A8核心 …

揭开芯面纱 主流平板电脑方案深度剖析之ARMv5,v6,v7架构阵营

一、全文框架 按阵营分为&#xff1a; Ⅰ、ARMv5架构阵营&#xff0c;代表核心&#xff1a; ARM9核心 Ⅱ、ARMv6架构阵营&#xff0c;代表核心&#xff1a; ARM11核心 Ⅲ、ARMv7架构阵营&#xff0c;代表核心&#xff1a; ① 高通Scorpion核心 ②Cortex A8核心 ③三星Hummi…