关于Vue.js

Vue.js是一款MVVM框架,上手快速简单易用,通过响应式在修改数据的时候更新视图。Vue.js的响应式原理依赖于Object.defineProperty,尤大大在Vue.js文档中就已经提到过,这也是Vue.js不支持IE8 以及更低版本浏览器的原因。Vue通过设定对象属性的 setter/getter 方法来监听数据的变化,通过getter进行依赖收集,而每个setter方法就是一个观察者,在数据变更的时候通知订阅者更新视图。

将数据data变成可观察(observable)的

那么Vue是如何将所有data下面的所有属性变成可观察的(observable)呢?

  1. function observe(value, cb) {
  2. Object.keys(value).forEach((key) => defineReactive(value, key, value[key] , cb))
  3. }
  4. function defineReactive (obj, key, val, cb) {
  5. Object.defineProperty(obj, key, {
  6. enumerable: true,
  7. configurable: true,
  8. get: ()=>{
  9. /*....依赖收集等....*/
  10. /*Github:https://github.com/answershuto*/
  11. return val
  12. },
  13. set:newVal=> {
  14. val = newVal;
  15. cb();/*订阅者收到消息的回调*/
  16. }
  17. })
  18. }
  19. class Vue {
  20. constructor(options) {
  21. this._data = options.data;
  22. observe(this._data, options.render)
  23. }
  24. }
  25. let app = new Vue({
  26. el: '#app',
  27. data: {
  28. text: 'text',
  29. text2: 'text2'
  30. },
  31. render(){
  32. console.log("render");
  33. }
  34. })

为了便于理解,首先考虑一种最简单的情况,不考虑数组等情况,代码如上所示。在initData中会调用observe这个函数将Vue的数据设置成observable的。当_data数据发生改变的时候就会触发set,对订阅者进行回调(在这里是render)。

那么问题来了,需要对app._data.text操作才会触发set。为了偷懒,我们需要一种方便的方法通过app.text直接设置就能触发set对视图进行重绘。那么就需要用到代理。

代理

我们可以在Vue的构造函数constructor中为data执行一个代理proxy。这样我们就把data上面的属性代理到了vm实例上。

  1. _proxy.call(this, options.data);/*构造函数中*/
  2. /*代理*/
  3. function _proxy (data) {
  4. const that = this;
  5. Object.keys(data).forEach(key => {
  6. Object.defineProperty(that, key, {
  7. configurable: true,
  8. enumerable: true,
  9. get: function proxyGetter () {
  10. return that._data[key];
  11. },
  12. set: function proxySetter (val) {
  13. that._data[key] = val;
  14. }
  15. })
  16. });
  17. }

我们就可以用app.text代替app._data.text了。