Vue3 源码解读系列(十二)——指令 directive

directive

定义:本质就是一个 JavaScript 对象,对象上挂着一些钩子函数。

实现:在元素的生命周期中注入代码。

指令注册

注册原理:把指令的定义保存到相应的地方,未来使用的时候可以从保存的地方拿到。

全局注册与局部注册的区别:

  • 全局注册存放在 instance.appContext
  • 局部注册存放在组件对象里
/*** 全局注册 app.directive*/
function createApp(rootComponent, rootProps = null) {const context = createAppContext()const app = {_component: rootComponent,_props: rootProps,directive(name, directive) {// 检测指令名是否与内置的指令名有冲突if ((process.env.NODE_ENV !== 'production')) {validateDirectiveName(name)}// 没有第二个参数,则获取对应的指令对象if (!directive) {return context.directives[name]}// 重复注册的警告if ((process.env.NODE_ENV !== 'production') && context.directives[name]) {warn(/* ... */)}context.directives[name] = directivereturn app}}return app
}

指令解析

const DIRECTIVES = 'directives';
/*** 解析指令 - 根据指令名称找到保存的指令的对象*/
function resolveDirective(name) {return resolveAsset(DIRECTIVES, name)
}/*** 解析资源*/
function resolveAsset(type, name, warnMissing = true) {// 获取当前渲染实例const instance = currentRenderingInstance || currentInstanceif (instance) {const Component = instance.type// 先通过 resolve 函数解析局部注册的资源,如果没有则解析全局注册的资源const res = resolve(Component[type], name) || resolve(instance.appContext[type], name)// 如果没有,则在非生产环境下报警告if ((process.env.NODE_ENV !== 'production') && warnMissing && !res) {warn(/* ... */)}return res} else if ((process.env.NODE_ENV !== 'production')) {warn(/* ... */)}
}/*** 解析*/
function resolve(registry, name) {// 先根据 name 匹配,如果失败则把 name 转换成驼峰格式继续匹配,如果失败则把 name 变为驼峰式后再首字母大写继续匹配return (registry && (registry[name] || registry[camelize(name)] || registry[capitalize(camelize(name))]))
}/*** 绑定指令 - 给 vnode 添加一个 dirs 属性,值为这个元素上所有指令构成的数组*/
function withDirectives(vnode, directives) {const internalInstance = currentRenderingInstanceif (internalInstance === null) {(process.env.NODE_ENV !== 'production') && warn(/* ... */)return vnode}const instance = internalInstance.proxyconst bindings = vnode.dirs || (vnode.dirs = [])// 遍历 directives,拿到每一个指令对象以及指令的相关内容for (let i = 0; i < directives.length; i++) {let [dir, value, arg, modifiers = EMPTY_OBJ] = directives[i]if (isFunction(dir)) {dir = {mounted: dir,updated: dir}}bindings.push({dir, // 指令instance, // 组件实例value, // 指令值oldValue: void 0,arg, // 参数modifiers // 修饰符})}return vnode
}

指令生命周期

挂载

/*** 挂载元素*/
const mountElement = (vnode, container, anchor, parentComponent, parentSuspense, isSVG, optimized) => {let elconst { type, props, shapeFlag, dirs } = vnode// 创建 DOM 元素节点el = vnode.el = hostCreateElement(vnode.type, isSVG, props && props.is)if (props) {// 处理 props,比如 class、style、event 等属性}// 处理子节点是纯文本的情况if (shapeFlag & 8/*TEXT_CHILDREN */) {hostSetElementText(el, vnode.children)}// 处理子节点是数组的情况,挂载子节点else if (shapeFlag & 16 /* ARRAY_CHILDREN*/) {mountChildren(vnode.children, el, null, parentComponent, parentSuspense, isSVG && type !== 'foreignObject', optimized || !!vnode.dynamicChildren)// 在元素插入到容器之前会执行指令的 beforeMount 钩子函数if (dirs) {invokeDirectiveHook(vnode, null, parentComponent, 'beforeMount')}// 把创建的 DOM 元素节点挂载到 container 上hostInsert(el, container, anchor)// 在元素插入到容器之后会执行指令的 mounted 钩子函数  if (dirs) {// 通过 queuePostRenderEffect 进行包装,目的是组件 render 后同步执行 mounted 钩子queuePostRenderEffect(() => {invokeDirectiveHook(vnode, null, parentComponent, 'mounted')})}}
}/*** 执行指令 hook* @param {Object} vnode - 新 vnode* @param {Object} prevVNode - 旧 vnode* @param {Object} instance - 组件实例* @param {string} name - 钩子名称*/
function invokeDirectiveHook(vnode, prevVNode, instance, name) {const bindings = vnode.dirsconst oldBindings = prevVNode && prevVNode.dirsfor (let i = 0; i < bindings.length; i++) {const binding = bindings[i]if (oldBindings) {binding.oldValue = oldBindings[i].value}const hook = binding.dir[name]if (hook) {callWithAsyncErrorHandling(hook, instance, 8/* DIRECTIVE_HOOK */, [vnode.el, binding, vnode, prevVNode])}}
}

更新

/*** 组件更新*/
const patchElement = (nl, n2, parentComponent, parentSuspense, isSVG, optimized) => {const el = (n2.el = nl.el)const oldProps = (n1 && n1.props) || EMPTY_OBJconst newProps = n2.props || EMPTY_OBJconst { dirs } = n2// 更新 propspatchProps(el, n2, oldProps, newProps, parentComponent, parentSuspense, isSVG)const areChildrenSVG = isSVG && n2.type !== 'foreignObject'// 在更新子节点之前会执行指令的 beforeUpdate 钩子if (dirs) {invokeDirectiveHook(n2, n1, parentComponent, 'beforeUpdate')}//更新子节点patchChildren(n1, n2, el, null, parentComponent, parentSuspense, areChildrenSVG)// 在更新子节点之后会执行指令的 updated 钩子if (dirs) {queuePostRenderEffect(() => {invokeDirectiveHook(vnode, null, parentComponent, 'updated')})}
}

卸载

/*** 组件卸载*/
const unmount = (vnode, parentComponent, parentSuspense, doRemove = false) => {const { type, props, children, dynamicChildren, shapeFlag, patchFlag, dirs } = vnodelet vnodeHookif ((vnodeHook = props && props.onVnodeBeforeUnmount)) {invokeVNodeHook(vnodeHook, parentComponent, vnode)}const shouldInvokeDirs = shapeFlag & 1/* ELEMENT */ && dirsif (shapeFlag & 6/* COMPONENT*/) {unmountComponent(vnode.component, parentSuspense, doRemove)} else {if (shapeFlag & 128/* SUSPENSE */) {vnode.suspense.unmount(parentSuspense, doRemove)return}// 在移除元素的子节点之前执行指令的 beforeUnmount 钩子if (shouldInvokeDirs) {invokeDirectiveHook(vnode, null, parentComponent, 'beforeUnmount')}// 卸载子节点,递归卸载自身节点和子节点if (dynamicChildren && (type !== Fragment || (patchFlag > 0 && patchFlag & 64/* STABLE_FRAGMENT */))) {unmountChildren(dynamicChildren, parentComponent, parentSuspense)} else if (shapeFlag & 16/* ARRAY_CHILDREN */) {unmountChildren(children, parentComponent, parentSuspense)}if (shapeFlag & 64/* TELEPORT */) {vnode.type.remove(vnode, internals)}// 移除自身节点if (doRemove) {remove(vnode)}}if ((vnodeHook = props && props.onVnodeUnmounted) || shouldInvokeDirs) {queuePostRenderEffect(() => {vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode)// 在移除元素的子节点之后执行指令的 unmounted 钩子if (shouldInvokeDirs) {invokeDirectiveHook(vnode, null, parentComponent, 'unmounted')}}, parentSuspense)}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/151253.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Swagger-----knife4j框架

简介 使得前后端分离开发更加方便&#xff0c;有利于团队协作 接口的文档在线自动生成&#xff0c;降低后端开发人员编写接口文档的负担 功能测试 Spring已经将Swagger纳入自身的标准&#xff0c;建立了Spring-swagger项目&#xff0c;现在叫Springfox。通过在项目中引入Spri…

mysql innodb buffer pool缓冲池命中率和命中了哪些表?—— 筑梦之路

环境说明 mysql 5.7及以上 公式 # InnoDB缓冲区缓存的命中率计算公式100 * (1 - (innodb_buffer_pool_reads/innodb_buffer_pool_read_requests ))注意: 对于具有大型缓冲池的系统&#xff0c;既要关注该比率&#xff0c;也要关注OS页面读写速率的变化可以更好地跟踪差异。s…

【BUG库】 记录自己学习工作中遇到的程序BUG

BUG库 CGoGO语言环境配置相关go mod algorithm环境相关vscode -- 保存vscode -- 注释 在这篇博客中 我会记录自己在学习和工作中遇到的一系列bug C Go GO语言环境配置相关 go mod go: go.mod file not found in current directory or any parent directory; see ‘go help …

苹果(Apple)公司的新产品开发流程(一)

目录 简介 ANPP CSDN学院推荐 作者简介 简介 苹果这家企业给人的长期印象就是颠覆和创新。 而流程跟创新似乎是完全不搭边的两个平行线&#xff1a; 流程是一个做事的标准&#xff0c;定义了权力的边界&#xff0c;对应人员按章办事&#xff1b;而创新的主旋律是发散&am…

实时监控电脑屏幕的软件丨同时查看12台电脑屏幕

Hello 大家好 又见面啦 今天给大家推荐两款比较实用的监控电脑使用情况、屏幕的软件&#xff01; 软件一 实时性能监控 从软件名就可以看出来&#xff0c;这是一款电脑性能监测工具。它可以实时监测内存、CPU、磁盘占用情况&#xff0c;也能一键结束进程&#xff0c;给电脑提…

德语B级SampleAcademy

德语B级 一, 反身代词&#xff08;1&#xff09;A 主语和宾语一致&#xff08;2&#xff09;D 双宾语&#xff0c;主语与直接宾语不一致(3), 补充单词&#xff08;4&#xff09;真反身代词(5)假反身代词(6)真假反身代词(7)相互反身(8)非反身#反身#相互反身 二&#xff0c;Nomen…

Java8实战-总结48

Java8实战-总结48 CompletableFuture&#xff1a;组合式异步编程对多个异步任务进行流水线操作实现折扣服务使用 Discount 服务 CompletableFuture&#xff1a;组合式异步编程 对多个异步任务进行流水线操作 实现折扣服务 你的“最佳价格查询器”应用现在能从不同的商店取得…

【zabbix监控三】zabbix之部署代理服务器

一、部署代理服务器 分布式监控的作用&#xff1a; 分担server的几种压力解决多机房之间的网络延时问题 1、搭建proxy主机 1.1 关闭防火墙&#xff0c;修改主机名 systemctl disbale --now firewalld setenforce 0 hostnamectl set-hostname zbx-proxy su1.2 设置zabbix下…

3 redis实现一个消息中间件

使用list实现一个队列&#xff0c;可以从左侧入队&#xff0c;也可以从右侧入对 即可以从左侧读取&#xff0c;也可以从右侧读取 1、Lindex Lindex 命令用于通过索引获取列表中的元素 也可以使用负数下标&#xff0c;以 -1 表示列表的最后一个元素&#xff0c; -2 表示列表的…

2023前端大厂高频面试题之CSS篇(2)

系列文章: 2023前端大厂高频面试题之JavaScript篇(1) 2023前端大厂高频面试题之JavaScript篇(2) 2023前端大厂高频面试题之JavaScript篇(3) 2023前端大厂高频面试题之JavaScript篇(4) 2023前端大厂高频面试题之HTML篇 2023前端大厂高频面试题之CSS篇(1) 2023前端大厂高频面试题…

new/delete 和malloc/free的区别

C中&#xff1a; 创建单个数据空间&#xff1a; char *ch new char; delete ch; ch NULL; 创建多个数据空间&#xff1a; char *ch new char[4]; delete [] ch; ch NULL; C语言中&#xff1a; 创建单个数据空间&#xff1a; char *ch malloc(sizeof(char)); fre…

BP神经网络原理与如何实现BP神经网络

本文部分图文来自《老饼讲解-BP神经网络》bp.bbbdata.com 目录 一、BP神经网络的背景生物学原理 二、BP神经网络模型 2.1 BP神经网络的结构 2.2 BP神经网络的激活函数 三、BP神经网络的误差函数 四、BP神经网络的训练 4.1 BP神经网络的训练流程 4.2 BP神经网络的训练流…

springboot(ssm大学生成绩管理系统 成绩管理平台Java(codeLW)

springboot(ssm大学生成绩管理系统 成绩管理平台Java(code&LW) 开发语言&#xff1a;Java 框架&#xff1a;ssm/springboot vue JDK版本&#xff1a;JDK1.8&#xff08;或11&#xff09; 服务器&#xff1a;tomcat 数据库&#xff1a;mysql 5.7&#xff08;或8.0&…

【运维篇】5.6 Redis server 主从复制配置

文章目录 0. 前言1. 配置方式步骤1: 准备硬件和网络步骤2: 安装Redis步骤3: 配置主服务器的Redis步骤4: 配置从服务器的Redis步骤5: 测试复制功能步骤6: 监控复制状态 2. 参考文档 0. 前言 在Redis运维篇的第5.6章节中&#xff0c;将讨论Redis服务器的主从复制配置。在开始之前…

如何实现MATLAB与Simulink的数据交互

参考链接&#xff1a;如何实现MATLAB与Simulink的数据交互 MATLAB是一款强大的数学计算软件&#xff0c;Simulink则是一种基于模型的多域仿真平台&#xff0c;常用于工程和科学领域中的系统设计、控制设计和信号处理等方面。MATLAB和Simulink都是MathWorks公司的产品&#xff0…

数据结构【DS】图的应用

图的连通性问题 最少边数 最多边数 无向图非连通 &#x1d48e;&#x1d7ce; &#x1d48e;&#x1d48f;−&#x1d7d0;∗(&#x1d48f;−&#x1d7cf;)/&#x1d7d0; 无向图连通 &#x1d48e;&#x1d48f;−&#x1d7cf; &#x1d48e;&#x1d48f;∗(&#…

在Sprinng Boot中使用Redis充当缓存

关于我们使用EhCache可以适应很多的应用场景了&#xff0c;但是因为EhCache是进程内的缓存框架&#xff0c;在集群模式下&#xff0c;我们在我们的应用服务器或者云服务器之间的缓存都是独立的。故而在不同的服务器之间的进程会存在缓存不一致的情况&#xff0c;就算我们的EhCa…

word批量图片导出wps office word 图片批量导出

word批量导出图片教程 背景 今天遇到了一个场景&#xff0c;因为word里的图片打开看太模糊了&#xff0c;如果一个一个导出来太麻烦。想批量将word中的图片全部导出 但是&#xff0c;wps导出的时候需要会员 教程开始&#xff1a; 将word保存为 .docx 格式&#xff0c;可以按F1…

Golang 协程、主线程

Go协程、Go主线程 原先的程序没有并发和并行的概念&#xff0c;没有多核的概念&#xff0c;就是一个进程打天下。后面发现这个效率太低了&#xff0c;就搞出了线程&#xff0c;这样极大的发挥CPU的效率&#xff0c;因为硬件总是比软件发展的快。 现在go考虑的是能不能让多核cp…

【数据结构】C语言实现队列

目录 前言 1. 队列 1.1 队列的概念 1.2 队列的结构 2. 队列的实现 2.1 队列的定义 2.2 队列的初始化 2.3 入队 2.4 出队 2.5 获取队头元素 2.6 获取队尾元素 2.7 判断空队列 2.8 队列的销毁 3. 队列完整源码 Queue.h Queue.c &#x1f388;个人主页&#xff1a…