vue2响应式原理-笔记

211 阅读1分钟

手写简单实现vue2响应式原理
举一个例子: 小明想买一个冰墩墩, 联系淘宝商家, 商家告知没货, 商家再从厂家订购回来, 再通知小明, 小明订阅了商家冰墩墩 当冰墩墩到货, 商家发布通知订阅者小明

// index.js
// 订阅器模型
let Dep = {
  chientList: {}, // 容器列表
  // 添加订阅  key键  fn为商品库  可以订阅多个商品
  listen: function (key, fn) {
    // 判断有没有小明的订阅库,没有就建立订阅容器,有的话把商品添加进去
    // if (!this.chientList[key]) {
    //   this.chientList[key] = []
    // }
    // this.chientList[key].push(fn)
    // 简化代码  短路表达式
    (this.chientList[key] || (this.chientList[key] = [])).push(fn)
  },
  // 发布 数据变化 通知订阅者
  trigger: function () {
    // 使用数组的原型方法,把arguments类数组 转为数组
    let key = Array.prototype.shift.call(arguments),
      fns = this.chientList[key];
    // 如果没有这个人,或者没有关注商品就不再执行通知
    if (!fns || fns.length == 0) {
      return false
    }
    for (let i = 0, fn; fn = fns[i++];) {
      // 绑定this
      fn.apply(this, arguments)
    }
  }
}
// 数据劫持  data劫持的数据  tag目标元素  目标的key 选择器
let dataHi = function ({ data, tag, datakey, selector }) {
  let value = '',
    el = document.querySelector(selector);  // 获取dom元素
  // 结合v-model原理
  Object.defineProperty(data, datakey, {
    // 取值
    get: function () {
      console.log('取值');
      return value;
    },
    // 设置值
    set: function (val) {
      console.log('设置值');
      value = val
      // 发布
      Dep.trigger(tag, val)
    }
  })
  // 订阅
  Dep.listen(tag, function (text) {
    el.innerHTML = text
  })
}
<!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>vue2响应式原理</title>
</head>

<body>
  <div id="app">
    <p>one: <span class="one"></span></p>
    <p>two: <span class="two"></span></p>
  </div>
  <script src="./index.js"></script>
  <script>
    let obj = {}
    dataHi({
      data: obj,
      tag: 'view-1',
      datakey: 'one',
      selector: '.one'
    })
    dataHi({
      data: obj,
      tag: 'view-2',
      datakey: 'two',
      selector: '.two'
    })
    obj.one = '1111111111'
    obj.two = '2222222222'
  </script>
</body>

</html>