从 vue 源码看问题 — vue 初始化都做了什么事?

前言

最近想要对 Vue2 源码进行学习,主要目的就是为了后面在学习 Vue3 源码时,可以有一个更好的对比和理解,所以这个系列暂时不会涉及到 Vue3 的内容,但是 Vue3 的核心模块和 Vue2 是一致的,只是在实现上改变了方式、进行了优化等。

准备工作

再开始阅读源码之前,有些事还是必须要做的,那就是拉取 源仓库代码 或者直接下载 ZIP 格式文件,处理好之后就需要打开神器 VScode
在这里插入图片描述

打开编辑器之后,你需要做的就是:

  • 安装依赖 install —— yarn 或者 npm

  • 找到 package.json 文件并找到里面的 scripts 部分,然后往 dev 命令中加入 --sourcemap 配置参数,或者新建建一个 dev:sourcemap 命令,其内容就是比 dev 命令多了个 --sourcemap 配置,其实主要就是为了生成 vue.js.map 文件方便后面调试
    在这里插入图片描述

  • 执行 npm run dev:sourcemap 命令,执行成功以后就会在 dist 目录下生成 vue.js.map 文件

    image.png

  • 然后就可以在 example 目录下,写一些自己想要测试的例子,然后通过 debug 的形式找到对应内容在代码中的位置

    image.png

深入源码

Vue 初始化的代码位置

既然要深入了解 Vue 初始化的内容,那么我们就得先找到 Vue 初始化是在哪里进行的,那么查找的方式有两种:

  • 通过 package.json 文件来进行查找 —— 在 script 脚本命令配置中有 "dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev",其中的 scripts/config.jsTARGET:web-full-dev 就为指明了文件里的具体配置,然后可以在逐层查找对应的入口文件
  • 通过 debug 模式进行查找 —— 首先在 example 目录下新建一个目录,可以是任何名字,本文讨论的是初始化的内容,这里就将其命名为 init,在 init 目录下新建一个 html 文件,在里面引入 dist 目录下的 vue.js, 因为生成的 map 文件是 vue.js.map,否则 debug 时不方便查找对应的文件位置
    在这里插入图片描述

这里选择方式二,毕竟方式一通过 rollup 配置查找还是过于繁琐,于是通过 debug 可以快速确定 Vue 进行初始化的文件位置.
在这里插入图片描述

this._init(options) 初始化

src>core>index.js 文件中,可以清晰的看到,在我们进行 new Vue() 时,调用的其实就是 this._init() 方法,而这个方法又是在 initMixin(Vue) 中进行定义的

在这里插入图片描述

initMixin(Vue) 方法

src>core>init.js 文件中,可以看到在 initMixin() 方法中最核心的就是处理组件配置项的部分,这一部分又分为 子组件根组件 的配置,又分别对应 initInternalComponent() 方法 和 mergeOptions()方法 + resolveConstructorOptions()方法.
在这里插入图片描述

同时,在 Vue.prototype._init 函数中还存在下面的这些方法的调用,后面会依次对每个方法进行解读:

在这里插入图片描述

子组件 —— initInternalComponent() 方法

这个方法做的事就是创建 $options 对象,然后对组件选项进行打平做性能优化,因为组件有很多的配置,其中也会存在各种嵌套的配置,在访问时免不了要通过原型链进行动态查找,会影响执行效率.

  • 根据 vm 的构造函数创建新的配置对象,即平时访问的 $options 对象
  • 把当前组件配置项打平然后赋值到 $options 对象,避免了原型链的动态查找
  • 如果当前组件配置项中存在 render 选项,就把它添加到 $options 对象上
    在这里插入图片描述

根组件 —— resolveConstructorOptions() 方法

src>core>init.js 文件中,resolveConstructorOptions() 方法其实最主要的事情就是从构造函数上解析配置对象,具体如下:

  • 如果构造函数的 super 属性存在,证明还有基类,此时需要递归进行对配置选项解析
  • 将构造函数的基类配置项进行缓存,然后比对当前配置项配置项进行对比,如果不一致,则表明基类的配置项已发生更改
  • 找出被更改的配置项和 extent 选项进行合并,并赋值给 $options
    在这里插入图片描述

根组件 —— mergeOptions() 方法

