Vue2.x源码学习笔记-从一个小例子查看vm实例生命周期

学习任何一门框架,都不可能一股脑儿的从入口代码从上到下,把代码看完,

这样其实是很枯燥的,我想也很少有人这么干,或者这么干着干着可能干不下去了。

因为肯定很无聊。

我们先从一个最最简单的小例子,来查看new Vue(options)实例,这个过程发生了什么。

vm实例上的属性又如何添加上去的,又如何渲染到浏览器页面上的。

关于vue的数据依赖和虚拟dom都是重点,必然会在以后的帖子记录。

这篇帖子就根据下例子,看看实例化一个vm实例做了啥吧。

先把小例子贴出来:

      <div id="app">
          <p>这是<span>静态内容</span></p>
          <p>{{message}}</p>
      </div>
      <script src="../../dist/vue.js"></script>
      <script>
          var vm = new Vue({
              el: ‘#app‘,
              data: {
                  message: ‘hi vue!‘
              }
          })
          console.log(vm)
      </script>

根据上篇介绍了vue的调式笔记,那我们快快进入源码吧

根据vue构造函数那篇笔记,我们知道了Vue原型上有哪些方法,_init方法就是其中一个方法

我们看到_init就把实例要做的事情都做完了,当然其中有的语句所做的事,太多了。我们先一点一点开see see吧。

看图不好玩,我把源码取出 来 好好瞧瞧

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

    let startTag, endTag
    /* istanbul ignore if 性能统计相关 */
    if (process.env.NODE_ENV !== ‘production‘ && config.performance && mark) {
      startTag = `vue-perf-init:${vm._uid}`
      endTag = `vue-perf-end:${vm._uid}`
      mark(startTag)
    }

    // a flag to avoid this being observed 监听对象变化时用于过滤vm
    vm._isVue = true
    // merge options  _isComponent是内部创建子组件时才会添加为true的属性
    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 {
      // mergeOptions 方法 合并构造器及构造器父级上定义的options
      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)
    initEvents(vm)
    initRender(vm)
    callHook(vm, ‘beforeCreate‘)
    initInjections(vm) // resolve injections before data/props
    initState(vm)
    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(`${vm._name} init`, startTag, endTag)
    }

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

1. 给实例添加了唯一标识uid

2.性能统计相关,先忽略

3. 给实例添加_isVue,监听对象变化时用于过滤vm

4. 选项对象如果有_isComponent,就初始化内部组件,_isComponent是内部创建子组件时才会添加为true的属性

5. 小例子会走分支,mergeOptions 方法 合并构造器及构造器父级上定义的options,resolveConstructorOptions方法后面笔记会详解,

mergeOptions方法接受3个参数。我们先简单看下resolveConstructorOptions方法的定义

export function resolveConstructorOptions (Ctor: Class<Component>) {
  let options = Ctor.options
  // 有super属性,说明Ctor是通过Vue.extend()方法创建的子类
  if (Ctor.super) {
    const superOptions = resolveConstructorOptions(Ctor.super)
    const cachedSuperOptions = Ctor.superOptions
    if (superOptions !== cachedSuperOptions) {
      // super option changed,
      // need to resolve new options.
      Ctor.superOptions = superOptions
      // check if there are any late-modified/attached options (#4976)
      const modifiedOptions = resolveModifiedOptions(Ctor)
      // update base extend options
      if (modifiedOptions) {
        extend(Ctor.extendOptions, modifiedOptions)
      }
      options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
      if (options.name) {
        options.components[options.name] = Ctor
      }
    }
  }
  return options
}

可以看出Ctor.options其实就是Vue构造函数自身,在Vue构造函数静态属性那篇笔记,Vue是拥有options属性的,且有截图,等下会再截图看下,

接着在该函数中有个if语句,我们小例子会跳过的,直接返回options。因为有super属性,说明Ctor是通过Vue.extend()方法创建的子类。那么

options是啥呢,如下图,

回到_init方法中,mergeOptions方法的第二个参数就是我们传入的options,第三个参数就是vm实例,把参数一起截个图吧,好回忆

mergeOptions是Vue中处理属性的合并策略的地方, 先看下它的定义

export function mergeOptions (
  parent: Object,
  child: Object,
  vm?: Component
): Object {
  if (process.env.NODE_ENV !== ‘production‘) {
    // 如果有options.components,则判断是否组件名是否合法
    checkComponents(child)
  }
  // 格式化child的props
  normalizeProps(child)
  // 格式化child的directives
  normalizeDirectives(child)
  const extendsFrom = child.extends
  if (extendsFrom) {
    parent = typeof extendsFrom === ‘function‘
      ? mergeOptions(parent, extendsFrom.options, vm)
      : mergeOptions(parent, extendsFrom, vm)
  }
  if (child.mixins) {
    for (let i = 0, l = child.mixins.length; i < l; i++) {
      let mixin = child.mixins[i]
      if (mixin.prototype instanceof Vue) {
        mixin = mixin.options
      }
      parent = mergeOptions(parent, mixin, vm)
    }
  }
  const options = {}
  let key
  for (key in parent) {
    mergeField(key)
  }
  for (key in child) {
    if (!hasOwn(parent, key)) {
      mergeField(key)
    }
  }
  function mergeField (key) {
    const strat = strats[key] || defaultStrat
    options[key] = strat(parent[key], child[key], vm, key)
  }
  return options
}

