ホームページ  >  記事  >  ウェブフロントエンド  >  vue2.x でのこのポインティングの問題について話しましょう。なぜ vue インスタンスを指すのでしょうか?

vue2.x でのこのポインティングの問題について話しましょう。なぜ vue インスタンスを指すのでしょうか?

青灯夜游
青灯夜游転載
2022-01-20 10:23:564060ブラウズ

この記事では、vue2.x における this のポインティングの問題について説明し、これが vue インスタンスを指す理由を紹介します。

vue2.x でのこのポインティングの問題について話しましょう。なぜ vue インスタンスを指すのでしょうか?

グループ内のコード ウォークスルーで、これがデータ、メソッド、プロパティ、および計算の値に直接呼び出せる理由について偶然言及しました。その後、誰もがいくつかの推測をしました。この疑問を解決するためにvueのソースコードを確認して、ある程度理解できたので記録として記事を書きました。

質問を投げる

通常、vue コードを開発する場合、ほとんどの場合次のように記述します

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

なぜここの this.name がデータに直接アクセスできるのでしょうか?で定義された名前について、または this.someFn はメソッドで定義された関数に直接アクセスできますか? この疑問をきっかけに、私は答えを見つけるために vue2.x のソース コードを調べ始めました。

ソース コード解析

vuevue ソース コード のソース コード アドレスは次のとおりです。まず、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 (!(thisinstanceof Vue)){} new キーワードを使用してコンストラクタを呼び出すかどうかを判断します。使用しない場合は、警告が表示されます。ここに this がスローされます。これは Vue のインスタンスを指します。普通に new キーワードを使うのであれば、_init 関数を使うだけです。とても簡単ではないでしょうか。

_init 関数の分析

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 !== &#39;production&#39; && 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 !== &#39;production&#39;) {
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }
    // expose real self
    vm._self = vm
    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    callHook(vm, &#39;beforeCreate&#39;)
    initInjections(vm) // resolve injections before data/props
    initState(vm)
    initProvide(vm) // resolve provide after data/props
    callHook(vm, &#39;created&#39;)

    /* istanbul ignore if */
    if (process.env.NODE_ENV !== &#39;production&#39; && 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 つのことを行っていることがわかります

  • プロパティの初期化
  • 初期化メソッド
  • 初期化データ
  • 計算された初期化
  • 初期化ウォッチ

最初に、初期化メソッドの機能に焦点を当てましょう

initMethods 初期化メソッド

function initMethods (vm, methods) {
    var props = vm.$options.props;
    for (var key in methods) {
      {
        if (typeof methods[key] !== &#39;function&#39;) {
          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] !== &#39;function&#39; ? noop : bind(methods[key], vm);
    }
}

initMethods は主にいくつかの判断で構成されます:

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

上記の判断を除き、最も重要なことはvue メソッド内のすべてのメソッドはインスタンス上で定義され、バインド関数を使用して関数の this を新しい Vue() のインスタンス オブジェクトである Vue インスタンス にポイントします。

これは、メソッド内のメソッドに直接アクセスできる理由を説明しています。

initData はデータを初期化します

function initData (vm) {
    var data = vm.$options.data;
    data = vm._data = typeof data === &#39;function&#39;
      ? getData(data, vm)
      : data || {};
    if (!isPlainObject(data)) {
      data = {};
      warn(
        &#39;data functions should return an object:\n&#39; +
        &#39;https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function&#39;,
        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 は何をしますか:

  • まず、instance_data に値を割り当て、getData 関数がそのデータを処理します。 data この関数は警告を与えるためのオブジェクトではなく、最終的に得られたデータを判定するためのオブジェクト
  • を返します。
  • メソッド内の関数がデータ内のキーと競合するかどうかを判断します。
  • props とデータ内のキーとの間に競合があるかどうかを判断します。
  • それが内部プライベートであるかどうかを判断します。そうでない場合は、プロキシのレイヤーを作成して _data にプロキシします
  • 最後にデータをリッスンして応答性の高いデータにします

プロキシの機能を見てみましょうdos:

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

をポイントします。残りの観察関数はこのディスカッションの範囲外です。興味のある友人はソースをチェックしてください。自分自身をコード化します。

概要

最初に挙げた質問に戻って、次のように答えます:

  • メソッド bind のメソッドは this を新しい Vue (vm) のインスタンスとして指定しており、メソッド内の関数も vm# で定義されています## がインストールされているため、this を通じて methods の関数に直接アクセスできます。

  • data

    関数によって返されたデータ オブジェクトは、新しい Vue インスタンス (vm) の _data## にも保存されます。 #上で、this.name にアクセスすると、実際にアクセスされるのは、プロキシ this._data.name の後の Object.defineProperty です。

    このデータの設計パターンの長所と短所については、この議論の一部ではないので、引き続き調査してください。
[関連する推奨事項:

vue.js ビデオ チュートリアル

]

以上がvue2.x でのこのポインティングの問題について話しましょう。なぜ vue インスタンスを指すのでしょうか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.cnで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。