How to understand the Reactivity System in Vue
As we all know,MVVM is an acronym of Model,View and ViewModel.
#
What's MVVM早先前端页面只是用来展示文本,与用户的交互很少。对于一些需要操作 DOM 的的需求使用 JQuery 这个解决方案就足够了。但随着需求的增多和复杂化,频繁操作 DOM 显得很不优雅。
MVVM 使数据和视图分开,Model 显示数据,View 显示 DOM 视图。View Model 负责监听数据的变化并将其渲染到 DOM,同时 DOM 的变化也会映射到 Model 层。
#
Reactivity SystemVue realize reactive by data binding.That means you can only mudify data,than Vue will update views without handle DOM.Actually Vue will update node by virtual DOM,it faster than actual DOM.Different from React,Vue is by Object.defineProperty
API and Observer design mode.
One of Vue’s most distinct features is the unobtrusive reactivity system. Models are proxied JavaScript objects. When you modify them, the view updates. It makes state management simple and intuitive, but it’s also important to understand how it works to avoid some common gotchas.
When you pass a normal JavaScript Object into a Vue instance as a data option, the Vue will walk through all the
property
of the Object and useObject.defineProperty
to convert them all togetter/setter
.Object.defineProperty
is a non-shim feature in ES5, which is why the Vue doesn't support IE8 and lower browsers.Thegetter/setter
is not visible to the user, but inside they make Vue could tracking dependencies, when theproperty
is accessed and modified notice changes. To note here is that the different browsers in the console print data object format is different withgetter/setter
, so suggest installvue-devtools
to obtain more user-friendly interface to check the data.Each component instance map to a watcher instance, it will contact in the process of the component rendering the passed data's
property
is recorded as a dependency. Then when the setter of the dependency fires, it notifies Watcher, causing its associated component to rerender.
简单的说就是 Vue 会把data
对象中所有property
reactive 化,也就是加上 setter 和 getter,而这对用户是不可见的。
#
Object.definePropertyWhat is Object.defineProperty
API,it play what role?For example,we define a variate b
from obj
like that:
let aValue;let obj = {};Object.defineProperty(obj, "b", { get() { console.log("监听正在获取a"); return aValue; }, set(newValue) { console.log("监听正在设置a"); aValue = newValue; }, enumerable: true, configurable: true,});obj.b;obj.b = 40;
We would get result:
监听正在获取 a
监听正在设置 a
So we know,it trigger getter
when we visit variate from an Object,and trigger setter
when we set the variate.Then,we can realize a sample reactive demo like that:
<input type="text" id="txt" /><br /> <span id="sp"></span>
<script> let obj = {}, txt = document.getElementById("txt"), sp = document.getElementById("sp");
Object.defineProperty(obj, "msg", { get() { return txt.value; }, set(newValue) { txt.value = newValue; sp.innerText = newValue; }, });
txt.addEventListener("keyup", (event) => { obj.msg = event.target.value; }); </script>
In effect,that's the principle for change detection in Vue.js.我们可以封装一下代码简化一下传参。
function defineReactive(data, key, val) { Object.defineProperty(data, key, { enumerable: true, configurable: true, get() { return val; }, set(newValue) { if (val === newValue) { return; } val = newValue; }, });}
变化侦测起到了追踪变化的作用,但是更重要的是要收集依赖,我们之所以要追踪变化,就是为了当数据的属性发生变化时,可以通知那些使用到该数据的地方不是吗?所以就需要先收集依赖,等到数据发生了变化,再把之前收集到的依赖循环触发一遍就好了。显然,这就是观察者模式需要处理的问题了。
#
Observe design modeOk,we know what's Object.defineProperty
now,how about Observer design mode?
这里举个很简单的栗子:
我们喜欢一个明星(such as Evan You
)就会去关注他的动态,例如去微博上点关注。此时微博就充当了观察者,它接收 Evan 发送的微博,然后将它们发送给订阅了 Evan 微博的粉丝。当黑粉取消订阅后,微博当然也就不再发送 Evan 的微博动态给黑粉们了。
So,there has three functions:the first is regist()
用来订阅.The second is notify()
用来广播动态.The third is remove()
用来取消订阅。Now we can realize a simple Observer model:
class Observer { constructor() { // we need container collect regist imformation this.dep = []; }
regist(fn) { this.dep.push(fn); }
notify() { this.dep.forEach((item) => item()); }}
#
Vue 的响应式原理- init 阶段:Vue 的 data 的
property
都会被reactive
化,也就是加上setter/getter
函数。
function defineReactive(obj: Object, key: string, ...) { const dep = new Dep()
Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { .... dep.depend() return value .... }, set: function reactiveSetter (newVal) { ... val = newVal dep.notify() ... } }) }
class Dep { static target: ?Watcher; subs: Array<Watcher>;
depend () { if (Dep.target) { Dep.target.addDep(this) } }
notify () { const subs = this.subs.slice() for (let i = 0, l = subs.length; i < l; i++) { subs[i].update() } }
源码将收集依赖的容器dep[]
封装到 Dep 类中,每个 data 的property
都有一个 dep 对象。当 getter 调用的时候去 dep 里执行depend()
收集依赖,当 setter 调用时去 dep 里执行notify()
触发依赖。
- mount 阶段
- update 阶段
参考 知乎还是掘金上的文章,腾讯女大佬。后面东西之后再补充吧...