原理:

实现响应式需要: 1.数据联动(双向绑定) 2.需要捕获到修改
实现原理: 发布订阅 + 数据劫持 Object.defineProperty
双向绑定通过Object.defineProperty ,但要实现响应式还缺少通知,于是加入一个发布订阅

初始化加载:
使用变量的模板 —-> 生成虚拟Dom —->生成真实Dom
更新时:
劫持数据(data) —>watcher(可以看做是整个data) —-发布—> 使用变量的模板 —-> 更新虚拟Dom —->更新真实
image.png

代码

  1. <body>
  2. <div id="app">
  3. 订阅视图1-<span class="box-1"></span>
  4. 订阅视图2-<span class="box-2"></span>
  5. </div>
  6. </body>
  7. <script>
  8. // 订阅器模型
  9. let Dep = {
  10. clientList: {}, //容器
  11. //添加订阅 key: 唯一id, fn: 做什么事
  12. listen: function (key, fn) {
  13. //(短路表达式)是否添加过订阅 没有则为[] 并添加执行事件到clientList容器 统一执行
  14. (this.clientList[key] || (this.clientList[key] = [])).push(fn)
  15. },
  16. //发布
  17. trigger: function () {
  18. //不接收参数 而是通过arguments 此处转换为真数组Array.prototype.slice.call(arguments);
  19. //shift() 方法从数组中删除第一个元素,并返回该元素的值
  20. let key = Array.prototype.shift.call(arguments);
  21. //获取订阅的事件
  22. let fns = this.clientList[key];
  23. //如果没有事件
  24. if (!fns || fns.length === 0) {
  25. return false;
  26. }
  27. //执行事件
  28. //for循环简写方式 定义变量i和fn
  29. //for (let i = 0, fn; fn = fns[i++];) {
  30. //fn.apply(this, arguments)
  31. //}
  32. for (let i = 0; i < fns.length; i++) {
  33. fns[i].apply(this, arguments)
  34. }
  35. }
  36. }
  37. //数据劫持
  38. let dataHi = function ({
  39. data,
  40. tag,
  41. datakey,
  42. selector
  43. }) {
  44. let value = '';
  45. //获取目标元素
  46. let el = document.querySelector(selector);
  47. Object.defineProperty(data, datakey, {
  48. get: function () {
  49. return value
  50. },
  51. set: function (val) {
  52. value = val
  53. //发布
  54. Dep.trigger(tag, val)
  55. }
  56. })
  57. //先订阅 后发布
  58. //订阅 tag=目标元素
  59. Dep.listen(tag, function (text) {
  60. el.innerText = text;
  61. })
  62. }
  63. //调用
  64. let obj = {};
  65. dataHi({
  66. data: obj,
  67. tag: 'view-1',
  68. datakey: 'one',
  69. selector: '.box-1'
  70. })
  71. dataHi({
  72. data: obj,
  73. tag: 'view-2',
  74. datakey: 'two',
  75. selector: '.box-2'
  76. })
  77. //初次渲染 一次
  78. obj.one = '这是视图1'
  79. obj.two = '这是视图2'
  80. //更新时 劫持数据,更新这赋值重新渲染 N次
  81. </script>

image.png