vue2.0中data如何被挂载的

在面试中被问到这个问题。

<html>
  <head>
    <style type="text/css">
      .div-cls {
        color: red;
      }
    </style>
  </head>
  <body>
    <script src="./vue.js"></script>
    <div id="app">
      <div id="div-1" class="div-cls">{{msg}}</div>
    </div>
    <script>
      const app = new Vue({
          data(){
              return{
                  msg: 'hello, world'
              }
          },
          created(){
              setTimeout(() => {
                  console.log('liubbc msg: ', this.msg)
              }, 2000)
          }
      }).$mount("#app");
    </script>
  </body>
</html>

也就是怎么可以通过this.msg就可以获取到data中定义的msg变量?

面试官想考察vue对data中属性进行了代理,以及为啥对其进行代理。

在进行vue实例化的时候对data进行了处理,步骤如下:

Vue.prototype._init  ->   initState  ->  initData

  function initData (vm) {
    var data = vm.$options.data;
    data = vm._data = typeof data === 'function'
      ? getData(data, vm)
      : data || {};
      console.log('liubbc data: ', 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); //在这里对data中定义的变量进行代理,代理到vm._data对象下
      }
    }
    // observe data
    observe(data, true /* asRootData */);
  }
  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);
  }

不进行代理的话(把proxy(vm, "_data", key);这一行给注释掉),怎么才能访问到data中的变量呢?

<div id="div-1" class="div-cls">{{_data.msg}}</div>

或

<div id="div-1" class="div-cls">{{$data.msg}}</div>

从代码中发现把data赋值给了vm._data,所以可以用_data.msg,另外搜索_data发现通过$data 也可以取到msg。

  function stateMixin (Vue) {
    // flow somehow has problems with directly declared definition object
    // when using Object.defineProperty, so we have to procedurally build up
    // the object here.
    var dataDef = {};
    dataDef.get = function () { return this._data };
    var propsDef = {};
    propsDef.get = function () { return this._props };
    {
      dataDef.set = function () {
        warn(
          'Avoid replacing instance root $data. ' +
          'Use nested data properties instead.',
          this
        );
      };
      propsDef.set = function () {
        warn("$props is readonly.", this);
      };
    }
    Object.defineProperty(Vue.prototype, '$data', dataDef);
    Object.defineProperty(Vue.prototype, '$props', propsDef);

    Vue.prototype.$set = set;
    Vue.prototype.$delete = del;

    Vue.prototype.$watch = function (
      expOrFn,
      cb,
      options
    ) {
      var vm = this;
      if (isPlainObject(cb)) {
        return createWatcher(vm, expOrFn, cb, options)
      }
      options = options || {};
      options.user = true;
      var watcher = new Watcher(vm, expOrFn, cb, options);
      if (options.immediate) {
        try {
          cb.call(vm, watcher.value);
        } catch (error) {
          handleError(error, vm, ("callback for immediate watcher \"" + (watcher.expression) + "\""));
        }
      }
      return function unwatchFn () {
        watcher.teardown();
      }
    };
  }

从上可以看出为什么对data进行代理:更加方便的操作data中的数据。如果不进行代理的话,我们访问data中的变量就得这样:this._data.msg 或 this.$data.msg  不方便。所以vue进行了代理,代理后我们就可以直接访问了: this.msg。

我们也可以对其实现原理进行模拟,代码如下:


//vue是怎么通过this.code 访问到data声明的code的

var sharedPropertyDefinition = {
    enumerable: true,
    configurable: true,
    get: () => {},
    set: () => {} 
}

function proxy(vm, sourceKey, key) {
    sharedPropertyDefinition.get = function() {
        return this[sourceKey][key]
    }

    sharedPropertyDefinition.set = function (val) {
        this[sourceKey][key] = val
    }

    Object.defineProperty(vm, key, sharedPropertyDefinition)
}

// 把vm的属性传给data函数使用
function getData(fn, vm) {
    return fn.call(vm, vm)
}

var vm = {
    props: {
        b: 123
    }
};

var data = function() {
    console.log('vm的props');
    console.log(this.props);

    return {
        code: 1,
        value: 'text'
    }
}

vm._data = getData(data, vm);

console.log('vm: ', JSON.stringify(vm)); // vm:  {"props":{"b":123},"_data":{"code":1,"value":"text"}}

proxy(vm, '_data', 'code');
proxy(vm, '_data', 'value');

console.log(vm.code); // '1'
console.log(vm.value); // 'text'

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值