当前位置: 首页 > Web前端 > vue.js

Vue.js源码分析

时间:2023-03-31 22:44:06 vue.js

1.简单描述一下Vue第一次渲染的过程。1、vue初始化,在调用newVue()之前,实例成员_init()方法$data、$props、$set、$delete、$watch属性_update、$forceUpdate、$destroy生命周期相关方法$on、$once、$off,$emitevent$nextTick,_rendermethodstaticmethodconfig,observable,utilobjectoptionsobject,andextendcomponents,directives,filters,_basemethodset,delete,nextTickmethodkeep-alivecomponentVue.use(),Vue.mixin(),Vue.extend(),Vue.directive(),Vue.component(),Vue.filter()平台相关的全局变量:v-model,v-show全局组件:v-Transition,v-transition-分组全局方法:__patch__、$mount$mountVue.compile()2、实例化Vue,调用this._init()方法初始化vm的生命周期相关变量,$children/$parent/$root/$refsvm事件监听器初始化,父组件绑定当前组件事件vm编译渲染初始化,$slots/$scopedSlots/_c/$createElement/$attrs/$listenersbeforeCreate生命钩子回调给我注入injectintovm初始化vm的_props/methods/_data/computed/watch初始化提供调用entry-runtime-with-compiler.js入口文件this.$mountcreated生命钩子回调3.编译模板,有两个this.$mount函数entry-runtime-with-compiler.js文件中的this.$mount,这个$mount()的核心作用就是帮助我们将模板编译成render函数。/src/platforms/web/runtime/index.js文件$m在this.$mountount保存后,重写options中判断是否有render函数。如果在/src/platforms/web/runtime/index.js中有直接调用$mount,如果不存在,则编译template模板获取options中的template。如果不存在,则获取el的outerHTML作为模板如果存在,则获取对应DOM对象的innerHTML作为模板如果模板类型为字符串类型,则获取id选择器如果模板为元素节点,则返回元素的innerHTML如果以上都不是,则警告并返回当前实例调用compileToFunctions将模板转换为渲染函数调用$mountin/src/platforms/web/runtime/index.jsthis.$mountinthefile/src/platforms/web/runtime/index.js重写获取el,防止运行版本不执行上一步时,调用mountComponent开始挂载组件。4、挂载组件,判断是否有页面渲染的render选项。如果不是,并且它是一个开发环境并且传入了模板,则在Mount之前发送警告。lifehook回调定义了updateComponent,这个方法最终会调用vm._update来比较updateview。这里只是创建一个观察者实例的定义。将updateComponent作为参数传入watcher构造函数会调用watcher.get()方法,get()方法会调用updateComponent函数并调用vm._render()创建一个VNode并调用vm.update()挂载realDOMmountedlifehook回调返回一个Vue实例2.简述Vue响应式原理。Vue实例化时,在init()方法中调用initState()方法初始化Vue实例的状态,其中initData()将data属性注入到Vue实例中,调用observe(data)将data对象转化为一个响应式对象observe是一个响应式条目。首先验证数据格式,判断是否为object类型,然后查看数据是否有__ob__属性,从而判断当前数据是否经过响应式处理。如果以上条件都正常(是对象类型,没有__ob__属性),实例化观察者对象,实例化观察者对象为当前数据对象定义不可枚举的__ob__属性,记录当前观察者对象,判断当前数据类型,做数组和对象类型数据不同处理数组保留数组原有的pop、push、shift、unshift、splice、sort、reverse方法,调用Object.defineProperty()重新定义数组方法,并重新遍历数组元素并将其设置为响应式数据对象来遍历对象的每个属性。为每个属性调用defineReactive方法。defineReactive会为每个属性创建一个对应的dep对象,让dep收集依赖。如果当前属性的值是一个对象,就会递归调用observe。defineReactive中的核心方法是getter和setter。getter的作用是收集依赖。收集依赖项时,收集每个属性的依赖项。如果这个属性的值是一个对象,它还会收集子对象的依赖,最后返回这个属性的值。添加或删除子属性的属性时发送通知。在设置器中,首先保存新值。如果新值是一个对象,调用observe将新设置的对象转化为响应对象,然后dispatchupdates(发送通知),调用dep.notify()收集依赖,在watcher对象的get方法中调用pushTarget,记录Dep.target属性,在data中访问成员时收集依赖,在defineReactive的getter中添加依赖,在dep的subs数组中添加属性对应的watcher对象,给childOb收集依赖,目的是发送子对象添加和删除成员时的通知。当数据发生变化时,会调用dep.notify()发送通知,dep.notify()会调用watcher对象的update()方法,update()中调用的queueWatcher()判断watcher是否被处理。如果watcher对象没有,添加到queue队列中,调用flushScheduleQueue(),flushScheduleQueue()触发beforeUpdate钩子函数调用watcher.run():run()-->get()-->getter()-->updateComponent()然后清除最后一个依赖,触发active钩子函数触发updated钩子函数。3、简述Key在虚拟DOM中的作用和好处。sameVnode方法通过tag和key比较判断两个Vnode是否是同一个节点。通过设置key,可以有效判断两个节点是否相同。functionsameVnode(a,b){return(a.key===b.key&&((a.tag===b.tag&&a.isComment===b.isComment&&isDef(a.data)===isDef(b.data)&&sameInputType(a,b))||(isTrue(a.isAsyncPlaceholder)&&a.asyncFactory===b.asyncFactory&&isUndef(b.asyncFactory.error))))}在updateChildren方法中在执行Diff算法时,设置key可以更有效地判断两个节点,减少patchVnode方法的调用,提高DOM渲染次数的性能。//补丁函数if(!isRealElement&&sameVnode(oldVnode,vnode)){//旧的和新的patchVnode(oldVnode,vnode,insertedVnodeQueue,null,null,removeOnly)}else{//当新旧Vnode都存在并且是不同的是,删除旧的一个Vnode,添加一个新的Vnode。}4。简述Vue中模板编译的过程。判断是否传入模板,如果没有传入,则获取el节点的outerHTML作为模板,从缓存中加载编译好的render函数。如果没有缓存,则调用compile进行编译,调用compile函数。合并options后,调用baseCompile方法parse将模板字符串转换成AST抽象语法树优化在AST上标记静态节点,主要用于虚拟DOM的渲染优化通过render函数将AST抽象语法树转换成js字符串生成并返回render函数的js字符串函数通过createFunction转换为可执行函数将可执行的render函数挂载到option来执行publicmount函数