vue的this巧妙用法分析——vue源码分析

this的用发很多,这里介绍一个点

不错鼓励并赞赏 标签: Javascript      评论 / 2019-09-15

vue初始化之this的巧妙用法分析——vue源码分析

这篇主要揭晓一点初始化的过程中this的一些巧妙用法

上来看一个大家比较熟悉的简单demo

这次采用的是基于webpack打包和编译的流程(Runtime Only),不是实时编译(Runtime and Compiler)。具体查看前面介绍的区别。

好了,上来简单介绍这个demo.vue文件

@import Vue from 'vue'

var app = new Vue({
  el:'#appId',
  mounted(){
      console.log(this.name)
  },
  data(){
      return{
          name: 'Hello World!'
      }
  }
})

请问mounted中输出数值this.name的含义是?

肯定很多人会说就是Hello World!,那请问如何取得的这个数值?不要直接意会就应该是这个,具体原因一起剖析一下吧。

首先回到Vue源代码库中的src/core/instance/init.js中,找到Vue.prototype._init这个方法。

其中细心的朋友可以发现方法的底部有很多初始化函数

    ...
/* 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')
    ...

其中我们一起看看initState(vm)这个方法。一起找到文件src/core/instance/state.js

function initState (vm: Component) {
  vm._watchers = []
  const opts = vm.$options
  if (opts.props) initProps(vm, opts.props)
  if (opts.methods) initMethods(vm, opts.methods)
  if (opts.data) {
    initData(vm)
  } else {
    observe(vm._data = {}, true /* asRootData */)
  }
  if (opts.computed) initComputed(vm, opts.computed)
  if (opts.watch && opts.watch !== nativeWatch) {
    initWatch(vm, opts.watch)
  }
}

到这里我们看看上面的代码中有个opts.data判断语句中initData(vm),这里就是关键点

看看initData中有什么好玩的吧。

let data = vm.$options.data
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {}
  if (!isPlainObject(data)) {
    data = {}
    process.env.NODE_ENV !== 'production' && warn(
      'data functions should return an object:\n' +
      'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
      vm
    )
  }
  // proxy data on instance
  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 (process.env.NODE_ENV !== 'production') {
      if (methods && hasOwn(methods, key)) {
        warn(
          `Method "${key}" has already been defined as a data property.`,
          vm
        )
      }
    }
    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)) {
      proxy(vm, `_data`, key)
    }
  }
  // observe data
  observe(data, true /* asRootData */)

哇哦,好多代码,不过咱们慢慢来分析,上来先搞清楚第一行let data = vm.$options.data 当然是获取了咱们前面定义的demo.vue文件中的data数据了,此时看后面会判断当前data是否是function类型, 当然这也是官方建议使用的返回类型。如果是函数类型,会进入到getData(data,vm)这个方法中。

哦,我们一起看看这个getData干嘛用的吧

function getData (data: Function, vm: Component): any {
  try {
    return data.call(vm)
  } catch (e) {
    handleError(e, vm, `data()`)
    return {}
  }
}

呃,原来就是讲函数借用构造一下。不懂得去查一下吧。好了我们继续回归主线继续分析

后面就是判断当前data如果不是对象则会报出一个警告

呃,好难受分析到这里,总之就是这个data是对象,不是就报错。

这里这里...

继续分析,后面看注释也能简单明白一下proxy data on instance看来用代理绑定数据了。这是啥

看到代码段,分别取出来了data对象中的key和vm中propsmethods,再往下看,原来是开始比对了。

如果data中的key和propsmethods的key一样就会报错。提出警告。为啥不能定义一样的key呢?

答案就是最后所有的对象属性都会挂在到同一个对象下$vm。所以要我说干脆就取消propsmethods让新手趟坑(作者本人也是亲历者)

那是如何挂在到this或者$vm上的呢?

其实就看代码else下面proxy的方法。里面发生了什么呢?也是本文章重点哦。来一起看看代码吧:

在同一个文件src/core/instance/state.js页面中找到proxy方法

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

通过这段可以得知,当mounted中调用this.name时候其实就是访问了this._data.name。这就是所谓的代理了。

个人认为这个和组合继承有异曲同工之妙。就是借用构造原型继承的组合用法。我相信翻译成es5之后也是这么去解释的。

好了。到这里也就明白了this的访问没那简单的。

最后还有一段observe(data, true /* asRootData */)数据的响应式处理。那就继续关注我吧,后续剖析。

总结

看来分析一个主线程要拆分很多模块,没必要的模块暂时不用看哦,先了解咱们要做什么去分析。也可以学习Vue作者的模块化拆分的精妙之处。

Hi 看这里!

大家好,我是PRO

我会陆续分享生活中的点点滴滴,当然不局限于技术。希望笔墨之中产生共鸣,每篇文章下面可以留言互动讨论。Tks bd!

博客分类

您可能感兴趣

作者推荐

呃,突然想说点啥

前端·博客

您的鼓励是我前进的动力---

使用微信扫描二维码完成支付