Vue数据双向绑定探究

栏目: JavaScript · 发布时间: 6年前

内容简介:使用过vue的小伙伴都会感觉,哇,这个框架对开发者这么友好,简直都要笑出声了。确实,使用过vue的框架做开发的人都会感觉到,以前写一大堆操作dom,bom的东西,现在用不着了,对开发者来说更容易去注重对操作逻辑的思考和实现,省了不少事儿呢!!!我是直接从原生js,jq的开发用过度到使用vue,对这个框架也是喜爱有加,闲来无事,去看了看它的一些实现原理。

前面的啰嗦话,写一点吧,或许就有点用呢

使用过vue的小伙伴都会感觉,哇,这个框架对开发者这么友好,简直都要笑出声了。

确实,使用过vue的框架做开发的人都会感觉到,以前写一大堆操作dom,bom的东西,现在用不着了,对开发者来说更容易去注重对操作逻辑的思考和实现,省了不少事儿呢!!!

我是直接从原生js,jq的开发用过度到使用vue,对这个框架也是喜爱有加,闲来无事,去看了看它的一些实现原理。

下面来介绍一下vue的一个非常"牛逼"的功能,数据双向绑定,也就是我们在项目里用到的v-model指令。

v-model在vue官方文档上是介绍在"表单输入绑定"那一节。

对于表单,大家肯定用得都已经超级熟练了,对于<input>、<textarea>和<select>标签在项目里面使用都已经没话说了

官方提到的v-model是一个语法糖,为什么这么说呢?下面看个例子:

<div id="test1">
  <input v-model="input">
  <span>input: {{ input }}</span>
</div>

如上,是一个简单的使用v-model的双向绑定,我们在改变input这个变量的值,即在输入框中去写内容的时候,在span标签内的插值(mustache)会同步更新我们刚刚输入的值

其实上面的也可以这样写:

<div id="test1">
  <input v-on:input="input = $event.target.value" v-bind:value='input'>
  <span>input: {{ input }}</span>
</div>

好了,前面啰里啰嗦半天,现在进入正题

想对比react和angular的双向绑定实现,我也不清楚,哈哈哈,直接说vue吧,不扯了

Reactivity 响应式系统

拿尤雨溪大佬做vue测试的的那个例子来说吧(购物车的例子)

<div id='app'>
    <div>
      <span>价格:</span>
      <input v-model.number="price">
    </div>
    <div>
      <span>数量:</span>
      <input v-model.number="quantity">
    </div>
    <p>价格:{{ price }}</p>
    <p>数量:{{ quantity }}</p>
    <p>总计:{{ total }}</p>
  </div>
data() {
      return {
        price: 5,
        quantity: 3
      }
    },
    computed: {
      total() {
        return this.price * this.quantity;
      }
    }

当我们在使用输入框的值的时候,下面的total会更新,我们对应输入值的变量也会更新

哇,好神奇,为什么呢,这不是JavaScript编程常规的工作方式!!!

因为我们用原生js写的时候是这样的:

let price = 5;
let quantity = 3;
let total = price * quantity;     // 等于15吧

price = 10;    // 改变价格;
console.log(total);    // bingo,打印的还是15

我们需要在找一种办法,把要运行计算的total放到别的时候去运行,当我们的价格、数量变化的时候执行

let price = 5;
let quantity = 3;
let total = 0;
let storage = [];   // 储存将要计算的操作,等到变量变化的时候去执行

let target= () => { total = price * quantity;}
function record () {
    storage.push(target);
}
function replay() {
    storage.forEach(run => run());
}

record();
target();

price = 10;
console.log(total);    // 依然是15
replay();
console.log(total);    // 执行结果是30

目的达到,但是这样肯定不是vue用来扩展使用的方式,我们用ES6的class类来做一个可维护的扩展,实现一个标准的观察者模式的依赖类

class Depend {
  constructor () {
    this.subscribers = [];
  }
  depend() {
    if(target && this,this.subscribers.includes(target)) {
      this.subscribers.push(target);
    }
  }
  notify() {
    this.subscribers.forEach(sub => sub());
  }
}

// 来执行上面写的class
const dep = new Depend();
let price = 5;
let quantity = 3;
let total = 0;
let target = () => { total = price * quantity };

dep.depend();
target();

console.log(total);    // total是15
price = 10;
console.log(total);    // 因为没有执行target,依旧是15
dep.notify();
console.log(total);    // 执行了存入的target,total为30

为了给每一个变量都设置一个Depend类。并且很好地控制监视更新的匿名函数的行为,我们把上面的代码做一些调整:

let target = () => { total = price * quantity };
dep.depend();
target();

修改为:

watcher(() => { total = price * quantity });

然后我们在watcher函数里面来做刚刚上面的result的设置和执行的功能

function watcher(fn) {
  target = fn;
  dep.depend();
  target();
  target = null;           // 重置一下,等待储存和执行下一次
}