src>core>options.js 文件中,mergeOptions() 方法主要做的事情就是:

  • 对配置选项进行标准化
  • 对传入的原始配置对象进行合并
  • 返回新的配置对象
    根组件合并配置项,就是将全局配置项合并到根组件局部配置项中,比如将全局注册的 Vue.componet(options) 全局配置合并到根组件 new Vue(options) 上得到类似:
   new Vue({el: xxx,data: { xxx },componets:{localComponents,globalComponents,}})

在这里插入图片描述

initLifecycle() 方法

src>core>init.js 文件中调用 initLifecycle()方法,方法的具体定义位置为src>core>lifecycle.js.

可能很多人会误以为该方法是初始化生命周期钩子函数的(因为其方法名),其实这个方法主要是对组件关系属性进行初始化,比如:$root、$parent、$children、$refs 等.

在这里插入图片描述

initEvents() 方法

src>core>init.js 文件中调用 initEvents()方法,方法的具体定义位置为src>core>events.js.

主要作用就是初始化自定义事件,但是针对这个方法目前先有个简单的了解即可,因为里面涉及到的处理比较多,所以本文中暂时不对其进行展开,但是后面涉及到 this 实例上的方法时在具体分析.

问题:存在一个组件并且绑定了事件,如 <comp @myclick="clickHandle" />,那么我们可以在 com 组件中通过 this.$emit('myClick') 的方式去触发,那是谁监听了这个事件呢?

回答:也许你会认为是 comp 的父组件去监听的,但其实是 com 组件自己监听的,因为 @myclick="clickHandle" 会被编译为 this.$on('myClick', function clickHandle(){ })this.$emit('myClick') 的形式,而其中的 this 指向的就是 组件本身.

在这里插入图片描述

initRender() 方法

src>core>init.js 文件中调用 initRender()方法,方法的具体定义位置为src>core>render.js.

这里具体的内容也不再展开,后面涉及到 render 部分在进行深入解读,其实主要就做了三件事:

  • 初始化插槽,如:vm.$slots、vm.$scopedSlots
  • 定义 _c 方法,即 createElement 方法,也就是 h 函数
  • $attrs$listeners 属性进行响应式处理

在这里插入图片描述

callHook(vm: Component, hook: string) 方法

src>core>init.js 文件中有调用 callHook(vm, 'beforeCreate')callHook(vm, 'created'),这就是平时我们在组件配置项中定义的生命周期函数被调用的形式.

callHook 方法的具体定义位置为src>core>lifecycle.js 中.

在这里插入图片描述
在这里插入图片描述

initInjections() 方法

src>core>init.js 文件中调用 initInjections()方法,方法的具体定义位置为src>core>inject.js.

provide/inject 的相关介绍和使用方法,可以点此 vue 文档 查看.

initInjections() 方法

主要做的就是获取解析之后的 inject 选项 result,然后把 result 的每一项都代理到 vm 实例上,也可以理解为是进行响应式处理,在组件中实现 this.key 的形式直接进行访问

在这里插入图片描述

resolveInject() 方法

  • resolveInject(inject: any, vm: Component) 方法中的 inject 选项到这之前就已经进行了标准化处理,所以这里的 inject 选项的格式一定为:
     inject = {key: {from: xxx,default: xxx}}
    
  • 遍历 inject 配置的所有 key,查找到对应 provide 中的值
    • inject 配置中获取 from 属性值
    • 循环在祖代组件中查找 provide 选项,如果找到了,就获取对应的值,保存到 result 中;如果没有找到,就继续向上查找,一直到根组件
  • 如果到了根组件还是没有找到 inject 中的 key 在对应祖代组件上 provide 的值,那么就检查 inject 中是否设置了默认值,如果设置了默认值,就将其赋值为默认值

在这里插入图片描述

initState() 方法

src>core>init.js 文件中调用 initState()方法,方法的具体定义位置为src>core>state.js.

它是响应式原理的核心,主要处理 props、data、methods、watch、computed 等,
因为这一块属于响应式的内容,因此,不在本文里面进行深入探讨,后面针对响应式的内容会进行解读.

在这里插入图片描述

initProvide() 方法

src>core>init.js 文件中调用 initProvide()方法,方法的具体定义位置为src>core>inject.js.

