寻找Vue实例

继上一篇“如何开始阅读VUE源码”,我们现在正式开始阅读VUE源码。
首先在代码中执行npm run dev,我们看到package.json后面,这个命令对应的target为web-full-dev
在这里插入图片描述
1,进入到配置文件config.js中,找到web-full-dev,找到entry入口文件为web/entry-runtime-with-compiler.js
在这里插入图片描述
一百来行代码,主要是扩展了$mount的方法,用来处理el或template

Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && query(el)

  /* istanbul ignore if */
  if (el === document.body || el === document.documentElement) {
    process.env.NODE_ENV !== 'production' && warn(
      `Do not mount Vue to <html> or <body> - mount to normal elements instead.`
    )
    return this
  }

  const options = this.$options
  // resolve template/el and convert to render function
  if (!options.render) {
    let template = options.template
    if (template) {
      if (typeof template === 'string') {
        if (template.charAt(0) === '#') {
          template = idToTemplate(template)
          /* istanbul ignore if */
          if (process.env.NODE_ENV !== 'production' && !template) {
            warn(
              `Template element not found or is empty: ${options.template}`,
              this
            )
          }
        }
      } else if (template.nodeType) {
        template = template.innerHTML
      } else {
        if (process.env.NODE_ENV !== 'production') {
          warn('invalid template option:' + template, this)
        }
        return this
      }
    } else if (el) {
      template = getOuterHTML(el)
    }
    if (template) {
      /* istanbul ignore if */
      if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
        mark('compile')
      }

      const { render, staticRenderFns } = compileToFunctions(template, {
        outputSourceRange: process.env.NODE_ENV !== 'production',
        shouldDecodeNewlines,
        shouldDecodeNewlinesForHref,
        delimiters: options.delimiters,
        comments: options.comments
      }, this)
      options.render = render
      options.staticRenderFns = staticRenderFns

      /* istanbul ignore if */
      if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
        mark('compile end')
        measure(`vue ${this._name} compile`, 'compile', 'compile end')
      }
    }
  }
  return mount.call(this, el, hydrating)
}

2,在上个文件中可以发现Vue实例,来源于platforms/web/runtime/index.js

// install platform patch function   
// 安装web平台特有指令和组件
// __patch__:patch就是补丁的意思,补丁函数,执行patch算法进行更新
Vue.prototype.__patch__ = inBrowser ? patch : noop

// public mount method
// $mount方法 将vue组件实例挂载到指定的dom结构上
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && inBrowser ? query(el) : undefined
  return mountComponent(this, el, hydrating)
}

3,vue/src/core/index.js
初始化全局API

initGlobalAPI(Vue)

这个方法下面具体的表现为:global-api/index.js

Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
initUse(Vue)  // 实现vue的use方法
initMixin(Vue)  // 实现vue的Mixin方法
initExtend(Vue)  // 实现vue的extend方法
initAssetRegisters(Vue) //注册实现

4,vue/src/core/instance/index.js
Vue构造函数定义
Vue实例API
那其实到这里我们就找到定义Vue实例的地方,接下来我们就看看在这个实例里面又做了那些操作,以及外围又引入了什么方法?

function Vue (options) {
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  this._init(options)
}

initMixin(Vue)  // 实现init方法
stateMixin(Vue)  // 状态相关api $data,$peops,$set,$delete,$watch
eventsMixin(Vue) //事件相关api $on,$once,$off,$emit
lifecycleMixin(Vue)  // 生命周期api _update,$forceUpdate,$destory
renderMixin(Vue)  // 渲染api _render,$nextTick

5,接下来看一上面这个文件的_init方法:vue/src/core/instance/init.js
创建组件实例,初始化其数据、属性、事件等

export function initMixin (Vue: Class<Component>) {
  Vue.prototype._init = function (options?: Object) {
    const vm: Component = this
    // a uid
    vm._uid = uid++

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

    // a flag to avoid this being observed
    vm._isVue = true
    // merge options
    if (options && options._isComponent) {
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options)
    } else {
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== 'production') {
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }
    // expose real self
    vm._self = vm
    initLifecycle(vm) // $parent,$root,$children,
    initEvents(vm)  // 处理父组件传递的事件和回调
    initRender(vm)  // $slot,$scopedSlots,_c,$createElement
    callHook(vm, 'beforeCreate')
    initInjections(vm) // resolve injections before data/props  获取注入数据
    initState(vm)  // 初始化props,methods,data,computed.watch
    initProvide(vm) // resolve provide after data/props 提供数据注入
    callHook(vm, 'created')

    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      vm._name = formatComponentName(vm, false)
      mark(endTag)
      measure(`vue ${vm._name} init`, startTag, endTag)
    }

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

6,mountComponent (在runtime/index.js里面调用)core/instance/lifecycle.js
执行挂载,获取vdom并转换为dom

7,_initRender() src\core\instance\render.js
渲染组件,获取vdom

vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
  // normalization is always applied for the public version, used in
  // user-written render functions.
  vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)

8,update() src\core\instance\lifecycle.js
执行更新,将传入vdom转换为dom,初始化时执行的是dom创建操作

我们可以自己写一个Vue实例,去检测他的整个的过程,我们会发现他的整体流程
new Vue() => _init() => $mount() => _render() => _update()

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值