这儿就是官方文档提到的订阅者模式:在每次watcher函数执行的时候,把参数fn设置成为我们全局目标属性,调用dep.depend()将目标添加为订阅者,调用然后重置

然后再继续

我们的目标是把每一个变量都设置一个Depend类,但是这儿有个问题:

先存一下数据:

let data = { price: 5, quantity: 3}

假设我们每个属性都有自己的内部Depend类

Vue数据双向绑定探究

当我们运行代码时:

watcher(() => { total = data.price * data.quantity})

由于访问了data.price值,希望price属性的Depend类将我们的匿名函数(存储在目标中)推送到其订阅者数组(通过调用dep.depend())。由于访问了data.quantity,还希望quantity属性Depend类将此匿名函数(存储在目标中)推送到其订阅者数组中。

Vue数据双向绑定探究

如果有另一个匿名函数,只访问data.price,希望只推送到价格属性Depend类。

Vue数据双向绑定探究

什么时候想要在价格订阅者上调用dep.notify()?我希望在设定价格时调用它们。为此,我们需要一些方法来挂钩数据属性(价格或数量),所以当它被访问时我们可以将目标保存到我们的订阅者数组中,当它被更改时,运行存储在我们的订阅者数组中的函数。let's go

Object.defineProperty来解决这个问题

Object.defineProperty函数是简单的ES5 JavaScript。它允许我们为属性定义getter和setter函数。继续啃

let data = { price: 5, quantity: 3};
let value = data.price
Object.defineProperty(data, 'price', {
  getter() {
    console.log(`获取price的值: ${value}`);
    return value;
  },
  setter(newValue) {
    console.log(`更改price的值': ${newValue}`);
    value = newValue;
  }
})
total = data.price * data.quantity;
data.price = 10;      // 更改price的值

上面通过defineProperty方法给price设置了获取和修改值的操作

如何给data对象所有的都加上这个defineProperty方法去设置值

大家还记得Object.keys这个方法吗?返回对象键的数组,咱们把上面的代码改造一下

let data = { price: 5, quantity: 3 };

Object.keys(data).forEach(key => {
  let value = data[key];
  Object.defineProperty(data, key, {
    getter() {
      console.log(`获取 ${key} 的值: ${value}`);
      return value;
    },
    setter(newValue) {
      console.log(`更改 ${key} 值': ${newValue}`);
      value = newValue;
    }
  })
})
total = data.price * data.quantity;
data.price = 10; // 更改price的值

接着上面的东西,在每次运行完获取key的值,我们希望key能记住这个匿名函数(target),这样有key的值变化的时候,它将触发这个函数来重新计算,大致思路是这样的:

getter函数执行的时候,记住这个匿名函数,当值在发生变化的时候再次运行它

setter函数执行的时候,运行保存的匿名函数,把当前的值存起来

用上面定义的Depend类来说就是:

getter执行,调用dep.depend()来保存当前的target

setter执行,在价格上调用dep.notify(),重新运行所有的target

来来来,把上面的东西结合到一起来

let data = { price: 5, quantity: 3 };
let total = 0;
let target = null;

class Depend {
  constructor() {
    this.subscribers = [];
  }
  depend() {
    if (target && this, this.subscribers.includes(target)) {
      this.subscribers.push(target);
    }
  }
  notify() {
    this.subscribers.forEach(sub => sub());
  }
}

Object.keys(data).forEach(key => {
  let value = data[key];
  const dep = new Depend();

  Object.defineProperty(data, key, {
    getter() {
      dep.depend();
      return value;
    },
    setter(newValue) {
      value = newValue;
      dep.notify();
    }
  })
});

function watcher(fn) {
  target = fn;
  target();
  target = null;
}

watcher(() => {
  total = data.price * data.quantity;
});

至此,vue的数据双向绑定已经实现,当我们去改变price和quantity的值,total会实时更改

然后咱们来看看vue的文档里面提到的这个插图:

Vue数据双向绑定探究

是不是感觉这个图很熟悉了?对比咱们上面研究的流程,这个图的data和watcher就很清晰了,大致思路如此,可能vue的内部实现和封装远比我这个研究流程内容大得多、复杂得多,不过有了这样的一个流程思路,再去看vue双向绑定源码估计也能看懂个十之八九了。

听说vue3.0准备把这个数据劫持的操作用ES6提供的proxy来做,效率更高,期待!!!!

参考和学习原文(可能需要翻墙,毕竟是外站啊)


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Web Designer's Idea Book, Vol. 2

The Web Designer's Idea Book, Vol. 2

Patrick McNeil / How / 2010-9-19 / USD 30.00

Web Design Inspiration at a Glance Volume 2 of The Web Designer's Idea Book includes more than 650 new websites arranged thematically, so you can easily find inspiration for your work. Auth......一起来看看 《The Web Designer's Idea Book, Vol. 2》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具