VueVue虚拟DOM文档对象模型或DOM定义了一个接口,可以让JacasCript访问和操作HTML文档。元素用数字中的节点表示,界面允许我们操作它们,但是大量非常频繁的DOM操作会拖慢页面。每个元素都是一个节点,每段文字也是一个节点。节点是页面的一部分。每个节点也有相应的子节点。VirtualDOM就是为了解决操作真实DOM带来的性能问题。虚拟DOM就是用js对象模拟真实的DOM节点(虚拟节点VNode),即更新DOM的所有操作。先在JS对象上操作内存中的js对象显然要快很多。更新完成后,将最终的JS对象映射到真实的DOM中,让浏览器绘制页面。总结一下,就是获取监听变化,将生成的虚拟节点号与上次虚拟DOM节点号进行比较,找出差异,渲染到真实DOM节点,更新diff算法。由于在浏览器中操作DOM的成本非常“昂贵”,因此在Vue中只使用了引入VirtualDOM,VirtualDOM是对真实DOM的抽象描述,不了解的朋友可以自行查阅相关资料。即使使用VirtualDOM来渲染真实DOM,当页面更新时,也无法完整渲染整个VirtualDOM,而是渲染变化的部分。这时候就需要对VirtualDOM树变化的部分进行计算。算法,这个算法就是Diff算法。简单的说,就是新旧虚拟dom的对比。如有差异,以新的为准,再插入真实dom。重新渲染只会做同级比较,不会做跨级比较。比较后,如果(oldVnode===vnode),它们的引用是一致的,可以认为没有变化if(oldVnode.text!==null&&vnode.text!==null&&oldVnode.text!==vnode.text),比较文本节点,需要修改,会调用Node.textContent=vnode.text。if(oldCh&&ch&&oldCh!==ch),两个节点都有子节点,而且不相同,所以我们会调用updateChildren函数比较子节点,这就是diffelseif(ch)的核心,只有new节点有子节点,调用createEle(vnode),vnode.el已经引用了旧dom节点,createEle函数会在旧dom节点上添加子节点。elseif(oldCh),新节点没有子节点,老节点有子节点,直接删除老节点。key的作用不是设置key。newCh和oldCh只会比较头尾端。设置key后,除了比较头尾,还会从用key生成的对象oldKeyToIdx中找到匹配的节点,所以为节点设置key可以更高效的利用DOM,所以我们需要用key为每个节点做一个唯一的标识,Diff算法才能正确识别这个节点,找到正确的位置插入新的节点。key的作用主要是高效的更新虚拟DOM。另外,在vue中对同标签名的元素进行transition时,也会用到key属性。目的也是为了让vue区分他们。否则,vue只会替换其内部属性,而不会触发过渡效果。v-for中的key主函数的主要作用:key的主要作用是提高渲染性能!key属性可以避免数据混淆(如果元素中包含有临时数据的元素,不使用key会造成数据混淆)。Vue的一大特色是双向数据绑定。一旦数据发生变化,页面将呈现一个新的页面。数据呈现在页面上。为了减少添加数据时的渲染值,重复使用未改变的值,从而提高效率。v-for默认使用重用策略。当列表数据被修改时,会根据key值判断某个值是否被修改。如果修改了,会重现渲染图,否则会重复使用。最好用一个value不变的item作为key,对应item,即每条数据都有一个唯一的id,用来标识这条数据的唯一性。vue双向绑定原理MVVMM(MODEL,模型层),V(View,视图层)VM(ViewModel,连接V和M的桥梁,也可以看成是控制器)MVVM支持双向绑定,也就是说当M层数据进行修改时,VM层会监听到变化,并通知V层进行相应的修改。否则,修改V层会通知M层数据被修改,从而实现视图层和模型层的相互解耦。vue双向数据绑定是通过数据劫持结合发布订阅模式实现的,也就是说数据和视图是同步的,数据变了,视图变了,视图变了,数据变了也有变化。核心:关于Vue双向数据绑定,其核心是Object.defineProperty()方法。下面介绍Object.defineProperty()方法Object.defineProperty(obj,prop,descriptor)。这个语法中有三个参数,分别是obj(要定义的属性的对象)prop(要定义或修改的属性)descriptor(具体的改变方法)简单的说,这个方法就是用来定义一个价值。调用的时候,我们使用里面的get方法。当我们给this赋属性的时候,会用到它里面的set方法。具体例子可以参考:https://www.jianshu.com/p/e7e...响应式的原理是将一个普通的JavaScript对象作为dataoption传给Vue实例,Vue会遍历这个对象的所有属性对象,并使用Object.defineProperty将所有这些属性转换为getters/setters。Object.defineProperty是ES5特性,这也是Vue不支持IE8及更低版本浏览器的原因。 这些getters/setters对用户是不可见的,但在内部它们允许Vue跟踪依赖项并在访问和修改属性时通知更改。 每个组件实例对应一个watcher实例,watcher实例会在组件渲染过程中记录“touched”的数据属性作为依赖。然后,当依赖项的设置器触发时,观察者会收到通知,从而导致其关联的组件重新呈现。 简单理解就是通过数据劫持结合发布订阅者模型实现的。说白了,Object.defineProperty()就是用来劫持对象属性的setter和getter操作,通过发布订阅者模型来实现数据变化时的更新尝试。computedcomputed属性是vue实例中的一个配置选项:computed,比如将三门课程的成绩写在data中,我们需要计算平均值。我们可以在方法中写函数来计算。但是更好的解决方案是vue提供的计算属性通常是计算相关的函数。函数里面可以写很多逻辑,最后返回计算出来的值,也就是我们可以把计算过程写到每个计算属性里面。,让它动态计算。计算属性通常用于从其他数据中计算出一个新数据,一个优点是它可以缓存新数据。在其他依赖的数据没有变化的情况下,调用缓存的数据,极大地提高了程序的性能,并且写在了方法中。数据完全没有缓存的概念,所以每次都会重新选择。这就是不使用方法的原因。Vue生命周期Vue实例有一个完整的生命周期,也就是说,从创建、初始化数据、编译模板、挂入DOM、渲染-更新-渲染、卸载等一系列过程,我们称之为Vue的生命周期,hook是为了让你有机会在某个阶段做一些处理beforeCreate(创建前):实例初始化后,在数据观察和事件配置之前调用。此时option对象还没有创建,el和data还没有初始化,所以不能访问上面的methods、data、computed等方法和数据。created(aftercreation):实例创建后调用。这一步实例已经完成了如下配置:数据观察,属性和方法的操作,watch/event事件回调,data数据的初始化已经完成,但是el还没有。但是,挂载阶段还没有开始,$el属性目前是不可见的。这是一个常见的生命周期,因为可以在methods中调用方法,在data中更改数据,修改可以通过Vue的响应式绑定体现在页面上,在computed中获取计算的属性等。通常我们可以进行预处理实例在这里,有些喜欢在这里发送ajax请求。值得注意的是,在这个循环中没有生命方法实例化过程被拦截,所以如果必须获取一些数据才能允许访问页面,不适合在这个方法中发送请求。建议在组件路由钩子中完成beforeMountbeforeRouterEnter:挂载开始前调用,相关render函数第一次调用(虚拟DOM),实例已经完成如下配置和编译模板,从数据生成html和data中的templates,完成el和data的初始化。注意此时html还没有挂载到页面上。mounted:挂载完成,即模板中的HTML渲染到HTML页面。这时候一般可以做一些ajax操作。mounted只会执行一次beforeUpdata:它在数据更新之前被调用,它发生在虚拟DOM重新渲染和修补之前。钩子中状态的进一步变化不会触发额外的重新渲染过程更新(updated):虚拟DOM重新渲染和打补丁只会因数据变化而被调用。当被调用的时候,组件DOM已经更新了,所以依赖可以执行基于DOM的操作,那么在大多数情况下,你应该避免在这期间更新状态,因为这可能会导致更新的死循环。服务器渲染期间不会调用此挂钩。beforeDestory(销毁前):在实例销毁之前调用,实例这一步还是完全可用的。此步骤也可用于获取实例。一般在这一步会进行一些复位操作,比如清除组件中的定时器,监听的dom事件等。destroyed(afterdestruction):实例被销毁后调用,调用后,同期所有事件都会被移除,所有子实例也会被销毁。服务器端渲染期间不会调用此挂钩。Vue和React在监控数据变化方面有不同的实现原理。Vue使用getter/setter和一些函数。劫持,可以准确知道数据的变化。React默认比较引用(diff)。如果不优化,可能会导致大量不必要的VDOM重新渲染。为什么React不能准确监控数据变化?由于Vue和React在设计理念上的差异,Vue使用可变数据,而React强调数据的不可变性。两者没有区别。Vue更简单,而React更适合构建大型应用。Vue1.0中可以根据不同的数据流实现两种双向绑定:父子组件之间,props可以双向绑定;组件和DOM之间可以通过v-model进行双向绑定。Vue2.x去掉了父子组件的双向绑定(但提供了一个语法糖来帮助你通过事件自动修改),并且Vue2.x已经不鼓励组件对自己的props进行任何更改。React从来不支持双向绑定当然提倡单向数据流,这就是所谓的onChange/setState()模式。但是,由于我们一般使用Vuex、Redux等状态管理框架进行单向数据流,所以往往感觉不到HoC和mixinVue的区别。不同功能组合的方式就是通过mixin。Vue中的组件是一个封装好的函数。,不仅仅是我们在定义组件时传入的对象或函数。比如我们定义的模板是如何编译的?例如,声明的props是如何接收的?这些都是Vue在创建组件实例时隐式做的事情。因为Vue默默的为我们做了这么多事情,如果我们直接把下面组件的声明包装起来,返回一个HoC,那么包装好的组件是无法正常工作的。React结合不同功能的方式是通过HoC(高阶组件)。React一开始也是用mixins,但是后来觉得这种方式侵入性太强,会带来很多问题,所以放弃了mixinx,改用HoC。高阶组件的本质是高阶函数。React组件是纯函数,因此高阶函数对于React来说非常简单。组件通信的区别Vue中实现组件通信的方式有3种:父组件通过props向子组件传递数据或回调。虽然可以传递回调,但我们一般只传递数据。子组件通过事件向父组件发送消息。通过V2.2.0父组件中新增的provide/inject可以向子组件注入数据,可以跨越多个层级。React也有对应的三种方法:父组件可以通过props向子组件传递数据或者回调可以通过context跨级通信,其实类似于provide/inject的作用。React本身不支持自定义事件,在Vue中子组件向父组件传递消息有两种方式:事件和回调函数,但Vue更喜欢使用事件。在React中,我们都使用回调函数,这可能是它们之间最大的区别。表层的模板渲染方式不同,模板语法也不同。React通过JSX渲染模板。Vue通过扩展的HTML语法来渲染,但实际上这只是表面现象。与React相比,它不必深入依赖JSX,模板的原理也不同。这就是本质区别:React是在组件的JS代码中实现的,通过原生JS实现模板中的常用语法,比如插值、条件、循环等,都是通过JS语法实现的,更纯净而原生。Vue是通过与组件JS代码分离的单独模板中的指令来实现的。例如,条件语句需要V-IF才能实现。对于这一点,这种做法似乎有点独特,而且会把HTML搞乱。举例说明React的好处:React中的render函数支持闭包特性,所以我们导入的组件可以直接在render中调用。但是在Vue中,由于模板中使用的数据一定要挂载到这个上,所以我们导入一个组件后,需要在components中重新声明一次,这看起来很奇怪,但又不得不这样做,Vue可以计算出差异当渲染过程不同时,VDOM的速度更快。这是因为它会在渲染过程中跟踪每个组件的依赖关系,不需要重新渲染整个组件库。组件会被重新渲染,这可以通过shouldComponentUpdate生命周期方法来控制,但Vue将此视为默认优化。如果应用程序中的交互很复杂,需要处理大量的UI变化,那么使用VDOM是个不错的主意。如果更新元素不频繁,那么VDOM不一定适用,性能可能不如直接操作DOM框架。Vue的本质不同于MVVM框架。React是从MVC发展而来的前端组件框架,而Vuex和Redux的区别是从后端组件化发展而来。从表面上看,商店注入和应用程序之间存在一些差异。在Vue中,$store是直接注入到组件实例中的,因此可以更灵活的应用:使用dispatch,commit提交更新,通过mapState或者直接通过this.$store读取数据。在Redux中,我们的每个组件都需要实际使用connect来连接所需的props和dispatch。另外,Vuex更加灵活。组件可以派发动作或提交更新,而Redux只能派发,不能直接调用reducer进行修改。在实现原理上,最大的区别有两点:Redux使用的是不可变数据,而Vuex的数据是可变的。所以Redux每次都是用新状态替换旧状态,而Vuex在Redux检测到数据变化时直接修改差异,通过diff比较差异。Vuex和Vue的原理一样,这两点的区别是通过getter/setter来比较的,也是因为React和Vue的设计理念不同。React更倾向于构建稳定的大型应用程序。它非常坚硬。Vue更倾向于简单快速的解决问题。它比较灵活,不严格遵守规章制度。因此,也会给人一种大项目用React,小项目用Vue的感觉。vue-router中keep-alive的使用背景:在基于vue的单页富应用开发中,总会有这样的产品需求,即从列表页跳转到详情页,以及状态返回时需要保存列表页面在这种场景下,如果往全局状态写入数据,总是会面临复杂的页面路由判断来清除和保存页面状态。keep-alive介绍在2.x版本中,Vue将keep-alive的既定属性扩展为内置抽象组件
