vue中的computed实现原理、vue中响应属性、computed/watch/methods区别
vue中的computed的实现原理---------需要建立数据依赖搜集,动态计算实现原理1)问题:计算属性如何与属性建立依赖关系?属性发生变化又如何通知到计算属性重新计算?如何建立依赖关系?----------利用 JavaScript 单线程原理和 Vue 的 Getter 设计,通过一个简单的发布订阅,就可以在一次计算属性求值的过程中收集到相关依赖2)data 属性初始化 ge...
vue中的computed的实现原理---------需要建立数据依赖搜集,动态计算实现原理
1)问题:计算属性如何与属性建立依赖关系?属性发生变化又如何通知到计算属性重新计算?
如何建立依赖关系?----------利用 JavaScript 单线程原理和 Vue 的 Getter 设计,通过一个简单的发布订阅,就可以在一次计算属性求值的过程中收集到相关依赖
2)data 属性初始化 getter setter:通过Object.defineProperty()给data设置setter getter
// src/observer/index.js
// 这里开始转换 data 的 getter setter,原始值已存入到 __ob__ 属性中
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
// 判断是否处于依赖收集状态
if (Dep.target) {
// 建立依赖关系
dep.depend()
...
}
return value
},
set: function reactiveSetter (newVal) {
...
// 依赖发生变化,通知到计算属性重新计算
dep.notify()
}
})
3)computed属性初始化
// src/core/instance/state.js
// 初始化计算属性
function initComputed (vm: Component, computed: Object) {
...
// 遍历 computed 计算属性
for (const key in computed) {
...
// 创建 Watcher 实例
// create internal watcher for the computed property.
watchers[key] = new Watcher(vm, getter || noop, noop, computedWatcherOptions)
// 创建属性 vm.reversedMessage,并将提供的函数将用作属性 vm.reversedMessage 的 getter,
// 最终 computed 与 data 会一起混合到 vm 下,所以当 computed 与 data 存在重名属性时会抛出警告
defineComputed(vm, key, userDef)
...
}
}
export function defineComputed (target: any, key: string, userDef: Object | Function) {
...
// 创建 get set 方法
sharedPropertyDefinition.get = createComputedGetter(key)
sharedPropertyDefinition.set = noop
...
// 创建属性 vm.reversedMessage,并初始化 getter setter
Object.defineProperty(target, key, sharedPropertyDefinition)
}
function createComputedGetter (key) {
return function computedGetter () {
const watcher = this._computedWatchers && this._computedWatchers[key]
if (watcher) {
if (watcher.dirty) {
// watcher 暴露 evaluate 方法用于取值操作
watcher.evaluate()
}
// 同第1步,判断是否处于依赖收集状态
if (Dep.target) {
watcher.depend()
}
return watcher.value
}
}
}
4)无论是属性还是计算属性,都会生成一个对应的 watcher 实例
// src/core/observer/watcher.js
// 当通过 vm.reversedMessage 获取计算属性时,就会进到这个 getter 方法
get () {
// this 指的是 watcher 实例
// 将当前 watcher 实例暂存到 Dep.target,这就表示开启了依赖收集任务
pushTarget(this)
let value
const vm = this.vm
try {
// 在执行 vm.reversedMessage 的函调函数时,会触发属性(步骤1)和计算属性(步骤2)的 getter
// 在这个执行过程中,就可以收集到 vm.reversedMessage 的依赖了
value = this.getter.call(vm, vm)
} catch (e) {
if (this.user) {
handleError(e, vm, `getter for watcher "${this.expression}"`)
} else {
throw e
}
} finally {
if (this.deep) {
traverse(value)
}
// 结束依赖收集任务
popTarget()
this.cleanupDeps()
}
return value
}
5)Dep 的代码短小精悍,但却承担着非常重要的依赖收集环节
// src/core/observer/dep.js
export default class Dep {
static target: ?Watcher;
id: number;
subs: Array<Watcher>;
constructor () {
this.id = uid++
this.subs = []
}
addSub (sub: Watcher) {
this.subs.push(sub)
}
removeSub (sub: Watcher) {
remove(this.subs, sub)
}
depend () {
if (Dep.target) {
Dep.target.addDep(this)
}
}
notify () {
const subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
// 更新 watcher 的值,与 watcher.evaluate() 类似,
// 但 update 是给依赖变化时使用的,包含对 watch 的处理
subs[i].update()
}
}
}
// 当首次计算 computed 属性的值时,Dep 将会在计算期间对依赖进行收集
Dep.target = null
const targetStack = []
export function pushTarget (_target: Watcher) {
// 在一次依赖收集期间,如果有其他依赖收集任务开始(比如:当前 computed 计算属性嵌套其他 computed 计算属性),
// 那么将会把当前 target 暂存到 targetStack,先进行其他 target 的依赖收集,
if (Dep.target) targetStack.push(Dep.target)
Dep.target = _target
}
export function popTarget () {
// 当嵌套的依赖收集任务完成后,将 target 恢复为上一层的 Watcher,并继续做依赖收集
Dep.target = targetStack.pop()
}
总结一下依赖收集、动态计算的流程:
1. data 属性初始化 getter setter
2. computed 计算属性初始化,提供的函数将用作属性 vm.reversedMessage 的 getter
3. 当首次获取 reversedMessage 计算属性的值时,Dep 开始依赖收集
4. 在执行 message getter 方法时,如果 Dep 处于依赖收集状态,则判定 message 为 reversedMessage 的依赖,并建立依赖关系
5. 当 message 发生变化时,根据依赖关系,触发 reverseMessage 的重新计算
vue中响应式属性有哪些?
在data中声明的属性为响应式属性,还有哪些呢????
观察属性watch和计算属性computed、methods的区别
定义:
1)computed---计算属性将被混入到 Vue 实例中。所有 getter 和 setter 的 this 上下文自动地绑定为 Vue 实例
类型:{ [key: string]: Function | { get: Function, set: Function } }
计算属性computed的结果会被缓存,只要依赖的响应式属性发生变化的时候,才会重新计算,否则计算属性不会被更新----主要当做属性来使用
2)methods---methods 将被混入到 Vue 实例中。可以直接通过 VM 实例访问这些方法,或者在指令表达式中使用。方法中的 this
自动绑定为 Vue 实例----主要书写业务逻辑
类型:{ [key: string]: Function }
3)watch---一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象。Vue 实例将会在实化时例调用 $watch()
,遍历 watch 对象的每一个属性------主要是监听某些特定数据的变化,从而进行某些特定的业务逻辑操作,可看做computed和methods的结合
类型:{ [key: string]: string | Function | Object | Array }
触发的时机:
1)computed是在HTML DOM加载后马上执行的,如赋值;
2)而methods则必须要有一定的触发条件才能执行,如点击事件;
3)watch用于观察Vue实例上的数据变动。对应一个对象,键是观察表达式,值是对应回调。值也可以是方法名,或者是对象,包含选项。
执行顺序:
默认加载的时候先computed再watch,不执行methods;
等触发某一事件后,则是:先methods再watch
watch支持同步、异步请求;而computed只支持同步请求
转载地址 https://blog.csdn.net/smartdt/article/details/75557369
更多推荐
所有评论(0)