In diesem Artikel geht es um das Zeigeproblem in vue2.x und es wird erläutert, warum dies auf die vue-Instanz verweist. Ich hoffe, dass es für alle hilfreich ist!
In der exemplarischen Vorgehensweise zum Gruppencode habe ich versehentlich erwähnt, warum dies direkt auf die Werte in Daten, Methoden, Requisiten und Berechnungen angewendet werden kann. Dann hatte jeder einige Vermutungen, aber es gab keine klare Antwort Schauen Sie sich dieses Problem an. Nachdem ich den Quellcode von Vue verstanden habe, werde ich einen Artikel schreiben, um ihn aufzuzeichnen.
Normalerweise wird Vue-Code entwickelt und fast immer so geschrieben
export default { data() { return { name: '彭鱼宴' } }, methods: { greet() { console.log(`hello, 我是${this.name}`) } } }
Warum kann this.name hier direkt auf den in Daten definierten Namen zugreifen, oder this.someFn kann direkt auf Methoden zugreifen Wie bei der definierten Funktion Mit dieser Frage im Hinterkopf begann ich, den Quellcode von vue2.x zu durchsuchen, um die Antwort zu finden.
Hier ist die Quellcodeadresse von Vuevue-Quellcode. Werfen wir zunächst einen Blick auf den Konstruktor der Vue-Instanz. Der Konstruktor befindet sich im Quellcodeverzeichnis /vue/src/core/instance/index.js. Es gibt nicht viel Code für einen Blick Der Konstruktor ist sehr einfach: if (!(diese Instanz von Vue)){}
Bestimmen Sie, ob das Schlüsselwort new
zum Aufrufen des Konstruktors verwendet wird. Andernfalls wird eine Warnung ausgegeben . Hier ist dies</code >Bezieht sich auf eine Instanz von Vue. Wenn das Schlüsselwort <code>new
normal verwendet wird, verwenden Sie einfach die Funktion _init
. Ist das nicht sehr einfach?
if (!(this instanceof Vue)){}
判断是不是用了 new
关键词调用构造函数,没有则抛出warning,这里的this
指的是Vue的一个实例。如果正常使用了new
关键词,就走_init
函数,是不是很简单。
_init函数分析
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
_init函数有点长,做了很多事情,这里就不一一解读,和我们此次探索相关的内容应该在initState(vm)这个函数中,我们继续到initState这个函数里看看。
initState函数分析
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) } } }
可以看出initState做了5件事情
我们先重点看看初始化methods做了什么
initMethods 初始化方法
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) } }
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); } }
除去上述说的这些判断,最重要的就是在vue实例上定义了一遍methods里所有的方法,并且使用bind函数将函数的this指向Vue实例上,就是我们new Vue()的实例对象上。
这就解释了为啥this可以直接访问到methods里的方法。
initData 初始化数据
判断methods中定义的函数是不是函数,不是函数就抛warning; 判断methods中定义的函数名是否与props冲突,冲突抛warning; 判断methods中定义的函数名是否与已经定义在Vue实例上的函数相冲突,冲突的话就建议开发者用_或者$开头命名;
initdata做了哪些事情呢:
再看下proxy函数做了什么:
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 */); }
其实这里的Object.defineProperty
就是用来定义对象的
proxy
的用处就是使this.name
指向this._data.name
剩下的observe函数不在此次探讨范围,感兴趣的朋友可以自己去看看源码。
回到一开始抛出的问题做一个解答:
methods
里的方法通过 bind
指定了this
为 new Vue的实例(vm
),methods里的函数也都定义在vm
上了,所以可以直接通过this
直接访问到methods
里面的函数。
data
函数返回的数据对象也都存储在了new Vue的实例(vm
)上的_data
上了,访问 this.name
时实际访问的是Object.defineProperty
代理后的 this._data.name
_init-Funktionsanalyse
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); }
initState-Funktionsanalyserrreee
Es ist ersichtlich, dass initState 5 Dinge tut🎜Object.defineProperty
Hier ist der Zweck vonproxy
, der zum Definieren von Objekten verwendet wird, darin, this.name
auf this._data.name
verweisen zu lassen 🎜🎜der Rest Die Beobachtungsfunktion ist nicht Gegenstand dieser Diskussion. Interessierte Freunde können sich den Quellcode selbst ansehen. 🎜🎜🎜Zusammenfassung🎜🎜🎜Gehen Sie zurück zur am Anfang gestellten Frage und geben Sie eine Antwort: 🎜methods code> Durch <code>bind
wird this
als Instanz von new Vue (vm
) angegeben, und die Funktionen in Methoden werden auch in vm
, sodass Sie über this
direkt auf die Funktionen in methods
zugreifen können. 🎜🎜data
zurückgegebenen Datenobjekte werden auch in _data
auf der neuen Vue-Instanz (vm
) gespeichert Beim Zugriff auf this.name
wird tatsächlich auf this._data.name
nach dem Object.defineProperty
-Proxy zugegriffen. 🎜🎜🎜🎜Was die Vor- und Nachteile dieses Datenentwurfsmusters betrifft, können Sie weiter erforschen, schließlich ist es nicht Teil dieser Diskussion. 🎜🎜【Verwandte Empfehlung: 🎜vue.js Video-Tutorial🎜】🎜Das obige ist der detaillierte Inhalt vonLassen Sie uns über das Zeigeproblem in vue2.x sprechen. Warum zeigt es auf die Vue-Instanz?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!