响应式原理
监听数据的 set 和 get 的事件
Vue 内部使用了 Object.defineProperty() 来实现数据响应式,通过这个函数可以监听到 set 和 get 的事件
数据劫持
以上代码简单的实现了如何监听数据的 set 和 get 的事件,但是仅仅如此是不够的,因为自定义的函数一开始是不会执行的。<br>只有先执行了依赖收集,才能在属性更新的时候派发更新,所以接下来我们需要先触发依赖收集。
依赖收集
<div><br> {{name}}<br></div>
在解析如上模板代码时,遇到 {{name}} 就会进行依赖收集。
实现一个 Dep 类,<br>用于<font color="#c41230">解耦属性的依赖收集和派发更新操</font>作
以上的代码实现很简单,当需要依赖收集的时候调用 addSub,当需要派发更新的时候调用 notify。
接下来我们先来简单的了解下<font color="#c41230"> Vue 组件挂载时添加响应式的过程。<br>在组件挂载时,会先对所有需要的属性调用 Object.defineProperty(),然后实例化 Watcher,传入组件更新的回调。<br>在实例化过程中,会对模板中的属性进行求值,触发依赖收集</font>。
触发依赖收集时的操作
因为这一小节主要目的是学习响应式原理的细节,所以接下来的代码会简略的表达触发依赖收集时的操作。
以上就是 Watcher 的简单实现,在执行构造函数的时候将 Dep.target 指向自身,从而使得收集到了对应的 Watcher,<br>在派发更新的时候取出对应的 Watcher 然后执行 update 函数。
接下来,需要对 defineReactive 函数进行改造,<br>在自定义函数中添加依赖收集和派发更新相关的代码。
以上所有代码实现了一个简易的数据响应式,核心思路就是手动触发一次属性的 getter 来实现依赖收集。
Object.defineProperty 的缺陷
缺陷
通过<font color="#c41230">下标方式修改数组数据</font>或者<font color="#c41230">给对象新增属性</font>并不会触发组件的重新渲染
产生这写缺陷的原因
<font color="#c41230">因为 Object.defineProperty 不能拦截到这些操作</font>,<br><font color="#c41230">更精确的来说,对于数组而言,大部分操作都是拦截不到的,只是 Vue 内部通过重写函数的方式解决了这个问题</font>
怎么解决
Vue 提供了一个 API <br>解决添加对象属性不响应的问题<br><font color="#c41230">Vue.set( target, key, value )</font><br>
对于数组而言,<br>Vue 内部重写了以下函数实现派发更新
vue2.x中如何监测数组变化
使用了<font color="#c41230">函数劫持</font>的方式,<font color="#c41230">重写了数组的方法</font>,Vue将data中的数组进行了<font color="#c41230">原型链重写,指向了自己定义的数组原型方法</font>。<br>这样当调用数组api时,可以通知依赖更新。<br>如果<font color="#c41230">数组中包含着引用类型</font>,会<font color="#c41230">对数组中的引用类型再次递归遍历进行监控</font>。<br>这样就实现了监测数组变化。
NextTick 原理分析
nextTick 可以让我们<font color="#c41230">在下次 DOM 更新循环结束之后执行延迟回调,用于获得更新后的 DOM</font>。
nextTick主要使用了宏任务和微任务<br>根据执行环境分别尝试采用<br>
<ul><li>Promise</li><li>MutationObserver</li><li>setImmediate</li><li><span style="font-size: inherit;">如果以上都不行则采用setTimeout</span></li></ul>
定义了一个异步方法,多次调用nextTick会将方法存入队列中,通过这个异步方法清空当前队列
v-model
<font color="#c41230">语法糖</font>
<font color="#c41230">v-model是v-bind:value和v-on:input的简写</font>,<br>所以在父组件你完全可以直接写<font color="#c41230"> :value</font>="name", <font color="#c41230">@input</font>="val => name = val"