位置: IT常识 - 正文

Vue3组件挂载之创建组件实例详解(vue挂载dom)

编辑:rootadmin
这篇文章主要为大家介绍了Vue3组件挂载之创建组件实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪 目录

推荐整理分享Vue3组件挂载之创建组件实例详解(vue挂载dom),希望有所帮助,仅作参考,欢迎阅读内容。

Vue3组件挂载之创建组件实例详解(vue挂载dom)

文章相关热门搜索词:vue手动挂载组件,vue挂载axios,vue挂载点,vue 挂载组件,vue挂载点,vue3全局挂载组件,vue 挂载组件,vue3全局挂载组件,内容如对您有帮助,希望把文章链接给更多的朋友!

前情提要mountComponent创建组件实例总结前情提要

上文我们讲解了执行createApp(App).mount('#root')中的mount函数,我们分析了创建虚拟节点的几个方法,以及setRef的执行机制、本文我们继续讲解mountComponent,挂载组件的流程。

本文主要内容

createComponentInstance发生了什么?如何标准化组件定义的props、emits?为什么provide提供的值子组件都能访问到?组件的v-model实现原理、组件v-model修饰符实现原理。组件emit实现原理。once修饰符实现原理。几乎所有组件实例属性的详细讲解。mountComponent这个方法主要用于挂载组件,根据传递的虚拟节点创建组件实例,调用setupComponent函数进行初始化组件实例的插槽和props,如果是有状态组件还需要处理setup的返回值。最后调用setupRenderEffect绑定副作用更新函数。这个函数比较简单,我们将重心放到createComponentInstance、setupComponent、setupRenderEffect当中、后文也是围绕这三个方法进行依次讲解。const mountComponent = (initialVNode,container,anchor,parentComponent,parentSuspense,isSVG,optimized) => {//创建组件实例const instance = (initialVNode.component = createComponentInstance(initialVNode,parentComponent,parentSuspense));setupComponent(instance);if (instance.asyncDep) {//处理异步逻辑,suspense的时候在进行讲解}setupRenderEffect(instance,initialVNode,container,anchor,parentSuspense,isSVG,optimized);};创建组件实例createComponentInstance: 根据虚拟节点创建组件实例的方法,简单的说就是创建一个instance对象,这个上面有许多的属性用来描述当前这个组件以及存储需要后续使用到的属性,所以我们主要讲解各个属性的作用,我们先来看看这个函数的源码。uid: 标识当前实例的id,这是一个从0开始的递增值,同时他也是Vue调度器的优先级值,越小的值优先级越高,试想一下,父组件先挂载,然后才会挂载子节点,子节点如果包含组件,那么uid的值将会比父组件的uid值大,当父组件挂载完毕,父组件的兄弟组件开始挂载,那么uid将会更大,这样也符合流程更新逻辑,关于调度器我们之后单独开一章节来进行讲解。vnode: 实例的虚拟节点。parent: 当前组件实例的父组件实例。appContext:createApp的时候注入的一个上下文,我们通过app.component,app.mixin调用后缓存的component和mixin都放在这里面,如果没有parent表示当前是根组件,那么就是这个appContext,如果有parent则继承parent的appContext。我们可以通过这里发现所有的组件的appContext都会是唯一的一个context。因此所有组件实例都将能访问到全局注册的component、mixin、directive等。我们来回顾一下它的结构(省略了部分属性)。{app: null,config: {globalProperties: {},optionMergeStrategies: {},compilerOptions: {},},mixins: [],components: {},directives: {},};next:组件更新有两种形式,一种是组件内部的状态发生了改变,引起了组件自身更新;另外一种是父组件传递了props,props改变,父组件更新、导致子组件引起了更新。第一种next是null。如果是第二种,那么需要给next赋值为当前子组件最新的VNode(虚拟节点)。而next将会用于更新组件的props以及slots等属性。组件自身状态改变引起的更新是不需要更新props的。subTree: 调用render函数之后返回的组件要渲染的虚拟根节点,可以通过这个属性读取到组件需要渲染的所有虚拟节点。effect: 在setupRenderEffect中创建的reactiveEffect,我们在Vue3源码分析(2)中讲解了reactiveEffect,不了解的可以在阅读一下,目前简单理解为在调用render函数的时候收集依赖,如果响应式的值发生了改变会重新调用update函数执行更新流程。我们将会在讲解setupRenderEffect中详细讲解这一部分。update: 组件更新函数,只要调用了这个函数,组件就会强制更新。同时响应式发生改变调用的也是这个函数。render: 渲染函数。可以通过编译<template></template>得到,也可以通过写template属性获得,也可以通过自己写render函数。exposed: 组件指定的暴露到外部的属性。provides: 用户在组件中设置了provide,子代组件可以通过inject收到传递的值,provides:parent ? parent.provides : Object.create(appContext.provides)通过这段代码分析我们可以知道,provides中至少含有全局提供的provides,如果当前组件提供了provide,后面会将其混合,并且继承自父组件的provides,这也就解释了为什么provides可以向下传递,因为每一层都可以收到本组件的provides和父组件的provides并进行合并。components: 如果你想在template中使用组件,需要在这里注册,对于使用了<script setup>语法糖的会将其编译到components属性当中,当然这个属性就是用来存放这些注册的组件的。directives: 存放自定义指令。propsOptions: 合并了mixins和extends后的props,这里的属性为什么是propsOptions而不是props呢?这个VNode中的props又有什么关系呢?实际上propsOptions代表的是用户在组件内定义的props,而VNode中的props是外部传递给组件的props。这一点要加以区分。同时这里调用了normalizePropsOptions来对propsOptions进行标准化。接下来我们分析一下normalizePropsOptions函数。这个函数比较长我们分成三个部分来分析。首先从appContext中获取props缓存,避免处理过了组件props重复处理。如果comp不是一个函数,这个判断是因为,Vue3中允许函数组件的存在,但是函数组件是无状态组件也没有props,所以不做处理。那么剩下的都是有状态组件了,这里会处理全局注册的mixins,组件本身的mixins和extends。我们可以发现全局的最先处理,所以全局注册的mixins的优先级将会是最低的,其次是extends,显然优先级最高的则是组件自身的mixins,因为他最后执行,那么shared.extend将会最后覆盖之前的props。我们还可以发现extends属性和mixins属性在实现上没有任何区别,只是mixins可以是数组,而extends不能是数组。最后说一下asMixin,我们知道全局的mixins只需要合并一次,但是normalizePropsOptions会调用多次,为了避免全局属性混合的多次执行,设置了asMixin这个参数。当asMixin为true的时候表示不需要在合并全局的mixins了。 特别提示:shared.extend就是Object.assign。function normalizePropsOptions(comp, appContext, asMixin = false) {//获取props的缓存const cache = appContext.propsCache;const cached = cache.get(comp);//这个缓存是一个type对应一个[normalized, needCastKeys]//normalized表示合并了mixins和extends后的propsif (cached) {return cached;}const raw = comp.props;const normalized = {};const needCastKeys = [];let hasExtends = false;if (!shared.isFunction(comp)) {//用于合并props的函数,因为extends和mixins//中还可以写mixins和extends所以需要递归合并/*** 例如const mixins = [{* extends:{},* mixins:[{props}],* props* }]*/const extendProps = (raw) => {hasExtends = true;const [props, keys] = normalizePropsOptions(raw, appContext, true);//normalized为合并后的propsshared.extend(normalized, props);if (keys) needCastKeys.push(...keys);};//首先合并全局注册的mixins中的props属性(最先合并的优先级最低)if (!asMixin && appContext.mixins.length) {appContext.mixins.forEach(extendProps);}//然后合并extends属性(中间合并的优先级居中)//(extends功能与mixins几乎一样)但是更注重于继承//并且extends不能是数组if (comp.extends) {extendProps(comp.extends);}//最后合并组件自身的mixins(最后合并的优先级最高)if (comp.mixins) {comp.mixins.forEach(extendProps);}}//省略第二部分的代码...}我们知道这个函数主要是对propsOptions进行标准化,简单的说就是将各式各样的propsOptions统一成唯一标准。当传递的props:['msg']数组形式的时候,我们需要将其转化为props:{msg:{}}。validatePropName用于检测key是否合法,Vue中,组件传递参数不能以$开头定义数据。例如$msg就是不合法的。function normalizePropsOptions(comp, appContext, asMixin = false) {//省略第一部分的代码...//如果没有props且没有全局的mixins//组件本身的mixins、extends则设置//当前实例的props缓存为空if (!raw && !hasExtends) {if (shared.isObject(comp)) {cache.set(comp, shared.EMPTY_ARR);}return shared.EMPTY_ARR;}//处理这种类型props:['msg','hello']if (shared.isArray(raw)) {for (let i = 0; i < raw.length; i++) {if (!shared.isString(raw[i])) {console.warn(`props must be strings when using array syntax. ${raw[i]}`);}//将v-data-xxx转化为驼峰式vDataXxx//但是不能以$开头const normalizedKey = shared.camelize(raw[i]);if (validatePropName(normalizedKey)) {//将其变为props:{"msg":{}}normalized[normalizedKey] = shared.EMPTY_OBJ;}}}//省略第三部分的代码...}在上面的代码中频繁出现needCastKeys,这个代表的是需要特殊处理的key,例如:props:{msg:{default:"msg"}}含有default,那么理论上我们应当判断传递的属性值是否存在,然后在决定是否使用default的值,但是这里我们仅进行标准化,所以对于含有default属性的我们需要单独放入needCastKeys中,便于后面对props中的处理。再比如说<Comp yes></Comp>传递了yes属性,在propsOptions中props:{yes:{type:Boolean}}这样的key=>"yes"也是需要处理的,yes的值应该为true,所以对于type中含有Boolean的也需要放入needCastKeys中。export function normalizePropsOptions(comp, appContext, asMixin = false) {//省略第二部分代码...if (shared.isArray(raw)) {//省略...}//处理props:{msg:String}else if (raw) {if (!shared.isObject(raw)) {warn(`invalid props options`, raw);}//循环遍历所有的keyfor (const key in raw) {//"v-data-xxx"=>"vDataXxx"变为小驼峰式const normalizedKey = shared.camelize(key);//检验key是否合法if (validatePropName(normalizedKey)) {const opt = raw[key]; //获取value//如果获取的value是数组或函数转化则为{type:opt}//props:{"msg":[]||function(){}}=>//props:{"msg":{type:msg的值}}const prop = (normalized[normalizedKey] =shared.isArray(opt) || shared.isFunction(opt) ? { type: opt } : opt);if (prop) {//找到Boolean在prop.type中的位置 Boolean,["Boolean"]const booleanIndex = getTypeIndex(Boolean, prop.type);//找到String在prop.type中的位置const stringIndex = getTypeIndex(String, prop.type);prop[0] = booleanIndex > -1; //type中是否包含Boolean//type中不包含String或者Boolean的位置在String前面//例如:"msg":{type:["Boolean","String"]}prop[1] = stringIndex < 0 || booleanIndex < stringIndex;//如果有default属性,或者type中包含Boolean放入needCastKeys中if (booleanIndex > -1 || shared.hasOwn(prop, "default")) {needCastKeys.push(normalizedKey);}}}}}const res = [normalized, needCastKeys];//设置缓存if (shared.isObject(comp)) {cache.set(comp, res);}return res; //返回合并后的normalized}emitsOptions :合并了mixins和extends后的emits属性。并且对emits进行了标准化。主要是调用了normalizeEmitsOptions进行处理,这个函数的逻辑和normlizePropsOptions非常相似,如果你看懂了上述的解释,这个函数相信你很容易就能看懂,这里就不在做多余的解释了。export function normalizeEmitsOptions(comp, appContext, asMixin = false) {//获取appContext中的缓存const cache = appContext.emitsCache;const cached = cache.get(comp);//如果已经读取过返回缓存if (cached !== undefined) {return cached;}//获取组件的emits属性{emits:['']}//还可以对象写法{emits:{'':null||function(){}}}const raw = comp.emits;//最终的合并对象let normalized = {};let hasExtends = false;if (!shared.isFunction(comp)) {//合并emits的方法const extendEmits = (raw) => {const normalizedFromExtend = normalizeEmitsOptions(raw, appContext, true);if (normalizedFromExtend) {hasExtends = true;shared.extend(normalized, normalizedFromExtend);}};//最先合并appContext中的(优先级最低)if (!asMixin && appContext.mixins.length) {appContext.mixins.forEach(extendEmits);}//然后合并实例的extends(优先级居中)if (comp.extends) {extendEmits(comp.extends);}//最后合并组件自身的(优先级最高)if (comp.mixins) {comp.mixins.forEach(extendEmits);}}if (!raw && !hasExtends) {//设置缓存if (shared.isObject(comp)) {cache.set(comp, null);}return null;}//即使emits:[]是数组最终也会被转化为对象//emits:['m']=>emits:{'m':null}if (shared.isArray(raw)) {raw.forEach((key) => (normalized[key] = null));} else {//合并shared.extend(normalized, raw);}if (shared.isObject(comp)) {cache.set(comp, normalized);}return normalized;}attrs: 如果你给组件传递了没有在组件内部
本文链接地址:https://www.jiuchutong.com/zhishi/303735.html 转载请保留说明!

上一篇:python如何检测pygame中的碰撞(Python如何检测两个相同的列表)

下一篇:python处理mysql如何拿到表头

  • 微信公众平台营销问题的解决(微信公众平台营销策略分析)

    微信公众平台营销问题的解决(微信公众平台营销策略分析)

  • 微信朋友圈怎么发长图连着的照片(微信朋友圈怎么编辑修改)

    微信朋友圈怎么发长图连着的照片(微信朋友圈怎么编辑修改)

  • 荣耀30pro如何强制关机(华为荣耀30pro怎么关机重启)

    荣耀30pro如何强制关机(华为荣耀30pro怎么关机重启)

  • 快手极速版进度条不转了是怎么回事(快手极速版进度条怎么调出来)

    快手极速版进度条不转了是怎么回事(快手极速版进度条怎么调出来)

  • ios13.5什么时候更新(ios13.5什么时候出的)

    ios13.5什么时候更新(ios13.5什么时候出的)

  • 卡贴无服务怎么恢复(卡贴插卡无服务)

    卡贴无服务怎么恢复(卡贴插卡无服务)

  • 华为手机掉水里没有声音怎么办(华为手机掉水里自动关机了怎么办)

    华为手机掉水里没有声音怎么办(华为手机掉水里自动关机了怎么办)

  • 怎么控制qq群里的骰子(怎么控制qq群人数)

    怎么控制qq群里的骰子(怎么控制qq群人数)

  • 怎么看手机充电器多少w(怎么看手机充电功率多少w)

    怎么看手机充电器多少w(怎么看手机充电功率多少w)

  • 微信粤省事人脸识别失败原因(粤省事怎么人脸登录不了)

    微信粤省事人脸识别失败原因(粤省事怎么人脸登录不了)

  • ipadmini5能打电话吗(ipad mini6 可以打电话)

    ipadmini5能打电话吗(ipad mini6 可以打电话)

  • 为什么苹果照片右下角有感叹号(为什么苹果照片发给别人是反的)

    为什么苹果照片右下角有感叹号(为什么苹果照片发给别人是反的)

  • 快手私聊别人能看见吗(快手私聊别人能看到吗)

    快手私聊别人能看见吗(快手私聊别人能看到吗)

  • 魅族x8怎么关闭root权限(魅族手机模式怎么关闭)

    魅族x8怎么关闭root权限(魅族手机模式怎么关闭)

  • qq私密说说如何公开(QQ私密说说如何公开)

    qq私密说说如何公开(QQ私密说说如何公开)

  • 亲情号知道我买了啥吗(亲情号知道我买的东西吗)

    亲情号知道我买了啥吗(亲情号知道我买的东西吗)

  • 天猫扣分什么时候清零(天猫扣分年底会清零吗)

    天猫扣分什么时候清零(天猫扣分年底会清零吗)

  • ipx防水4能不能淋浴(ipx4防水能达到什么地步)

    ipx防水4能不能淋浴(ipx4防水能达到什么地步)

  • 淘宝帮买消息如何取消(淘宝帮买消息是什么意思)

    淘宝帮买消息如何取消(淘宝帮买消息是什么意思)

  • 照片打印出来黑怎么办(照片打印出来黑色背景去掉用手机)

    照片打印出来黑怎么办(照片打印出来黑色背景去掉用手机)

  • 小米8微信震动无法关闭(小米微信震动怎么关闭不了)

    小米8微信震动无法关闭(小米微信震动怎么关闭不了)

  • 蓝牙耳机红蓝一直闪(蓝牙耳机红蓝一直亮怎么回事)

    蓝牙耳机红蓝一直闪(蓝牙耳机红蓝一直亮怎么回事)

  • 花呗授权额度怎么取消(花呗授权额度怎么关闭)

    花呗授权额度怎么取消(花呗授权额度怎么关闭)

  • vivox6d是骁龙多少(vivox6骁龙多少)

    vivox6d是骁龙多少(vivox6骁龙多少)

  • 乐视视频如何截视频(乐视怎么截图手机屏幕)

    乐视视频如何截视频(乐视怎么截图手机屏幕)

  • YOLOv5/v7 引入 最新 BiFusion Neck | 附详细结构图(yolov5 output)

    YOLOv5/v7 引入 最新 BiFusion Neck | 附详细结构图(yolov5 output)

  • 企业名称税号
  • 税后经营净利润加折旧与摊销
  • 商会费用会计分录
  • 进项认证以后如何开具红字信息表
  • 工商年报纳税总额怎么算
  • 年金的扣费标准
  • 增值税开票内容货物及应税劳务服务名称都有哪些
  • 民营非营利企业会计分录大全
  • 房地产企业拆迁安置土地增值税清算疑点
  • 收到发票当月不抵扣怎么做账
  • 个人捐赠支出扣除30%和100%扣除
  • 质量问题扣款账务处理
  • 不动产评估需要明确的基本事项包括哪些内容
  • 普通机打发票可以抵税吗
  • 汇率变动引起的经济危机
  • 发工资时忘记扣个税了怎么做账
  • 营改增后一般纳税人税率是多少
  • 本年利润总账需要本年合计吗?
  • 缴纳人身意外保险凭证是什么
  • win10 20h2更新后闪屏
  • window10的点在哪里
  • 生产成本包括哪三类
  • 水利基金返还分录怎么写
  • 公司总经理报销找谁签字
  • php安装部署
  • 餐饮发票可以计入研发费用吗
  • 系统搜索力
  • 专项用途财政性资金纳税调整明细表
  • win10 累积更新
  • php ftp功能
  • 所有者权益会计科目借贷方向
  • 虚拟机不能互相访问
  • fsck是什么文件
  • 混合销售会计处理
  • 超期未认证的发票怎么处理
  • 收到税务局退回的企业所得税分录
  • 前端数据加密的三种方式
  • 投喂小鸟
  • 普通纳税人怎么交税
  • 前端实现文件上传的命令
  • 智慧工厂协同管控下载
  • 金税盘开票系统复核人员设置在哪里
  • 非三包退车能否退款
  • 卷票要盖发票专用章吗?
  • 企业所得税包含员工工资吗
  • 用友T3财务报表没有数据
  • PostgreSQL中调用存储过程并返回数据集实例
  • mysql数据库恢复方式
  • 处置废品收入税率
  • 坏账准备的账务处理
  • 长期股权投资中级会计例题
  • 预缴土地增值税税率表2023年最新
  • 工程物资主要包括建筑材料
  • 关于成本核算的论文
  • 未分配利润是不是净利润的意思
  • 付国外客户佣金怎么代扣代缴增值税
  • 公司破产账务处理
  • 小规模纳税人怎么申报个人所得税
  • 事业单位固定资产入账标准最新规定
  • 主营业务成本做多了怎么办
  • Select count(*)、Count(1)和Count(列)的区别及执行方式
  • win10下载mysql图解
  • sqlserver如何查询表数据
  • centos 拷贝
  • win7防病毒设置在哪
  • u盘安装ubuntu找不到硬盘
  • win8语言栏不见了 怎么调出来
  • ubuntu系统如何
  • vim编辑器命令大全
  • shell(bash)下“time” 命令的输出详解
  • css expression 隔行换色
  • vue3 todo
  • js实现滚动条
  • js tab页面切换
  • Javascript & DHTML 实例编程(教程)(四)初级实例篇2—动画
  • 使用jquery计算li元素的个数
  • 广东省税务登记网
  • 山东2022新生儿数量统计表
  • 什么是财务舞弊行为
  • 企业税收有哪些税种
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设