Vue.js 2.0 的基础
Vue.js 2.0 的基础可以概括为以下几点:
- 数据响应式。
- 组件化。
- 指令系统。
一个 Vue.js 应用程序由多个 Vue 实例组成,每个 Vue 实例都代表一个特定的区域内的视图,Vue 实例包含了一个数据模型和一个视图模板。Vue.js 的核心是数据响应式,也就是当我们改变数据时,视图会自动更新。
Vue.js 的源码结构
Vue.js 源码非常庞大,整个源码包括以下几个部分:
- Observer:用于实现数据响应式的核心组件。
- Watcher:用于处理视图中具体的更新操作。
- Compiler:用于将组件模板编译成渲染函数。
- Runtime:用于实现组件和指令等运行时的公共逻辑。
- Platform:用户平台特定的逻辑。
Vue.js 源码解析
在整个 Vue.js 源码中,Observer 和 Watcher 是比较核心和重要的两个部分。
- Observer
Observer 的主要作用是实现数据响应式机制,它通过递归的方式对数据对象进行劫持。当数据对象的某个属性发生变化时,会自动通知上层依赖进行更新。
Observer 的实现代码如下:
class Observer {
constructor(value) {
this.value = value
this.dep = new Dep()
// 如果该对象不可扩展,则直接返回。
if (!Array.isArray(value)) {
this.walk(value)
}
}
/**
* 循环遍历对象的每个属性,使得每个属性都成为响应式的。
*/
walk(obj) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i], obj[keys[i]])
}
}
}
/**
* 对一个对象的属性进行劫持,即实现数据响应式机制。
*/
function defineReactive(obj, key, val) {
const dep = new Dep()
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
if (Dep.target) {
dep.depend()
}
return val
},
set: function reactiveSetter(newVal) {
if (newVal === val) {
return
}
val = newVal
dep.notify()
}
})
}
2. Watcher
Watcher 的主要作用是收集依赖,并在数据变化时触发相应的更新操作。当一个组件初始化完毕时,会创建一个渲染 Watcher,它会在组件触发更新时执行渲染函数,并进行更新操作。
Watcher 的实现代码如下:
class Watcher {
constructor(vm, expOrFn, callback) {
this.vm = vm
this.callback = callback
this.depIds = {}
if (typeof expOrFn === 'function') {
this.getter = expOrFn
} else {
this.getter = parsePath(expOrFn)
}
// 在实例化 Watcher 对象时,将它自身赋值给 Dep.target,此时
// Dep.target 维护了一个栈结构。
Dep.target = this
this.value = this.get()
Dep.target = null
}
/**
* 收集依赖。
*/
addDep(dep) {
if (!this.depIds.hasOwnProperty(dep.id)) {
dep.addSub(this)
this.depIds[dep.id] = dep
}
}
/**
* 计算表达式的值并返回。
*/
get() {
// 当计算表达式的值时,依赖会被自动收集。
return this.getter.call(this.vm, this.vm)
}
/**
* 当表达式的值发生变化时会被调用,调用一系列的回调函数进行更新操作。
*/
update() {
const oldValue = this.value
this.value = this.get()
this.callback.call(this.vm, this.value, oldValue)
}
}
/**
* 将字符串路径解析成函数。
*/
function parsePath(path) {
const segments = path.split('.')
return function(obj) {
for (let i = 0; i < segments.length; i++) {
if (!obj) return
obj = obj[segments[i]]
}
return obj
}
}
代码展示
下面是一个简单的代码示例,它可以在控制台中输出 data 对象中 count 属性的值以及更新后的值:
<body>
<div id="app">
{{ count }}
</div>
</body>
<script>
const data = { count: 0 }
new Vue({
el: '#app',
data: data
})
// 修改 count 属性的值,触发更新操作。
data.count = 1
</script>