vue源码阅读记录3-$mount

开始

上篇说到vue的_init函数,在改函数的最后有一个$mount 函数

if (vm.$options.el) {
  vm.$mount(vm.$options.el)
} 

说$mount之前先说下编译,可以看到plantforms平台下面的几个入口文件。这个是打包编译的入口,和我们相关的三个文件。entry-compiler.js就是编译的入口文件,简单的说就是把我们写的.vue文件的template这种格式的进行编译,编译成render类型的函数,类似这样的。

 render: function (createElement) {
        return createElement(
          'h1',   // 标签名称
          12312312 // 子节点数组
        )
      }, 

而entry-runtime.js文件就是我们直接运行编译好render函数这种函数的方式的文件,而entry-runtime-with-compiler.js就是两者结合,现编译再运行的文件入口。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F9JTlDby-1656482098753)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/18ff3703c89b43559968a72bfa881098~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image?)]

就看比较简单的runtime.js文件吧,可以发现找到了src/platform/web/runtime/index.js,其中就有一个$mount函数,也绑定再了vue的原型上了,而执行的mountComponent就是src/core/instance/lifecycle中的mountComponent

.....
     callHook(vm, 'beforeMount')

  let updateComponent
  /* istanbul ignore if */
  if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
    updateComponent = () => {
      const name = vm._name
      const id = vm._uid
      const startTag = `vue-perf-start:${id}`
      const endTag = `vue-perf-end:${id}`

      mark(startTag)
      const vnode = vm._render()
      mark(endTag)
      measure(`vue ${name} render`, startTag, endTag)

      mark(startTag)
      vm._update(vnode, hydrating)
      mark(endTag)
      measure(`vue ${name} patch`, startTag, endTag)
    }
  } else {
    updateComponent = () => {
      vm._update(vm._render(), hydrating)
    }
  } 

截取了比较重要的一段。首先限制性了beforeMount这个钩子函数,然后有两个比较重要的函数_update,_render两个函数,这边主要看_render,这个函数是在renderMixin的时候绑定的, 这边看到这个 vnode = render.call(vm._renderProxy, vm.$createElement) 这个就是我们再render方法中直接执行render中方法,,再看 vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)可以看到,render 函数中的 createElement 方法就是 vm.$createElement 方法。这边 vm._render 最终是通过执行 createElement 方法并返回的是 vnode,继续下去看creteElement

export function _createElement (
  context: Component,
  tag?: string | Class<Component> | Function | Object,
  data?: VNodeData,
  children?: any,
  normalizationType?: number
): VNode | Array<VNode> {
  if (isDef(data) && isDef((data: any).__ob__)) {
    process.env.NODE_ENV !== 'production' && warn(
      `Avoid using observed data object as vnode data: ${JSON.stringify(data)}\n` +
      'Always create fresh vnode data objects in each render!',
      context
    )
    return createEmptyVNode()
  }
  // object syntax in v-bind
  if (isDef(data) && isDef(data.is)) {
    tag = data.is
  }
  if (!tag) {
    // in case of component :is set to falsy value
    return createEmptyVNode()
  }
  // warn against non-primitive key
  if (process.env.NODE_ENV !== 'production' &&
    isDef(data) && isDef(data.key) && !isPrimitive(data.key)
  ) {
    if (!__WEEX__ || !('@binding' in data.key)) {
      warn(
        'Avoid using non-primitive value as key, ' +
        'use string/number value instead.',
        context
      )
    }
  }
  // support single function children as default scoped slot
  if (Array.isArray(children) &&
    typeof children[0] === 'function'
  ) {
    data = data || {}
    data.scopedSlots = { default: children[0] }
    children.length = 0
  }
  if (normalizationType === ALWAYS_NORMALIZE) {
    children = normalizeChildren(children)
  } else if (normalizationType === SIMPLE_NORMALIZE) {
    children = simpleNormalizeChildren(children)
  }
  let vnode, ns
  if (typeof tag === 'string') {
    let Ctor
    ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)
    if (config.isReservedTag(tag)) {
      // platform built-in elements
      if (process.env.NODE_ENV !== 'production' && isDef(data) && isDef(data.nativeOn) && data.tag !== 'component') {
        warn(
          `The .native modifier for v-on is only valid on components but it was used on <${tag}>.`,
          context
        )
      }
      vnode = new VNode(
        config.parsePlatformTagName(tag), data, children,
        undefined, undefined, context
      )
    } else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
      // component
      vnode = createComponent(Ctor, data, context, children, tag)
    } else {
      // unknown or unlisted namespaced elements
      // check at runtime because it may get assigned a namespace when its
      // parent normalizes children
      vnode = new VNode(
        tag, data, children,
        undefined, undefined, context
      )
    }
  } else {
    // direct component options / constructor
    vnode = createComponent(tag, data, context, children)
  }
  if (Array.isArray(vnode)) {
    return vnode
  } else if (isDef(vnode)) {
    if (isDef(ns)) applyNS(vnode, ns)
    if (isDef(data)) registerDeepBindings(data)
    return vnode
  } else {
    return createEmptyVNode()
  }
} 

很长一串,其实都是做了一些判断,真正核心的语句就是创建VNODE,

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

这边就讲到了虚拟DOM,这个下面会讲。再回到mountComponent方法执行了Watch这个实例,后面再讲watcher。执行了beforeUpdate这个生命周期函数和mounted这个生命周期函数。至此$mout这个函数就完成了。

new Watcher(vm, updateComponent, noop, {
    before: function before () {
      if (vm._isMounted && !vm._isDestroyed) {
        callHook(vm, 'beforeUpdate');
      }
    }
  }, true /* isRenderWatcher */); 

总结

看源码的时候不要一个一个的仔细去看每个方法里面执行的过程。大概知道这个函数是干嘛的,先看个大概流程,边看边猜,最后再回过头把猜的那一部分仔细阅读,脑子里要有个大概的流程,琢磨久了,很多之前不理解的问题也就迎刃而解了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值