上一篇博客我和你们分享了vue render函数的基础使用html
这篇博客咱们来简单讲一讲render函数他是怎么实现得vue
先来一张官方得图node
在实例初始化得时候,html经过render函数编译生成了一个虚拟dom,视图层会根据虚拟dom生成一个真实domgit
而后若是响应数据发生变化得时候,render函数会从新调用,在更新得时候render函数会返回一个新的虚拟dom , 这个新的虚拟dom并不会直接把以前得替换掉,他会对比新旧dom,而后经过diff算法,把改动最小得结果,拿去更新真实dom,避免发生大量得dom回流和重绘github
刚才在上边已经说过了,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
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插件,官方插件链接
render函数里边用的createElement方法建立的vnode,createElement方法在vue的源码里边是对_createElement方法的封装,以前是由五个参数的,其中有一个参数是对子节点的犯规,
咱们使用的createElement在通过规范化以后,默认返回的一个vnode类型的数组
vnode有四个属性
第一个是节点的名称 首先会对tag进行一个判断,若是是字符串的话,会去看是否是html规范的一些检点,若是是的话会建立一个普通的vnode节点
若是是以恶搞注册过的组件名称,则会createComponent 建立一个组件类型的vnode
若是都不是则会建立一个位置的标签
所谓虚拟DOM,是一个用于表示真实 DOM 结构和属性的 JavaScript 对象,这个对象用于对比虚拟 DOM 和当前真实 DOM 的差别化,而后进行局部渲染从而实现性能上的优化。
在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函数的从响应到渲染还有他的做用的理解 有不对的地方欢迎批评指正