指南
基础
- 安装
- 简介
- Vue 实例
- 模板语法
- 计算属性和侦听器
- 类和样式绑定
- 条件渲染
- 列表渲染
- 事件处理
- 表单输入绑定
- 组件基础
组件深入
- 组件注册
- Props
- 自定义事件
- 插槽
- 动态和异步组件
- 处理边缘情况
过渡和动画
- 进入/离开和列表过渡
- 状态过渡
可复用性和组合
- 混入
- 自定义指令
- 渲染函数和 JSX
- 插件
- 过滤器
工具
- 单文件组件
- 测试
- TypeScript 支持
- 生产环境部署
扩展
- 路由
- 状态管理
- 服务器端渲染
- 安全
内部机制
- 深入响应式
迁移
- 从 Vue 1.x 迁移
- 从 Vue Router 0.7.x 迁移
- 从 Vuex 0.6.x 迁移到 1.0
- 迁移到 Vue 2.7
元数据
- 与其他框架的比较
- 加入 Vue.js 社区!
- 认识团队
深入响应式
现在是深入了解的时候了!Vue 最独特的特性之一是其非侵入式的响应式系统。模型只是普通的 JavaScript 对象。当你修改它们时,视图会更新。这使得状态管理变得简单直观,但了解其工作原理也很重要,可以避免一些常见的陷阱。在本节中,我们将深入探讨 Vue 响应式系统的底层细节。
如何跟踪更改
当你将一个普通的 JavaScript 对象作为 data
选项传递给 Vue 实例时,Vue 会遍历其所有属性,并使用 getter/setter 将它们转换为 Object.defineProperty
。这是一个 ES5 独有的不可模拟的功能,这就是为什么 Vue 不支持 IE8 及以下版本的原因。
getter/setter 对用户是不可见的,但在幕后,它们使 Vue 能够在访问或修改属性时执行依赖项跟踪和更改通知。需要注意的是,当转换后的数据对象被记录时,浏览器控制台会以不同的方式格式化 getter/setter,因此你可能需要安装 vue-devtools 来获得更友好的检查界面。
每个组件实例都有一个对应的 **观察者** 实例,它会记录组件渲染期间“触碰”的任何属性作为依赖项。稍后,当依赖项的 setter 被触发时,它会通知观察者,观察者反过来会使组件重新渲染。
更改检测注意事项
由于 JavaScript 的限制,Vue **无法检测**某些类型的更改。但是,有一些方法可以规避它们以保持响应性。
对于对象
Vue 无法检测属性添加或删除。由于 Vue 在实例初始化期间执行 getter/setter 转换过程,因此属性必须存在于 data
对象中,才能使 Vue 转换它并使其具有响应性。例如
|
Vue 不允许在已创建的实例中动态添加新的顶级响应式属性。但是,可以使用 Vue.set(object, propertyName, value)
方法将响应式属性添加到嵌套对象中
|
你也可以使用 vm.$set
实例方法,它是全局 Vue.set
的别名
|
有时你可能希望将多个属性分配给现有对象,例如使用 Object.assign()
或 _.extend()
。但是,添加到对象的新属性不会触发更改。在这种情况下,创建一个新的对象,其中包含原始对象和混合对象中的属性
|
对于数组
Vue 无法检测对数组的以下更改
- 当你直接使用索引设置一个项目时,例如
vm.items[indexOfItem] = newValue
- 当你修改数组的长度时,例如
vm.items.length = newLength
例如
|
为了克服第一个问题,以下两种方法都将实现与 vm.items[indexOfItem] = newValue
相同的效果,但也会在响应式系统中触发状态更新
|
|
你也可以使用 vm.$set
实例方法,它是全局 Vue.set
的别名
|
为了解决第二个问题,可以使用 splice
|
声明响应式属性
由于 Vue 不允许动态添加顶级响应式属性,因此你必须通过预先声明所有顶级响应式数据属性来初始化 Vue 实例,即使是空值
|
如果你没有在 data 选项中声明 message
,Vue 会警告你渲染函数正在尝试访问不存在的属性。
这种限制背后有技术原因 - 它消除了依赖项跟踪系统中的一类边缘情况,也使 Vue 实例与类型检查系统更加兼容。但从代码可维护性的角度来看,还有一个重要的考虑因素:data
对象就像你组件状态的模式。预先声明所有响应式属性,使组件代码在稍后重新访问或被其他开发人员阅读时更容易理解。
异步更新队列
如果你还没有注意到,Vue 是 **异步** 执行 DOM 更新的。每当观察到数据更改时,它会打开一个队列,并缓冲在同一个事件循环中发生的任何数据更改。如果同一个观察者被多次触发,它只会被推入队列一次。这种缓冲去重对于避免不必要的计算和 DOM 操作非常重要。然后,在下一个事件循环“tick”中,Vue 会刷新队列,并执行实际的(已去重的)工作。在内部,Vue 尝试使用原生 Promise.then
、MutationObserver
和 setImmediate
来进行异步排队,并回退到 setTimeout(fn, 0)
。
例如,当你设置 vm.someData = 'new value'
时,组件不会立即重新渲染。它将在下一个“tick”中更新,此时队列被刷新。大多数情况下我们不需要关心这一点,但当你想做一些依赖于更新后 DOM 状态的事情时,它可能会很棘手。虽然 Vue.js 通常鼓励开发人员以“数据驱动”的方式思考,并避免直接触碰 DOM,但有时可能需要动手操作。为了等待 Vue.js 在数据更改后完成 DOM 更新,你可以在数据更改后立即使用 Vue.nextTick(callback)
。回调将在 DOM 更新后被调用。例如
|
|
还有一个 vm.$nextTick()
实例方法,它在组件内部特别方便,因为它不需要全局 Vue
,并且其回调的 this
上下文将自动绑定到当前 Vue 实例
|
由于 $nextTick()
返回一个 Promise,你可以使用新的 ES2017 async/await 语法来实现与上述相同的效果
|