1。MVC与MVVM的区别MVC MVC全名是ModelViewController,是模型(model)视图(view)控制器(controller)的缩写,一种软件设计典范Model(模型):是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据View(视图):是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的Controller(控制器):是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据 下面看斯坦福大学公开课上的这幅图来说明,这可以说是最经典和最规范的MVC标准 几乎所有的App都只干这么一件事:将数据展示给用户看,并处理用户对界面的操作。MVC的思想:一句话描述就是Controller负责将Model的数据用View显示出来,换句话说就是在Controller里面把Model的数据赋值给View。 MVVM MVVM:Model、View、ViewModel。 你会下意识地把它和MVC来对比,你会发现,MVVM多了一个ViewModel而少了Controller。 首先说一下多出来的ViewModel(VM,不是显存)。VM的意义,和Model一样,在于数据。Model负责对数据进行取和存,然而我们对数据的操作除了取和存以外,还有一个非常重要的操作:解析 M:对应于MVC的M V:对应于MVC的V VM:ViewModel,是把MVC里的controller的数据加载,加工功能分离出来区别MVVM与MVC最大的区别就是:它实现了View和Model的自动同步,也就是当Model的属性改变时,我们不用再自己手动操作Dom元素,来改变View的显示,而是改变属性后该属性对应View层显示会自动改变(对应Vue数据驱动的思想) Vue并没有完全遵循MVVM的思想 这一点Vue官网自己也有说明 这是因为从严格意义上来讲,MVVM要求View与Model是不能直接通信的,而Vue提供了refs这个属性,让Model可以直接操作View,违反了这一规定,所以说Vue没有完全遵循MVVM。2。为什么data需要是一个函数? 这个说法主要是在组件中出现,因为组件是可以复用的,js里对象是引用关系,如果组件data是一个对象,那么子组件中的data属性值会相互污染,产生副作用。如果组件中data选项是一个函数,那么每个实例可以维护一份被返回对象的独立的拷贝,组件实例之间的data属性值不会互相影响;而newVue的实例,是不会被复用的,因此不存在引用对象的问题。3。vif与vshowvif与vshow的区别 vif是真正的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是惰性的:如果在初始渲染时条件为假,则什么也不做直到条件第一次变为真时,才会开始渲染条件块。 vshow就简单得多不管初始条件是什么,元素总是会被渲染,并且只是简单地基于CSS的display属性进行切换。 所以,vif适用于在运行时很少改变条件,不需要频繁切换条件的场景;vshow则适用于需要非常频繁切换条件的场景。vshow指令算是重排吗? vshow本质是通过元素css的display属性来控制是否显示,在DOM渲染时仍然会先渲染元素,然后才会进行判断是否显示(通过display属性),而对于重排的定义是渲染树中的节点信息发生了大小、边距等改变,要重新计算各节点和css具体的大小和位置。当用display来控制元素的显示和隐藏时,会改变节点的大小和渲染树的布局,导致发生重排,因此vshow指令算是重排。4。vforvif与vfor为什么不建议一起使用? 首先,关于vif和vfor的优先级,可以在源码compilercodegenindex。js中找到genElement方法,里面的ifelse判断,可以清楚看到for的判断在if判断之上,由此,可证明vfor的优先级高于vif 如果vif和vfor同时出现,分两种情况:当同时出现在同一标签内,可以通过vue。options。render打印出渲染函数,可以清晰的看到会优先执行for循环,再执行if判断当vif出现在父级中,子级有vfor,此时再打印vue。options。render,会发现会优先执行if判断。 若想优化,提升性能,vif需要优先执行,可以在vfor外层加一层template搭配vif使用。若是vif与vfor必须出现在同一层或vif为vfor的子级的情况下,优化的方式可以将for循环的数组提前通过计算属性处理,尽量减少过多渲染导致的性能消耗。vfor中的key有什么作用?为什么在vfor中的key不推荐使用随机数或者index? key的作用:可以使vue的diff操作更加准确和快速 如果不使用key,Vue会使用一种最大限度减少动态元素并且尽可能的尝试就地修改复用相同类型元素的算法。key是为Vue中vnode的唯一标记,通过这个key,我们的diff操作可以更准确、更快速 更准确:因为带key就不是就地复用了,在sameNode函数a。keyb。key对比中可以避免就地复用的情况。所以会更加准确。 更快速:利用key的唯一性生成map对象来获取对应节点,比遍历方式更快 为什么在vfor中的key不推荐使用随机数或者index? 因为在插入数据或者删除数据的时候,会导致后面的数据的key绑定的index变化,进而导致重新渲染,效率会降低,同时也会导致渲染出错;当数据进行更改的时候,会通过key来判断虚拟dom树是否进行了更改。如果发现了相同的domkey就可以直接复用。减少了渲染的性能损耗。所以使用随机数或index作为key会导致性能浪费,并且使用index作为key可能会导致渲染出错。vfor遍历对象时,是按什么顺序遍历的?如何保证顺序? 1、会先判断是否有iterator接口,如果有循环执行next()方法 2、没有iterator的情况下,会调用Object。keys()方法,在不同浏览器中,JS引擎不能保证输出顺序一致 3、保证对象的输出顺序可以把对象放在数组中,作为数组的元素5。常见的Vue内置指令 6。Vue组件通信的几种方式propsemit这个一般用于父子组件之间的通信,父组件通过props的方式向子组件传递数据,子组件可以通过emit的方式向父组件进行通信。 !父组件templatechildItem:listlistupdateupdatechildItemtemplate!子组件templatespanvfor(item,index)inlist:keyitem。idclickupdate(index){{item}}spantemplate 总结:props只可以从上一级组件传递到下一级组件(父子组件),即所谓的单向数据流。而且是props只读的,不可被修改,所有修改都会失效并警告。emit绑定一个自定义事件,当这个语句被执行时,就会将参数传递给父组件,父组件通过von监听并接收参数。parentChildren 我们来看看官方是怎么解释这两个API的: 从上面详细中我们可以知道,通过parent和children就可以访问到对应组件的实例,既然都访问到组件实例了,那么组件内的所有内容(data、methods等)就都能够访问到了。!父组件templatechildItemchildItemtemplate!子组件templatetemplate ref如果在普通的DOM元素上使用,引用指向的就是DOM元素;如果用在子组件上,引用就指向组件实例 !子组件exportdefault{data(){return{name:nanjiu}},methods:{sayHello(){console。log(hello,Iamnanjiu)}}}!父组件templatechildrefchildchildtemplateprovideinjectprovideinject是vue2。2。0新增的api,简单来说就是父组件中通过provide来提供变量,然后再子组件中通过inject来注入变量。并且不论子组件嵌套有多深,只要调用了inject那么就可以注入provide中的数据 !根组件templatechildItemchildItemtemplate!子组件templatesubItemtemplate!孙子组件templatetemplateeventBuseventBus又称为事件总线,在vue中可以使用它来作为沟通桥梁的概念,就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件,所以组件都可以通知其他组件。使用on订阅事件,emit发布事件 index。jsVue。prototype。busnewVue()使用一个vue实例来承载中央事件订阅事件templatechildItem:listlistupdateupdatechildItemtemplate发布事件templatechildtemplateattrslisteners 如果遇到跨级组件使用props与emit来通信的话,那么就需要将数据与事件一层一层往下传递,这样做太麻烦了。所以在vue2。4中,为了解决该需求,引入了attrs和listeners,新增了inheritAttrs选项。在版本2。4以前,默认情况下,父作用域中不作为props被识别(且获取)的特性绑定(class和style除外),将会回退且作为普通的HTML特性应用在子组件的根元素上。根组件templatechildItem:listlist:msgmsgupdateupdaterootFunrootFunchildItemtemplate子组件templatesubItemvbindattrsvonlistenerstemplate孙子组件templatetemplateVuex 1。Vuex介绍 Vuex是一个专为Vue。js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。 Vuex解决了多个视图依赖于同一状态和来自不同视图的行为需要变更同一状态的问题,将开发者的精力聚焦于数据的更新而不是数据在组件之间的传递上 2。Vuex各个模块state:用于数据的存储,是store中的唯一数据源getters:如vue中的计算属性一样,基于state数据的二次包装,常用于数据的筛选和多个数据的相关性计算mutations:类似函数,改变state数据的唯一途径,且不能用于处理异步事件actions:类似于mutation,用于提交mutation来改变状态,而不直接变更状态,可以包含任意异步操作modules:类似于命名空间,用于项目中将各个模块的状态分开定义和操作,便于维护localStoragesessionStorage 使用localStoragesessionStorage也能够进行通信,缺点就是不易维护总结父子组件通信:props;parentchildren;provideinject;ref;attrslisteners兄弟组件通信:eventBus;vuex跨级通信:eventBus;Vuex;provideinject、attrslisteners7。怎么理解Vue的单向数据流? 在Vue中,所有的prop都使得其父子prop之间形成了一个单向数据流:父级prop的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。 额外的,每次父级组件发生更新时,子组件中所有的prop都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变prop。如果你这样做了,Vue会在浏览器的控制台中发出警告。子组件想修改时,只能通过emit派发一个自定义事件,父组件接收到后,由父组件修改。8。computed和watch的区别和运用的场景? 区别: computed:是计算属性,依赖其它属性值,并且computed的值有缓存,只有它依赖的属性值发生改变,下一次获取computed的值时才会重新计算computed的值;支持缓存,只有依赖数据发生改变,才会重新进行计算不支持异步,当computed内有异步操作时无效,无法监听数据的变化computed属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过或者父组件传递的props中的数据通过计算得到的值如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed如果computed属性属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法。 watch:更多的是观察的作用,类似于某些数据的监听回调,每当监听的数据变化时都会执行回调进行后续操作;不支持缓存,数据变,直接会触发相应的操作;watch支持异步;监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;当一个属性发生变化时,需要执行对应的操作;一对多;监听数据必须是data中声明过或者父组件传递过来的props中的数据,当数据变化时,触发其他操作, watch和computed各自处理的数据关系场景不同:watch擅长处理的场景:一个数据影响多个数据computed擅长处理的场景:一个数据受多个数据影响9。Vue2最低兼容到IE几? vue2兼容IE8以上版本,IE8及以下版本不支持Object。defineProperty方法,但这个是vue实现响应式的所必须的。10。说说Vue的生命周期,一般在哪个钩子发请求? beforeCreate在实例初始化之后,数据观测(dataobserver)和eventwatcher事件配置之前被调用。在当前阶段data、methods、computed以及watch上的数据和方法都不能被访问 created实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(dataobserver),属性和方法的运算,watchevent事件回调。这里没有如果非要想与进行交互,可以通过nextTick来访问Dom beforeMount在挂载开始之前被调用:相关的render函数首次被调用。 mounted在挂载完成后发生,在当前阶段,真实的Dom挂载完毕,数据完成双向绑定,可以访问到Dom节点 beforeUpdate数据更新时调用,发生在虚拟DOM重新渲染和打补丁(patch)之前。可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程 updated发生在更新完成之后,当前阶段组件Dom已完成更新。要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新,该钩子在服务器端渲染期间不被调用。 beforeDestroy实例销毁之前调用。在这一步,实例仍然完全可用。我们可以在这时进行善后收尾工作,比如清除计时器。 destroyedVue实例销毁后调用。调用后,Vue实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。 activatedkeepalive专属,组件被激活时调用 deactivatedkeepalive专属,组件被销毁时调用 异步请求在哪一步发起? 可以在钩子函数created、beforeMount、mounted中进行异步请求,因为在这三个钩子函数中data已经创建,可以将服务端返回的数据进行赋值。 如果异步请求不需要依赖Dom推荐在created钩子函数中调用异步请求,因为在created钩子函数中调用异步请求有以下优点:能更快获取到服务端数据,减少页面loading时间;ssr不支持beforeMount、mounted钩子函数,所以放在created中有助于一致性;11。说说Vue2的数据响应式原理 推荐阅读 【Vue源码学习】响应式原理探秘 【Vue源码学习】依赖收集Vue2与Vue3的数据响应式原理有什么区别Vue2中的变化侦测实现对Object及Array分别进行了不同的处理,Objcet使用了Object。definePropertyAPI,Array使用了拦截器对Array原型上的能够改变数据的方法进行拦截。虽然也实现了数据的变化侦测,但存在很多局限,比如对象新增属性无法被侦测,以及通过数组下边修改数组内容,也因此在Vue2中经常会使用到set这个方法对数据修改,以保证依赖更新。Vue3中使用了es6的ProxyAPI对数据代理,没有像Vue2中对原数据进行修改,只是加了代理包装,因此首先性能上会有所改善。其次解决了Vue2中变化侦测的局限性,可以不使用set新增的对象属性及通过下标修改数组都能被侦测到。12。Vue的父组件和子组件生命周期钩子函数执行顺序?加载渲染过程父beforeCreate父created父beforeMount子beforeCreate子created子beforeMount子mounted父mounted子组件更新过程父beforeUpdate子beforeUpdate子updated父updated父组件更新过程父beforeUpdate父updated销毁过程父beforeDestroy子beforeDestroy子destroyed父destroyed13。Vue事件绑定原理原生事件绑定是通过addEventListener绑定给真实元素的,组件事件绑定是通过Vue自定义的on实现的。如果要在组件上使用原生事件,需要加。native修饰符,这样就相当于在父组件中把子组件当做普通html标签,然后加上原生事件。 1。原生dom事件的绑定,采用的是addEventListener实现Vue在创建真是dom时会调用createElm,默认会调用invokeCreateHooks会遍历当前平台下相对的属性处理代码,其中就有updateDOMListeners方法,内部会传入add方法functionupdateDOMListeners(oldVnode:VNodeWithData,vnode:VNodeWithData){if(isUndef(oldVnode。data。on)isUndef(vnode。data。on)){return}constonvnode。data。on{}constoldOnoldVnode。data。on{}targetvnode。elmnormalizeEvents(on)updateListeners(on,oldOn,add,remove,createOnceHandler,vnode。context)targetundefined}functionadd(name:string,handler:Function,capture:boolean,passive:boolean){target。addEventListener(给当前的dom添加事件name,handler,supportsPassive?{capture,passive}:capture)} 2。组件绑定事件采用的是on方法exportfunctionupdateComponentListeners(vm:Component,listeners:Object,oldListeners:?Object){targetvmupdateListeners(listeners,oldListeners{},add,remove,createOnceHandler,vm)targetundefined}functionadd(event,fn){target。on(event,fn)}14。直接给一个数组项赋值,Vue能检测到变化吗? 看过源码应该都知道这是不能的,因为Vue2对数组的劫持本质上是劫持数组原型上的那七个方法,所以只有通过调用这七个方法中的其中一个Vue才能够监测到变化。 但我们可以通过调用set或set来来触发视图更新mounted(){this。set(this。list,this。list〔0〕,{id:1,title:南玖});}, vm。set的实现原理是:如果目标是数组,直接使用数组的splice方法触发相应式;如果目标是对象,会先判读属性是否存在、对象是否是响应式,最终如果要对属性进行响应式处理,则是通过调用defineReactive方法进行响应式处理(defineReactive方法就是Vue在初始化对象时,给对象属性采用Object。defineProperty动态添加getter和setter的功能所调用的方法)15。vue中data的属性可以和methods中的方法同名吗?为什么? 这个通过看源码也能知道这个不行的,vue在初始化过程中会先进行初始化props(initProps),然后会初始化methods(initMethods),这里会判断是否与props中有重名的key,有的话会告警,然后再初始化data(initData),这里又会判断是否有与props和methods重名的属性,有的话会告警。16。父组件可以监听到子组件的生命周期吗? 答案是可以的。 第一种可以使用emit来实现Parent。vueChildmounteddoSomethingChild。vuemounted(){this。emit(mounted);} 第二种可以使用hook来实现Parent。vuetemplatechildItemhook:mountedchildMountedchildItemtemplateChild。vuemounted(){console。log(子组件mounted);} 打印顺序应该是:子组件mounted父组件监听到子组件mounted父组件mounted17。虚拟DOM虚拟DOM是什么? 虚拟DOM是将状态映射成试图的众多解决方案之一。页面交互的本质还是通过改变状态(变量)来改变试图渲染,而框架(像主流框架vue、react、angular)的应用可以让我们把关注的焦点更多的放在状态上,省略对DOM的操作(框架内部已经帮我们完成了)。而虚拟DOM映射视图的方式是通过状态生成一个虚拟节点树,然后通过虚拟节点树进行渲染。虚拟DOM本质上是一个普通的js对象,它包含了创建一个DOM元素所需要的属性。虚拟DOM的优缺点 优点:保证性能下限:框架的虚拟DOM需要适配任何上层API可能产生的操作,它的一些DOM操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的DOM操作性能要好很多,因此框架的虚拟DOM至少可以保证在你不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限;无需手动操作DOM:我们不再需要手动去操作DOM,只需要写好ViewModel的代码逻辑,框架会根据虚拟DOM和数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率;具备跨平台的优势:由于虚拟DOM是以JavaScript对象为基础而不依赖真实平台环境,所以使它具有了跨平台的能力,比如说浏览器平台、Weex、Node等。 缺点:首次渲染大量DOM时,由于多了一层虚拟DOM的计算,会比innerHTML插入慢。18。Vuerouter的路由钩子函数是什么,以及执行顺序是怎样的?vuerouter提供的导航守卫主要用来通过跳转或取消的方式守卫导航,Vue的路由钩子分为:全局守卫、路由守卫、组件守卫 执行顺序导航被触发。在失活的组件里调用beforeRouteLeave守卫。调用全局的beforeEach守卫。在重用的组件里调用beforeRouteUpdate守卫(2。2)。在路由配置里调用beforeEnter。解析异步路由组件。在被激活的组件里调用beforeRouteEnter。调用全局的beforeResolve守卫(2。5)。导航被确认。调用全局的afterEach钩子。触发DOM更新。调用beforeRouteEnter守卫中传给next的回调函数,创建好的组件实例会作为回调函数的参数传入。19。说说你对slot的理解Slot名为插槽,我们可以理解为solt在组件模板中占好了位置,当使用该组件标签时候,组件标签里面的内容就会自动填坑(替换组件模板中slot位置),作为承载分发内容的出口 slot可以分为三类 默认插槽 子组件用标签来确定渲染的位置,标签里面可以放DOM结构,当父组件使用的时候没有往插槽传入内容,标签内DOM结构就会显示在页面 父组件在使用的时候,直接在子组件的标签内写入内容即可!父组件Child默认插槽Child!子组件templateslotp插槽后备的内容slottemplate 具名插槽 子组件用name属性来表示插槽的名字,不传为默认插槽 父组件中在使用时在默认插槽的基础上加上slot属性,值为子组件插槽name属性值!父组件childtemplatevslot:default具名插槽template!具名插槽插槽名做参数templatevslot:content内容。。。templatechild!子组件templateslot插槽后备的内容slotslotnamenanjiu插槽后备的内容slottemplate 作用域插槽 子组件在作用域上绑定属性来将子组件的信息传给父组件使用,这些属性会被挂在父组件vslot接受的对象上 父组件中在使用时通过vslot:(简写:)获取子组件的信息,在内容中使用!父组件child!把vslot的值指定为作域上下对象templatevslot:defaultslotProps来组件数据:{{slotProps。testProps}}templatetemplatedefaultslotProps来组件数据:{{slotProps。testProps}}templatechild!子组件templateslotnamefootertestProps子组件的值h3没传footer插槽h3slottemplate 小结:vslot属性只能在上使用,但在只有默认插槽时可以在组件标签上使用默认插槽名为default,可以省略default直接写vslot缩写为时不能不写参数,写成default使用作用域插槽时可以通过解构获取vslot{msg},还可以重命名vslot{msg:newMsg}和定义默认值vslot{msg默认值} 插槽的原理: slot本质上是返回VNode的函数,一般情况下,Vue中的组件要渲染到页面上需要经过templaterenderfunctionVNodeDOM过程。组件挂载的本质就是执行渲染函数得到VNode,至于datapropscomputed这些属性都是给VNode提供数据来源。 在2。5之前,如果是普通插槽就直接是VNode的形式了,而如果是作用域插槽,由于子组件需要在父组件访问子组件的数据,所以父组件下是一个未执行的函数(slotScope)returnh(p,slotScope。msg),接受子组件的slotProps参数,在子组件渲染实例时会调用该函数传入数据。 在2。6之后,两者合并,普通插槽也变成一个函数,只是不接受参数了。20。说说Vue的nextTick的原理 Vue在更新DOM时是异步执行的。只要侦听到数据变化,Vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个watcher被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和DOM操作是非常重要的。然后,在下一个的事件循环tick中,Vue刷新队列并执行实际(已去重的)工作。Vue在内部对异步队列尝试使用原生的Promise。then、MutationObserver和setImmediate,如果执行环境不支持,则会采用setTimeout(fn,0)代替。(后面会写一篇关于nextTick的源码的文章)21。说说你对函数式组件的理解 1。函数式组件需要在声明组件是指定functional:true 2。不需要实例化,所以没有this,this通过render函数的第二个参数context来代替 3。没有生命周期钩子函数,不能使用计算属性,watch 4。不能通过emit对外暴露事件,调用事件只能通过context。listeners。click的方式调用外部传入的事件 5。因为函数式组件是没有实例化的,所以在外部通过ref去引用组件时,实际引用的是HTMLElement6。函数式组件的props可以不用显示声明,所以没有在props里面声明的属性都会被自动隐式解析为prop,而普通组件所有未声明的属性都解析到attrs里面,并自动挂载到组件根元素上面(可以通过inheritAttrs属性禁止) 优点1。由于函数式组件不需要实例化,无状态,没有生命周期,所以渲染性能要好于普通组件2。函数式组件结构比较简单,代码结构更清晰22。常见的Vue性能优化有哪些?响应式数据对象层级不要过深,非响应式数据不要放在data里面或者使用Object。freeze()冻结数据合理使用vif和vshow,vif适用于切换不频繁的场景,vshow适用于切换频繁的场景computed和watch区分使用场景,能使用computed实现的就不用watch(computed具有缓存效果)vfor遍历必须加key,key最好是id值,并且避免同时使用vif大数据列表和表格性能优化虚拟列表虚拟表格防止内部泄漏,组件销毁后还应该把全局变量和事件销毁图片懒加载路由懒加载第三方插件的按需引入适当采用keepalive缓存组件防抖、节流运用服务端渲染SSR或者预渲染23。Vue。mixin的使用场景及原理mixin(混入),提供了一种非常灵活的方式,来分发Vue组件中的可复用功能。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。混入也可以进行全局注册。使用时格外小心!一旦使用全局混入,它将影响每一个之后创建的Vue实例。 mixin。jsexportdefault{data(){return{title:我是mixin中的title}},}templatetemplate 先打印出我是mixin中的created,然后接着打印合并后的data(包含根组件的data与mixin中的data),再打印出我是根组件中的created 使用场景: 在日常的开发中,我们经常会遇到在不同的组件中经常会需要用到一些相同或者相似的代码,这些代码的功能相对独立这时,可以通过Vue的mixin功能将相同或者相似的代码提出来。 原理: 主要原理就在于这个merOptions方法exportfunctionmergeOptions(parent:Object,child:Object,vm?:Component):Object{if(child。mixins){判断有没有mixin有的话递归进行合并for(leti0,lchild。mixins。length;il;i){parentmergeOptions(parent,child。mixins〔i〕,vm)}}constoptions{}letkeyfor(keyinparent){mergeField(key)先遍历parent的key}for(keyinchild){if(!hasOwn(parent,key)){如果parent已经处理过某个key就不处理了mergeField(key)处理child中的key也就parent中没有处理过的key}}functionmergeField(key){conststratstrats〔key〕defaultStratoptions〔key〕strat(parent〔key〕,child〔key〕,vm,key)根据不同类型的options调用strats中不同的方法进行合并}returnoptions} 优先递归处理mixins,先遍历合并parent中的key,调用mergeField方法进行合并,然后保存在变量options,再遍历child,合并补上parent中没有的key,调用mergeField方法进行合并,保存在变量options。24。Vue。extend的作用与原理使用基础Vue构造器,创建一个子类。参数是一个包含组件选项的对象。 data选项是特例,需要注意在Vue。extend()中它必须是函数,该方法返回一个与Vue具有相同功能的构造函数(其实为创建了一个组件)属性options是合并基础Vue构造器与extend的参数的对象, 原理: Vue。extend核心思路就是新建一个VueComponent构造函数命名为Sub,通过将VueComponent的原型指向Vue构造函数的原型的方式继承Vue构造函数原型上所有的属性和方法,接着检查传入的extendOptions是否拥有props和computed属性,如果有就进行初始化。如果检测出传入的extendOptions中含有name属性,则将其自动放入VueComponent的全局组件中。然后将Vue。options保存到VueComponent。superOptions属性中,将传入的extendOptions保存到VueComponent。extendOptions属性中,并将传入的extendOptions封存一份保存到VueComponent。sealedOptions中。最后将VueComponent返回,这就是继承了Vue构造函数的Vue组件的构造函数。(后面会写文章细讲) 与mixin的区别:mixin是对Vue类的options进行混入。所有Vue的实例对象都会具备混入进来的配置行为。extend是产生一个继承自Vue类的子类,只会影响这个子类的实例对象,不会对Vue类本身以及Vue类的实例对象产生影响。25。既然vue通过数据劫持可以精准的探测数据变化,为什么还要进行diff检测差异? 现在前端框架有两种数据变动侦测方式,一种是pull,一种是push。pull的代表是React,在进行setState操作后显示更新数据,React会使用diff算法一层层找出差异,然后patch到DOM树上,React一开始不知道那里变化了,只是知道变化了,然后暴力进行查找那变化了,另一个代表是Angular的脏检查。Vue的响应式系统就是Push的代表,Vue初始化的时候就会对data的数据进行依赖收集,因此Vue能实时知道那里发生了变化,一般绑定的细粒度过高,会生成大量的Watcher实例,则会造成过大的内存和依赖追踪的开销,而细粒度过低无法侦测到变化。因此,Vue采用的是中等细粒度的方案,只针对组件级别的进行响应式监听也就是push,这样可以知道那个组件发生了变化,再对组件进行diff算法找到具体变化的位置,这是pull操作,vue是pullpush结合进行变化侦测的。