This article will talk about the pointing problem of this in vue2.x, and introduce why this points to the vue instance. I hope it will be helpful to everyone!
The code walkthrough in the group accidentally mentioned why this can be directly called to the values in data, methods, props, and computed. Then everyone had some guesses, but none of them were clear. In order to clarify this question, I checked the source code of vue. I have some understanding and wrote an article to record it.
Normally develop vue code, almost always write it like this
export default { data() { return { name: '彭鱼宴' } }, methods: { greet() { console.log(`hello, 我是${this.name}`) } } }
Why can this.name here directly access data? What about the name defined in, or this.someFn can directly access the function defined in methods? With this question, I started to look at the source code of vue2.x to find the answer.
Here is the source code address of vuevue source code. Let’s first take a look at the constructor of the vue instance. The constructor is in the source code directory/vue/src/core/instance/index.js. There is not much code. Post it all to see.
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
The constructor is very Simple, if (!(this instanceof Vue)){}
Determine whether the new
keyword is used to call the constructor, if not, a warning will be thrown, here is this
refers to an instance of Vue. If the new
keyword is used normally, just use the _init
function. Isn’t it very simple?
_init function analysis
let uid = 0 export function initMixin (Vue: Class<Component>) { 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) } } }
_The init function is a bit long and does a lot of things, so I won’t explain it one by one here, and we will explore it this time The relevant content should be in the initState(vm) function. Let's continue to the initState function.
initState function analysis
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) } }
It can be seen that initState has done 5 things
Let’s first focus on what the initialization methods do
initMethods initialization method
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 mainly consists of some judgments:
判断methods中定义的函数是不是函数,不是函数就抛warning; 判断methods中定义的函数名是否与props冲突,冲突抛warning; 判断methods中定义的函数名是否与已经定义在Vue实例上的函数相冲突,冲突的话就建议开发者用_或者$开头命名;
Except for the above judgments, the most important thing is in vue All methods in methods are defined on the instance, and the bind function is used to point the function's this to the Vue instance, which is the instance object of our new Vue().
This explains why this can directly access the methods in methods.
initData initializes data
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 */); }
What does initdata do:
Let’s take a look at what the proxy function does:
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); }
In fact, the Object.defineProperty
here is used to define the object. The purpose of
proxy
is to use this.name
Point tothis._data.name
The remaining observe functions are not within the scope of this discussion. Interested friends can check out the source code themselves.
Go back to the question raised at the beginning and give an answer:
methods
The method in bind
specifies this
as an instance of new Vue (vm
), and the functions in methods are also defined in vm
is installed, so you can directly access the functions in methods
through this
.
data
The data objects returned by the function are also stored in _data## on the new Vue instance (
vm) #Up, when accessing
this.name, what is actually accessed is
Object.defineProperty after proxy
this._data.name.
vue.js video tutorial]
The above is the detailed content of Let's talk about the pointing problem of this in vue2.x. Why does it point to the vue instance?. For more information, please follow other related articles on the PHP Chinese website!