非侵入性的响应式系统是Vue最独特的特性之一。 那么非侵入性的响应式系统是什么呢?
Vue是mvvm框架。而module仅仅只是普通的JavaScript对象。当你修改这些对象时,视图也会进行更新。这就是非侵入性的响应式。
那么响应式底层原理是什么呢?
简单说:当你把一个普通的JavaScript对象传入Vue实例作为data时,Vue将会遍历这个对象的所有属性。在遍历时会使把这些属性全部转为getter或者setter。由于
Object.defineProperty是ES5中一个无法shim的特性,因此Vue不支持IE8以及更低版本的浏览器。
当然getter/setter对用户是不可见的,但是Vue能够在内部通过它们追踪依赖。所以当属性被读写时,Vue也能及时的变更属性。
那么Vue是如何在内部追踪依赖呢?
其实每个组件实例都对应一个watcher实例,它会在组件渲染的过程中把监听到的数据属性记录为依赖。之后当该依赖的setter被触发时,会反馈给watcher,使该依赖和相关联的组件重新渲染。
以下是图解:
当属性检测时,应当注意如下几项:
一、由于Object.observe
被废弃,Vue无法检测到对象属性的添加或删除操作。但是Vue会在初始化实例 对象时执行getter/setter转化。也因此属性必须写在data对象上,只有如此Vue才会将该属性传唤成响应式。
二、对于已经创建的实例,Vue是 不允许动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, propertyName, value)
方法向嵌套对象添加响应式属性。例如,
Vue.set(vm.someObject, 'b', 2)
还可以使用 vm.$set
实例方法,这也是全局 Vue.set
方法的别名:
this.$set(this.someObject,'b',2)
有时你可能需要为已有对象赋值多个新属性,比如使用 Object.assign()
或 _.extend()
。但是,这样添加到对象上的新属性不会触发更新。在这种情况下,你应该用原对象与要混合进去的对象的属性一起创建一个新的对象。
由于 Vue 不允许动态添加根级响应式属性,所以你必须在初始化实例前声明所有根级响应式属性,哪怕只是一个空值:
如果你未在 data
选项中声明 message
,Vue 将警告你渲染函数正在试图访问不存在的属性。
这样的限制在背后是有其技术原因的,它消除了在依赖项跟踪系统中的一类边界情况,也使 Vue 实例能更好地配合类型检查系统工作。但与此同时在代码可维护性方面也有一点重要的考虑:data
对象就像组件状态的结构 (schema)。
提前声明所有的响应式属性,可以让组件代码在未来维护时,给其他开发人员阅读时更易于理解。