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

vue原理初探

时间:2023-03-31 15:43:58 vue.js

目录结构├──benchmarksperformance,benchmarktest├──distbuildandpackageoutputdirectory├──examplescase目录├──flowtype流语法声明├──packagessome额外的包,例如:负责服务器端渲染的包vue-server-renderer,与vue-loader一起使用的vue-template-compiler,以及weex相关的包│├──vue-server-renderer│├──vue-template-compiler│├──weex-template-compiler│└──weex-vue-framework├──存放所有配置文件的脚本,比如rollup配置文件├──srcvue源码目录│├──compiler编译器│├──coreruntime核心包││├──components全局组件,比如keep-alive││├──config.js一些默认的配置项││├──global-api全局API,比如熟悉的:Vue。use()、Vue.component()等││├──instanceVue实例相关,比如本目录下的Vue构造函数││├──观察者响应原理││├──util工具方法││└──vdomVirtualDOM相关,比如大家熟悉的补丁算法就在这里│├──platforms平台相关的编译代码││├──web││└──weex│├──server服务器渲染相关├──test测试目录├──typesTStypedeclarationVue初始化过程(newVue(options))做了什么?1、处理组件配置项时,在初始化根组件时进行了选项合并操作,将全局配置合并到根组件的本地配置中。在初始化各个子组件时做了一些性能优化,将组件配置对象上的一些深层次属性放入vm.$options选项中,提高代码执行效率2.初始化组件实例的关系属性,比如$parent,$children,$root,$refs等3.处理自定义事件4.调用beforeCreate钩子函数5.初始化组件的inject配置项获取ret[key]=val形式的配置对象,以及然后对配置对象进行浅响应处理(只处理对象的一级数据),将每个key代理给vm实例6.数据响应,处理props、methods、data、computed、watch等选项7.解析组件配置项上的provide对象,挂载到vm._provided属性8.调用创建的钩子函数9.如果找到配置项中有el选项,$mount方法会被自动调用。也就是说,有了el选项,就不需要手动调用$mount方法了。相反,如果没有提供el选项,则必须调用$mount10。然后进入挂载阶段总结:Vue初始化主要做了几件事情,合并配置,初始化生命周期,初始化事件中心,初始化渲染,初始化数据,props,computed,watcher等。Vue实例挂载的实现在Vue中,我们使用$mount实例方法来挂载vm。$mount方法实际上会调用mountComponent方法。mountComponent的核心是先实例化一个渲染Watcher,在其回调函数中会调用updateComponent方法。该方法中调用vm._render方法生成虚拟Node,最后调用vm._update更新DOMWatcher。它有两个功能。一种是在初始化时执行回调函数,另一种是当vm实例中的监控数据发生时。更改时执行回调函数,最后在判断为根节点时设置vm._isMounted为true,表示实例已经挂载,同时执行挂载的钩子函数。renderVue的_render方法是实例的私有方法,用于将实例Rendering转为虚拟Node,多写成template模板。在mounted方法的实现中,模板会被编译到render方法中。VirtualDOM真实的DOM元素非常大,因为浏览器标准设计的DOM非常复杂。当我们做频繁的DOM更新时,会出现一定的性能问题。VirtualDOM使用一个原生的JS对象来描述一个DOM节点,因此它比创建一个DOM便宜很多。在Vue.js中,VirtualDOM是用VNode这样的类来描述的,VNode是对真实DOM的抽象描述。它的核心定义无非就是几个关键的属性,比如标签名、数据、子节点、键值等,其他属性用来扩展VNode的灵活性和一些特殊功能的实现。由于VNode只是用来映射到真实的DOM进行渲染,不需要包含操作DOM的方法,所以是一个非常轻量简单的VirtualDOM。除了定义其数据结构外,映射到真实DOM其实还需要经过VNodecreate、diff、patch等过程。Vue.js使用createElement方法创建VNode。createElement创建一个VNode进程。每个VNode都有children,children的每一个元素也是一个VNode,这样就形成了一个VNodeTree,这就很好的描述了我们。DOM树。updateVue的_update是实例的私有方法。它的调用有两个时机,一个是第一次渲染,一个是更新数据时从初始化Vue到最终渲染的整个过程(图片)的生命周期:beforeCreate&createdbeforeCreate和created函数都是在实例化Vue的阶段。它在_init方法中执行。这两个钩子函数在执行的时候,并没有渲染DOM,所以我们无法访问到DOM。一般来说,如果组件加载的时候需要和后台进行交互,就可以执行这两个钩子函数。如果需要访问props、data等数据,需要使用创建的钩子函数beforeMount&mountedbeforeMount。hook函数发生在mount上,即DOMmount之前,在执行vm._render()函数渲染VNode之前在mountComponent函数中调用,执行beforeMounthook函数,执行vm._update()之后将VNodepatch到真实的DOM,执行mountedhookbeforeUpdate&updatedbeforeUpdate和updatedhook函数的执行时机应该是数据更新的时候。beforeUpdate的执行时机在渲染Watcher的before函数中。请注意,beforeUpdate挂钩函数将在组件挂载后调用。beforeDestroy&destroyedbeforeDestroy和destroyhooks函数的执行时机在组件销毁阶段,最后会调用$destroy方法。$destroy在执行过程中,会执行vm.__patch__(vm._vnode,null)来触发其子组件的销毁钩子函数,这样一个层层的递归调用,所以销毁钩子函数的执行顺序是先child后parent,同挂载进程activated&deactivatedactivated和deactivated钩子函数是专门为keep-alive组件定制的钩子keep-alive参考:https://www.jianshu.com/p/952...response公式初始化的过程将原始数据映射到DOM,数据的变化会触发DOM的变化1.数据渲染到页面2.处理用户交互的原生js方法监听点击事件,修改数据,手动操作DOM重新渲染vue方法使用了Object.defineProperty的原理。详见:https://developer.mozilla.org...Object.defineProperty(obj,prop,descriptor)对于描述符中的get和set,get是为properties提供的getter方法,当我们访问property时,getter方法将被触发;set是为属性提供的setter方法,当我们修改属性时会触发setter方法。一旦对象有了getter和setter,我们就可以简单的把这个对象称为响应式对象代理。proxy的作用是将props和data上的属性代理给vm实例props已定义,可以通过vm实例访问.msg访问props中定义的msg。这个过程发生在代理阶段。实现原理如下:constsharedPropertyDefinition={enumerable:true,configurable:true,get:noop,set:noop}exportfunctionproxy(target:Object,sourceKey:string,key:string){sharedPropertyDefinition.get=functionproxyGetter(){returnthis[sourceKey][key]}sharedPropertyDefinition.set=functionproxySetter(val){this[sourceKey][key]=val}Object.defineProperty(target,key,sharedPropertyDefinition)}observe观察方法的功能就是给非VNode对象类型的数据添加一个Observer。如果已经添加,则直接返回。否则,如果满足某些条件,将实例化一个Observer对象实例。ObserverObserver是一个类,它的作用是为对象的属性添加getter和setter,用于依赖收集和调度更新/***附加到每个被观察*对象的Observer类。一旦附加,观察者将目标*对象的属性键转换为getter/setter*收集依赖项和di补丁更新。*/exportclassObserver{值:任意;部门:部门;vmCount:数量;//以该对象为根的虚拟机数量$dataconstructor(value:any){this.value=valuethis.dep=newDep()this.vmCount=0def(value,'__ob__',this)//对value进行判断,数组调用observeArray方法,否则调用纯对象的walk方法if(Array.isArray(value)){constaugment=hasProto?protoAugment:copyAugmentaugment(value,arrayMethods,arrayKeys)这个。observeArray(值)}else{这个。walk(value)}}/***遍历每个属性并将它们转换为*getter/setter。仅当*值类型为Object时才应调用此方法。*/walk(obj:Object){constkeys=Object.keys(obj)for(leti=0;i){for(leti=0,l=items.length;i