这里面做的事情很简单:

  • $options 上获取 provide 选项
  • provide 选项存在时,判断它是不是函数,如果是函数就调用然后获取配置对象,如果不是函数就直接使用 provide 选项
  • vm 实例上挂载 _provide 属性,值就为上面的 provide 的具体内容

在这里插入图片描述

总结

上面根据 Vue.prototype._init 初始化函数中使用的每个方法进行了分析,在这里需要稍微总结一下,顺便配合一些问题进行理解。

合并组件配置

子组件

子组件进行合并配置项时,主要是通过打平配置项,减少原型链动态查找,达到性能优化的目的.

根组件

根组件合并配置,就是将全局配置项合并到根组件局部配置项中.

根组件合并会发生在三个地方:

  • 就是初始化时的这种情况
  • Vue.component(name, Comp) 时,将合并 Vue 内置全局组件用户注册的全局组件,最终都会合并到跟组件上配置上的 components 选项中
  • { components:{xxx} } 局部注册,执行编译器生成 render 函数时,
    会合并全局配置对象到组件局部配置对象上

组件关系属性的初始化

如需要初始化 $root、$parent、$children、$refs 等.

初始化自定义事件

问题:当在组件上使用自定义事件时,父组件和子组件谁负责监听这个事件?

 // 这是一个伪代码 <parentComp><comp @myClick="clickHandle" /></parentComp>

其实 @myClick="clickHandle" 会被编译为 this.$emit('myClick')this.$on('myClick', function clickHandle(){}) 的形式,而这个 this 就是组件实例,即谁需要触发事件,谁就需要监听事件.

初始化插槽 & 定义 _c 方法

  • 初始化插槽,如:vm.$slots、vm.$scopedSlots
  • 定义 _c 方法,即 createElement 方法,也就是 h 函数

通过 callHook 执行 beforeCreate 和 created 生命周期函数

问题:beforeCreate 中能获取能访问什么内容,data 可以访问吗?

从源码中很容易看出来,在 beforeCreate 之前只初始化了 组件关系属性自定义事件插槽_c 方法,所以关于可以访问的就是这些内容.

因为 data、props、methods 等都没有进行初始化,所以就都不能进行访问,当然如果你在 beforeCreate 中通过异步的方式访问,比如 setTimeout 其实是可以的,最早能访问数据的地方其实就是 created 当中了.

在这里插入图片描述

初始化 inject 选项

  • 根据 inject 选项从祖代组件配置项中找到对应的 provide 选项,从而获取对应 key 中的值,得到 result[key] = val 形式的结果
  • 如果找到根组件上还不存在,就判断是否有 default 选项,有就设置默认值
  • 把得到的 result 结果进行响应式处理,代理的 vm 实例上

初始化 state 数据

响应式原理的核心,主要处理 props、data、methods、watch、computed

处理 provide 选项

vm.$options 配置对象上获取 provide 选项,provide 选项存在时,判断 provide 是不是函数,是函数就调用获取返回配置项,否则就直接使用 provide 选项

$mount 挂载

根据 $options 中是否存在 el 选项,决定是否自动调用 $mount 方法进入挂载阶段,没有则需要用户手动调用 $mount 进行挂载.

结合生命周期回答组件渲染流程

以下是 beforeCreatecreated 生命周期前后需要处理的详细内容:

  • 合并组件选项,将组件配置项打平,存放到 vm.$options 选项上,减少原型链的查找
  • 对组件关系属性进行初始化,比如:$root、$parent、$children、$refs
  • 初始化自定义事件,比如:@myclick="clickHandle" 会被编译为this.$on('myClick', function clickHandle(){})this.$emit('myClick') 的形式
  • 初始化插槽vm.$slots、vm.$scopedSlots)、定义_c方法,即createElementh函数)、对$attrs$listeners属性进行响应式处理
  • 执行 beforeCreate 生命周期函数
  • 初始化 inject 选项,根据inject选项从祖代组件配置项中找到对应的provide选项,从而获取对应key中的值,得到result[key] = val形式的结果
  • 初始化 state 数据,如:props、data、methods、watch、computed
  • 处理 provide 选项,判断 provide是不是函数,是函数就调用获取返回配置项,否则就直接使用provide选项
  • 执行 created 生命周期函数
  • 其他部分可以直接按照图示回答
    在这里插入图片描述

最后

