瀏覽代碼

chore: modify typos

miaoyuxinbaby 7 年之前
父節點
當前提交
8fb8a30e0e
共有 2 個文件被更改,包括 19 次插入21 次删除
  1. 10 12
      docs/art/8vue-reactive-dep-watch.md
  2. 9 9
      docs/art/9vue-state-init.md

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

@@ -2033,7 +2033,7 @@ export function initState (vm: Component) {
 }
 ```
 
-如上高亮代码所示,在这个 `if` 条件语句块中,调用 `initWatch` 函数,这个函数用来初始化 `watch` 选项,至于判断条件我们就不多讲了,前面讲解中我们已经讲解过类似的判断条件。至于 `initWatch` 函数,它就定义在 `createWatcher` 函数的上方,如下是其全部代码:
+如上高亮代码所示,在这个 `if` 条件语句块中,调用 `initWatch` 函数,这个函数用来初始化 `watch` 选项,至于判断条件我们就不多讲了,前面讲解中我们已经讲解过类似的判断条件。至于 `initWatch` 函数,它就定义在 `createWatcher` 函数的上方,如下是其全部代码:
 
 ```js
 function initWatch (vm: Component, watch: Object) {
@@ -2226,7 +2226,7 @@ if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode) {
 }
 ```
 
-这段代码是对参数 `val` 的检查,后面我们统一称 `val` 为**被观察属性的值**,我们知道既然是深度观测,所以被观察属性的值要么是一个对象要么是一个数组,并且该值不能是冻结的,同时也不应该是 `VNode` 实例(这是Vue单独做的限制)。只有当被观察属性的值满足这些条件时,才会对其进行深度观测,只要有一项不满足 `_traverse` 就会 `return` 结束执行。所以上面这段 `if` 语句可以理解为是在检测被观察属性的值能否进行深度观测,一旦能够深度观测将会继续执行之后的代码,如下:
+这段代码是对参数 `val` 的检查,后面我们统一称 `val` 为 **被观察属性的值**,我们知道既然是深度观测,所以被观察属性的值要么是一个对象要么是一个数组,并且该值不能是冻结的,同时也不应该是 `VNode` 实例(这是Vue单独做的限制)。只有当被观察属性的值满足这些条件时,才会对其进行深度观测,只要有一项不满足 `_traverse` 就会 `return` 结束执行。所以上面这段 `if` 语句可以理解为是在检测被观察属性的值能否进行深度观测,一旦能够深度观测将会继续执行之后的代码,如下:
 
 ```js
 if (isA) {
@@ -2253,7 +2253,7 @@ if (val.__ob__) {
 }
 ```
 
-这段代码的作用不容忽视,它解决了循环引用导致死循环的问题,为了更好说明问题我们举个例子,如下:
+这段代码的作用不容忽视,它解决了循环引用导致死循环的问题,为了更好说明问题我们举个例子,如下:
 
 ```js
 const obj1 = {}
@@ -2475,7 +2475,7 @@ if (!isSSR) {
 }
 ```
 
-只有在非服务端渲染时才会执行 `if` 语句块内的代码,因为服务端渲染中计算属性的实现本质上和使用 `methods` 选项差不多。这里我们着重讲解非服务端渲染的实现,这时 `if` 语句块内的代码会被执行,可以看到在 `if` 语句块内创建了一个观察者实例对象,我们称之为**计算属性的观察者**,同时会把计算属性的观察者添加到 `watchers` 常量对象中,键值是对应计算属性的名字,注意由于 `watchers` 常量与 `vm._computedWatchers` 属性具有相同的引用,所以对 `watchers` 常量的修改相当于对 `vm._computedWatchers` 属性的修改,现在你应该知道了,`vm._computedWatchers` 对象是用来存储计算属性观察者的。
+只有在非服务端渲染时才会执行 `if` 语句块内的代码,因为服务端渲染中计算属性的实现本质上和使用 `methods` 选项差不多。这里我们着重讲解非服务端渲染的实现,这时 `if` 语句块内的代码会被执行,可以看到在 `if` 语句块内创建了一个观察者实例对象,我们称之为 **计算属性的观察者**,同时会把计算属性的观察者添加到 `watchers` 常量对象中,键值是对应计算属性的名字,注意由于 `watchers` 常量与 `vm._computedWatchers` 属性具有相同的引用,所以对 `watchers` 常量的修改相当于对 `vm._computedWatchers` 属性的修改,现在你应该知道了,`vm._computedWatchers` 对象是用来存储计算属性观察者的。
 
 另外有几点需要注意,首先创建计算属性观察者时所传递的第二个参数是 `getter` 函数,也就是说计算属性观察者的求值对象是 `getter` 函数。传递的第四个参数是 `computedWatcherOptions` 常量,它是一个对象,定义在 `initComputed` 函数的上方:
 
@@ -2711,7 +2711,7 @@ depend () {
 }
 ```
 
-此时我们已经知道了 `this.dep` 属性是一个 `Dep` 实例对象,所以 `this.dep.depend()` 这句代码的作用就是用来收集依赖。那么它收集到的东西是什么呢?这就要看 `Dep.target` 属性的值是什么了,我们回想一下整个过程:首先渲染函数的执行会读取计算属性 `compA` 的值,从而触发计算属性 `compA` 的 `get` 拦截器函数,最终调用了 `this.dep.depend()` 方法收集依赖。这个过程中的关键一步就是渲染函数的执行,我们知道在渲染函数执行之前 `Dep.target` 的值必然是**渲染函数的观察者对象**。所以计算属性观察者对象的 `this.dep` 属性中所收集的就是渲染函数的观察者对象。
+此时我们已经知道了 `this.dep` 属性是一个 `Dep` 实例对象,所以 `this.dep.depend()` 这句代码的作用就是用来收集依赖。那么它收集到的东西是什么呢?这就要看 `Dep.target` 属性的值是什么了,我们回想一下整个过程:首先渲染函数的执行会读取计算属性 `compA` 的值,从而触发计算属性 `compA` 的 `get` 拦截器函数,最终调用了 `this.dep.depend()` 方法收集依赖。这个过程中的关键一步就是渲染函数的执行,我们知道在渲染函数执行之前 `Dep.target` 的值必然是 **渲染函数的观察者对象**。所以计算属性观察者对象的 `this.dep` 属性中所收集的就是渲染函数的观察者对象。
 
 记得此时计算属性观察者对象的 `this.dep` 中所收集的是渲染函数观察者对象,假设我们把渲染函数观察者对象称为 `renderWatcher`,那么:
 
@@ -2802,7 +2802,7 @@ compA () {
 
 大家想一想这个函数的执行会发生什么事情?我们知道数据对象的 `a` 属性是响应式的,所以如上函数的执行将会触发属性 `a` 的 `get` 拦截器函数。所以这会导致属性 `a` 将会收集到一个依赖,这个依赖实际上就是计算属性的观察者对象。
 
-现在思路大概明朗了,如果计算属性 `compA` 依赖了数据对象的 `a` 属性,那么属性 `a` 将收集计算属性 `compA` 的**计算属性观察者对象**,而**计算属性观察者对象**将收集**渲染函数观察者对象**,整个路线是这样的:
+现在思路大概明朗了,如果计算属性 `compA` 依赖了数据对象的 `a` 属性,那么属性 `a` 将收集计算属性 `compA` 的 **计算属性观察者对象**,而 **计算属性观察者对象** 将收集 **渲染函数观察者对象**,整个路线是这样的:
 
 ![](http://7xlolm.com1.z0.glb.clouddn.com/2018-06-10-074626.png)
 
@@ -2845,7 +2845,7 @@ update () {
 
 ## 同步执行观察者
 
-通常情况下当数据状态发生改变时,所有 `Watcher` 都为异步执行,这么做的目的是于对性能的考虑。但在某些场景下我们仍需要同步执行的观察者,我们可以使用 `sync` 选项定义同步执行的观察者,如下:
+通常情况下当数据状态发生改变时,所有 `Watcher` 都为异步执行,这么做的目的是于对性能的考虑。但在某些场景下我们仍需要同步执行的观察者,我们可以使用 `sync` 选项定义同步执行的观察者,如下:
 
 ```js
 new Vue({
@@ -2860,7 +2860,7 @@ new Vue({
 
 如上代码所示,我们在定义一个观察者时使用 `sync` 选项,并将其设置为 `true`,此时当数据状态发生变化时该观察者将以同步的方式执行。这么做当然没有问题,因为我们仅仅定义了一个观察者而已。
 
-`Vue` 官方推出了 [vue-test-utils](https://github.com/vuejs/vue-test-utils) 测试工具库,这个库的一个特点是,当你使用它去辅助测试 `Vue` 单文件组件时,数据变更将会以同步的方式触发组件变更,这对于测试而言会提供很大帮助。大家思考一下 [vue-test-utils](https://github.com/vuejs/vue-test-utils) 库是如何实现这个功能的?我们知道开发者在开发组件的时候基本不太可能手动指定一个观察者为同步的,所以 [vue-test-utils](https://github.com/vuejs/vue-test-utils) 库需要有能力拿到组件的定义并人为把组件中定义的所有观察者都转换为同步的,这是一个繁琐并容易引起 `bug` 的工作,为了解决这个问题,`Vue` 提供了 `Vue.config.async` 全局配置,它的默认值为 `true`,我们可以在 `src/core/config.js` 文件中看到这样一句代码,如下:
+`Vue` 官方推出了 [vue-test-utils](https://github.com/vuejs/vue-test-utils) 测试工具库,这个库的一个特点是,当你使用它去辅助测试 `Vue` 单文件组件时,数据变更将会以同步的方式触发组件变更,这对于测试而言会提供很大帮助。大家思考一下 [vue-test-utils](https://github.com/vuejs/vue-test-utils) 库是如何实现这个功能的?我们知道开发者在开发组件的时候基本不太可能手动指定一个观察者为同步的,所以 [vue-test-utils](https://github.com/vuejs/vue-test-utils) 库需要有能力拿到组件的定义并人为把组件中定义的所有观察者都转换为同步的,这是一个繁琐并容易引起 `bug` 的工作,为了解决这个问题,`Vue` 提供了 `Vue.config.async` 全局配置,它的默认值为 `true`,我们可以在 `src/core/config.js` 文件中看到这样一句代码,如下:
 
 ```js {8}
 export default ({
@@ -2897,7 +2897,7 @@ export function queueWatcher (watcher: Watcher) {
 }
 ```
 
-如上高亮代码所示,在非生产环境下如 `!config.async` 为真,则说明开发者配置了 `Vue.config.async = false`,这意味着所有观察者需要同步执行,所以只需要把原本通过 `nextTick` 包装的 `flushSchedulerQueue` 函数单独拿出来执行即可。另外通过如上高亮的代码我们也能够明白一件事儿,那就是 `Vue.config.async` 这个配置项只会在非生产环境生效。
+如上高亮代码所示,在非生产环境下如 `!config.async` 为真,则说明开发者配置了 `Vue.config.async = false`,这意味着所有观察者需要同步执行,所以只需要把原本通过 `nextTick` 包装的 `flushSchedulerQueue` 函数单独拿出来执行即可。另外通过如上高亮的代码我们也能够明白一件事儿,那就是 `Vue.config.async` 这个配置项只会在非生产环境生效。
 
 为了实现同步执行的观察者,除了把 `flushSchedulerQueue` 函数从 `nextTick` 中提取出来之外,还需要做一件事儿,我们打开 `src/core/observer/dep.js` 文件,找到 `notify` 方法,如下:
 
@@ -2917,6 +2917,4 @@ notify () {
 }
 ```
 
-在异步执行观察者的时候,当数据状态方式改变时,会通过如上 `notify` 函数通知变化,从而把执行所有观察者的 `update` 方法,在 `update` 方法内会将所有即将被执行的观察者都添加到观察者队列中,并在 `flushSchedulerQueue` 函数内对观察者回调的执行顺序进行排序。但是当同步执行的观察者时,由于 `flushSchedulerQueue` 函数是立即执行的,它不会等待所有观察者入队之后再去执行,这就没有办法保证观察者回调的正确更新顺序,这时就需要如上高亮的代码,其实现方式是在执行观察者对象的 `update` 更新方法之前就对观察者进行排序,从而保证正确的更新顺序。
-
-
+在异步执行观察者的时候,当数据状态方式改变时,会通过如上 `notify` 函数通知变化,从而执行所有观察者的 `update` 方法,在 `update` 方法内会将所有即将被执行的观察者都添加到观察者队列中,并在 `flushSchedulerQueue` 函数内对观察者回调的执行顺序进行排序。但是当同步执行的观察者时,由于 `flushSchedulerQueue` 函数是立即执行的,它不会等待所有观察者入队之后再去执行,这就没有办法保证观察者回调的正确更新顺序,这时就需要如上高亮的代码,其实现方式是在执行观察者对象的 `update` 更新方法之前就对观察者进行排序,从而保证正确的更新顺序。

+ 9 - 9
docs/art/9vue-state-init.md

@@ -218,7 +218,7 @@ const value = validateProp(key, propsOptions, propsData, vm)
 
 首先将 `prop` 的名字(`key`)添加到 `keys` 数组中,我们知道常量 `keys` 与 `vm.$options._propKeys` 属性具有相同的引用,所以这等价于将 `key` 添加到 `vm.$options._propKeys` 属性中,至于为什么添加到 `vm.$options._propKeys` 属性,我们会在后面讲到。
 
-接着定义了 `value` 常量,该常量的值为 `validateProp` 函数的返回值。一句话概括 `validateProp` 函数的作用:用来校验名字给定的 `prop` 数据是否符合预期的类型,并返回相应 `prop` 的值(或默认值)。至于 `validateProp` 函数的具体实现我们放到后面讲,现在大家只需要知道 `validateProp` 函数会返回给定名字的 `prop` 的值即可,也就是说常量 `value` 中保存着 `prop` 的值。
+接着定义了 `value` 常量,该常量的值为 `validateProp` 函数的返回值。一句话概括 `validateProp` 函数的作用:用来校验名字(`key`)给定的 `prop` 数据是否符合预期的类型,并返回相应 `prop` 的值(或默认值)。至于 `validateProp` 函数的具体实现我们放到后面讲,现在大家只需要知道 `validateProp` 函数会返回给定名字的 `prop` 的值即可,也就是说常量 `value` 中保存着 `prop` 的值。
 
 接着是一个 `if...else` 语句块:
 
@@ -554,7 +554,7 @@ someData instanceof Array
 
 这种方式的问题就在于,不同 `iframes` 之间的 `Array` 构造函数本身都是不相等的。所以以上判断方法只适用于在同一个 `iframes` 环境下。
 
-同理,为了做到更严谨判断,我们需要使用 `getType` 函数,如下:
+同理,为了做到更严谨判断,我们需要使用 `getType` 函数,如下:
 
 ```js
 function getType (fn) {
@@ -710,7 +710,7 @@ stringIndex < 0 || booleanIndex < stringIndex
 <some-comp prop1="" />
 <!-- 等价于 -->
 <some-comp prop1 />
-``` 
+```
 
 最后我们再来回顾一下 `validateProp` 函数中的这段代码:
 
@@ -841,7 +841,7 @@ if (vm && vm.$options.propsData &&
 vm.$options.propsData[key] === undefined
 ```
 
-大家别忘了我们目前讲解的代码是 `getPropDefaultValue` 函数中的代码,代码既然已经执行到了 `getPropDefaultValue` 函数那么说明外界没有向组件传递该 `prop` 数据,那也就是说 `vm.$options.propsData[key]` 很显然的应该是 `undefined`。为什么还需要如上判断呢?实际上事情并非像我们想象的那样。这是因为**组件第一次创建与后续的更新走的是两套不太一致的逻辑**。为了证明这一点,我们需要打开 `src/core/instance/lifecycle.js` 文件找到 `updateChildComponent` 函数,大家现在只需要知道组件的更新是由 `updateChildComponent` 函数来完成的即可,在 `updateChildComponent` 函数内有这样一段代码:
+大家别忘了我们目前讲解的代码是 `getPropDefaultValue` 函数中的代码,代码既然已经执行到了 `getPropDefaultValue` 函数那么说明外界没有向组件传递该 `prop` 数据,那也就是说 `vm.$options.propsData[key]` 很显然的应该是 `undefined`。为什么还需要如上判断呢?实际上事情并非像我们想象的那样。这是因为 **组件第一次创建与后续的更新走的是两套不太一致的逻辑**。为了证明这一点,我们需要打开 `src/core/instance/lifecycle.js` 文件找到 `updateChildComponent` 函数,大家现在只需要知道组件的更新是由 `updateChildComponent` 函数来完成的即可,在 `updateChildComponent` 函数内有这样一段代码:
 
 ```js {8}
 if (propsData && vm.$options.props) {
@@ -1170,7 +1170,7 @@ if (!valid && t === 'object')
 * 1、期望的类型是这五种类型之一:`'String'`、`'Number'`、`'Boolean'`、`'Function'` 以及 `'Symbol'`
 * 2、并且通过 `typeof` 操作符取到的该 `prop` 值的类型为 `object`
 
-这时我们能够否定 `prop` 的值不符合预期吗?答案是不能的,因为在 `javascript` 有个概念叫做**基本包装类型**,比如可以这样定义一个字符串:
+这时我们能够否定 `prop` 的值不符合预期吗?答案是不能的,因为在 `javascript` 有个概念叫做 **基本包装类型**,比如可以这样定义一个字符串:
 
 ```js
 const str = new String('基本包装类型')
@@ -1288,7 +1288,7 @@ if (process.env.NODE_ENV !== 'production') {
 }
 ```
 
-这段代码用来检测该方法是否真正的有定义,如果没有定义则打印警告信息,提示开发者是否正确引用了函数。
+这段代码用来检测该方法是否真正的有定义,如果没有定义则打印警告信息,提示开发者是否正确引用了函数。
 
 接着是如下这段检测代码:
 
@@ -1334,7 +1334,7 @@ methods: {
 
 ## provide 选项的初始化及实现
 
-再往下我们将研究最后两个选项的初始化工作,即 `provide` 选项以及 `inject` 选项。在这之前我们来回顾一下这两个选项的作用,实际上 `Vue` 的官方文档已经明确告诉我们这两个选项主要是用来辅助测试的,在真正的业务代码中是不推荐使用的,一般情况下我们也不需要使用这两个选项。
+再往下我们将研究最后两个选项的初始化工作,即 `provide` 选项 `inject` 选项。在这之前我们来回顾一下这两个选项的作用,实际上 `Vue` 的官方文档已经明确告诉我们这两个选项主要是用来辅助测试的,在真正的业务代码中是不推荐使用的,一般情况下我们也不需要使用这两个选项。
 
 如果一个组件使用了 `provide` 选项,那么该选项指定的数据将会被注入到该组件的所有后代组件中,在后代组件中可以使用 `inject` 选项选择性注入,这样后代组件就拿到了祖先组件提供的数据,这么做的好处是方便了为高阶组件提供数据并测试。
 
@@ -1490,7 +1490,7 @@ const keys = hasSymbol
   : Object.keys(inject)
 ```
 
-现在我们知道 `keys` 常量中保存 `inject` 选项对象的每一个键名,但我们注意到这里有一个对 [hasSymbol](../appendix/core-util.html#hassymbol) 的判断,其目的是保证 `Symbol` 类型与 `Reflect.ownKeys` 可用且为宿主环境原生提供,如果 `hasSymbol` 为真,则说明可用,此时会使用 `Reflect.ownKeys` 获取 `inject` 对象中所有可枚举的键名,否则使用 `Object.keys` 作为降级处理。实际上 `Reflect.ownKeys` 配合可枚举过滤等价于 `Object.keys` 与 `Object.getOwnPropertySymbols` 配合可枚举过滤之和,其好处是支持 `Symbol` 类型作为键名,当然了这一切都建立在宿主环境的支持之上,所以 `Vue` 官网中提到了**`inject` 选项对象的属性可以使用 `ES2015 Symbols` 作为 `key`,但是只在原生支持 `Symbol` 和 `Reflect.ownKeys` 的环境下可工作**。
+现在我们知道 `keys` 常量中保存 `inject` 选项对象的每一个键名,但我们注意到这里有一个对 [hasSymbol](../appendix/core-util.md#hassymbol) 的判断,其目的是保证 `Symbol` 类型与 `Reflect.ownKeys` 可用且为宿主环境原生提供,如果 `hasSymbol` 为真,则说明可用,此时会使用 `Reflect.ownKeys` 获取 `inject` 对象中所有可枚举的键名,否则使用 `Object.keys` 作为降级处理。实际上 `Reflect.ownKeys` 配合可枚举过滤等价于 `Object.keys` 与 `Object.getOwnPropertySymbols` 配合可枚举过滤之和,其好处是支持 `Symbol` 类型作为键名,当然了这一切都建立在宿主环境的支持之上,所以 `Vue` 官网中提到了**`inject` 选项对象的属性可以使用 `ES2015 Symbols` 作为 `key`,但是只在原生支持 `Symbol` 和 `Reflect.ownKeys` 的环境下可工作**。
 
 回过头来继续看 `resolveInject` 函数的代码,接下来的代码使用 `for` 循环,用来遍历刚刚获取到的 `keys` 数组:
 
@@ -1592,6 +1592,6 @@ toggleObserving(true)
 
 另外大家也注意到了在使用 `defineReactive` 函数为组件实例对象定义属性之前,调用了 `toggleObserving(false)` 函数关闭了响应式定义的开关,之后又将开关开启:`toggleObserving(true)`。前面我们已经讲到了类似的情况,这么做将会导致使用 `defineReactive` 定义属性时不会将该属性的值转换为响应式的,所以 `Vue` 文档中提到了:
 
->提示:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。
+> 提示:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。
 
 当然啦,如果父代组件提供的数据本身就是响应式的,即使 `defineReactive` 不转,那么最终这个数据也还是响应式的。