vue render函数进阶学习

上一篇博客我和大家分享了vue render函数的基础使用

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

先来一张官方得图

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

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

render函数触发的时机

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

之前就听说过vue实现的原理是数据劫持,那么他到底是怎么做到的数据劫持这件事呢?

在vue的源码里边可以看到,vue实例在初始化的时候会把data里边所有的属性添加一个对象进去

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函数 更新dom

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函数的从响应到渲染还有他的作用的理解 有不对的地方欢迎批评指正

原文地址:https://www.cnblogs.com/netUserAdd/p/10923570.html