Vue源码:vue实例挂载篇

0.从new一个Vue对象开始

let vm = new Vue({
    el: '#app',
    /*some options*/
}); 

在new一个Vue对象的时候,内部究竟发生了什么?

newVue.png

1.Vue构造函数

Vue的构造类只做了一件事情,就是调用_init函数进行初始化。

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)
} 

2._init,初始化

_init主要做了这两件事:

  1. 初始化(包括生命周期、事件、render 函数、state 等)。
  2. $mount 组件。
Vue.prototype._init = function (options?: Object) {
    const vm: Component = this
​
    vm._isVue = true
  
    vm._self = vm
    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    callHook(vm, 'beforeCreate')
    initInjections(vm) 
    initState(vm)
    initProvide(vm) 
    callHook(vm, 'created')
​
    if (vm.$options.el) {
      /*把组件挂载到页面成为真实DOM*/
      vm.$mount(vm.$options.el)
    }
  } 

initData

  1. 首先判断data是不是函数,如果是函数的话执行getData。getData做的事情很简单:return data.call(vm, vm);,因为vue推荐我们把data定义为一个函数然后返回具体的data对象,因此getData只需执行一次data拿到返回值即可。
  2. 判断是否与props属性名冲突,然后将data挂载到vue实例中。这里是通过proxy对vue实例的getter,setter做一层代理转发,比如this.message访问data时实际上转发到this.data.message。
  3. 对数据做响应式化。
function initData(vm: Component) {
  let data = vm.$options.data;
  data = vm._data = typeof data === "function" ? getData(data, vm) : data || {};
​
  const keys = Object.keys(data);
  const props = vm.$options.props;
  const methods = vm.$options.methods;
  let i = keys.length;
  while (i--) {
    const key = keys[i];
    if (props && hasOwn(props, key)) {
      process.env.NODE_ENV !== "production" &&
        warn(
          `The data property "${key}" is already declared as a prop. ` +
            `Use prop default value instead.`,
          vm
        );
    } else if (!isReserved(key)) {
      // 将data挂载到vue实例中
      proxy(vm, `_data`, key);
    }
  }
    
  observe(data, true /* asRootData */);
} 
export function proxy(target: Object, sourceKey: string, key: string) {
  sharedPropertyDefinition.get = function proxyGetter() {
    return this[sourceKey][key];
  };
  sharedPropertyDefinition.set = function proxySetter(val) {
    this[sourceKey][key] = val;
  };
  Object.defineProperty(target, key, sharedPropertyDefinition);
} 

3.mount,将vue实例挂载到页面

  1. 首先将原型上的$mount方法缓存下来。

  2. 然后重新定义$mount方法。

    之所以这么设计完全是为了复用,因为原生的$mount方法是可以被runtime only版本的 Vue 直接使用的。而在runtime-compiler版本中需要添加额外的逻辑。

  3. 接下来的是很关键的逻辑 —— 如果没有定义 render 方法,则会把 template 字符串或者是 el 编译成 render 函数。 即调用compileToFunctions处理模板templete。如果存在render方法则优先使用render。

const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && query(el)
​
  /* 对 el 做了限制,Vue 不能挂载在 body、html 这样的根节点上 */
  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
  if (!options.render) {
    let template = options.template
    /*template存在的时候取template,不存在的时候取el的outerHTML*/
    if (template) {
      /*当template是字符串的时候*/
      if (typeof template === 'string') {
        if (template.charAt(0) === '#') {
          template = idToTemplate(template)
          if (process.env.NODE_ENV !== 'production' && !template) {
            warn(
              `Template element not found or is empty: ${options.template}`,
              this
            )
          }
        }
      } else if (template.nodeType) {
        /*当template为DOM节点的时候*/
        template &#
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值