明天再说吧,累了,要睡觉

时间: 2024-10-31 18:05:59

Vue2.x源码学习笔记-从一个小例子查看vm实例生命周期的相关文章

Vue2.x源码学习笔记-Vue静态方法和静态属性整理

Vue静态方法和静态属性,其实直接在浏览器中可以查看到的,如下 圈起来的是其静态属性,但是有的属性对象中的属性的值又是函数.未圈起来的则是函数. 其实它来自如下各个目录下的js文件 // src/core/index.js Vue.version = '__VERSION__' // src/entries/web-runtime-with-compiler.js Vue.compile = compileToFunctions // 把模板template转换为render函数 // src/

Hadoop源码学习笔记(1) ——第二季开始——找到Main函数及读一读Configure类

Hadoop源码学习笔记(1) ——找到Main函数及读一读Configure类 前面在第一季中,我们简单地研究了下Hadoop是什么,怎么用.在这开源的大牛作品的诱惑下,接下来我们要研究一下它是如何实现的. 提前申明,本人是一直搞.net的,对java略为生疏,所以在学习该作品时,会时不时插入对java的学习,到时也会摆一些上来,包括一下设计模式之类的.欢迎高手指正. 整个学习过程,我们主要通过eclipse来学习,之前已经讲过如何在eclipse中搭建调试环境,这里就不多述了. 在之前源码初

Hadoop源码学习笔记(4) ——Socket到RPC调用

Hadoop源码学习笔记(4) ——Socket到RPC调用 Hadoop是一个分布式程序,分布在多台机器上运行,事必会涉及到网络编程.那这里如何让网络编程变得简单.透明的呢? 网络编程中,首先我们要学的就是Socket编程,这是网络编程中最底层的程序接口,分为服务器端和客户端,服务器负责监听某个端口,客户端负责连接服务器上的某个端口,一旦连接通过后,服务器和客户端就可以双向通讯了,我们看下示例代码: ServerSocket server = new ServerSocket(8111); S

Spring源码学习笔记(6)

Spring源码学习笔记(六) 前言-- 最近花了些时间看了<Spring源码深度解析>这本书,算是入门了Spring的源码吧.打算写下系列文章,回忆一下书的内容,总结代码的运行流程.推荐那些和我一样没接触过SSH框架源码又想学习的,阅读郝佳编著的<Spring源码深度解析>这本书,会是个很好的入门. 上一篇中我们梳理到 Spring 加载 XML 配置文件, 完成 XML 的解析工作,接下来我们将进入 Spring 加载 bean 的逻辑. 我们使用 Spring 获取 XML

Spring源码学习笔记(3)

Spring源码学习笔记(三) 前言----     最近花了些时间看了<Spring源码深度解析>这本书,算是入门了Spring的源码吧.打算写下系列文章,回忆一下书的内容,总结代码的运行流程.推荐那些和我一样没接触过SSH框架源码又想学习的,阅读郝佳编著的<Spring源码深度解析>这本书,会是个很好的入门. DispatcherServlet 实现核心功能 和普通的 Servelt 类一样, DispatcherServlet 中的 doGet() 和 doPost() 方法

Hadoop源码学习笔记(3) ——初览DataNode及学习线程

Hadoop源码学习笔记(3) ——初览DataNode及学习线程 进入了main函数,我们走出了第一步,接下来看看再怎么走: public class DataNode extends Configured implements InterDatanodeProtocol,       ClientDatanodeProtocol, FSConstants, Runnable {      public static DataNode createDataNode(String args[],

菜鸟的jQuery源码学习笔记(二)

jQuery对象是使用构造函数和原型模式相结合的方式创建的.现在来看看jQuery的原型对象jQuery.prototype: 1 jQuery.fn = jQuery.prototype = { 2 //成员变量和方法 3 } 这里给原型对象起了一个别名叫做jQuery.fn.要注意的是这个jQuery.fn可不是jQuery对象的属性,而是jQuery构造方法本身的属性,它是不会传给它所创建的对象的.如果你在控制台敲$().fn的话输出的结果会是undefined.接下来看看原型对象里面有些

Java多线程之JUC包:ReentrantReadWriteLock源码学习笔记

若有不正之处请多多谅解,并欢迎批评指正. 请尊重作者劳动成果,转载请标明原文链接: http://www.cnblogs.com/go2sea/p/5634701.html ReentrantLock提供了标准的互斥操作,但在应用中,我们对一个资源的访问有两种方式:读和写,读操作一般不会影响数据的一致性问题.但如果我们使用ReentrantLock,则在需要在读操作的时候也独占锁,这会导致并发效率大大降低.JUC包提供了读写锁ReentrantReadWriteLock,使得读写锁分离,在上述情

Java多线程之JUC包:Semaphore源码学习笔记

若有不正之处请多多谅解,并欢迎批评指正. 请尊重作者劳动成果,转载请标明原文链接: http://www.cnblogs.com/go2sea/p/5625536.html Semaphore是JUC包提供的一个共享锁,一般称之为信号量. Semaphore通过自定义的同步器维护了一个或多个共享资源,线程通过调用acquire获取共享资源,通过调用release释放. 源代码: /* * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to lic