看源码的过程中,一定要学会放弃某些内容解读,找准本次你要了解和学习的主线内容,否则就容易在阅读源码时产生各种支线问题,从而阻塞了主线的内容,对应的模块在学习对应内容时深入解读就好,不要一次性解读所有的内容。

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

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

相关文章

nginx系列--(一)--调试环境搭建

辅助脚本&#xff1a; #!/bin/bash mkdir -p $(pwd)/nginxhome # 生成 Makefile,--prefix need a absolute path --with-stream表示要包括stream模块 auto/configure --prefix$(pwd)/nginxhome --with-stream # lsof -i tcp:10086 && fuser -k 10086/tcp ||true # 定…

Qt/C++地图导航app/支持qml/手机运行/输入起点终点规划路径/模拟轨迹移动

一、前言说明 搞Qt地图开发这块&#xff0c;随着研究的深入&#xff0c;用户的需求变化&#xff0c;最近又需要在手机上运行&#xff0c;由于本地图组件依赖浏览器控件&#xff0c;而手机安卓上的Qt并没有带qwebengine控件&#xff0c;怎么办呢&#xff0c;不断的努力验证下&a…

使用VS Code 安装VUE.js开发环境的搭建并创建第一个项目

初步掌握VUE.js开发环境的搭建并创建第一个项目的操作方法和实验步骤 题目 安装Visual Studio Code。安装VS Code汉化插件。安装Vue官方支持插件。使用VS Code运行第一个HTML页面。安装Node.js并验证其版本。验证npm版本。配置npm的下载镜像源。配置Yarn的下载镜像源。使用Vi…

记本地第一次运行seatunnel示例项目

前置 静态源码编译通过&#xff1a;https://blog.csdn.net/u011924665/article/details/143372464 参考 seatunnel官方的开发环境搭建文档&#xff1a;https://seatunnel.incubator.apache.org/zh-CN/docs/2.3.5/contribution/setup 安装scala 下载scala 去官网下载&…

Maven下载安装配置(环境、本地仓库、阿里云、jdk、idea)(Win11)

目录 Maven3.9.9工具参考下载安装配置环境变量配置验证是否安装完成本地仓库位置存放配置阿里云镜像加速配置jdk版本 配置 idea Maven3.9.9 工具 系统&#xff1a;Windows 11 环境&#xff1a;JDK-8 软件&#xff1a;IDEA-2024.2.1 参考 本人写的《JDK安装与环境配置&#…

袁庭新陕西理工大学演讲——AIGC时代面临的机遇与挑战

大家好&#xff0c;我是袁庭新。分享一篇我在陕西理工大学给计算机专业、人工智能专业和网络工程专业的演讲内容。 各位计算机学院的小伙伴们&#xff0c;大家好啊&#xff01;欢迎各位来到今天的分享会&#xff0c;非常荣幸能在这里和大家相聚。今天在这里&#xff0c;我要与大…

以客户为导向在开源 AI 智能名片 2 + 1 链动模式 S2B2C 商城小程序内容创作中的实践与价值

摘要&#xff1a;本文深入探讨了在开源 AI 智能名片 2 1 链动模式 S2B2C 商城小程序相关内容创作中以客户为导向的方法和意义。阐述了如何在创作过程中通过与客户对话和转换客户视角来优化内容&#xff0c;以提升该小程序在市场中的竞争力和用户接受度。 一、引言 在数字化商…

QT——TCP网络调试助手

目录 一.项目展示 ​编辑 二.开发流程 三.QTcpServer、QTcpSocket、QUdpSocket类的学习 1.QTcpServer服务端 2.QTcpSocket客户端 3.Udp通信 四.网络调试助手 1.首先我们实现当用户选择不同协议类型时不同的UI组件如何切换 2.实现打开/关闭按键图片的切换 方式一&…

【CSS3】css开篇基础(5)

1.❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; Hello, Hello~ 亲爱的朋友们&#x1f44b;&#x1f44b;&#xff0c;这里是E绵绵呀✍️✍️。 如果你喜欢这篇文章&#xff0c;请别吝啬你的点赞❤️❤️和收藏&#x1f4d6;&#x1f4d6;。如果你对我的…

C++学习路线(数据库部分)二

