框架和类库——vue

  • 1.熟练使用VueAPI、生命周期、钩子函数
  • 2.MVVM框架设计理念
  • 3.Vue双向绑定实现原理、Diff算法的内部实现
  • 4.Vue的事件机制
  • 5.从template转换成真实DOM的实现机制

1.熟练使用VueAPI、生命周期、钩子函数

每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。
比如 created 钩子可以用来在一个实例被创建之后执行代码:

new Vue({
  data: {
    a: 1
  },
  created: function () {
    // `this` 指向 vm 实例
    console.log('a is: ' + this.a)
  }
})
// => "a is: 1"

也有一些其它的钩子,在实例生命周期的不同阶段被调用,如 mountedupdateddestroyed。生命周期钩子的 this 上下文指向调用它的 Vue 实例。

生命周期推送图示

2.MVVM框架设计理念

MVC框架:


MVC框架
  • M-Model:业务逻辑和实体模型(biz/bean)
  • V-View:布局文件(XML)
  • C-Controller:控制器(Activity)

view传送指令到Controller,Controller完成业务逻辑后,要求Model改变状态,Model将新的数据发送到View,用户得到反馈。
相信大家都熟悉这个框架,这个也是初学者最常用的框架,该框架虽然也是把代码逻辑和UI层分离,但是View层能做的事情还是很少的,很多对于页面的呈现还是交由C实现,这样会导致项目中C的代码臃肿,如果项目小,代码臃肿点还是能接受的,但是随着项目的不断迭代,代码量的增加,你就会没办法忍受该框架开发的项目,这时MVP框架就应运而生。

MVP框架:


MVP框架
  • M-Model:业务逻辑和实体模型(biz/bean)
  • V-View:布局文件(XML)和Activity
  • P-Presenter:完成View和Model的交互
    MVP框架相对于MVC框架做了较大的改变,将Activity当做View使用,代替MVC框架中的C的是P,对比MVC和MVP的模型图可以发现变化最大的是View层和Model层不在直接通信,所有交互的工作都交由Presenter层来解决。既然两者都通过Presenter来通信,为了复用和可拓展性,MVP框架基于接口设计的理念大家自然就可以理解其用意。
    但MVP框架也有不足之处:
  1. 接口过多,一定程度影响了编码效率。
  2. 业务逻辑抽象到Presenter中,较为复杂的界面Activity代码量依然会很多。
  3. 导致Presenter的代码量过大。
    MVVM框架:


    MVVM框架
  • M-Model:实体模型(biz/bean)
  • V-View:布局文件(XML)
  • VM-ViewModel:DataBinding所在之处,对外暴露出公共属性,View和Model的绑定器
    对比MVP和MVVM模型图可以看出,他们之间区别主要体现在以下两点:
  1. 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多View重用这段视图逻辑。 在Android中,布局里可以进行一个视图逻辑,并且Model发生变化,View也随着发生变化。
  2. 低耦合。以前Activity、Fragment中需要把数据填充到View,还要进行一些视图逻辑。现在这些都可在布局中完成(具体代码请看后面) 甚至都不需要再Activity、Fragment去findViewById()。这时候Activity、Fragment只需要做好的逻辑处理就可以了。
