一、new Vue()的执行过程
1.1 数据劫持和初步渲染
function Vue (options) {
// 初始化
this._init(options)
// 执行render函数
this.$mount()
}
Vue.prototype._init = function (options) {
const vm = this
// 把options挂载到this上
vm.$options = options
if (options.data) {
// 数据响应式
initState(vm)
}
if (options.computed) {
// 初始化计算属性
initComputed(vm)
}
if (options.watch) {
// 初始化watch
initWatch(vm)
}
}
在initState()
中实现数据劫持和代理:
function initState(vm) {
// 拿到配置的data属性值
let data = vm.$options.data;
// 判断data 是函数还是别的类型
data = vm._data = typeof data === 'function' ? data.call(vm, vm) : data || {
};
// 数据代理
const keys = Object.keys(data);
let i = keys.length;
while(i--) {
// 从this上读取的数据全部拦截到this._data到里面读取
// 例如 this.name 等同于 this._data.name
proxy(vm, '_data', keys[i]);
}
// 数据观察
observe(data);
}
// 数据观察函数
function observe(data) {
if (typeof data !== 'object' && data != null) {
return;
}
return new Observer(data)
}
// 从this上读取的数据全部拦截到this._data到里面读取
// 例如 this.name 等同于 this._data.name
function proxy(vm, source, key) {
Object.defineProperty(vm, key, {
get() {
return vm[source][key] // this.name 等同于 this._data.name
},
set(newValue) {
return vm[source][key] = newValue
}
})
}
class Observer{
constructor(value) {
// 给每一个属性都设置get set
this.walk(value)
}
walk(data) {
let keys = Object.keys(data);
for (let i = 0, len = keys.length; i < len; i++) {
let key = keys[i]
let value = data[key]
// 给对象设置get set
defineReactive(data, key, value)
}
}
}
function defineReactive(data, key, value) {
Object.defineProperty(data, key, {
get() {
return value
},
set(newValue) {
if (newValue == value) return
observe(newValue) // 给新的值设置响应式
value = newValue
}
})
// 递归给数据设置get set
observe(value);
}
initComputed()
以及initWatch()
后面再实现,这时候new Vue()
中的this._init(options)
执行完成,接着执行this.$mount()
// 挂载方法
Vue.prototype.$mount = function () {
const vm = this
new Watcher(vm, vm.$options.render, () => {
}, true) // 这里的参数true表示是渲染Watcher
}
在这里可以看到new
了一个Watcher
,此处的Watcher
就是渲染Watcher
,先看看Watcher
的初步实现:
let wid = 0
class Watcher {
constructor(vm, exprOrFn, cb, options) {
this.vm = vm // 把vm挂载到当前的this上
if (typeof exprOrFn === 'function') {
this.getter = exprOrFn // 把exprOrFn挂载到当前的this上,这里exprOrFn 等于 vm.$options.render
}
this.cb = cb // 把cb挂载到当前的this上
this.options = options // 把options挂载到当前的this上
this.id = wid++ // 每个Watcher都有其id
this.value = this.get() // 相当于运行 vm.$options.render()
}
get() {
const vm = this.vm
let value = this.getter.call(vm, vm) // 把this指向到vm,此时的getter即是render函数
return value
}
}
可以看到,当在this.$mount()
中new Watcher()
(渲染Watcher
)时,会立刻执行this.get()
方法去获取数据渲染,至此,首页渲染就完成了
1.2 Dep Watcher
在首页渲染的过程中,是data
订阅的时期,以实现数据修改时能够修改视图
Dep
类的实现:
// 依赖收集
let dId = 0
class Dep{
constructor() {
this.id = dId++