怎么使用Object.defineProperty实现简易的vue功能

其他教程   发布日期:2025年03月23日   浏览次数:97

这篇“怎么使用Object.defineProperty实现简易的vue功能”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“怎么使用Object.defineProperty实现简易的vue功能”文章吧。

vue 双向绑定的原理

实现

  1. vue
的双向绑定,
  1. v-text
  1. v-model
  1. v-on
方法

  1. Vue
响应系统,其核心有三点:
  1. observewatcherdep
    1. observe
    :遍历
    1. data
    中的属性,使用
    1. Object.defineProperty
    1. get/set
    方法- 对其进行数据劫持;
    1. dep
    :每个属性拥有自己的消息订阅器
    1. dep
    ,用于存放所有订阅了该属性的观察者对象;
    1. watcher
    :观察者(对象),通过
    1. dep
    实现对响应属性的监听,监听到结果后,主动触发自己的回调进行响应。
  1. class MinVue {
  2. constructor(options) {
  3. this.$data = options.data;
  4. this.$methods = options.methods;
  5. this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : el;
  6. this.bindData(this.$data);
  7. new Observer(this.$data);
  8. new Compile(this);
  9. }
  10. bindData(data) {
  11. Object.keys(data).forEach(key => {
  12. /*
  13. *当value值为基本数据类型时,this.key数据变化,对用的$data不会变化
  14. *只有当value值为对象或者数组类型时,数据变化会同步
  15. **/
  16. Object.defineProperty(this, key, {
  17. enumerable: true,
  18. configurable: true,
  19. get: () => {
  20. return data[key];
  21. },
  22. set: newValue => {
  23. data[key] = newValue;
  24. },
  25. });
  26. });
  27. }
  28. }
  29. class Observer {
  30. constructor(data) {
  31. this.work(data);
  32. }
  33. work(data) {
  34. if (Object.prototype.toString.call(data) === '[object Object]') {
  35. Object.keys(data).forEach(key => {
  36. this.defineReactive(data, key, data[key]);
  37. });
  38. }
  39. }
  40. defineReactive(data, key, value) {
  41. const observer = this;
  42. const dep = new Dep();
  43. // 当value为对象时,递归调用
  44. this.work(value);
  45. /*
  46. *当一个变量需要影响多个元素时,即一个变量变化需要响应多个元素内容的变化
  47. *先记录下所有的依赖
  48. */
  49. Object.defineProperty(data, key, {
  50. enumerable: true,
  51. configurable: true,
  52. get: () => {
  53. if (Dep.target) {
  54. dep.add(Dep.target);
  55. }
  56. return value;
  57. },
  58. set: newValue => {
  59. value = newValue;
  60. // 赋新值后,新值有可能为对象,重新绑定get set方法
  61. observer.work(newValue);
  62. dep.notify();
  63. },
  64. });
  65. }
  66. }
  67. class Dep {
  68. constructor() {
  69. this.watcher = new Set();
  70. }
  71. add(watcher) {
  72. if (watcher && watcher.update) this.watcher.add(watcher);
  73. }
  74. notify() {
  75. this.watcher.forEach(watch => watch.update());
  76. }
  77. }
  78. class Watcher {
  79. constructor(vm, key, cb) {
  80. Dep.target = this;
  81. this.vm = vm;
  82. this.key = key;
  83. // 会触发Observer定义的getter方法,收集Dep.target
  84. this._old = vm.$data[key];
  85. this.cb = cb;
  86. Dep.target = null;
  87. }
  88. update() {
  89. const newValue = this.vm.$data[this.key];
  90. this.cb(newValue);
  91. this._old = newValue;
  92. }
  93. }
  94. class Compile {
  95. constructor(vm) {
  96. this.vm = vm;
  97. this.methods = vm.$methods;
  98. this.compile(vm.$el);
  99. }
  100. compile(el) {
  101. const childNodes = el.childNodes;
  102. Array.from(childNodes).forEach(node => {
  103. if (this.isTextNode(node)) {
  104. this.compileTextNode(node);
  105. } else if (this.isElementNode(node)) {
  106. this.compileElement(node);
  107. }
  108. if (node.childNodes && node.childNodes.length) this.compile(node);
  109. });
  110. }
  111. isTextNode(node) {
  112. return node.nodeType === 3;
  113. }
  114. isElementNode(node) {
  115. return node.nodeType === 1;
  116. }
  117. compileTextNode(node) {
  118. // .+?正则懒惰匹配
  119. const reg = /{{(.+?)}}/g;
  120. const text = node.textContent;
  121. if (reg.test(text)) {
  122. let key = RegExp.$1.trim();
  123. node.textContent = text.replace(reg, this.vm[key]);
  124. new Watcher(this.vm, key, newValue => {
  125. node.textContent = newValue;
  126. });
  127. }
  128. }
  129. compileElement(node) {
  130. const attrs = node.attributes;
  131. if (attrs.length) {
  132. Array.from(attrs).forEach(attr => {
  133. if (this.isDirective(attr.name)) {
  134. // 根据v-来截取一下后缀属性名
  135. let attrName = attr.name.indexOf(':') > -1 ? attr.name.substr(5) : attr.name.substr(2);
  136. let key = attr.value;
  137. this.update(node, attrName, key, this.vm[key]);
  138. }
  139. });
  140. }
  141. }
  142. isDirective(dir) {
  143. return dir.startsWith('v-');
  144. }
  145. update(node, attrName, key, value) {
  146. if (attrName === 'text') {
  147. node.textContent = value;
  148. new Watcher(this.vm, key, newValue => {
  149. node.textContent = newValue;
  150. });
  151. } else if (attrName === 'model') {
  152. node.value = value;
  153. new Watcher(this.vm, key, newValue => {
  154. node.value = newValue;
  155. });
  156. node.addEventListener('input', e => {
  157. this.vm[key] = node.value;
  158. });
  159. } else if (attrName === 'click') {
  160. node.addEventListener(attrName, this.methods[key].bind(this.vm));
  161. }
  162. }
  163. }

测试 MinVue

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <div id="app">
  11. <h4>{{ msg }}</h4>
  12. <p>{{ count }}</p>
  13. <h2>v-text</h2>
  14. <p v-text="msg"></p>
  15. <input type="text" v-model="count" />
  16. <button type="button" v-on:click="increase">add+</button>
  17. <button type="button" v-on:click="changeMessage">change message!</button>
  18. <button type="button" v-on:click="recoverMessage">recoverMessage!</button>
  19. </div>
  20. </body>
  21. <script src="./js/min-vue.js"></script>
  22. <script>
  23. new MinVue({
  24. el: '#app',
  25. data: {
  26. msg: 'hello,mini vue.js',
  27. count: 666,
  28. },
  29. methods: {
  30. increase() {
  31. this.count++;
  32. },
  33. changeMessage() {
  34. this.msg = 'hello,eveningwater!';
  35. },
  36. recoverMessage() {
  37. console.log(this);
  38. this.msg = 'hello,mini vue.js';
  39. },
  40. },
  41. });
  42. </script>
  43. </html>

以上就是怎么使用Object.defineProperty实现简易的vue功能的详细内容,更多关于怎么使用Object.defineProperty实现简易的vue功能的资料请关注九品源码其它相关文章!