千家信息网

this为什么指向vue实例

发表于:2025-01-19 作者:千家信息网编辑
千家信息网最后更新 2025年01月19日,这篇文章主要为大家展示了"this为什么指向vue实例",内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下"this为什么指向vue实例"这篇文章吧。抛出问题正常
千家信息网最后更新 2025年01月19日this为什么指向vue实例

这篇文章主要为大家展示了"this为什么指向vue实例",内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下"this为什么指向vue实例"这篇文章吧。

抛出问题

正常开发vue代码,大差不差都会这么写

export default {    data() {        return {            name: '彭鱼宴'        }    },    methods: {        greet() {            console.log(`hello, 我是${this.name}`)        }    }}

为什么这里的this.name可以直接访问到data里定义的name呢,或者this.someFn可以直接访问到methods里定义的函数呢,带着这个问题开始看vue2.x的源码找答案。

源码分析

这里先贴个vue的源码地址vue源码。我们先看看vue实例的构造函数,构造函数在源码的目录/vue/src/core/instance/index.js下,代码量不多,全部贴出来看看

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)stateMixin(Vue)eventsMixin(Vue)lifecycleMixin(Vue)renderMixin(Vue)export default Vue

构造函数很简单,if (!(this instanceof Vue)){} 判断是不是用了 new 关键词调用构造函数,没有则抛出warning,这里的this指的是Vue的一个实例。如果正常使用了new关键词,就走_init函数,是不是很简单。

_init函数分析

let uid = 0export function initMixin (Vue: Class) {  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)    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(`vue ${vm._name} init`, startTag, endTag)    }    if (vm.$options.el) {      vm.$mount(vm.$options.el)    }  }}

_init函数有点长,做了很多事情,这里就不一一解读,和我们此次探索相关的内容应该在initState(vm)这个函数中,我们继续到initState这个函数里看看。

initState函数分析

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

可以看出initState做了5件事情

  • 初始化props

  • 初始化methods

  • 初始化data

  • 初始化computed

  • 初始化watch

我们先重点看看初始化methods做了什么

initMethods 初始化方法

function initMethods (vm, methods) {    var props = vm.$options.props;    for (var key in methods) {      {        if (typeof methods[key] !== 'function') {          warn(            "Method \"" + key + "\" has type \"" + (typeof methods[key]) + "\" in the component definition. " +            "Did you reference the function correctly?",            vm          );        }        if (props && hasOwn(props, key)) {          warn(            ("Method \"" + key + "\" has already been defined as a prop."),            vm          );        }        if ((key in vm) && isReserved(key)) {          warn(            "Method \"" + key + "\" conflicts with an existing Vue instance method. " +            "Avoid defining component methods that start with _ or $."          );        }      }      vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm);    }}

initMethods主要是一些判断:

判断methods中定义的函数是不是函数,不是函数就抛warning;判断methods中定义的函数名是否与props冲突,冲突抛warning;判断methods中定义的函数名是否与已经定义在Vue实例上的函数相冲突,冲突的话就建议开发者用_或者$开头命名;

除去上述说的这些判断,最重要的就是在vue实例上定义了一遍methods里所有的方法,并且使用bind函数将函数的this指向Vue实例上,就是我们new Vue()的实例对象上。

这就解释了为啥this可以直接访问到methods里的方法。

initData 初始化数据

function initData (vm) {    var data = vm.$options.data;    data = vm._data = typeof data === 'function'      ? getData(data, vm)      : data || {};    if (!isPlainObject(data)) {      data = {};      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    var keys = Object.keys(data);    var props = vm.$options.props;    var methods = vm.$options.methods;    var i = keys.length;    while (i--) {      var key = keys[i];      {        if (methods && hasOwn(methods, key)) {          warn(            ("Method \"" + key + "\" has already been defined as a data property."),            vm          );        }      }      if (props && hasOwn(props, key)) {        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 */);}

initdata做了哪些事情呢:

  • 先在实例 _data 上赋值,getData函数处理 data 这个 function,返回的是一个对象

  • 判断最终获取到的 data, 不是对象给出警告。

  • 判断methods里的函数和data里的key是否有冲突

  • 判断props和data里的key是否有冲突

  • 判断是不是内部私有的保留属性,若不是就做一层代理,代理到 _data 上

  • 最后监听data,使之成为响应式数据

再看下proxy函数做了什么:

function noop (a, b, c) {}var sharedPropertyDefinition = {    enumerable: true,    configurable: true,    get: noop,    set: noop};function proxy (target, sourceKey, key) {    sharedPropertyDefinition.get = function proxyGetter () {      return this[sourceKey][key]    };    sharedPropertyDefinition.set = function proxySetter (val) {      this[sourceKey][key] = val;    };    Object.defineProperty(target, key, sharedPropertyDefinition);}

其实这里的Object.defineProperty就是用来定义对象的

proxy的用处就是使this.name指向this._data.name

以上是"this为什么指向vue实例"这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!

0