@@ -69,3 +69,129 @@ export function nextTick(cb?: Function, ctx?: Object) {
69
69
70
70
# 生命周期分析
71
71
72
+ 生命周期函数就是组件在初始化或者数据更新时会触发的钩子函数。
73
+
74
+ ![ ] ( https://user-gold-cdn.xitu.io/2018/7/12/1648d9df78201f07?w=1200&h=3039&f=png&s=50021 )
75
+
76
+ 在初始化时,会调用以下代码,生命周期就是通过 ` callHook ` 调用的
77
+
78
+ ``` js
79
+ Vue .prototype ._init = function (options ) {
80
+ initLifecycle (vm)
81
+ initEvents (vm)
82
+ initRender (vm)
83
+ callHook (vm, ' beforeCreate' ) // 拿不到 props data
84
+ initInjections (vm)
85
+ initState (vm)
86
+ initProvide (vm)
87
+ callHook (vm, ' created' )
88
+ }
89
+ ```
90
+
91
+ 可以发现在以上代码中,` beforeCreate ` 调用的时候,是获取不到 props 或者 data 中的数据的,因为这些数据的初始化都在 ` initState ` 中。
92
+
93
+ 接下来会执行挂载函数
94
+
95
+ ``` js
96
+ export function mountComponent {
97
+ callHook (vm , ' beforeMount' )
98
+ // ...
99
+ if (vm.$vnode == null) {
100
+ vm ._isMounted = true
101
+ callHook (vm, ' mounted' )
102
+ }
103
+ }
104
+ ```
105
+
106
+ ` beforeMount ` 就是在挂载前执行的,然后开始创建 VDOM 并替换成真实 DOM,最后执行 ` mounted ` 钩子。这里会有个判断逻辑,如果是外部 ` new Vue({}) ` 的话,不会存在 ` $vnode ` ,所以直接执行 `` mounted `` 钩子了。如果有子组件的话,会递归挂载子组件,只有当所有子组件全部挂载完毕,才会执行根组件的挂载钩子。
107
+
108
+ 接下来是数据更新时会调用的钩子函数
109
+
110
+ ``` js
111
+ function flushSchedulerQueue () {
112
+ // ...
113
+ for (index = 0 ; index < queue .length ; index++ ) {
114
+ watcher = queue[index]
115
+ if (watcher .before ) {
116
+ watcher .before () // 调用 beforeUpdate
117
+ }
118
+ id = watcher .id
119
+ has[id] = null
120
+ watcher .run ()
121
+ // in dev build, check and stop circular updates.
122
+ if (process .env .NODE_ENV !== ' production' && has[id] != null ) {
123
+ circular[id] = (circular[id] || 0 ) + 1
124
+ if (circular[id] > MAX_UPDATE_COUNT ) {
125
+ warn (
126
+ ' You may have an infinite update loop ' +
127
+ (watcher .user
128
+ ? ` in watcher with expression "${ watcher .expression } "`
129
+ : ` in a component render function.` ),
130
+ watcher .vm
131
+ )
132
+ break
133
+ }
134
+ }
135
+ }
136
+ callUpdatedHooks (updatedQueue)
137
+ }
138
+
139
+ function callUpdatedHooks (queue ) {
140
+ let i = queue .length
141
+ while (i-- ) {
142
+ const watcher = queue[i]
143
+ const vm = watcher .vm
144
+ if (vm ._watcher === watcher && vm ._isMounted ) {
145
+ callHook (vm, ' updated' )
146
+ }
147
+ }
148
+ }
149
+ ```
150
+
151
+ 上图还有两个生命周期没有说,分别为 ` activated ` 和 ` deactivated ` ,这两个钩子函数是 ` keep-alive ` 组件独有的。用 ` keep-alive ` 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 ` deactivated ` 钩子函数,命中缓存渲染后会执行 ` actived ` 钩子函数。
152
+
153
+ 最后就是销毁组件的钩子函数了
154
+
155
+ ``` js
156
+ Vue .prototype .$destroy = function () {
157
+ // ...
158
+ callHook (vm, ' beforeDestroy' )
159
+ vm ._isBeingDestroyed = true
160
+ // remove self from parent
161
+ const parent = vm .$parent
162
+ if (parent && ! parent ._isBeingDestroyed && ! vm .$options .abstract ) {
163
+ remove (parent .$children , vm)
164
+ }
165
+ // teardown watchers
166
+ if (vm ._watcher ) {
167
+ vm ._watcher .teardown ()
168
+ }
169
+ let i = vm ._watchers .length
170
+ while (i-- ) {
171
+ vm ._watchers [i].teardown ()
172
+ }
173
+ // remove reference from data ob
174
+ // frozen object may not have observer.
175
+ if (vm ._data .__ob__ ) {
176
+ vm ._data .__ob__ .vmCount --
177
+ }
178
+ // call the last hook...
179
+ vm ._isDestroyed = true
180
+ // invoke destroy hooks on current rendered tree
181
+ vm .__patch__ (vm ._vnode , null )
182
+ // fire destroyed hook
183
+ callHook (vm, ' destroyed' )
184
+ // turn off all instance listeners.
185
+ vm .$off ()
186
+ // remove __vue__ reference
187
+ if (vm .$el ) {
188
+ vm .$el .__vue__ = null
189
+ }
190
+ // release circular reference (#6759)
191
+ if (vm .$vnode ) {
192
+ vm .$vnode .parent = null
193
+ }
194
+ }
195
+ ```
196
+
197
+ 在执行销毁操作前会调用 ` beforeDestroy ` 钩子函数,然后进行一系列的销毁操作,如果有子组件的话,也会递归销毁子组件,所有子组件都销毁完毕后才会执行根组件的 ` destroyed ` 钩子函数。
0 commit comments