vue render函数进阶学习

上一篇博客我和你们分享了vue render函数的基础使用html

这篇博客咱们来简单讲一讲render函数他是怎么实现得vue

先来一张官方得图node

在实例初始化得时候,html经过render函数编译生成了一个虚拟dom,视图层会根据虚拟dom生成一个真实domgit

而后若是响应数据发生变化得时候,render函数会从新调用,在更新得时候render函数会返回一个新的虚拟dom , 这个新的虚拟dom并不会直接把以前得替换掉,他会对比新旧dom,而后经过diff算法,把改动最小得结果,拿去更新真实dom,避免发生大量得dom回流和重绘github

render函数触发的时机

刚才在上边已经说过了,render函数会在响应数据发生改变的时候去被触发,那么就不得不说一说vue的响应式原理算法

以前就据说过vue实现的原理是数据劫持,那么他究竟是怎么作到的数据劫持这件事呢?数组

在vue的源码里边能够看到,vue实例在初始化的时候会把data里边全部的属性添加一个对象进去babel

function observe (obj) {

 // 迭代对象的全部属性
 // 并使用Object.defineProperty()转换成getter/setters
 Object.keys(obj).forEach(key => {
  let internalValue = obj[key]

// 每一个属性分配一个Dep实例
const dep = new Dep()

Object.defineProperty(obj, key, {

  // getter负责注册订阅者
  get () {
    dep.depend()
    return internalValue
  },

  // setter负责通知改变
  set (newVal) {
    const changed = internalValue !== newVal
    internalValue = newVal
    
        // 触发后从新计算
        if (changed) {
          dep.notify()
        }
      }
    })
})
return obj
}

在没有更改属性的原有行为的基础上加了一个依赖对象进去,而后经过这个以来对象来发送通知告诉监听watcher 数据发生了变化 ,而后watcher去调用render函数 更新domdom

render函数中的jsx语法

jsx事js内定义的一套类xml语法,能够解析出js代码,由js引擎解析,在上一篇博客里边我使用的是render函数自己的写法,可是感受十分的冗余,jsx里边则很是简介,你能够直接返回一个标签的全部属性,感受和html没什么两样 好比说函数

render() {
const { count, onChange } = this;
return (
  <div>
    <componment
      style={{ marginTop: "10px" }}
      count={count}
      type="button"
      onChange={onChange}
    />
    <componment
      style={{ marginTop: "10px" }}
      count={count}
      type="button"
      domPropsInnerHTML={`hello ${this.count}.`}
      onChange={onChange}
    />
  </div>
);
}

上边的代码能够轻松的渲染出来两个组件
须要注意的是使用jsx语法的时候须要babel插件,官方插件链接

createElement

render函数里边用的createElement方法建立的vnode,createElement方法在vue的源码里边是对_createElement方法的封装,以前是由五个参数的,其中有一个参数是对子节点的犯规,
咱们使用的createElement在通过规范化以后,默认返回的一个vnode类型的数组

vnode有四个属性

  • tag
  • data
  • context
  • children

第一个是节点的名称 首先会对tag进行一个判断,若是是字符串的话,会去看是否是html规范的一些检点,若是是的话会建立一个普通的vnode节点

若是是以恶搞注册过的组件名称,则会createComponent 建立一个组件类型的vnode

若是都不是则会建立一个位置的标签

什么是vnode

所谓虚拟DOM,是一个用于表示真实 DOM 结构和属性的 JavaScript 对象,这个对象用于对比虚拟 DOM 和当前真实 DOM 的差别化,而后进行局部渲染从而实现性能上的优化。

vnode的渲染

在render函数生成完毕虚拟dom树以后 就开始了vnode的首次渲染,vue里边调用的是_update方法

// src/core/instance/lifecycle.js
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
const vm: Component = this
if (vm._isMounted) {
  callHook(vm, 'beforeUpdate')
}
const prevEl = vm.$el
const prevVnode = vm._vnode
const prevActiveInstance = activeInstance
activeInstance = vm
vm._vnode = vnode

if (!prevVnode) {
  // 初始化渲染
  vm.$el = vm.__patch__(
    vm.$el, vnode, hydrating, false /* removeOnly */,
    vm.$options._parentElm,
    vm.$options._refElm
  )
  // no need for the ref nodes after initial patch
  // this prevents keeping a detached DOM tree in memory (#5851)
  vm.$options._parentElm = vm.$options._refElm = null
} else {
  // 更新渲染
  vm.$el = vm.__patch__(prevVnode, vnode)
}
activeInstance = prevActiveInstance
// update __vue__ reference
if (prevEl) {
  prevEl.__vue__ = null
}
if (vm.$el) {
  vm.$el.__vue__ = vm
}
// if parent is an HOC, update its $el as well
if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
  vm.$parent.$el = vm.$el
}
// updated hook is called by the scheduler to ensure that children are
// updated in a parent's updated hook.
}

能够看出无论如何最终都是调用的__patch__方法
这个__patch__方法主要有两个做用 第一个是建立真实dom 另一个是发生变化以后把新老的虚拟dom进行对比而后更新真实dom
这个方法对应的方法是createPatchFunction() 有兴趣的能够去源码里边搜一下

结束语

以上是我对render函数的从响应到渲染还有他的做用的理解 有不对的地方欢迎批评指正

相关文章
相关标签/搜索