vue源码–变化侦测
1.什么是渲染
将dom数据输出到页面展示的过程叫做渲染。
2.变化侦测
vue渲染实现:vue将页面模板数据(即data对象的数据),通过“变化侦测”输出到dom,然后渲染页面。
什么是变化侦测?意指:监听到模板数据状态改变并将新状态输出到dom。具体流程为模板数据状态改变时,将状态刷新到虚拟dom(即vue组件data),然后将虚拟dom中的差异化数据刷新到dom。
为什么引入虚拟dom?如果模板数据直接绑定dom节点,当模板数据改变时直接修改所有关联节点,这种监测粒度过细,依赖追踪产生的内存开销较大。为了解决这一问题引入虚拟dom概念,即将状态绑定到一个组件,状态变化时通知组件更新数据。
3.变化侦测代码实现:
实现思路:通过Observer构造vue对象 , 对象所有属性添加getter,setter方法并将该对象的依赖存入依赖dep(dep中包含该对象的所有依赖和相关操作函数),只有通过get,set调用的改变才能被vue监听到触发变化侦测,对象的第一个改变$.watcher中的update方法,后续 dep.notify() 层层调用
*构造vue对象*
class Observer {
constructor(value) {
this.value = value
if(!Array.isArray(value) {
this.walk(value)
}
}
walk (obj) {
const keys = Object.keys(obj)
for(let i = 0; i < keys.length; i++) {// 所有对象初始化加上getter,setter函数
definedReactive(obj, keys[i], obj[keys[i]])
}
}
}
function definedReactive(data, key, value) {
// 递归,子属性也加上getter,setter函数
if(typeof val === 'object') {
new Observer(value)
}
let dep = new Dep() //依赖于当前对象的数组
Object.defineProperty(data, key, { //javaScript原生对象定义
enumberable: true,
configurable: true,
get: function () {
dep.depend()
return value
},
set: function (newVal) {
if(value === newVal) {
return
}
value = newVal
dep.notify()
}
})
}
vue依赖管理类
class Dep {
constructor () {
this.subs = [] //存放所有依赖的数组
}
addSub () {
this.subs.push(sub)
},
remove () {
remove(this.subs, sub)
},
depend () {
if(window.target) {
this.addSub(window.target) //window.target 是this,watcher的上下文
}
},
notify () { // 通知所有依赖更新状态
const subs = this.subs.slice()
for(let i = 0, l = subs.length; i < l; i ++) {
subs[i].update()
}
}
}
function remove(arr, item) {
if(arr.length) {
const index = arr.indexOf(item)
if(index > -1) {
return arr.splice(index, 1)
}
}
}
watcher类:
class Watcher {
constructor (vm, expOrFn, cb) {
this.vm = vm
this.getter = parsePath(expOrFn)
this.cb = cb
this.value = this.get()
}
get() {
window.target = this
let value = this.getter.call(this.vm, this.vm)
window.target = undefined
return value
}
update() { // 第一个组件直接调用update函数,引发层层调用
const oldValue = this.value
this.value = this.get()
this.cb.call(this.vm, this.value, oldValue)
}
}
*注释: Object.defineProperty //javaScript原生对象定义
this.getter.call(this.vm, this.vm) this.vm对象绑定this.getter函数并调用