Vue2.x源码:new Vue()做了啥
- 例子1
- new Vue做了啥?
- new Vue做了啥,源码解析
- initMixin函数 初始化 – 初始化Vue实例的配置
- initLifecycle函数 – 初始化生命周期钩子函数
- initEvents – 初始化事件系统
- 初始化渲染 initRender
- 初始化inject选项
例子1
<div id="app"><div class="home"><h1>{{title}}</h1></div>
</div>
new Vue({el: '#app',data: () => {return {title: 'Home Page'}}
})
更多详细内容,请微信搜索“前端爱好者
“, 戳我 查看 。
new Vue()做了啥?
在Vue.js 2.x的源码中,new Vue()
的主要作用是创建一个Vue实例。当我们调用new Vue()
时,会经过以下几个步骤:
- 初始化Vue实例的配置:Vue会将我们传入的配置对象进行初始化,包括数据、计算属性、方法等。
- 初始化生命周期钩子函数:Vue会在初始化过程中执行一系列的生命周期钩子函数,例如
beforeCreate
、created
、beforeMount
、mounted
等。 - 初始化事件系统:Vue会为实例创建一个事件总线,用于处理事件的监听和触发。
- 解析模板:如果配置中指定了
template
选项,Vue会将模板解析成渲染函数,以便后续的渲染过程中使用。 - 初始化渲染:Vue会创建一个虚拟DOM,并将之前解析的渲染函数进行渲染,生成真实DOM,并将其挂载到页面上。
- 数据响应式处理:Vue会对实例中的数据进行响应式处理,即通过劫持数据的访问和修改,实现数据的双向绑定。
在这个过程中,Vue还会做很多其他的工作,例如处理指令、组件注册、依赖收集等,以确保整个应用的正常运行和响应式更新。
以上只是对new Vue()
的简单概述,具体的实现细节涉及到很多源码内容,在Vue.js的源码中可以详细了解其中的实现原理。
new Vue()做了啥,源码解析
vue源码版本vue2.5.2
new Vue()会执行_init方法,而_init方法在initMixin函数中定义。
src/core/instance/index.js文件中定义了Vue
function Vue (options) {this._init(options)
}initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)export default Vue
initMixin函数 初始化 – 初始化Vue实例的配置
- const vm: Component = this;定义vm等于Vue;
- vm._uid = uid++; vm存在_uid,自增
- vm._isVue = true;只有是经过vue实例化的。vm都存在_isVue属性为true值
- options && options._isComponent 当options存在_isComponent属性时,执行initInternalComponent(vm, options)
- 当组件options._isComponen不存在时,执行 mergeOptions( resolveConstructorOptions(vm.constructor), options || {},vm )
- m._renderProxy = vm;给vm定义一个_renderProxy 属性等于自身
- vm._self = vm;给vm定义一个_self 属性等于自身
- initLifecycle(vm);初始化生命周期相关实例属性
- initEvents(vm);初始化事件
- initRender(vm);定义$createElement() createElement的执行过程
- callHook(vm, ‘beforeCreate’);执行beforeCreate生命周期钩子
- initInjections(vm);
- initState(vm);将data代理至Vue._data data的代理
- initProvide(vm);初始化provide
- callHook(vm, ‘created’);执行created生命周期钩子
- vm. m o u n t ( v m . mount(vm. mount(vm.options.el)执行挂载
定义的 Vue.prototype._init
export function initMixin (Vue: Class<Component>) {Vue.prototype._init = function (options?: Object) {const vm: Component = this// a uid,自增IDvm._uid = uid++vm._isVue = true// merge options 合并optionsif (options && options._isComponent) {initInternalComponent(vm, options)} else {vm.$options = mergeOptions(resolveConstructorOptions(vm.constructor),options || {},vm)}vm._renderProxy = vm// expose real selfvm._self = vm// 初始化生命周期相关实例属性initLifecycle(vm)//初始化事件initEvents(vm)//定义$createElement() createElement的执行过程initRender(vm)// 执行beforeCreate生命周期钩子callHook(vm, 'beforeCreate')initInjections(vm) // resolve injections before data/props//将data代理至Vue._data data的代理initState(vm)// 初始化provideinitProvide(vm) // resolve provide after data/props// 执行created生命周期钩子callHook(vm, 'created')// 当options中存在el属性,则执行挂载if (vm.$options.el) {//挂载#app $mount执行过程vm.$mount(vm.$options.el)}}
}
initLifecycle函数 – 初始化生命周期钩子函数
initLifecycle 初始化生命周期相关变量即在vue实例上挂载一些属性并设置默认值,如
- p a r e n t , parent, parent,root,
- $children,
- $ref,vm._watcher,
- vm.__inactive,
- vm._directInactive,
- vm._isMounted,
- vm._isDestroyed,
- vm._isBeingDestroyed
export function initLifecycle (vm: Component) {const options = vm.$options// locate first non-abstract parentlet parent = options.parent//当前组件存在父级并且当前组件不是抽象组件if (parent && !options.abstract) {//通过while循环来向上循环,如果当前组件的父级是抽象组件并且也存在父级,那就继续向上查找当前组件父级的父级while (parent.$options.abstract && parent.$parent) {//更新parentparent = parent.$parent}//把该实例自身添加进找到的父级的$children属性中parent.$children.push(vm)}//直到找到第一个不是抽象类型的父级时,将其赋值vm.$parentvm.$parent = parent//设置实例根元素。判断如果当前实例存在父级,那么当前实例的根实例$root属性就是其父级的根实例$root属性,如果不存在,那么根实例$root属性就是它自己vm.$root = parent ? parent.$root : vmvm.$children = []vm.$refs = {}vm._watcher = nullvm._inactive = nullvm._directInactive = false//实例是否已挂载vm._isMounted = false//实例是否已销毁vm._isDestroyed = false//实例是否正准备销毁vm._isBeingDestroyed = false
}
合并options之后
initEvents – 初始化事件系统
initEvents 初始化事件。 初始化的是父组件在模板中使用v-on或@注册的监听子组件内触发的事件。
export function initEvents (vm: Component) {
//在vm上新增_events属性并将其赋值为空对象,用来存储事件。vm._events = Object.create(null)vm._hasHookEvent = false// init parent attached events//获取父组件注册的事件赋给listeners,const listeners = vm.$options._parentListeners//如果listeners不为空,则调用updateComponentListeners函数,将父组件向子组件注册的事件注册到子组件的实例中if (listeners) {updateComponentListeners(vm, listeners)}
}
初始化渲染 initRender
initRender函数 初始化渲染.。
export function initRender (vm: Component) {vm._vnode = null // the root of the child treevm._staticTrees = null // v-once cached treesconst options = vm.$optionsconst parentVnode = vm.$vnode = options._parentVnode // the placeholder node in parent treeconst renderContext = parentVnode && parentVnode.context//实例上插槽vm.$slots = resolveSlots(options._renderChildren, renderContext)vm.$scopedSlots = emptyObject//在实例上定义_c函数和$_createElement函数vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)const parentData = parentVnode && parentVnode.datadefineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true)defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null, true)}
初始化inject选项
inject 选项中的每一个数据key都是由其上游父级组件提供的,所以我们应该把每一个数据key从当前组件起,不断的向上游父级组件中查找该数据key对应的值,直到找到为止。
如果在上游所有父级组件中没找到,那么就看在inject 选项是否为该数据key设置了默认值,如果设置了就使用默认值,如果没有设置,则抛出异常。
export function initInjections (vm: Component) {
//调用resolveInject把inject选项中的数据转化成键值对的形式赋给resultconst result = resolveInject(vm.$options.inject, vm)if (result) {toggleObserving(false)//遍历result中的每一对键值Object.keys(result).forEach(key => {//调用defineReactive函数将其添加当前实例上defineReactive(vm, key, result[key])})toggleObserving(true)}
}
注意,在把result中的键值添加到当前实例上之前,会先调用toggleObserving(false),而这个函数内部是把shouldObserve = false,这是为了告诉defineReactive函数仅仅是把键值添加到当前实例上而不需要将其转换成响应式
resolveInject函数内部是如何把inject 选项中数据转换成键值对的。
export function resolveInject (inject: any, vm: Component): ?Object {if (inject) {// inject is :any because flow is not smart enough to figure out cached//创建一个空对象result,用来存储inject 选项中的数据key及其对应的值,作为最后的返回结果。const result = Object.create(null)const keys = hasSymbol? Reflect.ownKeys(inject).filter(key => {/* istanbul ignore next */return Object.getOwnPropertyDescriptor(inject, key).enumerable}): Object.keys(inject)for (let i = 0; i < keys.length; i++) {const key = keys[i]//provideKey就是上游父级组件提供的源属性const provideKey = inject[key].fromlet source = vm//while循环,从当前组件起,不断的向上游父级组件的_provided属性中(父级组件使用provide选项注入数据时会将注入的数据存入自己的实例的_provided属性中)查找,直到查找到源属性的对应的值,将其存入result中while (source) {if (source._provided && hasOwn(source._provided, provideKey)) {result[key] = source._provided[provideKey]break}source = source.$parent}if (!source) {//是否有default属性,如果有的话,则拿到这个默认值,官方文档示例中说了,默认值可以为一个工厂函数,所以当默认值是函数的时候,就去该函数的返回值,否则就取默认值本身。如果没有设置默认值,则抛出异常。if ('default' in inject[key]) {const provideDefault = inject[key].defaultresult[key] = typeof provideDefault === 'function'? provideDefault.call(vm): provideDefault} else if (process.env.NODE_ENV !== 'production') {warn(`Injection "${key}" not found`, vm)}}}return result}
}
官方文档中说inject 选项可以是一个字符串数组,也可以是一个对象,在上面的代码中只看见了处理当为对象的情况,那如果是字符串数组呢?怎么没有处理呢?
其实在初始化阶段_init函数在合并属性的时候还调用了一个将inject 选项数据规范化的函数normalizeInject
参考文档:
https://blog.csdn.net/weixin_40119412/article/details/128880336
相关文章:

Vue2.x源码:new Vue()做了啥
例子1new Vue做了啥?new Vue做了啥,源码解析 initMixin函数 初始化 – 初始化Vue实例的配置initLifecycle函数 – 初始化生命周期钩子函数initEvents – 初始化事件系统初始化渲染 initRender初始化inject选项 例子1 <div id"app"><div class"home&…...

iOS 借助DSYMTools工具定位到闪退的具体行数和方法名
1、下载 dSYMTools-master 工具,下载安装后,如下图: 2、通过Bugly或友盟等异常记录工具,找到闪退的内存地址和偏移量信息上图是Bugy记录的闪退信息,友盟的参考如下: 关于工具的原理和其他描述,…...

分布式解决方案与实战
分布式多线程性能调优 使用多线程优化接口 //下单业务public Object order( long userId){long start System.currentTimeMillis();//方法的开始时间戳(ms)JSONObject orderInfo remoteService.createOrder(userId);Callable<JSONObject> calla…...

GitHub入门介绍
GitHub是一个基于web的版本控制系统,主要用于代码管理和协作开发。它是开源的,并且提供了一系列的功能,方便开发人员进行版本控制、代码托管和团队协作。 以下是GitHub的一些基本概念和功能: 版本控制:GitHub使用Git作…...

IP与子网掩码之间的关系
子网掩码用于确认IP所在的网段,网络位与子网掩码相匹配 如果有另一台主机想要与这个IP地址进行通信,这时需要看两台主机的IP地址是否处于同一网段,处于同一网段才能相互ping通。 那么怎么判断是否处于同一网段呢?我们就看子网掩…...

文档或书籍扫描为 PDF:ScanPapyrus Crack
ScanPapyrus 可让您快速轻松地将文档或书籍扫描为 PDF,批处理模式使扫描过程快速高效,自动处理书籍并将其拆分为单独的页面 用于快速扫描文档、书籍或打印照片的扫描仪软件 快速扫描文档 使用此扫描仪软件,您无需在扫描仪和计算机之间来回移动…...

Clickhouse RoaringBitmap
https://blog.csdn.net/penriver/article/details/119736050 https://juejin.cn/post/7179956435806076988 BitMap适合连续密集的正整数存储,对于稀疏的正整数存储,其性能在很多时候是没办法和int数组相比的,尤其是正整数跨度较大的场景&…...

C语言第四十九弹----模拟使用strcpy函数
使用C语言模拟使用strcpy函数 定义:strcpy 函数是 C 标准库中用于字符串复制的函数。它接受两个参数,第一个参数 dest 是目标字符串的指针,第二个参数 src 是源字符串的指针,函数的功能是将源字符串复制到目标字符串中࿰…...

docker搭建maven私库Nexus3
什么是Maven私服? Maven 私服是一种特殊的Maven远程仓库,它是架设在局域网内的仓库服务,用来代理位于外部的远程仓库(中央仓库、其他远程公共仓库)。 当然也并不是说私服只能建立在局域网,也有很多公司会…...

Java 基础学习(十)包装类、异常
1 包装类 1.1 包装类概述 1.1.1 什么是包装类 在进行类型转换时,有一种特殊的转换:将 int 这样的基本数据类型转换为对象,如下图所示: 所有基本类型都有一个与之对应的类,即包装类(wrapper)。…...

STM32的基本定时器注意点
本文介绍了STM32基本定时器3个重要的寄存器PSC、ARR、CNT,以及缓冲机制和计数细节。 基本定时器的框图 预分频器寄存器(TIMx_PSC)可以在运行过程中修改它的数值,新的预分频数值将在下一个更新事件时起作用。因为更新事件发生时,会把 TIMx_PS…...

浅谈NLP和大模型的关系
目录 一、什么是NLP 二、NLP的应用举例 三、NLP的Python实现举例 四、NLP和大模型的关系 五、NLP的难点 5.1 内容的有效界定 5.2 消歧和模糊性 5.3 有瑕疵的或不规范的输入 5.4 语言行为与计划 六、研究热点 一、什么是NLP 如果单独说NLP这3个字母,具有两…...

k8s上安装KubeSphere
🍩安装KubeSphere 🍪前置环境🍪安装nfs-server文件系统🍪配置nfs-client🍪配置默认存储🍪创建了一个存储类🍪metrics-server集群指标监控组件 🍪安装KubeSphere🍪执行安装…...

Linux 链接器如何使用静态库来解析引用
文章目录 通过进行代码实践静态库在编译时被链接到可执行文件中的基本原理原理总结 QA:.obj文件是什么? 通过进行代码实践 链接器在解析引用时,可以使用静态库来满足对未定义符号的引用。以下是使用静态库的一般步骤: 编写代码: 首先&#…...

vue实现滑动验证
效果图: 源码地址:github文档地址: https://github.com/monoplasty/vue-monoplasty-slide-verify 使用步骤:1,安装插件: npm install --save vue-monoplasty-slide-verify 在main.js中使用一下ÿ…...

***Cpolar配置外网访问和Dashy
Dashy是一个开源的自托管的导航页配置服务,具有易于使用的可视化编辑器、状态检查、小工具和主题等功能。你可以将自己常用的一些网站聚合起来放在一起,形成自己的导航页。一款功能超强大,颜值爆表的可定制专属导航页工具 结合cpolar内网工具,我们实现无需部署到公网服务器…...

Rancher中使用promtail+loki+grafna收集k8s日志并展示
Rancher中使用promtail+loki+grafna收集k8s日志并展示 根据应用需求和日志数量级别选择对应的日志收集、过滤和展示方式,当日志量不太大,又想简单集中管理查看日志时,可使用promtail+loki+grafna的方式。本文找那个loki和grafana外置在了k8s集群之外。 1、添加Chart Repo …...

modelbox线程爆满宕机bug
序 该bug的解决需要特别感谢张同学。有了大佬的帮助,这个bug才得以解决。 问题现象 modelbox可以进行模型推理,但压测一段时间后,modelbox会宕机,并发生段错误。 “libgomp: Thread creation failed: Resource temporarily una…...

KUKA机器人如何在程序中编辑等待时间?
KUKA机器人如何在程序中编辑等待时间? 如下图所示,如何实现在P1点和P2点之间等待设定的时间? 如下图所示,可以直接输入wait sec 2(等待2秒), 如下图所示,再次选中该程序后&#…...

MQ入门简介
当年入门MQ时跟着尚硅谷RabbitMQ视频学习所做的一些笔记,现在上传方便有需要小伙伴查看 一:MQ的相关概念 1.什么是MQ MQ(message queue),从字面意思上看,本质是个队列,FIFO 先入先出,只不过队列中存放的内…...

如何正确使用缓存来提升系统性能
文章目录 引言什么时候适合加缓存?示例1示例2:示例3: 缓存应该怎么配置?数据分布**缓存容量大小:**数据淘汰策略 缓存的副作用总结 引言 在上一篇文章IO密集型服务提升性能的三种方法中,我们提到了三种优化…...

IDEA中Terminal配置为bash
简介 我们日常命令行都是使用Linux的bash指令,但是我们的开发基本都是基于Windows上的IDEA进行开发的,对此我们可以通过将IDEA将终端Terminal改为git bash自带的bash.exe解决问题。 配置步骤 安装GIT 这步无需多说了,读者可自行到官网下载…...

C# 字符串格式化
写在前面 在日常编程中,经常需要对字符串进行格式化操作,以便呈现为不同的格式,满足各种各样的显示需求,C#的字符串格式化参数是非常丰富的,这里做个简单的列举,以供后续参考和延伸。 代码实现 var curr…...

基于亚马逊云科技新功能:Amazon SageMaker Canvas无代码机器学习—以构建货物的交付状态检测模型实战为例深度剖析以突显其特性
授权说明:本篇文章授权活动官方亚马逊云科技文章转发、改写权,包括不限于在亚马逊云科技开发者社区、 知乎、自媒体平台、第三方开发者媒体等亚马逊云科技官方渠道。 目录 🚀一. Amazon SageMaker 🔎1.1 新功能发布:A…...

基于Spring Boot、Mybatis、Redis和Layui的企业电子招投标系统源码实现与立项流程
招投标管理系统是一款适用于招标代理、政府采购、企业采购和工程交易等领域的企业级应用平台。该平台以项目为主线,从项目立项到项目归档,实现了全流程的高效沟通和协作。通过该平台,用户可以实时共享项目数据信息,实现规范化管理…...

electron这样使用更安全
背景: electron大家平时为了方便使用,或是一些网上demo的引导,会让渲染进程的业务界面支持直接使用nodejs,这种开发方式有一定的安全隐患,如果业务界面因为xss之类的漏洞被注入其他代码,危害非常大&#x…...

DPDK多进程之间的通信
文章目录 前言本机DPDK IPC API介绍demo演示 前言 DPDK的主进程和辅助进程之间共享大页内存。关于DPDK多进程的支持文档介绍见:47. 多进程支持。 本文介绍本机DPDK的主进程和辅助进程之间交换短消息的API的使用。 前置要求:DPDK-Hello-World示例应用程…...

Python文本信息解析:从基础到高级实战‘[pp]]‘[
更多Python学习内容:ipengtao.com 大家好,我是彭涛,今天为大家分享 Python文本信息解析:从基础到高级实战,全文3600字,阅读大约10分钟。 文本处理是Python编程中一项不可或缺的技能,覆盖了广泛的…...

c语言多线程队列实现
为了用c语言实现队列进行多线程通信,用于实现一个状态机。 下面是实现过程 1.实现多线程队列入栈和出栈,不加锁 发送线程发送字符1,接收线程接收字符并打印。 多线程没有加锁,会有危险 #include "stdio.h" #include …...

一分钟带你了解电容
电容器中的电容究竟是怎么定义的? 一个电容器,如果带1库的电量时两级间的电势差是1伏,这个电容器的电容就是1法拉,即:CQ/U 。但电容的大小不是由Q(带电量)或U(电压)决定…...