vue原理代码实现

vue2.X实现原理

vue初始化的时候,会用Object.definePropery重新定义data中的所有属性,当页面使用对应属性的时候

watcher进行依赖收集

如果属性发生变化会通知依赖进行更新操作-发布订阅

通过 Object.defineProperty 实现的

  1. <body>
  2. <div id="app">
  3. <input type="text" id="txt">
  4. <p id="show-txt"></p>
  5. </div>
  6. <script>
  7. var obj = {}
  8. Object.defineProperty(obj, 'txt', {
  9. get: function () {
  10. return obj
  11. },
  12. set: function (newValue) {
  13. document.getElementById('txt').value = newValue
  14. document.getElementById('show-txt').innerHTML = newValue
  15. }
  16. })
  17. document.addEventListener('keyup', function (e) {
  18. obj.txt = e.target.value
  19. })
  20. </script>
  21. </body>

关于defineProperty的缺点很明显:

  • Object.defineProperty监听的是对象的属性,如果对象比较复杂,需要逐个深层遍历他的属性来实现监听,耗费性能
  • Object.defineProperty无法监听数组的变化,使Vue不得不对数组做了额外的hack。

vue3.0实现原理

改用proxy替代Object.definePropery,它可以监听对象和数组的变化,有13种种拦截方法

  1. // 语法
  2. let p = new Proxy(target, handler);

这里重点说一下handlerhandler本身就是ES6所新设计的一个对象.它的作用就是用来自定义代理对象的各种可代理操作。它本身一共有13中方法,每种方法都可以代理一种操作,常用的几种方法如下:

  1. // 在定义代理对象某个属性时的属性描述时触发该操作,比如在执行 Object.defineProperty(proxy, "foo", {}) 时。
  2. handler.defineProperty()
  3. // 在判断代理对象是否拥有某个属性时触发该操作,比如在执行 "foo" in proxy 时。
  4. handler.has()
  5. // 在读取代理对象的某个属性时触发该操作,比如在执行 proxy.foo 时。
  6. handler.get()
  7. // 在给代理对象的某个属性赋值时触发该操作,比如在执行 proxy.foo = 1 时。
  8. handler.set()
  9. // 在删除代理对象的某个属性时触发该操作,比如在执行 delete proxy.foo 时。
  10. handler.deleteProperty()
  11. // 在获取代理对象的所有属性键时触发该操作,比如在执行 Object.getOwnPropertyNames(proxy) 时。
  12. handler.ownKeys()
  13. // 在调用一个目标对象为函数的代理对象时触发该操作,比如在执行 proxy() 时。
  14. handler.apply()
  15. // 在给一个目标对象为构造函数的代理对象构造实例时触发该操作,比如在执行new proxy() 时。
  16. handler.construct()

与其说vue3.0的新特性,倒不如说是Proxy的优点:

  • 直接监听对象
  • 可以监听数组变化
  • 多种拦截方式更加强大

同样的功能,那么proxy如何实现呢?

  1. let input = document.getElementById('txt')
  2. let p = document.getElementById('show-txt')
  3. const obj = {}
  4. const newObj = new Proxy(obj, {
  5. get: function (target, key, receiver) {
  6. console.log(target, key, receiver, 'get');
  7. return Reflect.get(target, key, receiver);
  8. },
  9. set: function (target, key, value, receiver) {
  10. console.log(target, key, value, receiver, 'set');
  11. if (key === "txt") {
  12. input.value = value
  13. p.innerHTML = value;
  14. }
  15. return Reflect.set(target, key, value, receiver);
  16. }
  17. })
  18. input.addEventListener('keyup', function(e) {
  19. newObj.txt = e.target.value;
  20. })

检测数组的时候可能多次触发多次get/set,防止多次触发?

判断key是否为当前被代理对象target自身属性,也可以判断旧值和新值是否相等

满足条件,再执行trigger