Skip to content

Commit c15b8ca

Browse files
author
zhangxixi
committed
docs(src): 更新文档
1 parent f1d369c commit c15b8ca

File tree

4 files changed

+201
-2
lines changed

4 files changed

+201
-2
lines changed

src/core/instance/init.js

+12
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,18 @@ export function initMixin (Vue: Class<Component>) {
6666
measure(`vue ${vm._name} init`, startTag, endTag)
6767
}
6868

69+
/*
70+
我们在main.js里实例化Vue时,可能会在options里传入el,也可能是写$mount方法
71+
new Vue({
72+
el: '#app'
73+
})
74+
或者
75+
new Vue({
76+
77+
}).$mount('#app')
78+
79+
所以如果有el,说明没有$mount,则这里需要执行$mount。如果没有el,说明是我们自己手动执行的$mount。
80+
*/
6981
if (vm.$options.el) {
7082
vm.$mount(vm.$options.el)
7183
}

src/core/instance/state.js

+8
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ function initProps (vm: Component, propsOptions: Object) {
7070
const isRoot = !vm.$parent
7171
// root instance props should be converted
7272
if (!isRoot) {
73+
// 把shouldObserve设为false
7374
toggleObserving(false)
7475
}
7576
for (const key in propsOptions) {
@@ -85,6 +86,7 @@ function initProps (vm: Component, propsOptions: Object) {
8586
vm
8687
)
8788
}
89+
// 不允许为props设置值,保证单向数据流
8890
defineReactive(props, key, value, () => {
8991
if (!isRoot && !isUpdatingChildComponent) {
9092
warn(
@@ -102,10 +104,13 @@ function initProps (vm: Component, propsOptions: Object) {
102104
// static props are already proxied on the component's prototype
103105
// during Vue.extend(). We only need to proxy props defined at
104106
// instantiation here.
107+
// 代理props到vm上,允许我们通过this[key]访问到props里的属性。
108+
// 假设我们的props里定义了name,当我们通过this.name访问时,其实访问的就是this._props[key]
105109
if (!(key in vm)) {
106110
proxy(vm, `_props`, key)
107111
}
108112
}
113+
// 把shouldObserve设为true
109114
toggleObserving(true)
110115
}
111116

@@ -123,12 +128,14 @@ function initData (vm: Component) {
123128
)
124129
}
125130
// proxy data on instance
131+
// 代理data到vue实例上,这样通过this.[key]就能访问到this._data[key]
126132
const keys = Object.keys(data)
127133
const props = vm.$options.props
128134
const methods = vm.$options.methods
129135
let i = keys.length
130136
while (i--) {
131137
const key = keys[i]
138+
// 检查methods和props中是否已经定义过了,会先检查methods,再检查props
132139
if (process.env.NODE_ENV !== 'production') {
133140
if (methods && hasOwn(methods, key)) {
134141
warn(
@@ -336,6 +343,7 @@ export function stateMixin (Vue: Class<Component>) {
336343
warn(`$props is readonly.`, this)
337344
}
338345
}
346+
// TODO: 为什么这里用Object.defineProperty,下面的$set,$delete不需要
339347
Object.defineProperty(Vue.prototype, '$data', dataDef)
340348
Object.defineProperty(Vue.prototype, '$props', propsDef)
341349

src/core/observer/index.js

+6
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@ export class Observer {
4141

4242
constructor (value: any) {
4343
this.value = value
44+
// 实例化被观察者
4445
this.dep = new Dep()
4546
this.vmCount = 0
47+
// 根据__ob__属性判断是否已经被观察了
4648
def(value, '__ob__', this)
4749
if (Array.isArray(value)) {
4850
if (hasProto) {
@@ -107,6 +109,7 @@ function copyAugment (target: Object, src: Object, keys: Array<string>) {
107109
* returns the new observer if successfully observed,
108110
* or the existing observer if the value already has one.
109111
*/
112+
// 创建一个观察者对象
110113
export function observe (value: any, asRootData: ?boolean): Observer | void {
111114
if (!isObject(value) || value instanceof VNode) {
112115
return
@@ -139,6 +142,7 @@ export function defineReactive (
139142
customSetter?: ?Function,
140143
shallow?: boolean
141144
) {
145+
// 这里再实例一次被观察者对象
142146
const dep = new Dep()
143147

144148
// 获取属性描述对象,如: {value: "test", writable: true, enumerable: true, configurable: true}
@@ -161,6 +165,7 @@ export function defineReactive (
161165
get: function reactiveGetter () {
162166
const value = getter ? getter.call(obj) : val
163167
if (Dep.target) {
168+
// 把this添加到给观察者的依赖中
164169
dep.depend()
165170
if (childOb) {
166171
childOb.dep.depend()
@@ -189,6 +194,7 @@ export function defineReactive (
189194
val = newVal
190195
}
191196
childOb = !shallow && observe(newVal)
197+
// 通知它的订阅者(即观察者)更新
192198
dep.notify()
193199
}
194200
})

vue源码解析.md

+175-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: vue源码解析
33
date: 2019-06-22 16:56:20
44
tags:
55
---
6-
## 首先是入口文件: `src/platforms/web/entry-runtime-with-compiler.js`
6+
## <span id="entry">首先是入口文件: `src/platforms/web/entry-runtime-with-compiler.js`</span>
77

88
做了哪些事情呢?
99

@@ -31,7 +31,7 @@ new Vue({
3131
这里的`$mount`就是上面重写后的`Vue.prototype.$mount`,所以执行`$mount`时实际执行的是`mount.call(this, el, hydrating)`,也就是重写前的`Vue.prototype.$mount`。重写前的`Vue.prototype.$mount`来自`src/platforms/web/runtime/index.js`。我们去看一下这个文件里做了哪些事情。
3232

3333

34-
## `src/platforms/web/runtime/index.js`
34+
## <span id="web-runtime-index">`src/platforms/web/runtime/index.js`</span>
3535
做了哪些事情呢?
3636

3737
1.`src/core/index`里导入`Vue`
@@ -70,3 +70,176 @@ new Vue({
7070
可以看到这个文件里定义了我们执行的`$mount`,而返回的是`mountComponent(this, el, hydrating)`,这个`mountComponent`方法来自`src/core/instance/lifecycle`
7171

7272
## `src/core/instance/lifecycle`
73+
`mountComponent`函数做了哪些事情呢?
74+
75+
1. 设置`vm.$el`: `vm.$el = el`
76+
2. 执行`beforeMount`钩子: `callHook(vm, 'beforeMount')`
77+
3. 实例化一个`watcher`
78+
```javascript
79+
new Watcher(vm, updateComponent, noop, {
80+
before () {
81+
if (vm._isMounted && !vm._isDestroyed) {
82+
callHook(vm, 'beforeUpdate')
83+
}
84+
}
85+
}, true
86+
```
87+
4. 执行`mounted`钩子
88+
```javascript
89+
if (vm.$vnode == null) {
90+
vm._isMounted = true
91+
callHook(vm, 'mounted')
92+
}
93+
```
94+
5. 返回`return vm`
95+
96+
可以看到这里就完成了整个组件的挂载,并且在这里调用了`new Watcher`,如果有更新,就执行`beforeUpdate`钩子。
97+
98+
这个`new Watcher`我们稍后再说。
99+
100+
总结:上面这些是顺着`.$mount('#app')`讲的,说明了执行`.$mount('#app')`过程中做了什么事情。那么`new Vue({// 这里是我们写的配置,对应的是this.$options})`这个实例化Vue的过程做了什么呢?请接着阅读下文
101+
102+
103+
# Vue的实例化过程
104+
从[第一部分入口文件](#entry)和[第二部分](#web-runtime-index)分析可知,最终我们在`main.js`里使用的`Vue`来自`src/core/index.js`,继续追踪`src/core/index.js`,发现`Vue`来自`src/core/instance/index.js`
105+
106+
## `src/core/instance/index.js`
107+
这个文件,定义了`Vue`函数,然后下面执行的一系列函数,就是在Vue的原型上添加属性。
108+
109+
```javascript
110+
function Vue (options) {
111+
if (process.env.NODE_ENV !== 'production' &&
112+
!(this instanceof Vue)
113+
) {
114+
warn('Vue is a constructor and should be called with the `new` keyword')
115+
}
116+
this._init(options)
117+
}
118+
119+
initMixin(Vue)
120+
stateMixin(Vue)
121+
eventsMixin(Vue)
122+
lifecycleMixin(Vue)
123+
renderMixin(Vue)
124+
125+
export default Vue
126+
127+
```
128+
129+
这一系列函数就是关键了。
130+
131+
## `initMixin`
132+
来自`src/core/instance/init.js`,它在`Vue`原型上设置了`_init`方法(只是定义,没有执行),来看一下这个`_init`方法:
133+
134+
1. 设置`vm`等于`this`
135+
2. 设置`vm.$options`
136+
3. 设置`vm._renderProxy`
137+
4. 设置`vm._self = vm`
138+
5. 初始化生命周期之类的:
139+
```javascript
140+
initLifecycle(vm)
141+
initEvents(vm)
142+
initRender(vm)
143+
callHook(vm, 'beforeCreate')
144+
initInjections(vm) // resolve injections before data/props
145+
initState(vm)
146+
initProvide(vm) // resolve provide after data/props
147+
callHook(vm, 'created')
148+
```
149+
6. 如果有el,则执行$mount。
150+
151+
### `initLifecycle`
152+
```javascript
153+
vm.$parent = parent
154+
vm.$root = parent ? parent.$root : vm
155+
156+
vm.$children = []
157+
vm.$refs = {}
158+
159+
vm._watcher = null
160+
vm._inactive = null
161+
vm._directInactive = false
162+
vm._isMounted = false
163+
vm._isDestroyed = false
164+
vm._isBeingDestroyed = false
165+
```
166+
167+
### `initEvents`
168+
```javascript
169+
vm._events = Object.create(null)
170+
vm._hasHookEvent = false
171+
// init parent attached events
172+
const listeners = vm.$options._parentListeners
173+
if (listeners) {
174+
updateComponentListeners(vm, listeners)
175+
}
176+
```
177+
178+
### `initRender`
179+
```javascript
180+
vm._vnode = null // the root of the child tree
181+
vm._staticTrees = null
182+
vm.$slots
183+
vm.$scopedSlots
184+
vm._c
185+
vm.$createElement
186+
defineReactive(vm, '$attrs',...
187+
defineReactive(vm, '$listeners',...
188+
```
189+
190+
### `initInjections`
191+
解析inject,关闭响应式
192+
193+
### `initState`
194+
设置`vm._watchers = []`
195+
1. `initProps`
196+
定义`vm._props`
197+
校验`props`
198+
关闭响应式,然后`defineReactive(props, key, value,...`
199+
proxy(vm, `_props`, key)
200+
2. `initMethods`: `vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)`
201+
3. `initData`
202+
定义`vm._data`
203+
`proxy(vm, '_data', key)`
204+
`observe(data, true)`
205+
4. `initComputed`
206+
定义`vm._computedWatchers`
207+
对每个key new一个watcher
208+
5. `initWatch`
209+
`createWatcher`
210+
211+
### `initProvide`
212+
设置`vm._provided`vm.$options.provide
213+
214+
215+
## `stateMixin`
216+
来自`src/core/instance/state.js`
217+
218+
1. 定义`Vue.prototype.$data`,其get方法是返回`this._data`
219+
2. 定义`Vue.prototype.$props`,其get方法是返回`this._props`
220+
3. 定义`Vue.prototype.$set = set`, 这个`set`来自`src/core/observer/index`
221+
4. 定义`Vue.prototype.$delete = del`, 这个`del`来自`src/core/observer/index`
222+
5. 定义`Vue.prototype.$watch`
223+
224+
## `eventsMixin`
225+
来自`src/core/instance/event.js`
226+
227+
1. 定义`Vue.prototype.$on``Vue.prototype.$once``Vue.prototype.$off``Vue.prototype.$emit`
228+
229+
## `lifecycleMixin`
230+
来自`src/core/instance/lifecycle.js`
231+
232+
1. 定义`Vue.prototype._update``Vue.prototype.$forceUpdate``Vue.prototype.$destroy`
233+
234+
## `renderMixin`
235+
来自`src/core/instance/render.js`
236+
237+
1. 执行`installRenderHelpers`,略
238+
2. 定义`Vue.prototype.$nextTick`
239+
3. 定义`Vue.prototype._render`
240+
241+
242+
# `new Watcher`
243+
还记得我们上面分析`.$mount`执行过程时,留着一个`new Watcher`没说呢。`Watcher`是在`src/core/observer/watcher.js`里定义的。下面就开始分析:
244+
245+

0 commit comments

Comments
 (0)