虚拟DOM与render函数

目录

一、虚拟DOM

1、虚拟DOM是什么

2、为什么要使用虚拟DOM

(1)浏览器显示网页的五步过程:

(2)虚拟DOM的优点

3、Diff算法

二、VNode简介

1、VNode是什么

2、VNode的作用

 3、VNode的优点

4、VNode如何生成:在Vue源码中,VNode是通过一个构造函数生成的。

 VNode的生成过程如下:

 三、render函数

1、初识render函数:

 2、为什么使用render函数

3、render函数的解析

四、实例属性

1、组件树的访问

2、虚拟DOM的访问:

五、实例方法

1、实例DOM方法的使用

2、实例event方法的使用

3、$watch()的使用


一、虚拟DOM

1、虚拟DOM是什么

​ (1) Vue通过建立一个虚拟DOM树对真实DOM发生的变化保持追踪。

​ (2)一棵真实DOM树的渲染需要先解析CSS样式和DOM树,然后将其整合成一棵渲染树,再通过布局算法去计算每个节点在浏览器中的位置,最终输出到显示器上。而虚拟DOM则可以理解为保存了一棵DOM树被渲染前所包含的所有信息,这些信息可以 通过对象的形式一直保存在内存中,并通过javascript的操作进行维护

2、为什么要使用虚拟DOM

(1)浏览器显示网页的五步过程:

​         a、解析标签,生成元素树(DOM树)

        ​ b、解析样式,生成样式树

​         c、生成元素与样式的关系

        ​ d、生成元素的显示坐标

        ​ e、显示页面

若修改真实的DOM元素,那么上述5步将重新走一遍,修改几次就走几遍,性能很差。如果使用虚拟DOM,虚拟DOM存储在内存中,对每个元素的修改是在内存中进行的,修改完后,比较虚拟DOM和真实DOM的差异,当有差异时,再一次过去更新网页的显示,而不是每次都重新按照浏览器的运行过程走。

(2)虚拟DOM的优点

a、保证性能的下限:框架的虚拟DOM需要适应任何上层API可能产生的操作,它的一些DOM操作的实现必须是普适的,所以它的性能并不是最优的,但是比起粗暴的DOM操作性能要好很多,因此框架的虚拟DOM至少可以保证在不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限

b、无须手动操作DOM:只需要编写好View-Model的代码逻辑,框架会根据虚拟DOM和数据双向绑定,以可以预期的方式更新视图,极大的提高开发效率

c、跨平台:虚拟DOM本质是javascript对象,而DOM与平台强相关。相比之下虚拟DOM可以进行更方便地跨平台操作

3、Diff算法

当虚拟DOM发生改变时,为了以最小的性能开销完成更新操作,需要比较新旧虚拟DOM,用于比较的算法称为Diff算法

​ Diff算法本身非常复杂,实现难度很大。常用的核心函数有两个

(1)patch(container,vnode):将虚拟DOM渲染成真正的DOM。即在初次渲染的时候,将虚拟的DOM渲染成真正的DOM,然后插入到容器里面

function createElement(vnode){
    var tag = vnode.tag
    var attrs = vnode.attrs || {}
    var children = vnode.children || []
    if(!tag){
       return null
    }
    //创建真实的DOM元素
    var vm = document.createElement(tag)
    //定义属性
    var attrName
    for(attrName in attrs){
       if(attrs.hasOwnProperty(attrName)){
           vm.setAttribute(attrName,attrs[attrName])
       }
    }
    //定义子元素
    children.forEach(function(childVnode){
       //给vm添加子元素,如果还有子节点,则递归的生成子节点
       vm.appendChild(createElement(childVnode))
    })
    //返回真实的DOM元素
    return  vm
}

(2)patch(vnode,newVnode);再次渲染的时候,将新的VNode和旧的VNode进行对比,然后将之间的差异应用到所构建的真实DOM树上。

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

二、VNode简介

1、VNode是什么

 VNode是JavaScript对象。VNode表示Virtual DOM,用JavaScript对象来描述真实的DOM把DOM标签,属性,内容都变成对象的属性。就像使用JavaScript对象对一种动物进行说明一样{name: ‘Hello Kitty’, age: 1, children: null}。

2、VNode的作用

通过rendertemplate模版描述成VNode,然后进行一系列操作之后形成真实的DOM进行挂载。

 3、VNode的优点

(1)兼容性强,不受执行环境的影响。VNode因为是JS对象,不管Node还是浏览器,都可以统一操作,从而获得了服务端渲染、原生渲染、手写渲染函数等能力。

(2)减少操作DOM,任何页面的变化,都只使用VNode进行操作对比,只需要在最后一步挂载更新DOM,不需要频繁操作DOM,从而提高页面性能。

4、VNode如何生成:在Vue源码中,VNode是通过一个构造函数生成的。

