MobX 会对在任何一个被监控的 function 执行的过程中被读取的已经存在的 observable 的属性进行反应。
上面的这句话里面有几个关键的点:
1:正在被读取
正在读取的意思是:取用一个对象的属性。例如:user.name 或者 user[‘name’]
2:可被监控的 function
可被监控的 function 是 computed 的一个表述,有以下几种情况:
1:一个 observer 的组件的 render()方法
2:作为第一个参数传给 when(), reaction()和 autorun()的 function
3:在 … 过程中
在 … 过程中,意思是只有在方法的执行中被读取的 observable 才被 track 了。这些 observable 是被直接的还是间接地被使用,并不影响。
换句话说,MboX 不会对以下情况进行反应:
1:从一个 observable 读取的值,但是没有被用在一个可被监控的 function 里面
2:在一个异步调用的代码块里被读取的变量
一:MobX 监控属性的读取,而不是属性的值
我们以下面的一段代码示例来解释上面的这条规则是设么意思:
let message = observable({
title: "Foo",
author: {name: "Michel"},
likes: ["John", "Sara"]
})
在内存里面它看起来如下图所示。绿色的模块表示是 observable 的属性。注意,这些数值本身不是 observable 的!
现在基本上,MobX 做的事情就是记录在你的 function 里面,你使用的是哪个箭头。在那之后,不论哪个箭头发生变化,他都会重新运行。也就是当这些箭头指向一个其他的值。
Examples
我们基于上面的 message 这个 observable。
正确:在一个被监控的方法里面读取一个属性:
autorun(()=>{console.log(message.title)
})
message.title = 'Bar'
以上代码会如我们期待的那样。因为.title 是在 autorun 里面被读取,且之后改变了,所以这个改变会被识别到。
不正确:修改一个不是 observable 的 reference
autorun(() => {console.log(message.title)
})
message = observable({title: "Bar"})
上面的代码不会反应。因为虽然 message 改变了,但是 message 不是一个 observable,而仅仅是一个指向一个 observable 的变量,而这个变量(这个 reference)它自己不是一个 observable。
不正确:在一个监控方法外读取属性
var title = message.title
autorun(() => {console.log(title)
})
message.title = "Bar"
这段代码也不会反应,因为 message.title 是在监控方法外被读取,因而它只是包含 message.title 的值(也就是 ’Foo’)。所以,title 不是一个 observable,所以 autorun 不会反应。
正确: 在一个监控方法内部读取属性
autorun(() => {console.log(message.author.name)
})
message.author.name = "Sara"
message.author = {name: "John"}
以上代码的最后 2 行代码都会导致 MboX 反应。因为.author 和.author.name 都在一个 autorun 里面被引用,这就允许 MboX 去跟踪这些 references。
不正确:给一个 observable 存储了一个没有被跟踪的本地变量
const author = message.author
autorun(() => {console.log(author.name)
})
message.author.name = "Sara"
message.author = {name: "John"}
上面的代码,倒数第一行不会触发 MobX 反应,而倒数第二行会。因为,在 autorun 里面只获取了 author.name,而 message.author 没有在 autorun 里面被使用,所以 message.author 的变化没有被跟踪,在 autorun 里面使用的还是老的 author。
接下来我们要看 Array 了。Array 的情况与 Object 有一些区别。
以下的例子,依然基于以下的代码:
let message = observable({
title: "Foo",
author: {name: "Michel"},
likes: ["John", "Sara"]
})
正确:在一个被监控的方法里读取 Array 的属性
autorun(() => {console.log(message.likes.length)
})
message.likes.push("Jennifer")
以上的代码会按照我们期待的那样执行。.length 是 Array 的一个属性。注意:上面的代码会对 array 的任何改变都作出反应。Array 是检测它的全部,而不是像 map 或者对象那样监控每一个键值对。例如:
autorun(() => {console.log(message.likes[0])
})
message.likes.push("Jennifer")
以上的这段代码,新增加了一个元素,使得 Array 本身改变了,所以会导致 autorun 里面的代码会被重新执行一遍。Array 的下标被当做属性被访问,但是只有当下标没有越界的情况下才有效。Mobx 不会监控 还不存在 的下标或者对象的属性(除非是用 map)。
不正确:在被监控的方法里访问越界的元素
autorun(() => {console.log(message.likes.join(","))
})
message.likes[2] = "Jennifer"
以上的代码不会成功。因为访问了越界的下标。
正确: 在监控方法里面访问 Array 的方法
autorun(() => {console.log(message.likes.join(","))
})
message.likes.push("Jennifer")
以上代码会按照期望的触发 autorun 的执行。Array 所有的没有修改原 Array 的方法,都会被自动监控。
不正确: 使用了 observable,但是没有读取它的任何属性
autorun(() => {message.likes})
message.likes.push("Jennifer")
以上的代码不会使得 autorun 在执行。因为,likes 数组自己本身没有被 autorun 使用,而是 likes 数组的 reference 被 autorun 使用了。