赞
踩
vue 的一个特点就是数据驱动视图,也就是它的响应式系统,下面研究一下。
提示:以下是本篇文章正文内容,下面案例可供参考
还记得上一篇具体流程里面_init()里面的initState(vm)
让我们进入其中src\core\instance\state.js
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props)//初始化prop里面的数据,并响应式prop
if (opts.methods) initMethods(vm, opts.methods)//防止在methops 里面定义响应式数据,并把methods 的方法挂载在实列上,this.xx()
if (opts.data) {
initData(vm)//响应数据data
} else {
observe(vm._data = {}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed)//响应式Computed
if (opts.watch && opts.watch !== nativeWatch) {//响应式watcher
initWatch(vm, opts.watch)
}
}
先不管这些props 先进入initData
function initData (vm: Component) {
let data = vm.$options.data
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {}
// proxy data on instance
const keys = Object.keys(data)
let i = keys.length
while (i--) {
const key = keys[i]
proxy(vm, `_data`, key)
}
// observe data
observe(data, true /* asRootData */)
}
略掉一些,生命看出data 可以是对象或者函数。
_data
, key) 把用户数据与_data 进行绑定const sharedPropertyDefinition = {}
export function proxy (target: Object, sourceKey: string, key: string) {
sharedPropertyDefinition.get = function proxyGetter () {
return this[sourceKey][key]
}
sharedPropertyDefinition.set = function proxySetter (val) {
this[sourceKey][key] = val
}
Object.defineProperty(target, key, sharedPropertyDefinition)
}
把访问this.xx 等于访问this._data.xx,用Object.defineProperty做了一层映射
就是this.name === this._data.name true 的原因
\src\core\observer\index.js
代码如下(示例):
export function observe (value: any, asRootData: ?boolean): Observer | void {
if (!isObject(value) || value instanceof VNode) {
return//不能是vnode, 且是一个对象
}
let ob: Observer | void
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__//是否已经被响应式了
} else if (
...
) {
ob = new Observer(value)
}
....
}
export class Observer { value: any; dep: Dep; vmCount: number; // number of vms that have this object as root $data constructor(value: any) { this.value = value this.dep = new Dep() this.vmCount = 0 def(value, '__ob__', this)//作用是给value 一个标志,已经响应 if (Array.isArray(value)) { this.observeArray(value) } else { this.walk(value) } } walk(obj: Object) { const keys = Object.keys(obj) for (let i = 0; i < keys.length; i++) { defineReactive(obj, keys[i]) } } observeArray(items: Array < any > ) { for (let i = 0, l = items.length; i < l; i++) { observe(items[i])//递归了 } } }
export function defineReactive( obj: Object, key: string, val: any, ) { const dep = new Dep() let childOb = !shallow && observe(val)//递归子对象了 Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter() { const value = getter ? getter.call(obj) : val if (Dep.target) { dep.depend()//加人依赖 if (childOb) { childOb.dep.depend()//当对象数据变动也会触发父对象的视图变化,至于视图咋变就是diff算法的事情了 if (Array.isArray(value)) { dependArray(value) } } } return value }, set: function reactiveSetter(newVal) { if (setter) { setter.call(obj, newVal) } else { val = newVal } childOb = !shallow && observe(newVal) dep.notify() } }) }
递归调用,这些一般看得懂,如何把响应与视图图结合才是难点。
这时候数据响应已经好了
dep 是什么,响应式对象都有一个dep 实列,子对象也有一个dep进Dep 类看一下
当执行完initdata 就该渲染视图了,还记得上篇:
updateComponent = () => {
vm._update(vm._render(), hydrating)
}
new Watcher(vm, updateComponent, noop, {
before() {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}
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() if (process.env.NODE_ENV !== 'production' && !config.async) { subs.sort((a, b) => a.id - b.id) } for (let i = 0, l = subs.length; i < l; i++) { subs[i].update() } } } Dep.target = null const targetStack = [] //全局的数组 export function pushTarget (target: ?Watcher) { targetStack.push(target) Dep.target = target } export function popTarget () { targetStack.pop() Dep.target = targetStack[targetStack.length - 1] }
让我们进入watcher
export default class Watcher { ... constructor ( vm: Component, expOrFn: string | Function, cb: Function, options?: ?Object, isRenderWatcher?: boolean ) { this.vm = vm if (isRenderWatcher) {//是渲染watcher vm._watcher = this } vm._watchers.push(this)//全局watchers 大数组 // options ... this.getter = expOrFn ... this.value = this.lazy ? undefined : this.get() } /** * Evaluate the getter, and re-collect dependencies. */ get () { pushTarget(this) let value const vm = this.vm try { value = this.getter.call(vm, vm) } catch (e) { return value } /** * Add a dependency to this directive. */ addDep (dep: Dep) { const id = dep.id if (!this.newDepIds.has(id)) { this.newDepIds.add(id) this.newDeps.push(dep) if (!this.depIds.has(id)) { dep.addSub(this) } } } /** * Depend on all deps collected by this watcher. */ depend () { let i = this.deps.length while (i--) { this.deps[i].depend() } } } update () { /* istanbul ignore else */ if (this.lazy) { this.dirty = true } else if (this.sync) { this.run() } else { queueWatcher(this) } }
说明:
- new Watcher 里面执行get () 后执行Dep 类的
pushTarget(this)
往Dep全局数组targetStack里面,把这个Watcher 实列也就是包含了updateComponent
的渲染函数- 执行updateComponent
里面有 执行_render和update,而_render()是把生成vdom 的函数执行它如果视图里面有使用了响应式数据,触发get
get: function reactiveGetter() {
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend()//加人依赖
}
- 去Dep 类触发dep.depend()
Dep.target.addDep(this)//Dep类,但是Dep.target 是渲染watcher,
- 所以addDep(this) 是渲染watcher.addDep(dep) 该dep 为响应式数据dep,
//Watcher
addDep (dep: Dep) {
const id = dep.id
if (!this.newDepIds.has(id)) {
this.newDepIds.add(id)
this.newDeps.push(dep)
if (!this.depIds.has(id)) {
dep.addSub(this)
}
}
}
- 最终还是 渲染watcher 加入响应数据的订阅系统
可能有点乱。
//注意Dep 和Watcher 都有depend 方法,这里没用上Watcher 的depend,其实每个watcher 都有自己的dep
简单的来说就是 new Watcher(vm, updateComponent)
的_render 触发了data ,使得data 的dep 收集了这个渲染watcher
有点乱
这就是收集依赖过程,触发更新,稍等。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。