export default class VNode {
  constructor (
    tag?: string,
    data?: VNodeData,
    children?: ?Array<VNode>,
    text?: string,
    elm?: Node,
    context?: Component,
    componentOptions?: VNodeComponentOptions,
    asyncFactory?: Function
  ) {
    this.tag = tag
    this.data = data
    this.children = children
    this.text = text
    this.elm = elm
    this.ns = undefined
    this.context = context
    this.fnContext = undefined
    this.fnOptions = undefined
    this.fnScopeId = undefined
    this.key = data && data.key
    this.componentOptions = componentOptions
    this.componentInstance = undefined
    this.parent = undefined
    this.raw = false
    this.isStatic = false
    this.isRootInsert = true
    this.isComment = false
    this.isCloned = false
    this.isOnce = false
    this.asyncFactory = asyncFactory
    this.asyncMeta = undefined
    this.isAsyncPlaceholder = false
  }
}

 VNode的生成过程如下:

// 模版
<a class="demo" style="color: red" href="#">
    generator VNode
</a>
// VNode描述
{
  tag: 'a',
  data: {
    class: 'demo',
    attrs: {
      href: '#'
    },
    style: {
      color: 'red'
    }
  },
  children: ['generator VNode']
}
//这个JS对象,已经囊括了整个模板的所有信息,完全可以根据这个对象来构造真实DOM。

 三、render函数

1、初识render函数:

import Vue from 'vue'
import App from './App'
Vue.config.productionTip = false
new Vue({
  el: '#app',
  render: h => h(App)
})

 2、为什么使用render函数

 vue推荐在绝大多数情况下使用template来创建我们的HTML。然而在一些场景中,我们真的需要JavaScript的完全编程的能力,这就是render函数,它比template更接近编译器。(这是官方的话)

import Vue from "vue";

这一个引入你看似没有任何问题,但问题恰恰就是出在这。在不同版本的vue中,有vue.js和vue.runtime.xxx.js这两种js文件。其中 

(1)vue.js是完整版的vue,包含核心功能+模板解析器。

​ (2)vue.runtime.xxx.js是运行版vue,只包含核心功能,没有模板解析器。

​ vue开发者为了让我们打包的文件能尽可能小一点,在上述引入的是运行版vue。因为vue.runtime.xxx.js没有模板解析器,所以不能使 用template配置项,这时候就需要使用render函数去接收到的createElement函数去指定具体内容,创建html模板。

3、render函数的解析

​ render 函数即渲染函数,它是个函数,它的参数 createElement 也是个函数。

​ 上边的代码中 render: h => h(App) ,这是 ES6的箭头函数的写法,可以把 h 当作 createElement 的别名。所以这段代码其实相当​ 于:

render: function (createElement) {
    return createElement(App);
}

 这个函数的作用就是生成一个 VNode节点,render 函数得到这个 VNode 节点之后,返回给 Vue.js 的 mount 函数,渲染成真实DOM 节点,并挂载到根节点上。

createElement 函数的返回值是 VNode(即:虚拟节点)

​ createElement 函数的3个参数

(1)一个 HTML 标签字符串,组件选项对象,或者解析上述任何一种的一个 async 异步函数。类型:String | Object | Function。必 需。

​ (2)一个包含模板相关属性的数据对象,你可以在 template 中使用这些特性。类型:Object。可选。

​ (3)子虚拟节点 (VNodes),由 createElement() 构建而成,也可以使用字符串来生成“文本虚拟节点”。类型:String | Array。可选。

new Vue({
  el: '#app',
  render:function (createElement) {
    //1.普通用法
    // createElement(标签,{属性},[内容])
    return createElement("h2",{class:"box"},['hello',createElement("button",["按钮"])])
  }
})

 同时createElement也可以传进去一个组件,因此

render: h => h(App)
//等同于
render:function (createElement) {
    return createElement(App)
  }

四、实例属性

​ Vue实例暴露了一些有用的实例属性与方法,这些实例属性与方法都有前缀$,以便与代理的数据属性区分

1、组件树的访问

​ (1)$parent:用来访问组件实例的父实例(父组件)

​ (2)$root:用来访问当前组件树的根实例(根组件)

​ (3)$children:用来访问当前组件实例的直接子组件实例

​ (4)$refs:用来访问v-refs指令的子组件

2、虚拟DOM的访问:

​ (1)$el:表示挂载当前组件实例的DOM元素

​ (2)$data:用来访问组件实例观察的数据对象

五、实例方法

1、实例DOM方法的使用

​ (1)$appendTo(elementOrSelector,callback):将el所指的DOM元素插入目标元素

​ (2)$before(elementOrSelector,callback):将el所指的DOM元素插入目标元素之前

​ (3)$after(elementOrSelector,callback):将el所指的DOM元素插入目标元素之后

​ (4)$remove(callback):将el所指的DOM元素或片段从DOM中删除

​ (5)$nextTick(callback):用来在下一次DOM更新循环后执行指定的回调函数

2、实例event方法的使用

​ (1)$on(event,callback):监听实例的自定义事件

​ (2)$once(event,callback):监听实例的自定义事件,但是只能触发一次

​ (3)$dispatch(event,args):派发事件,先在当前实例触发,再沿父链一层层向上,对应的监听函数返回false则停止

​ (4)$emit(event,args):触发事件

3、$watch()的使用

var obj = { a: 1 }
var vm = new Vue({
   el: '#app',
   data: obj
})
vm.$data === obj  //-> true
vm.$el === document.getElementById('app') // ->true
vm.$watch('a',function(newVal,oldVal){
    //这个回调函数将在vm.a改变后触发
})

猜你喜欢

转载自blog.csdn.net/weixin_46672437/article/details/128822770