Vue实例挂载的过程
一、思考
我们都听过知其然知其所以然这句话
那么不知道大家是否思考过new Vue()这个过程中究竟做了些什么?
过程中是如何完成数据的绑定,又是如何将数据渲染到视图的等等
二、分析
首先找到vue的构造函数
源码位置:src\core\instance\index.js
function Vue (options) {if (process.env.NODE_ENV !== 'production' &&!(this instanceof Vue)) {warn('Vue is a constructor and should be called with the `new` keyword')}this._init(options)
}
options是用户传递过来的配置项,如data、methods等常用的方法
vue构建函数调用_init方法,但我们发现本文件中并没有此方法,但仔细可以看到文件下方定定义了很多初始化方法
initMixin(Vue); // 定义 _init
stateMixin(Vue); // 定义 $set $get $delete $watch 等
eventsMixin(Vue); // 定义事件 $on $once $off $emit
lifecycleMixin(Vue);// 定义 _update $forceUpdate $destroy
renderMixin(Vue); // 定义 _render 返回虚拟dom
首先可以看initMixin方法,发现该方法在Vue原型上定义了_init方法
源码位置:src\core\instance\init.js
Vue.prototype._init = function (options?: Object) {const vm: Component = this// a uidvm._uid = uid++let startTag, endTag/* istanbul ignore if */if (process.env.NODE_ENV !== 'production' && config.performance && mark) {startTag = `vue-perf-start:${vm._uid}`endTag = `vue-perf-end:${vm._uid}`mark(startTag)}// a flag to avoid this being observedvm._isVue = true// merge options// 合并属性,判断初始化的是否是组件,这里合并主要是 mixins 或 extends 的方法if (options && options._isComponent) {// optimize internal component instantiation// since dynamic options merging is pretty slow, and none of the// internal component options needs special treatment.initInternalComponent(vm, options)} else { // 合并vue属性vm.$options = mergeOptions(resolveConstructorOptions(vm.constructor),options || {},vm)}/* istanbul ignore else */if (process.env.NODE_ENV !== 'production') {// 初始化proxy拦截器initProxy(vm)} else {vm._renderProxy = vm}// expose real selfvm._self = vm// 初始化组件生命周期标志位initLifecycle(vm)// 初始化组件事件侦听initEvents(vm)// 初始化渲染方法initRender(vm)callHook(vm, 'beforeCreate')// 初始化依赖注入内容,在初始化data、props之前initInjections(vm) // resolve injections before data/props// 初始化props/data/method/watch/methodsinitState(vm)initProvide(vm) // resolve provide after data/propscallHook(vm, 'created')/* istanbul ignore if */if (process.env.NODE_ENV !== 'production' && config.performance && mark) {vm._name = formatComponentName(vm, false)mark(endTag)measure(`vue ${vm._name} init`, startTag, endTag)}// 挂载元素if (vm.$options.el) {vm.$mount(vm.$options.el)}}
仔细阅读上面的代码,我们得到以下结论:
在调用beforeCreate之前,数据初始化并未完成,像data、props这些属性无法访问到
到了created的时候,数据已经初始化完成,能够访问data、props这些属性,但这时候并未完成dom的挂载,因此无法访问到dom元素
挂载方法是调用vm.$mount方法
initState方法是完成props/data/method/watch/methods的初始化
源码位置:src\core\instance\state.js
export function initState (vm: Component) {// 初始化组件的watcher列表vm._watchers = []const opts = vm.$options// 初始化propsif (opts.props) initProps(vm, opts.props)// 初始化methods方法if (opts.methods) initMethods(vm, opts.methods)if (opts.data) {// 初始化data initData(vm)} else {observe(vm._data = {}, true /* asRootData */)}if (opts.computed) initComputed(vm, opts.computed)if (opts.watch && opts.watch !== nativeWatch) {initWatch(vm, opts.watch)}
}
我们和这里主要看初始化data的方法为initData,它与initState在同一文件上
function initData (vm: Component) {let data = vm.$options.data// 获取到组件上的datadata = vm._data = typeof data === 'function'? getData(data, vm): data || {}if (!isPlainObject(data)) {data = {}process.env.NODE_ENV !== 'production' && warn('data functions should return an object:\n' +'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',vm)}// proxy data on instanceconst keys = Object.keys(data)const props = vm.$options.propsconst methods = vm.$options.methodslet i = keys.lengthwhile (i--) {const key = keys[i]if (process.env.NODE_ENV !== 'production') {// 属性名不能与方法名重复if (methods && hasOwn(methods, key)) {warn(`Method "${key}" has already been defined as a data property.`,vm)}}// 属性名不能与state名称重复if (props && hasOwn(props, key)) {process.env.NODE_ENV !== 'production' && warn(`The data property "${key}" is already declared as a prop. ` +`Use prop default value instead.`,vm)} else if (!isReserved(key)) { // 验证key值的合法性// 将_data中的数据挂载到组件vm上,这样就可以通过this.xxx访问到组件上的数据proxy(vm, `_data`, key)}}// observe data// 响应式监听data是数据的变化observe(data, true /* asRootData */)
}
仔细阅读上面的代码,我们可以得到以下结论:
初始化顺序:props、methods、data
data定义的时候可选择函数形式或者对象形式(组件只能为函数形式)
关于数据响应式在这就不展开详细说明
上文提到挂载方法是调用vm.$mount方法
源码位置:
Vue.prototype.$mount = function (el?: string | Element,hydrating?: boolean
): Component {// 获取或查询元素el = el && query(el)/* istanbul ignore if */// vue 不允许直接挂载到body或页面文档上if (el === document.body || el === document.documentElement) {process.env.NODE_ENV !== 'production' && warn(`Do not mount Vue to <html> or <body> - mount to normal elements instead.`)return this}const options = this.$options// resolve template/el and convert to render functionif (!options.render) {let template = options.template// 存在template模板,解析vue模板文件if (template) {if (typeof template === 'string') {if (template.charAt(0) === '#') {template = idToTemplate(template)/* istanbul ignore if */if (process.env.NODE_ENV !== 'production' && !template) {warn(`Template element not found or is empty: ${options.template}`,this)}}} else if (template.nodeType) {template = template.innerHTML} else {if (process.env.NODE_ENV !== 'production') {warn('invalid template option:' + template, this)}return this}} else if (el) {// 通过选择器获取元素内容template = getOuterHTML(el)}if (template) {/* istanbul ignore if */if (process.env.NODE_ENV !== 'production' && config.performance && mark) {mark('compile')}/*** 1.将temmplate解析ast tree* 2.将ast tree转换成render语法字符串* 3.生成render方法*/const { render, staticRenderFns } = compileToFunctions(template, {outputSourceRange: process.env.NODE_ENV !== 'production',shouldDecodeNewlines,shouldDecodeNewlinesForHref,delimiters: options.delimiters,comments: options.comments}, this)options.render = renderoptions.staticRenderFns = staticRenderFns/* istanbul ignore if */if (process.env.NODE_ENV !== 'production' && config.performance && mark) {mark('compile end')measure(`vue ${this._name} compile`, 'compile', 'compile end')}}}return mount.call(this, el, hydrating)
}
阅读上面代码,我们能得到以下结论:
不要将根元素放到body或者html上
可以在对象中定义template/render或者直接使用template、el表示元素选择器
最终都会解析成render函数,调用compileToFunctions,会将template解析成render函数
对template的解析步骤大致分为以下几步:
将html文档片段解析成ast描述符
将ast描述符解析成字符串
生成render函数
生成render函数,挂载到vm上后,会再次调用mount方法
源码位置:src\platforms\web\runtime\index.js
// public mount method
Vue.prototype.$mount = function (el?: string | Element,hydrating?: boolean
): Component {el = el && inBrowser ? query(el) : undefined// 渲染组件return mountComponent(this, el, hydrating)
}
调用mountComponent渲染组件
export function mountComponent (vm: Component,el: ?Element,hydrating?: boolean
): Component {vm.$el = el// 如果没有获取解析的render函数,则会抛出警告// render是解析模板文件生成的if (!vm.$options.render) {vm.$options.render = createEmptyVNodeif (process.env.NODE_ENV !== 'production') {/* istanbul ignore if */if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||vm.$options.el || el) {warn('You are using the runtime-only build of Vue where the template ' +'compiler is not available. Either pre-compile the templates into ' +'render functions, or use the compiler-included build.',vm)} else {// 没有获取到vue的模板文件warn('Failed to mount component: template or render function not defined.',vm)}}}// 执行beforeMount钩子callHook(vm, 'beforeMount')let updateComponent/* istanbul ignore if */if (process.env.NODE_ENV !== 'production' && config.performance && mark) {updateComponent = () => {const name = vm._nameconst id = vm._uidconst startTag = `vue-perf-start:${id}`const endTag = `vue-perf-end:${id}`mark(startTag)const vnode = vm._render()mark(endTag)measure(`vue ${name} render`, startTag, endTag)mark(startTag)vm._update(vnode, hydrating)mark(endTag)measure(`vue ${name} patch`, startTag, endTag)}} else {// 定义更新函数updateComponent = () => {// 实际调⽤是在lifeCycleMixin中定义的_update和renderMixin中定义的_rendervm._update(vm._render(), hydrating)}}// we set this to vm._watcher inside the watcher's constructor// since the watcher's initial patch may call $forceUpdate (e.g. inside child// component's mounted hook), which relies on vm._watcher being already defined// 监听当前组件状态,当有数据变化时,更新组件new Watcher(vm, updateComponent, noop, {before () {if (vm._isMounted && !vm._isDestroyed) {// 数据更新引发的组件更新callHook(vm, 'beforeUpdate')}}}, true /* isRenderWatcher */)hydrating = false// manually mounted instance, call mounted on self// mounted is called for render-created child components in its inserted hookif (vm.$vnode == null) {vm._isMounted = truecallHook(vm, 'mounted')}return vm
}
三、结论
new Vue的时候调用会调用_init方法
定义 s e t 、 set、 set、get 、 d e l e t e 、 delete、 delete、watch 等方法
定义 o n 、 on、 on、off、 e m i t 、 emit、 emit、off等事件
定义 _update、 f o r c e U p d a t e 、 forceUpdate、 forceUpdate、destroy生命周期
调用$mount进行页面的挂载
挂载的时候主要是通过mountComponent方法
定义updateComponent更新函数
执行render生成虚拟DOM
_update将虚拟DOM生成真实DOM结构,并且渲染到页面中
相关文章:
Vue实例挂载的过程
一、思考 我们都听过知其然知其所以然这句话 那么不知道大家是否思考过new Vue()这个过程中究竟做了些什么? 过程中是如何完成数据的绑定,又是如何将数据渲染到视图的等等 二、分析 首先找到vue的构造函数 源码位置:src\core\instance\…...
dvwa xss通关
反射型XSS通关 low难度 选择难度: 直接用下面JS代码尝试: <script>alert(/xss/)</script>通关成功: medium难度 直接下面代码尝试后失败 <script>alert(/xss/)</script>发现这段代码直接被输出: 尝试…...
AD如何进行汉化
AD如何进行汉化 通过安装好AD后,默认都是英文界面模式,如果想汉化为中文模式,需要点击“DXP”->“参数选择”,打开界面如下: 然后将上图“本地化”下面的方框勾选上,点击“应用”,“确定”…...
【JUC基础】JUC入门基础
目录 什么是JUC线程和进程锁传统的 synchronizedLock 锁Synchronized 与 Lock 的区别 生产者和消费者问题Synchronized 版Lock版Condition 的优势:精准通知和唤醒线程 8 锁现象问题1:两个同步方法,先执行发短信还是打电话?问题2&a…...
自然语言处理: 第十章GPT的API使用
理论基础 现在的以GPT为首的生成类模型,它拥有对话的能力,它会根据你输入的暗示(prompt)或者指令(instruct)生成对应的回答。所以,不同的输入会导致不同的输出(其实由于chatgpt最终生成的答案是beam_search 以及随机采样的机制,所…...
docker使用harbor进行镜像仓库管理演示以及部分报错解决
目录 一.安装harbor和docker-compose 1.下载 2.将该文件修改为这样,修改好自己的hostname和port,后文的用户和密码可以不改也可以改,用于登录 3.安装 二.修改daemon.json文件和/etc/hosts文件 三.使用powershell作windows端域名映射 四…...
【精算研究01/10】 计量经济学的性质和范围
一、说明 计量经济学是使用统计方法来发展理论或测试经济学或金融学中的现有假设。计量经济学依赖于回归模型和零假设检验等技术。计量经济学也可以用来预测未来的经济或金融趋势。 图片来源:https://marketbusinessnews.com 二、 计量经济之简介 计量经济学是对经济…...
【python知识】用 Tkinter实现“剪刀-石头-布”和“弹球游戏 ”
一、提要 Tkinter是一个Python内置模块,它提供了一个简单易用的界面来创建GUI。 在实现一些动态的画面、如游戏还是需要一些创新性思维的。在本文中,我们将使用 Tkinter 探索 Python GUI 编程。我们将介绍 Tkinter 的基础知识,并演示如何使用…...
Android 绘制之文字测量
drawText() 绘制文字 绘制进度条:paint.strokeCap Paint.CAP.RONUD 线条两边样式 设置文字字体:paint.typeFace Resources.Compat.getFont(context,font) 设置加粗 paint.isFakeBoldText 设置居中: paint.setTextAlign Paint.Align.CENTER //居中, 并不是真正的居中 往…...
基于AVR128单片机智能传送装置
一、系统方案 1、板载可变电阻(电位器)R29的电压作为处理器ATmega128的模数转换模块中单端ADC0的模拟信号输入(跳线JP13短接)。 2、调节电位器,将改变AD转换接口ADC0的模拟信号输入,由处理器完成ADC0的A/D转…...
Nexus私有仓库+IDEA配置远程推送
目录 一、docker安装nexus本地私服,Idea通过maven配置deploy本地jar包(简单) 二、docker push镜像到第三方nexus远程私服(shell命令操作) 三、springboot通过maven插件自动生成docker镜像并push到nexus私服…...
idea2023项目上传到gitee
1、按照gitee插件 File——>Settings plugins——>Marketplace下面搜索gitee,然后按照gitee插件 2、上传项目 VCS_——>Share Project on Gitee 如果第一次没登录的需要先登录,登录完后就可以上传了...
【golang】派生数据类型---指针 标识符、关键字等
1、指针 对比C/C中的指针,go语言中的指针显得极为简洁,只是简单的获取某个空间的地址 或者 根据指针变量中的内容 获取对应存储空间的内容等操作。 具体示例如下: go中使用指针需要注意的点: 可以通过指针改变它所指向的内存空…...
深度学习技术
深度学习是什么? 深度学习,英文名为Deep Learning,其实就是机器学习的一种高级形式。它的灵感来源于人脑神经网络的工作方式,是一种让机器可以自主地从数据中学习和提取特征的技术。你可以把它想象成一位小侦探,通过不…...
TCP/IP网络江湖——物理层护江山:网络安全的铁壁防线(物理层下篇:物理层与网络安全)
TCP/IP网络江湖——物理层护江山:网络安全的铁壁防线(物理层下篇:物理层与网络安全) 〇、引言一、物理层的隐私与保密1.1 加密技术的护盾1.2 安全传输协议的密约1.3 物理层的安全控制1.4 面对未知威胁的准备二、电磁干扰与抵御2.1 电磁干扰的威胁2.2 抗干扰技术的应用2.3 屏…...
python-数据可视化-使用API
使用Web应用程序编程接口 (API)自动请求网站的特定信息而不是整个网页,再对这些信息进行可视化 使用Web API Web API是网站的一部分,用于与使用具体URL请求特定信息的程序交互。这种请求称为API调用 。请求的数据将以易于处理的…...
窗口看门狗
从下往上看: 1. 时钟设置 RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG,ENABLE);//使能独立看门狗时钟 WWDG_SetPrescaler(WWDG_Prescaler_8);//看门狗预分频器WWDG counter clock (PCLK1/4096)/8 2.设置窗口值 实际就是设置WWDG_CR的低七位值, 但是这个值要大于0x40(也就是…...
开发新能源的好处
风能无论是总装机容量还是新增装机容量,全球都保持着较快的发展速度,风能将迎来发展高峰。风电上网电价高于火电,期待价格理顺促进发展。生物质能有望在农业资源丰富的热带和亚热带普及,主要问题是降低制造成本,生物乙…...
error: can‘t find Rust compiler
操作系统 win11 pip install -r requirements.txt 报错如下 Using cached https://pypi.tuna.tsinghua.edu.cn/packages/56/fc/a3c13ded7b3057680c8ae95a9b6cc83e63657c38e0005c400a5d018a33a7/pyreadline3-3.4.1-py3-none-any.whl (95 kB) Building wheels for collected p…...
全面解析MES系统中的车间退料管理
一、车间退料管理的定义: 车间退料是指在生产过程中,将不合格或多余的物料、半成品或成品从车间环节返还到供应链的过程。车间退料管理则是指对这一退料过程进行规范化、系统化的管理和跟踪。 二、车间退料管理的流程: 1. 退料申请…...
探究finally代码块是否执行
情况一:try代码块正常执行,无异常,finally代码块无retrun; 代码演示 public class Test38 {public static void main(String[] args) {int foo foo();System.out.println("foo:" foo);}public static int foo() {tr…...
leetcode刷题(字符串相加、包含每个查询的最小区间、模拟行走机器人、环形子数组的最大和、满足不等式的最大值、四数之和、树中距离之和)
目录 1、字符串相加 2、包含每个查询的最小区间 3、模拟行走机器人 4、环形子数组的最大和 5、满足不等式的最大值 6、四数之和 7、 树中距离之和 1、字符串相加 class Solution:def addStrings(self, num1: str, num2: str) -> str:i len(num1) - 1 # num1的末…...
Grafana reporter定时报表踩坑记录
前言:本以为测试grafana reporter功能能很顺利,但按照网上大佬分享的记录进行操作,屡屡报错,不知是因为我的grafana部署在k8s中之前由低版本升级到高版本导致的,还是其他原因,在grafana中安装Grafana Image Renderer 一直报错。 Github地址:https://github.com/IzakMar…...
Flutter 状态管理引子
1、为了更好地了解状态管理,先看看什么是状态。 在类似Flutter这样的响应式编程框架中,我们可以认为U相关的开发就是对数据进行封装,将之转换为具体的U1布局或者组件。借用Flutter官网的一张图,可以把我们在第二部分做的所有开发…...
CFC编程入门_【10分钟学会】
什么是CFC: 【差不多10分钟全学会】 CFC是图形化编程, 跟单片机的连线一样, 唯一的区别:功能块右侧是【只能输出】引脚。 只有左侧引脚可以输入输出。 有哪些控件: 指针:用于拖动功能块。 控制点…...
golang无需创建新切片
在 Go 语言中,append(b, 0)[:len(b)] 是一种常见的用法,用于在切片 b 后追加一个元素,并返回旧切片的前 len(b) 个元素。 这种用法的目的是将一个新元素追加到切片中,并确保切片的长度保持不变。具体步骤如下: 1. ap…...
Django基础5——ORM中间程序
文章目录 一、基本了解二、ORM基本操作2.1 连接数据库2.1.1 使用sqlite数据库2.1.2 使用MySQL数据库 2.2 对数据库操作2.2.1 增(前端数据——>数据库)2.2.2 查(数据库——>前端展示)2.2.3 改(修改数据࿰…...
SpringAOP详解(上)
当需要在方法前后做一些操作就需要借助动态代理来实现 一、动态代理实现方法 1、jdk自带实现方式 jdk实现代理是被代理类实现接口的方式 public interface UserInterface {void test(); }public class UserService implements UserInterface {public void test() {System.o…...
C++ 存储类
存储类定义 C 程序中变量/函数的范围(可见性)和生命周期。这些说明符放置在它们所修饰的类型之前。下面列出 C 程序中可用的存储类: autoregisterstaticexternmutablethread_local (C11) 从 C 17 开始,auto 关键字不再是 C 存储…...
【教程分享】Docker搭建Zipkin,实现数据持久化到MySQL、ES
1 拉取镜像 指定版本,在git查看相应版本,参考: https://github.com/openzipkin/zipkin 如2.21.7 docker pull openzipkin/zipkin:2.21.7 2 启动 Zipkin默认端口为9411。启动时通过-e server.portxxxx设置指定端口 docker run --name zi…...
用手机怎样制作网站/南宁seo排名首页
交流电源有多种定义。 定义一:通过数字接口控制的开关电源(它强调的是交流电源的“通信”功能)。 定义二:具有数字控制功能的开关电源(它强调的是交流电源的“数控”功能)。 定义三:具有数字监测功能的开关电源(它强调的是交流电源对温度等参…...
html企业网站怎么做/网络推广都需要做什么
创建工程 修改字符串资源 打开/res/values/strings.xml文件。 点击Add按钮,添加字符串,输入字符串的name 和value 新建的工程含有三个已有字符串 新建颜色资源color.xml 使用资源颜色和字符串资源 颜色:R.color.red_gb 字符串:R.s…...
信阳专业做网站公司/怎么做百度关键词排名
一、赛题解读 1、赛题分析 赛题任务需要对添加了水印的图像,将水印擦除掉,还原原本的图的样子(图1)。 与手写文字擦除任务(图2)一个比较大的区别是:水印占据面积很大,因此对水印擦除后,还需要对被擦除的区域进行一个填…...
开展农业信息网站建设工作总结/营销策略分析
if (process.env.NODE_ENV "development"){// 开发环境 } if(process.env.NODE_ENV "production"){// 线上环境 }...
国内做网站公司哪家好/抚顺网站seo
● 把电脑的第一启动项设为USB设备启动以往用光盘装系统,必须调整启动项为光驱启动,而现在我们要用U盘装系统,所以要调整为U盘启动。关于这个,不同电脑不同版本的bios有不同的设置方法,不过都大同小异,目的…...
慈善网站建设方案/百度软文推广公司
在File类中,需要导入命名空间:using System.IO,不需要实例化,直接使用即可。 基本操作:盘存、复制、移动、删除。 基本方法:File.Exist()、File.Copy()、File.Move()、File.Delete() File.Create(path)----在指定路径…...