💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
## 响应式原理 ### 如何追踪变化 * Object.defineProperty ~~~ var obj = {}; var initValue = 'hello'; Object.defineProperty(obj,"newKey",{ get:function (){ //当获取值的时候触发的函数 return initValue; }, set:function (value){ //当设置值的时候触发的函数,设置的新值通过参数value拿到 initValue = value; } }); //获取值 console.log( obj.newKey ); //hello //设置值 obj.newKey = 'change value'; console.log( obj.newKey ); //change value ~~~ 当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器 ### 异步更新队列 可能你还没有注意到,Vue 异步执行 DOM 更新。只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作上非常重要。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作 Vue 在内部尝试对异步队列使用原生的 Promise.then 和 MessageChannel,如果执行环境不支持,会采用 setTimeout(fn, 0) 代替。 ~~~ <div id="example">{{message}}</div> ~~~ ~~~ var vm = new Vue({ el: '#example', data: { message: '123' } }) vm.message = 'new message' // 更改数据 vm.$el.textContent === 'new message' // false Vue.nextTick(function () { vm.$el.textContent === 'new message' // true }) ~~~ ### 数组更新检测 2.替换数组 变异方法 (mutation method),顾名思义,会改变被这些方法调用的原始数组。相比之下,也有非变异 (non-mutating method) 方法,例如:filter(), concat() 和 slice() 。这些不会改变原始数组,但总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组: ~~~ example1.items = example1.items.filter(function (item) { return item.message.match(/Foo/) }) ~~~ 3.注意事项 由于 JavaScript 的限制,Vue 不能检测以下变动的数组 ~~~ var vm = new Vue({ data: { items: ['a', 'b', 'c'] } }) vm.items[1] = 'x' // 不是响应性的 vm.items.length = 2 // 不是响应性的 ~~~ 为了解决第一类问题,以下两种方式都可以实现和 vm.items[indexOfItem] = newValue 相同的效果,同时也将触发状态更新: ~~~ // Vue.set Vue.set(vm.items, indexOfItem, newValue) // Array.prototype.splice vm.items.splice(indexOfItem, 1, newValue) ~~~ 你也可以使用 vm.$set 实例方法,该方法是全局方法 Vue.set 的一个别名 ~~~ vm.$set(vm.items, indexOfItem, newValue) ~~~ 或者使用变异方法 push() pop() shift() unshift() splice() sort() reverse() 对于已经创建的实例,Vue 不能动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, key, value) 方法向嵌套对象添加响应式属性。例如,对于: ~~~ var vm = new Vue({ data: { userProfile: { name: 'Anika' } } }) ~~~ 你可以添加一个新的 age 属性到嵌套的 userProfile 对象 ~~~ Vue.set(vm.userProfile, 'age', 27) ~~~ 有时你可能需要为已有对象赋予多个新属性,比如使用 Object.assign() 或 _.extend()。在这种情况下,你应该用两个对象的属性创建一个新的对象。所以,如果你想添加新的响应式属性,不要像这样: ~~~ vm.userProfile = Object.assign({}, vm.userProfile, { age: 27, favoriteColor: 'Vue Green' }) ~~~ ### 课后习题 1.vue响应原理主要依赖的哪个函数? 2.vue中如何让数组改变元素页面也能够响应 3.vue中nextTick的使用场景是什么?