未分类

项目思考v1.0

object.defineProperty和$set

情景:页面1的数据接口中返回所需要使用的字段叫’form’,页面2的数据接口中返回所需要使用的字段叫’tempData’,form和tempData内容相同且为数组类型(Array),但是组件1同时被页面1和页面2使用,此时需要做数据转化,以便数据可以在组件中正常展示

我们先来看一段代码

我们都知道引用类型的数据是不能直接赋值的,需要循环遍历,然后把数组中的每一项赋给新的数组,在这里说个前提,数组中的每一项都是基本数据类型

用红色框起来的部分是两种写法,一种是最普通的’=’赋值,还有一种是vue里边的$set,当我们使用’=’赋值的时候我们可以打印出新的对象(_newform)已经被正确赋值,但是在页面中并没有显示,而使用$set赋值的时候,新对象不仅被正确赋值,页面也会正常响应,这是为什么呢?

vue 响应式

当你把一个普通的JavaScript对象传给Vue实例的data选项,Vue将遍历此对象所有的属性,并使用Object.defineProperty把这些属性全部转为getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器。

这些getter/setter对用户来说是不可见的,但是在内部他们让vue追踪依赖,在属性被访问和修改时通知变化。每个组件实例都有相应的watcher实例对象,它会在组建渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。

对象更改检测

在一个组件实例中,只有在data里初始化的数据才是响应的,Vue不能检测到对象属性的添加或删除,没有在data里声明的属性不是响应的。

Vue不允许在已经创建的实例上动态添加根级响应式属性,但是可以使用$set方法将相应属性添加到嵌套的对象上。

这里需要提到js原生的方法Object.defineProperty()

官方文档对该方法的解释是:Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。

我们主要说一下description这个参数

数据描述符和存取描述符均具有以下可选键值

configurable

当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false。

enumerable

当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中。默认为 false。
数据描述符同时具有以下可选键值:

value

该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。

writable

当且仅当该属性的writable为true时,value才能被赋值运算符改变。默认为 false。

存取描述符同时具有以下可选键值:

get

一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象)。
默认为 undefined。

set

一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。
默认为 undefined。

我们直接采用’=’赋值相当于把configurable enumerable writable都赋值为true

$set

先上源码 vue->src->core->observer->index.js(201-231)

先进行一个判断,判断target不是undefined、null、string、number、symbol、boolean类型的数据。

1.如果target是一个数组,那么根据key值及数组长度更改数组的长度(取其中较大者),然后直接使用splice函数修改数组,虽然vue没有监听数组变化,但是监听了数组的push,pop,shift,unshift,splice,sort,reverse函数,所以使用splice也可以达到更新dom的目的

2.如果target是一个对象,且key是对象已存在的私有属性,那么直接赋值就可以了,因为这个key必然是被监听过的

3.如果这个key目前没有存在于对象中,那么会进行赋值并监听。这里省略了ob的判断,那么ob是什么呢,vue中初始化的数据(比如data中的数据)在页面初始化的时候都会被监听,而被监听的属性都会被绑定__ob__属性,这里就是判断这个数据有没有被监听的。如果这个数据没有被监听,那么就默认你不想监听这个数据,所以直接赋值并返回

综上所述:我们使用’=’直接赋值可以读写新的对象,但是由于在已经创建的实例上无法动态添加根级响应式属性,所以会有数据改变但是vue无法监听的情况出现