React或Vue如今已经成为前端开发人员必点的技能。之前已经对React进行过分析,今天便要从源码的角度来分析Vue网络请求的时间点。
1、Vue是一个函数,没有constructor
Vue使用单文件组件的模式进行开发,vue-loader将script及template的内容加载后通过vue-template-compiler编译成options供Vue.extend()调用,最终通过$mount方法挂载到DOM。
Vue.extend(options)生成的是一个Vue实例构造器,与Vue的区别是前者包含了传入的options。两者的实例化所做的操作是一样的,我们可以通过结构较为清晰的Vue来分析。Vue 被定义在了src/core/instance/index.js中:1
2
3
4
5
6
7
8function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
Vue只是一个简单的函数,与React不同的是不能采用ES6 class的写法来表示一个组件。所以我们就不能在constructor中进行网络请求。而且函数只调用了_init方法,并没有给开发者进行手动初始化的机会。
2、new Vue 发生了什么?
我们深入_init方法:1
2
3
4
5
6
7
8
9
10
11
12
13// ... 省略前部的数据校验、处理
vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
Vue源码的可阅读性是非常高的,将不同的逻辑封装成不同的函数,一目了然,犹如插件一般自如。
从_init方法中可以明显的看到beforeCreate和created两个生命周期函数被传入callHook,顾名思义就是调用options中对应的方法。在$mount函数中会依次调用beforeMount和mounted。_init方法主要完成了初始化生命周期,初始化事件中心,初始化渲染,初始化 data、props、computed、watcher 等等。
3、beforeCreate/created/mounted
初始化请求一般有两种:
- 想要造成页面渲染的
响应类请求 - 不关心response的
单一请求
响应类请求
这类请求在接收到response后需要设置this.data来触发页面渲染,所以前提条件是要保证response callback执行的时候this.data存在,否则设置将无效。
在_init中,第8行initState完成了data的初始化。所以请求必须要在其之后的created生命周期中发送。
单一请求
这类请求原则上不需要读/写组件的任何数据,可以放在最早执行的beforeCreate中发送。如果请求依赖于props,则需要将请求移动到created,因为这个时候props才有值。
服务端渲染
与React相同,Vue的SSR也不会进行组件的挂载操作,并不会执行beforeMount和mounted方法。所以如果网站考虑SSR的话,则需要尽量把请求放到mounted中,减少资源的浪费。
4、总结
| 场景 | 最早生命周期参考 |
|---|---|
| 响应类请求 | created |
| 单一请求 | beforeCreate |
| 单一请求(读取props) | created |
| SSR | mounted |

