Vue.js的this如何取到data和method里的属性?


theme: cyanosis
highlight: vs2015

「我正在参与掘金会员专属活动-源码共读第一期」

本篇文章介绍的是Vue.js如何取到datamethods里的属性?

准备工作

  • 克隆源码到本地
git clone https://github.com/vuejs/vue.git

下载完毕后,用vscode打开,目光移动到package.jsonscripts属性,我们看到有devbuilddev会启动一个开发环境的服务,也就是说,我们在源码里做的改动,都会及时生效。build就是打包。和我们平时开发Vue.js项目是一个道理。

我们首先安装一下Vue.js项目的依赖(使用pnpm),然后运行npm run dev。这样的好处就是我们能随时看到代码改动后的效果。

image.png

  • 接下来我们在examples目录下创建一个html文件,引入打包后的vue.js
<!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>
   <div id="app">
     <h1 @click="changeMsg">hello {{msg}}</h1>
   </div>
   <script src="../dist/vue.js"></script>
   <script>
     const vm = new Vue({
       el: '#app',
       data: {
         msg: 'world'
       },
       methods: {
         changeMsg() {
           this.msg = 'me'
         }
       }
     })
   </script>
 </body>
</html>
  • 安装一个serve全局包启动, 在根目录运行一下serve,就能看到页面展示出来了。

调试源码

我们这里使用谷歌浏览器调试,F12找到sources面板如下图所示的位置打上断点,接着刷新页面,就进入了调试模式。

image.png

然后,我们就通过step into按钮进入new Vue的函数内部。 接着进入_init的内部,找到initState(vm),也就是当前文件代码的4714行,这个函数的内部就是我们要研究的部分。

进入initState内部,我们看到

  if (opts.methods)
          initMethods(vm, opts.methods);
      if (opts.data) {
          initData(vm);
      }

initMethods

function initMethods(vm, methods) {
      var props = vm.$options.props;
      for (var key in methods) {
          {
              if (typeof methods[key] !== 'function') {
                  warn$2("Method \"".concat(key, "\" has type \"").concat(typeof methods[key], "\" in the component definition. ") +
                      "Did you reference the function correctly?", vm);
              }
              if (props && hasOwn(props, key)) {
                  warn$2("Method \"".concat(key, "\" has already been defined as a prop."), vm);
              }
              if (key in vm && isReserved(key)) {
                  warn$2("Method \"".concat(key, "\" conflicts with an existing Vue instance method. ") +
                      "Avoid defining component methods that start with _ or $.");
              }
          }
          vm[key] = typeof methods[key] !== 'function' ? noop : bind$1(methods[key], vm);
      }
  }
  • 首先判断组件内部是否声明了函数
  • 其次判断是否和props、保留键名的名字冲突了
  • 最后是处理逻辑,如果对应值的类型是函数将传入的vm对应的属性赋值,否则为noop,赋值的函数这里做了一个强绑(使用的bind, this指向vm)。这个bind$来自原生的bind方法
var bind$1 = Function.prototype.bind ? nativeBind : polyfillBind;

initData

调试完了initMehtods后,就开始initData,我们使用step out按钮就跳出了当前函数,接着进入initData内部。

function initData(vm) {
      var data = vm.$options.data;
      data = vm._data = isFunction(data) ? getData(data, vm) : data || {};
      if (!isPlainObject(data)) {
          data = {};
          warn$2('data functions should return an object:\n' +
                  'https://v2.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$2("Method \"".concat(key, "\" has already been defined as a data property."), vm);
              }
          }
          if (props && hasOwn(props, key)) {
              warn$2("The data property \"".concat(key, "\" is already declared as a prop. ") +
                      "Use prop default value instead.", vm);
          }
          else if (!isReserved(key)) {
              proxy(vm, "_data", key);
          }
      }
      // observe data
      var ob = observe(data);
      ob && ob.vmCount++;
  }

逻辑和initMethods类似,和propsmethods做了比对,最后通过proxydata,绑定到vm

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

最终我们知道data的值是通过Object.defineProperty,实现绑定的。

结束语

我们要研究一个源码,首先要准备源码、serve、和调试工具(谷歌浏览器),然后进入代码的内部,才能看的清楚明白,我们就此知道了Vue.jsthis如何取到datamethods的属性。

© 版权声明
THE END
喜欢就支持一下吧
点赞5 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容