图片来源于[https://zhuanlan.zhihu.com/p/59293752]

3.Vue双向绑定实现原理、Diff算法的内部实现

vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的

我们已经知道实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令(如v-model,v-on)对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。因此接下去我们执行以下3个步骤,实现数据的双向绑定:

  1. 实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
  2. 实现一个订阅者Watcher,每一个Watcher都绑定一个更新函数,watcher可以收到属性的变化通知并执行相应的函数,从而更新视图。
  3. 实现一个解析器Compile,可以扫描和解析每个节点的相关指令(v-model,v-on等指令),如果节点存在v-model,v-on等指令,则解析器Compile初始化这类节点的模板数据,使之可以显示在视图上,然后初始化相应的订阅者(Watcher)。

由于在浏览器中操作DOM的代价是非常“昂贵”的,所以才在Vue引入了Virtual DOM,Virtual DOM是对真实DOM的一种抽象描述。即使使用了Virtual DOM来进行真实DOM的渲染,在页面更新的时候,也不能全量地将整颗Virtual DOM进行渲染,而是去渲染改变的部分,这时候就需要一个计算Virtual DOM树改变部分的算法了,这个算法就是Diff算法。只比较同级的节点,若找不到与新节点类型相同的节点,则插入一个新节点,若有相同类型的节点则进行节点属性的更新,最后删除新节点列表中不包含的旧节点Vue中的Diff算法采用了React相似的思路,都是同层节点进行比较,在比较的过程中,使用了一些优先判断和就地复用策略,提高了Diff算法的效率

diff算法的实现过程

  1. patch(container,vnode) :初次渲染的时候,将VDOM渲染成真正的DOM然后插入到容器里面。
  2. patch(vnode,newVnode):再次渲染的时候,将新的vnode和旧的vnode相对比,然后之间差异应用到所构建的真正的DOM树上。
    patch(container,vnode)通过这个函数可以让VNode渲染成真正的DOM
function createElement(vnode) {    
var tag = vnode.tag  
var attrs = vnode.attrs || {}    
var children = vnode.children || []    
if (!tag) {       
 return null  
  }    
// 创建真实的 DOM 元素    
var elem = document.createElement(tag)   
 // 属性    
var attrName    
for (attrName in attrs) {    
    if (attrs.hasOwnProperty(attrName)) { 
           // 给 elem 添加属性
           elem.setAttribute(attrName, attrs[attrName])
        }
    }
    // 子元素
    children.forEach(function (childVnode) {
        // 给 elem 添加子元素,如果还有子节点,则递归的生成子节点。
        elem.appendChild(createElement(childVnode))  // 递归
    })    // 返回真实的 DOM 元素   
 return elem
}

patch(vnode,newVnode) 这里只考虑vnode与newVnode如何对比的情况

function updateChildren(vnode, newVnode) {
    var children = vnode.children || []
    var newChildren = newVnode.children || []
  // 遍历现有的children
    children.forEach(function (childVnode, index) {
        var newChildVnode = newChildren[index]
  // 两者tag一样
        if (childVnode.tag === newChildVnode.tag) {
            // 深层次对比,递归
            updateChildren(childVnode, newChildVnode)
        } else { 
  // 两者tag不一样
           replaceNode(childVnode, newChildVnode) 
       }
    }
)}

4.Vue的事件机制

vue.js为我们提供了四个事件API,分别是once,emit

  • on方法用来在vm实例上监听一个自定义事件,该事件可用emit触发
  • $once监听一个只能触发一次的事件,在触发以后会自动移除该事件
  • $off用来移除自定义事件
  • $emit用来触发指定的自定义事件

5.从template模板转换成真实DOM的实现机制

模版转换成视图的整个过程

  1. Vue.js通过编译将模版转换成渲染函数(render),执行渲染函数就可以得到一个虚拟DOM
  2. 在对模型进行操作的时候,会触发对应的Dep中的Watcher对象。Watcher对象会调用对应的update来修改视图。这个过程主要是将新旧虚拟DOM进行差异对比,然后根据结果进行对比。
    简单点讲,在Vue的实现上,Vue讲模版编译成虚拟DOM渲染函数。结合Vue自带的响应系统,在状态改变时,Vue能够智能地计算出重新渲染组件的最小代价并应用到DOM操作上。
模版转换成视图的整个过程
  • 渲染函数:渲染函数是用来生成虚拟DOM的。Vue推荐使用模版来构建我们的应用界面,在实现中Vue布局模版编译成渲染函数,当然我们也可以不写模版,直接写渲染函数,这样子更接近编译后的模版。
  • vnode虚拟节点:它可以代表一个真实的DOM节点通过createElement方法能将vnode渲染成DOM节点,简单地说,虚拟节点可以理解成节点描述对象,它描述了应该怎样去创建真实的DOM节点。
  • patch(也称为patching算法):虚拟DOM最核心的部分,它可以将vnode渲染成真实的DOM,这个过程是对比新旧虚拟节点之间有哪些不同,然后根据对比结果找出需要更新的的节点进行更新。这点我们从单词含义就可以看出, patch本身就有补丁、修补的意思,其实际作用是在现有DOM上进行修改来实现更新视图的目的。Vue的Virtual DOM Patching算法是基于Snabbdom的实现,并在些基础上作了很多的调整和改进。

总结:Vue.js通过编译将模版转换成渲染函数(render),执行渲染函数就可以得到一个虚拟节点树(虚拟DOM),虚拟节点树(虚拟DOM)提供虚拟节点vnode和对新旧两个vnode进行比对并根据比对结果进行DOM操作来更新视图,达到减少对DOM的目的,从而减少浏览器的开销,提高渲染速度,改善用户体验。

参考链接:https://blog.csdn.net/chun_long/java/article/details/52086565
https://blog.csdn.net/gts_0901y/article/details/86028766
https://www.jianshu.com/p/4dbb3712ced7

你可能感兴趣的:(框架和类库——vue)