【若川视野 x 源码共读】第23期 | 为什么 Vue2 this 能够直接获取到 data 和 methods

180 阅读2分钟

本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。

1.学习内容和学习资料:

vue(2.x)源码( 可选 ):

川哥的文章:juejin.cn/post/701092…

2.关键步骤

1.示例工程代码准备

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script src="https://unpkg.com/vue@2.6.14/dist/vue.js"></script>
    <script>
      const vm = new Vue({
        data: {
          name: "我是若川"
        },
        methods: {
          sayName() {
            console.log(this.name);
          }
        }
      });
      console.log(vm.name);
      console.log(vm.sayName());
    </script>
  </body>
</html>

2.安装http-server

npm i -g http-server

3.启动并调试项目

G:\WorkSpace\vue2-examples>http-server .

3.关键内容记录

3.1 函数调用链条

3.2 重要函数实现

bind函数整合了原生和垫片(腻子)脚本,如果浏览器原生不支持则使用垫片:

function polyfillBind (fn, ctx) {
  function boundFn (a) {
    var l = arguments.length;
    return l
      ? l > 1
      ? fn.apply(ctx, arguments)
    : fn.call(ctx, a)
    : fn.call(ctx)
  }

  boundFn._length = fn.length;
  return boundFn
}

function nativeBind (fn, ctx) {
  return fn.bind(ctx)
}

var bind = Function.prototype.bind
? nativeBind
: polyfillBind;

垫片脚本使用apply和call来实现了一个bind函数。在caniuse上可以看到浏览器对bind方法的支持程度:

另外发现浏览器对call和apply的支持程度要好于bind:

getData函数内部是调用data函数获取data对象:

function getData (data, vm) {
  // #7573 disable dep collection when invoking data getters
  pushTarget();
  try {
    return data.call(vm, vm)
  } catch (e) {
    handleError(e, vm, "data()");
    return {}
  } finally {
    popTarget();
  }
}

proxy函数其实就是用 Object.defineProperty 定义对象,这里用处是:this.xxx 则是访问的 this._data.xxx:

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

4.收获和总结

1.大致了解Vue构造函数流程

2.了解vue可以通过this访问methods和data的原理:

能够访问methods是因为使用bind指定了方法中的this为vue示例(vm), 然后再vue实例上增加了函数名为methods中函数名的键,指向相应的函数:

vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm);

通过 this 能够访问到 data 里面的数据是因为data里的属性最终会存储到new Vue的实例(vm)上的 _data对象中,访问 this.xxx,是访问Object.defineProperty代理后的 this._data.xxx。

Object.defineProperty(vm, 'data', sharedPropertyDefinition)