最新公告
  • 欢迎您光临网站无忧模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Vue.js源码学习——Vue如何和真实DOM建立联系

    正文概述 掘金(小馒头儿)   2021-02-05   581

    在之前mount实例的挂载的学习中,已经知道了mount挂载过程中最后调用了vm._update(vm._render(), hydrating)来将我们的Vue对象变成真实的DOM。

    这一行代码中,hydrating先忽略,这个参数和服务器渲染相关。主要的就是renderupdate这两个方法。

    render的执行过程

    在我们新建一个Vue对象的时候,进行了很多初始化的工作。

    src/core/instance/index.js文件中使用renderMixin(Vue)来定义了Vue.prototype._render。该方法的返回参数是一个vnode也就是Virtual DOM。这个vnode怎么来的呢?

    在函数定义中可以看到存在这么一句语句:vnode = render.call(vm._renderProxy, vm.$createElement)。

    render就是我们在创建Vue对象的时候自己定义的render函数。如下面的例子:

    new Vue({
      el: '#app',
      render: function (createElement) {
        return createElement('div', this.message)
      },
      data: {
        message: 'Hello world!'
      }
    })
    

    vm._renderProxy在生产环境中就是Vue实例,在非生产环境中,会使用Proxy进行代理,对一些错误进行提示。第二个参数则是在我们自定义render函数时候的一个方法。

    在initRender的时候,会初始化以下两行代码:

    vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
    
    vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
    

    第一行是通过模版编译或得到render函数的时候使用,第二个则是我们自定义render函数的时候使用。但本质上都调用了createElement这个方法。

    createElement

    export function createElement (
      context: Component,
      tag: any,
      data: any,
      children: any,
      normalizationType: any,
      alwaysNormalize: boolean
    ): VNode | Array<VNode> {
      if (Array.isArray(data) || isPrimitive(data)) {
        normalizationType = children
        children = data
        data = undefined
      }
      if (isTrue(alwaysNormalize)) {
        normalizationType = ALWAYS_NORMALIZE
      }
      return _createElement(context, tag, data, children, normalizationType)
    }
    

    createElement方法做了一下数据的处理:

    在传递参数的时候,data可能存在为空的情况,当data满足第一个if中的条件的时候,则说明data为空,通过参数的前移获取真正对应的参数。之后将处理后的参数传递进入_createElement

    以上面定义的例子进行单步调试:

    Vue.js源码学习——Vue如何和真实DOM建立联系 这个例子在_createElement调用的时候参数比较有限,运行的分支逻辑也比较简单。主要进行了两步操作:

    1. 将children属性标准化,将数组拍平成为一维数组

      if (normalizationType === ALWAYS_NORMALIZE) {
          children = normalizeChildren(children);
      } else if (normalizationType === SIMPLE_NORMALIZE) {
          children = simpleNormalizeChildren(children);
      }
      
    2. 创建一个vnode

      vnode = new VNode(
      	config.parsePlatformTagName(tag), data, children,
      	undefined, undefined, context
      );
      

    最后返回的这个vnode就是render函数返回的vnode。

    update的执行过程

    update的调用时机分为两个:

    • 初始渲染的时候
    • 数据变更→视图更新

    这里先看初始渲染的过程。

    src/core/instance/lifecycle.js里面定义Vue.prototype._update。由于是初始渲染的情况,prevNode是不存在的,就调用了vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)这一方法。

    Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
        const vm: Component = this
        const prevEl = vm.$el
        const prevVnode = vm._vnode
        const restoreActiveInstance = setActiveInstance(vm)
        vm._vnode = vnode
        // Vue.prototype.__patch__ is injected in entry points
        // based on the rendering backend used.
        if (!prevVnode) {
          // initial render
          vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
        } else {
          // updates
          vm.$el = vm.__patch__(prevVnode, vnode)
        }
        restoreActiveInstance()
        // 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.
      }
    

    通过单步调试来查看一下进入该方法的时候的初始化参数的情况: Vue.js源码学习——Vue如何和真实DOM建立联系 参数基本都为undefined,主要就执行了vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)。patch也是在Vue对象初始化的时候引入的。

    patch相关的定义在src/platforms/web/runtime/patch.js文件中:

    const patch: Function = createPatchFunction({ nodeOps, modules })
    

    该文件中内容相对较少,patch就是createPatchFunction返回的函数。nodeOps是操作真实dom的方法。这里通过函数柯里化的方式在一开始将平台差异抹平,减少后续操作的分支处理。createPatchFunction返回了一个patch方法。该返回的patch方法中和真实dom相关的就是createElm方法。

    patch

    进入patch的一些参数:

    Vue.js源码学习——Vue如何和真实DOM建立联系

    由于本次不使用服务器渲染,第一部分只执行了oldVnode = emptyNodeAt(oldVnode);来将从文档中获取的dom节点转化为vnode。patch方法通过递归构建子元素,然后将子元素插入父元素,最后将创建好的元素插入到真实的DOM树中,在这一步之后,我们可以看到浏览器中的DOM就已经渲染好了。

    总结

    Vue.js源码学习——Vue如何和真实DOM建立联系


    下载网 » Vue.js源码学习——Vue如何和真实DOM建立联系

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元