博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
人人都能懂的Vue源码系列(二)—Vue构造函数
阅读量:7060 次
发布时间:2019-06-28

本文共 3839 字,大约阅读时间需要 12 分钟。

上篇博文中说到Vue源码的目录结构是什么样的,每个目录的作用应该也有所了解。我们知道core/instance目录主要是用来实例化Vue对象,所以我们在这个目录下去寻找Vue构造函数。果然找到了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)}复制代码

当你新建一个Vue实例时候,会判断如果当前的环境不是生产环境,且你在调用Vue的时候,没有用new操作符。就会调用warn函数,抛出一个警告。告诉你Vue是一个构造函数,需要用new操作符去调用。这个warn函数不是单纯的console.warn,它的实现我们后面的博文会介绍。

接下来,把options作为参数调用_init方法。options不做过多的介绍了,就是你调用new Vue时候传入的参数。在深入_init方法之前,我们先把目光移到index.js文件里

function Vue (options) {  ...}initMixin(Vue)stateMixin(Vue)eventsMixin(Vue)lifecycleMixin(Vue)renderMixin(Vue)复制代码

在Vue的构造函数定义之后,有一系列方法会被调用,这些方法主要用来给Vue函数添加一些原型属性和方法的。其中就有接下来要介绍的Vue.prototyoe._init

Vue.prototype._init

在core/instance/init.js中我们找到了_init的定义。

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    // 有子组件时,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 {      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')    initState(vm)    initProvide(vm)     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)     }  }复制代码

我们逐一来分析上述代码。首先缓存当前的上下文到vm变量中,方便之后调用。然后设置_uid属性。_uid属性是唯一的。当触发init方法,新建Vue实例时(当渲染组件时也会触发)uid都会递增。

下面这段代码主要是用来测试代码性能的,在这个时候相当于打了一个"标记点"来测试性能。

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

对这部分内容感兴趣的朋友们可以点击我的另一篇文章查看。接下来执行这行代码vm._isVue = true,Vue的作者对这句话做了注释。

an flag to avoid this being observed

乍看起来好像不太明白,好像是说为了防止this被observed实例化。那这究竟是什么意思呢?我们来看observer的代码。

export function observe (value: any, asRootData: ?boolean): Observer | void {  ...  else if (    observerState.shouldConvert &&    !isServerRendering() &&    (Array.isArray(value) || isPlainObject(value)) &&    Object.isExtensible(value) &&    !value._isVue  ) {    ob = new Observer(value)  }  ...}复制代码

如果传入值的_isVue为ture时(即传入的值是Vue实例本身)不会新建observer实例(这里可以暂时理解新建observer实例就是让数据响应式)。

再回到init源码部分

if (options && options._isComponent) {    initInternalComponent(vm, options)} else {    vm.$options = mergeOptions(      resolveConstructorOptions(vm.constructor),        options || {},        vm     )}复制代码
  • 当符合第一个条件是,即当前这个Vue实例是组件。则执行initInternalComponent方法。(该方法主要就是为vm.$options添加一些属性, 后面讲到组件的时候再详细介绍)。
  • 当符合第二个条件时,即当前Vue实例不是组件。而是实例化Vue对象时,调用mergeOptions方法。

mergeOptions主要调用两个方法,resolveConstructorOptions和mergeOptions。这两个方法牵涉到了很多知识点,为了我们文章篇幅的考虑。接下来准备通过两篇博文来介绍这两个方法。

下篇博文主要介绍resolveConstructorOptions相关的内容,涉及到原型链和构造函数以及部分Vue.extend的实现,敬请期待!

转载地址:http://xdfll.baihongyu.com/

你可能感兴趣的文章
linux php 连接sql server2000
查看>>
可以拖动的弹出窗
查看>>
Tiny之Web工程构建
查看>>
android 的android httpClient详解
查看>>
IOS SEL的理解与使用
查看>>
iOS开发实用技巧—Objective-C中的各种遍历(迭代)方式
查看>>
iOS开发之SQLite--C语言接口规范(三)——Binding Values To Prepared Statements
查看>>
gdb 调试 sysbench
查看>>
Oracle官方并发教程之中断
查看>>
使用模板实现编译期间多态
查看>>
[置顶].NET平台开源项目速览(3)小巧轻量级NoSQL文件数据库LiteDB
查看>>
Cocos2D将v1.0的tileMap游戏转换到v3.4中一例(三)
查看>>
iOS Touch ID 简易开发教程
查看>>
结构体类型指针做函数参数(C++)
查看>>
Akka笔记之消息传递
查看>>
带你实现开发者头条(一) 启动页实现
查看>>
Android Listview中Button按钮点击事件冲突解决办法
查看>>
【技术篇】SQL的四种连接-左外连接、右外连接、内连接、全连接
查看>>
可翻折的TextViewExpandableTextView
查看>>
【软件工程】1.软件工程概述
查看>>