image.png
image.png

Vue如何监听data数据变化

封装一个观察者对象,观察对象或数组的变化,使用核心api,Object.defineProperty的get和set方法对数据进行监听,利用递归进行深度监听,但无法对新增或删除的属性进行监听,可以通过Vue.set和Vue.delete
数组的方法需要做特殊处理,声明一个对象的原型指向数组
//优秀代码

  1. observe.js
  2. // 触发更新视图
  3. function updateView() {
  4. console.log('视图更新')
  5. }
  6. // 重新定义数组原型
  7. const oldArrayProperty = Array.prototype
  8. // 创建新对象,原型指向 oldArrayProperty ,再扩展新的方法不会影响原型
  9. const arrProto = Object.create(oldArrayProperty);
  10. ['push', 'pop', 'shift', 'unshift', 'splice'].forEach(methodName => {
  11. arrProto[methodName] = function () {
  12. updateView() // 触发视图更新
  13. oldArrayProperty[methodName].call(this, ...arguments)
  14. // Array.prototype.push.call(this, ...arguments)
  15. }
  16. })
  17. // 重新定义属性,监听起来
  18. function defineReactive(target, key, value) {
  19. // 深度监听
  20. observer(value)
  21. // 核心 API
  22. Object.defineProperty(target, key, {
  23. get() {
  24. return value
  25. },
  26. set(newValue) {
  27. if (newValue !== value) {
  28. // 深度监听
  29. observer(newValue)
  30. // 设置新值
  31. // 注意,value 一直在闭包中,此处设置完之后,再 get 时也是会获取最新的值
  32. value = newValue
  33. // 触发更新视图
  34. updateView()
  35. }
  36. }
  37. })
  38. }
  39. // 监听对象属性
  40. function observer(target) {
  41. if (typeof target !== 'object' || target === null) {
  42. // 不是对象或数组
  43. return target
  44. }
  45. // 污染全局的 Array 原型
  46. // Array.prototype.push = function () {
  47. // updateView()
  48. // ...
  49. // }
  50. if (Array.isArray(target)) {
  51. target.__proto__ = arrProto
  52. }
  53. // 重新定义各个属性(for in 也可以遍历数组)
  54. for (let key in target) {
  55. defineReactive(target, key, target[key])
  56. }
  57. }
  58. // 准备数据
  59. const data = {
  60. name: 'zhangsan',
  61. age: 20,
  62. info: {
  63. address: '北京' // 需要深度监听
  64. },
  65. nums: [10, 20, 30]
  66. }
  67. // 监听数据
  68. observer(data)
  69. // 测试
  70. // data.name = 'lisi'
  71. // data.age = 21
  72. // // console.log('age', data.age)
  73. // data.x = '100' // 新增属性,监听不到 —— 所以有 Vue.set
  74. // delete data.name // 删除属性,监听不到 —— 所有已 Vue.delete
  75. // data.info.address = '上海' // 深度监听
  76. data.nums.push(4) // 监听数组

image.png

虚拟Dom

{tag,props: {style,id, className}, children}
image.png
image.png
image.png
image.png