类型 整形类型 整数类型是数据库中最基本的数据类型。标准SQL中支持INTEGER和SMALLINT这两种数据类型。MySQL数据库除了支持这两种类型以外&#xff0c;还扩展支持了TINYINT、MEDIUMINT和BIGINT。下表从不同整数类型的字节数、取值范围等方面进行对比。 类型名称后面的小括号…

IDEA中通义灵码的使用技巧

大家好&#xff0c;我是 V 哥。在日常写代码的过程中&#xff0c;通过 AI 工具辅助开发已是当下程序员惯用的方式&#xff0c;V 哥在使用了众多的 AI 工具后&#xff0c;多数情况下&#xff0c;选择通义灵码来辅助开发&#xff0c;尤其是解释代码和生成单元测试功能甚是好用&am…

Docker篇(实际应用)

目录 一、MySQL 部署 1. 拉取 MySQL 镜像 2. 查看镜像 3. 创建 MySQL 容器 4. 进入 MySQL 容器,登陆 MySQL 5. 远程登陆 MySQL 6. 查看容器 IP 地址 二、tomcat 部署 1. 拉取 tomcat 镜像 2. 创建 tomcat 容器 3. 搭建 Tomcat 服务并部署 web 应用 三、Nginx 部署 …

Darknet 连接教程

本篇文章仅供学习&#xff0c;严禁用于非法用途。 1&#xff0c;前言&#xff1a; 首先明确一点&#xff0c;Darknet真没那么神奇&#xff0c;虽然有些技术文章的确很有水平&#xff0c;对于前端学习&#xff0c;软件开发以及PHP和一些服务器端维护都有许多文章&#xff0c;但…

江协科技STM32学习- P33 实验-软件I2C读写MPU6050

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…

Hadoop生态圈框架部署(四)- Hadoop完全分布式部署

文章目录 前言一、Hadoop完全分布式部署&#xff08;手动部署&#xff09;1. 下载hadoop2. 上传安装包2. 解压hadoop安装包3. 配置hadoop配置文件3.1 虚拟机hadoop1修改hadoop配置文件3.1.1 修改 hadoop-env.sh 配置文件3.3.2 修改 core-site.xml 配置文件3.3.3 修改 hdfs-site…

立刻解决 gcc: error: unrecognized argument in option ‘-mabi=aapcs-linux’

unrecognized argument in option ‘-mabiaapcs-linux’ Linux 主线支持的硬件较少&#xff0c;一般是第三方开源&#xff08; Linaro/Yocto &#xff09;或者硬件厂商提供定制的嵌入式 Linux 如果确认主线支持自己的硬件&#xff0c;可以从 https://www.kernel.org/ 获取指定…

「Mac畅玩鸿蒙与硬件23」鸿蒙UI组件篇13 - 自定义组件的创建与使用

自定义组件可以帮助开发者实现复用性强、逻辑清晰的界面模块。通过自定义组件,鸿蒙应用能够提高代码的可维护性,并简化复杂布局的构建。本篇将介绍如何创建自定义组件,如何向组件传递数据,以及如何在不同页面间复用这些组件。 关键词 自定义组件复用组件属性传递组件通信组…

Windows版 nginx安装,启动,目录解析,常用命令

Windows版 nginx安装&#xff0c;启动&#xff0c;目录解析&#xff0c;常用命令 一级目录二级目录三级目录 1. 下载2. 启动方式一&#xff1a;方式二&#xff1a; 3. 验证是否启动4. 安装目录解析5. 常用命令 一级目录 二级目录 三级目录 1. 下载 官网下载&#xff1a;ngi…

Oracle视频基础1.3.7练习

1.3.7 看oracle是否启动构造一个pfile:boobooke.ora看spfilewilson内容修改alert log file里拷贝的参数内容&#xff0c;创建一个pfile boobooke.ora用新创建的pfile启动数据库&#xff0c;并创建新的spfile:spfilebbk.ora启动数据库&#xff0c;监听&#xff0c;看新的进程解…

【LeetCode】每日一题 2024_11_2 使两个整数相等的位更改次数(位运算/模拟)

前言 每天和你一起刷 LeetCode 每日一题~ LeetCode 启动&#xff01; 题目&#xff1a;使两个整数相等的位更改次数 代码与解题思路 先读题&#xff1a; 题目要我们把 n 这个数字转换成 k 这个数字&#xff0c;但是只能是二进制位 1 转换成 0 纯模拟的解法&#xff1a; f…