Bläddra i källkod

Merge branch 'master' of https://github.com/HcySunYang/vue-design

HcySunYang 7 år sedan
förälder
incheckning
90ed81a222
1 ändrade filer med 12 tillägg och 12 borttagningar
  1. 12 12
      docs/art/8vue-reactive-dep-watch.md

+ 12 - 12
docs/art/8vue-reactive-dep-watch.md

@@ -642,7 +642,7 @@ export function parsePath (path: string): any {
 
 首先定义 `segments` 常量,它的值是通过字符 `.` 分割 `path` 字符串产生的数组,随后 `parsePath` 函数将返回值一个函数,该函数的作用是遍历 `segments` 数组循环访问 `path` 指定的属性值。这样就触发了数据属性的 `get` 拦截器函数。但要注意 `parsePath` 返回的新函数将作为 `this.getter` 的值,只有当 `this.getter` 被调用的时候,这个函数才会执行。
 
-看完了 `parsePath` 函数,我们回到如下这段代码中:
+看完了 `parsePath` 函数,我们回到如下这段代码中:
 
 ```js {5-13}
 if (typeof expOrFn === 'function') {
@@ -726,7 +726,7 @@ export function popTarget () {
 }
 ```
 
-在 `src/core/observer/dep.js` 文件中定义了 `Dep` 类,我们在 [揭开数据响应系统的面纱](/art/7vue-reactive.html) 一章中就遇到过 `Dep` 类,当时我们说每个响应式数据的属性都通过闭包引用着一个用来收集属于自身依赖的“筐”,实际上那个“筐”就是 `Dep` 类的实例对象。更多关于 `Dep` 类的内容我们会在合适的地方讲解,现在我们的主要目的是搞清楚 `pushTarget` 函数是做什么的。在上面这段代码中我们可以看到 `Dep` 类拥有一个静态属性,即 `Dep.target` 属性,该属性的初始值为 `null`,其实 `pushTarget` 函数的作用就是用来为 `Dep.target` 属性赋值的,`pushTarget` 函数会将接收到的参数赋值给 `Dep.target` 属性,我们知道传递给 `pushTarget` 函数的参数就是调用该函数的观察者对象,所以 `Dep.target` 保存着一个观察者对象,其实这个观察者对象就是即将要收集的目标。
+在 `src/core/observer/dep.js` 文件中定义了 `Dep` 类,我们在 [揭开数据响应系统的面纱](./art/7vue-reactive.md) 一章中就遇到过 `Dep` 类,当时我们说每个响应式数据的属性都通过闭包引用着一个用来收集属于自身依赖的“筐”,实际上那个“筐”就是 `Dep` 类的实例对象。更多关于 `Dep` 类的内容我们会在合适的地方讲解,现在我们的主要目的是搞清楚 `pushTarget` 函数是做什么的。在上面这段代码中我们可以看到 `Dep` 类拥有一个静态属性,即 `Dep.target` 属性,该属性的初始值为 `null`,其实 `pushTarget` 函数的作用就是用来为 `Dep.target` 属性赋值的,`pushTarget` 函数会将接收到的参数赋值给 `Dep.target` 属性,我们知道传递给 `pushTarget` 函数的参数就是调用该函数的观察者对象,所以 `Dep.target` 保存着一个观察者对象,其实这个观察者对象就是即将要收集的目标。
 
 我们再回到 `this.get()` 方法中,如下是简化后的代码:
 
@@ -833,7 +833,7 @@ addDep (dep: Dep) {
 }
 ```
 
-可以看到 `addDep` 方法接收一个参数,这个参数是一个 `Dep` 对象,在 `addDep` 方法内部首先定义了常量 `id`,它的值是 `Dep` 实例对象的唯一 `id` 值。接着是一段 `if` 语句块,该 `if` 语句块的代码很关键,因为它的作用就是用来**避免收集重复依赖**的,既然是用来避免收集重复的依赖,那么就不得不用到我们前面提到过的两组属性,即 `newDepIds`、`newDeps` 以及 `depIds`、`deps`。为了让大家更好理解,我们思考一下可不可以把 `addDep` 方法修改成如下这样:
+可以看到 `addDep` 方法接收一个参数,这个参数是一个 `Dep` 对象,在 `addDep` 方法内部首先定义了常量 `id`,它的值是 `Dep` 实例对象的唯一 `id` 值。接着是一段 `if` 语句块,该 `if` 语句块的代码很关键,因为它的作用就是用来 **避免收集重复依赖** 的,既然是用来避免收集重复的依赖,那么就不得不用到我们前面提到过的两组属性,即 `newDepIds`、`newDeps` 以及 `depIds`、`deps`。为了让大家更好理解,我们思考一下可不可以把 `addDep` 方法修改成如下这样:
 
 ```js
 addDep (dep: Dep) {
@@ -912,7 +912,7 @@ addDep (dep: Dep) {
 }
 ```
 
-这里的判断条件 `!this.depIds.has(id)` 是什么意思呢?我们知道 `newDepIds` 属性用来避免在一次求值的过程中收集重复的依赖,其实 `depIds` 属性是用来在**多次求值**中避免收集重复依赖的。什么是多次求值,其实所谓多次求值是指当数据变化时重新求值的过程。大家可能会疑惑,难道重新求值的时候不能用 `newDepIds` 属性来避免收集重复的依赖吗?不能,原因在于每一次求值之后 `newDepIds` 属性都会被清空,也就是说每次重新求值的时候对于观察者实例对象来讲 `newDepIds` 属性始终是全新的。虽然每次求值之后会清空 `newDepIds` 属性的值,但在清空之前会把 `newDepIds` 属性的值以及 `newDeps` 属性的值赋值给 `depIds` 属性和 `deps` 属性,这样重新求值的时候 `depIds` 属性和 `deps` 属性将会保存着上一次求值中 `newDepIds` 属性以及 `newDeps` 属性的值。为了证明这一点,我们来看一下观察者对象的求值方法,即 `get()` 方法:
+这里的判断条件 `!this.depIds.has(id)` 是什么意思呢?我们知道 `newDepIds` 属性用来避免在 **一次求值** 的过程中收集重复的依赖,其实 `depIds` 属性是用来在 **多次求值** 中避免收集重复依赖的。什么是多次求值,其实所谓多次求值是指当数据变化时重新求值的过程。大家可能会疑惑,难道重新求值的时候不能用 `newDepIds` 属性来避免收集重复的依赖吗?不能,原因在于每一次求值之后 `newDepIds` 属性都会被清空,也就是说每次重新求值的时候对于观察者实例对象来讲 `newDepIds` 属性始终是全新的。虽然每次求值之后会清空 `newDepIds` 属性的值,但在清空之前会把 `newDepIds` 属性的值以及 `newDeps` 属性的值赋值给 `depIds` 属性和 `deps` 属性,这样重新求值的时候 `depIds` 属性和 `deps` 属性将会保存着上一次求值中 `newDepIds` 属性以及 `newDeps` 属性的值。为了证明这一点,我们来看一下观察者对象的求值方法,即 `get()` 方法:
 
 ```js {12}
 get () {
@@ -989,11 +989,11 @@ removeSub (sub: Watcher) {
 }
 ```
 
-它的内容很简单,接收一个要被移除的观察者作为参数,然后使用 `remove` 工具函数,将该观察者从 `this.subs` 数组中移除。其中 `remove` 工具函数来自 `src/shared/util.js` 文件,可以在 [shared/util.js 文件工具方法全解](../appendix/shared-util.html#remove) 中查看。
+它的内容很简单,接收一个要被移除的观察者作为参数,然后使用 `remove` 工具函数,将该观察者从 `this.subs` 数组中移除。其中 `remove` 工具函数来自 `src/shared/util.js` 文件,可以在 [shared/util.js 文件工具方法全解](../appendix/shared-util.md#remove) 中查看。
 
 ## 触发依赖的过程
 
-在上一小节中我们提到了,每次求值并收集完观察者之后,会将当次求值所收集到的观察者保存到另外一组属性中,即 `depIds` 和 `deps`,并将存有当次求值所收集到的观察者的属性清空,即清空 `newDepIds` 和 `newDeps`。我们当时也说过了,这么做的目的是为了对比当次求值与上一次求值所收集到的观察者的变化情况,并出合理的矫正工作,比如移除那些已经没有关联关系的观察者等。本节我们将以数据属性的变化为切入点,讲解重新求值的过程。
+在上一小节中我们提到了,每次求值并收集完观察者之后,会将当次求值所收集到的观察者保存到另外一组属性中,即 `depIds` 和 `deps`,并将存有当次求值所收集到的观察者的属性清空,即清空 `newDepIds` 和 `newDeps`。我们当时也说过了,这么做的目的是为了对比当次求值与上一次求值所收集到的观察者的变化情况,并出合理的矫正工作,比如移除那些已经没有关联关系的观察者等。本节我们将以数据属性的变化为切入点,讲解重新求值的过程。
 
 假设我们有如下模板:
 
@@ -1003,7 +1003,7 @@ removeSub (sub: Watcher) {
 </div>
 ```
 
-我们知道这段模板将会被编译成渲染函数,接着创建一个渲染函数的观察者,从而对渲染函数求值,在求值的过程中会触发数据对象 `name` 属性的 `get` 拦截器函数,进而将该观察者收集到 `name` 属性通过闭包引用的“筐”中,即收集到 `Dep` 实例对象中。这个 `Dep` 实例对象是属于 `name` 属性自身所拥有的,这样当我们尝试修改数据对象 `name` 属性的值时就会触发 `name` 属性的 `set` 拦截器函数,这样就有机会调用 `Dep` 实例对象的 `notify` 方法,从而触发了响应,如下代码截取 `defineReactive` 函数中的 `set` 拦截器函数:
+我们知道这段模板将会被编译成渲染函数,接着创建一个渲染函数的观察者,从而对渲染函数求值,在求值的过程中会触发数据对象 `name` 属性的 `get` 拦截器函数,进而将该观察者收集到 `name` 属性通过闭包引用的“筐”中,即收集到 `Dep` 实例对象中。这个 `Dep` 实例对象是属于 `name` 属性自身所拥有的,这样当我们尝试修改数据对象 `name` 属性的值时就会触发 `name` 属性的 `set` 拦截器函数,这样就有机会调用 `Dep` 实例对象的 `notify` 方法,从而触发了响应,如下代码截取 `defineReactive` 函数中的 `set` 拦截器函数:
 
 ```js {3}
 set: function reactiveSetter (newVal) {
@@ -1052,7 +1052,7 @@ if (process.env.NODE_ENV !== 'production' && !config.async) {
 }
 ```
 
-对于这段代码的作用,我们会在本章的 [同步执行观察者](#同步执行观察者) 一节中对详细讲解,现在大家可以完全忽略,这并不影响我们对代码的理解。如果我们去掉如上这段代码,那么 `notify` 函数将变为:
+对于这段代码的作用,我们会在本章的 [同步执行观察者](#同步执行观察者) 一节中对详细讲解,现在大家可以完全忽略,这并不影响我们对代码的理解。如果我们去掉如上这段代码,那么 `notify` 函数将变为:
 
 ```js
 notify () {
@@ -1228,7 +1228,7 @@ if (this.user) {
 
 ![](http://7xlolm.com1.z0.glb.clouddn.com/2018-05-23-131015.jpg)
 
-有时候这是致命的缺陷,想象一下复杂业务场景,你可能会同时修改很多属性的值,如果每次属性值的变化都要重新渲染,就会导致严重的性能问题,而异步更新队列就是用来解决这个问题的,为了让大家更好理解,我们同样用一张图来描述异步更新的过程,如下:
+有时候这是致命的缺陷,想象一下复杂业务场景,你可能会同时修改很多属性的值,如果每次属性值的变化都要重新渲染,就会导致严重的性能问题,而异步更新队列就是用来解决这个问题的,为了让大家更好理解,我们同样用一张图来描述异步更新的过程,如下:
 
 ![](http://7xlolm.com1.z0.glb.clouddn.com/2018-05-25-103029.jpg)
 
@@ -1283,7 +1283,7 @@ export function queueWatcher (watcher: Watcher) {
 }
 ```
 
-`queueWatcher` 函数接收观察者对象作为参数,首先定义了 `id` 常量,它的值是观察者对象的唯一 `id`,然后 `if` 判断语句,如下是简化的代码:
+`queueWatcher` 函数接收观察者对象作为参数,首先定义了 `id` 常量,它的值是观察者对象的唯一 `id`,然后执行 `if` 判断语句,如下是简化的代码:
 
 ```js {3-4}
 export function queueWatcher (watcher: Watcher) {
@@ -1330,7 +1330,7 @@ const queue: Array<Watcher> = []
 let flushing = false
 ```
 
-`flushing` 变量是一个标志,我们知道放入队列 `queue` 中的所有观察者将会在突变完成之后统一执行更新,当更新开始时会将 `flushing` 变量的值设置为 `true`,代表着此时正在执行更新,所以根据判断条件 `if (!flushing)` 可知只有当队列没有执行更新时才会简单将观察者追加到队列的尾部,有的同学可能会问:“难道在队列执行更新的过程中还会有观察者入队的操作吗?”,实际上是会的,典型的例子就是计算属性,比如队列执行更新时经常会执行渲染函数观察者的更新,渲染函数中很可能有计算属性的存在,由于计算属性在实现方式上与普通响应式属性有所不同,所以当触发计算属性的 `get` 拦截器函数时会有观察者入队的行为,这个时候我们需要特殊处理,也就是 `else` 分支的代码,如下:
+`flushing` 变量是一个标志,我们知道放入队列 `queue` 中的所有观察者将会在突变完成之后统一执行更新,当更新开始时会将 `flushing` 变量的值设置为 `true`,代表着此时正在执行更新,所以根据判断条件 `if (!flushing)` 可知只有当队列没有执行更新时才会简单将观察者追加到队列的尾部,有的同学可能会问:“难道在队列执行更新的过程中还会有观察者入队的操作吗?”,实际上是会的,典型的例子就是计算属性,比如队列执行更新时经常会执行渲染函数观察者的更新,渲染函数中很可能有计算属性的存在,由于计算属性在实现方式上与普通响应式属性有所不同,所以当触发计算属性的 `get` 拦截器函数时会有观察者入队的行为,这个时候我们需要特殊处理,也就是 `else` 分支的代码,如下:
 
 ```js {10-14}
 export function queueWatcher (watcher: Watcher) {
@@ -1468,7 +1468,7 @@ if (isIOS) setTimeout(noop)
 
 ```js {5}
 if (typeof Promise !== 'undefined' && isNative(Promise)) {
-  // 省略... 
+  // 省略...
 } else {
   // fallback to macro
   microTimerFunc = macroTimerFunc