翼度科技»论坛 编程开发 JavaScript 查看内容

Object.assign触发watch原理示例解析

7

主题

7

帖子

21

积分

新手上路

Rank: 1

积分
21
为什么可以用Object.assign触发$watch

Object.assign,这个api在简单拷贝可枚举对象的属性值时经常用到。这里介绍一个在vue2中Object.assign的用法,这个用法在官网文档 有详细介绍:
  1. watch: {
  2.         someObject(nvalue, ovalue) {
  3.                 ...
  4.         }
  5. }
  6. // 为对象添加新属性
  7. this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
复制代码
而且要注意的是如果像下面这样添加上去的新属性无法触发更新:
  1. this.someObject = Object.assign(this.someObject, { a: 1, b: 2 })
复制代码
问题是为什么前面那种写法会有效?

先看vue2文档

在vue2的文档中有详细说明,在组件的依赖收集过程中,所有property 在被访问和修改时会通知变更,对于对象来说,Vue 无法检测 property 的添加或移除。

一般情况下,在vue中,如果要对data对象中实例添加根级别property,我们可以这样操作:
  1. Vue.set(someObject, 'name', value)
复制代码
或者这样操作
  1. this.$set(this.someObject,'name',2)
复制代码
但是如果我们要对一个对象添加多个属性,同时还要保持对象的响应性,这种情况下就要用到开篇提到的方法。
mdn 上,对
  1. Object.assign
复制代码
有这一句解释:该方法使用源对象的
  1. [[Get]]
复制代码
和目标对象的
  1. [[Set]]
复制代码
,所以它会调用相关 getter 和 setter。对这句,我们用下面的例子a来理解。
  1. var obj = {};
  2. var c = null
  3. Object.defineProperty(obj, 'c', {
  4.   set:function(x){
  5.     console.log('c被赋值:',x);
  6.     c=x
  7.   },
  8.   get:function(){
  9.     console.log('c被取出:',c)
  10.     return c
  11.   }
  12. })
  13. obj.c=3  //c被赋值: 3
  14. obj.c  //c被取出:  3
  15. obj = Object.assign(obj, {c: 'wer23e'}) // 触发了set!
  16. obj = Object.assign(obj, {a: 'wer23e'}) // 由于事先未用defineProperty定义a,所以无法监听
  17. // 由于目标对象未定义属性,无法监听
  18. obj = Object.assign({},obj, {a: 'wer23e',c: 'dfrr23e'})
复制代码
通过这段代码可以理解上面说的“Object.assign会使用目标对象的
  1. [[Set]]
复制代码
”,同时,这段代码也演示了vue2中响应原理,因为vue2中所有需要响应的属性都是用
  1. Object.defineProperty
复制代码
进行响应绑定,这样所有的访问和修改动作都会被追踪到。
但是对于没有事先被
  1. Object.defineProperty
复制代码
定义的属性,比如添加一个属性就无法监听到了。在上面的示例中,即使我用文档提到的用法
  1. obj = Object.assign({},obj, {a: 'wer23e',c: 'dfrr23e'})
复制代码
仍然无法触发c属性的
  1. set
复制代码
。走到这一步,是不是得看watch源码了?其实不必!

用Object.assign触发watch原理

针对这个问题,watch的源码不必看,但是
  1. Object.assign
复制代码
的源码必须要看,
  1. if (typeof Object.assign !== 'function') {
  2.   // Must be writable: true, enumerable: false, configurable: true
  3.   Object.defineProperty(Object, "assign", {
  4.     value: function assign(target, varArgs) { // .length of function is 2
  5.       'use strict';
  6.       if (target === null || target === undefined) {
  7.         throw new TypeError('Cannot convert undefined or null to object');
  8.       }
  9.       var to = Object(target);
  10.       for (var index = 1; index < arguments.length; index++) {
  11.         var nextSource = arguments[index];
  12.         if (nextSource !== null && nextSource !== undefined) {
  13.           for (var nextKey in nextSource) {
  14.             // Avoid bugs when hasOwnProperty is shadowed
  15.             if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
  16.               to[nextKey] = nextSource[nextKey];
  17.             }
  18.           }
  19.         }
  20.       }
  21.       return to;
  22.     },
  23.     writable: true,
  24.     configurable: true
  25.   });
  26. }
复制代码
其实就是把
  1. assign
复制代码
方法中的参数的可枚举属性全部复制到此方法的第一参数上去。回头再去理解下例子a,
  1. obj = Object.assign({},obj, {a: 'wer23e',c: 'dfrr23e'})
复制代码
无法触发c属性的
  1. set
复制代码
函数是因为,obj引用关系已经被改变了,不再是原来那个对象,也就没有了对应的属性监控,但是为什么官方文档会建议这么用呢?
接下来,我写了个小demo,来帮你理解,你可以试下效果
  1. <template>
  2. <div style="width: 100px; height: 50px; position: absolute; top: 100px" @click="clickme()"> 点我啊 {{ test.a }}</div>
  3. </template>
  4. <script>
  5. export default {
  6.         data() {
  7.                 test: { a: 3 },
  8.         },
  9.         watch: {
  10.     test(n, o) {
  11.       debugger
  12.       console.log(n)
  13.     }
  14.   },
  15.         methods: {
  16.                 clickme() {
  17.       debugger
  18.       this.test = Object.assign(this.test, { a: 1 })
  19.       debugger
  20.       this.test = Object.assign(this.test, { a: 1, b: 2 })
  21.       debugger
  22.       this.test = Object.assign({}, this.test, { a: 1, b: 2 })
  23.     },       
  24. }
  25. }
  26. </script>
复制代码
可以看到,确实,最后一种Object.assign方法会触发test对象监听。前面两种写法,只能触发对象的属性更新响应,如果给obj对象添加属性,就无法监测到obj的变化。到这一步,其实如果要彻底弄清楚,最好还是看下
  1. watch
复制代码
源码。我在仔细分析了后,才发现其实跟
  1. watch
复制代码
源码没有什么关系,所以本文就不展开分析了,这是另一篇文章的事了。
如果看不明白
  1. watch
复制代码
源码也没关系,其实简单解释就是,用
  1. obj = Object.assign({},obj, {a: 'wer23e',c: 'dfrr23e'})
复制代码
这种方式,改变了obj的引用关系,也就是obj的值变了,所以如果你在watch函数中监听了obj,obj是变化了的,只不过obj的值是一个
  1. Object
复制代码
对象而已,所以会触发obj对象的响应。
参考资料:
cn.vuejs.org/v2/guide/re…
developer.mozilla.org/zh-CN/docs/…
https://www.jb51.net/article/265630.htm
https://www.jb51.net/article/266683.htm
以上就是Object.assign触发watch原理示例解析的详细内容,更多关于Object.assign触发watch的资料请关注脚本之家其它相关文章!

来源:https://www.jb51.net/article/266687.htm
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具