• vue源码学习(2)- Vue初始化过程


    Vue 

    /src/core/instance/index.js.    //vue初始化,也就是vue构造函数的位置

    1. import {initMixin} from './init'
    2. //vue 构造函数
    3. function Vue(options){
    4. if (!(this instanceof Vue)) {
    5. warn$2('Vue is a constructor and should be called with the `new` keyword');
    6. }
    7. //调用Vue.prototype._init 方法,该方法是在initMinin中定义的
    8. this._init(options);
    9. }
    10. // 定义Vue.prototype._init方法
    11. initMixin(Vue)
    12. export default Vue

     Vue.prototype._init

    /src/core/instance/init.js

    1. /**
    2. *定义Vue.prototype._init 方法
    3. *@param{*} Vue构造函数
    4. */
    5. var uid=0
    6. export function initMixin(Vue: Class){
    7. //负责 Vue的初始化过程
    8. Vue.prototype._init=function(options?:Object){
    9. // vue实例
    10. const vm: Component = this
    11. // 每个vue实例都有一个_uid,并且是依次递增的
    12. vm.uid = uid++
    13. vm._isVue = true
    14. //处理组件配置项
    15. if(options && options_isComponent){
    16. /**
    17. *每个子组件初始化时走这里,这里只做了一些性能优化
    18. *将组件配置对象上的一些深层次属性放到vm.$options 选项中,以提高代码的执行效率
    19. */
    20. initInternalComponent(vm, options)
    21. }else{
    22. /**
    23. *初始化根组件时走这里,合并Vue的全局配置到根组件的局部配置,比如Vue.component 注册的全局组件会合并到根实例的components选项中
    24. *至于每个子组件的选项合并则发生在两个地方:
    25. * 1.Vue.component 方法注册的全局组件在注册时做了选项合并
    26. * 2.{component:{xx}}方式注册的局部组件在执行编译器生成的render函数时选项合并,包括根组件中的components配置
    27. */
    28. vm.$options=mergeOptions(
    29. resolveConstructorOptions(vm.constructor),options || {}, vm)
    30. )
    31. }
    32. /* istanbul ignore else */
    33. if(process.env.NODE_ENV !== 'production'){
    34. //设置代理,将vm实例上的属性代理到vm._renderProxy
    35. initProxy(vm)
    36. }else{
    37. vm._renderProxy = vm
    38. }
    39. //expose real self
    40. vm._self = vm
    41. //初始化组件实例关系属性,比如$parent、$children、$root、$refs等
    42. initLifecycle(vm)
    43. /**
    44. *初始化自定义事件,这里需要注意一点,所有我们在@click='handleClick'/>上注册的事件,监听者不是父组件,而是子组件本身,也就是说事件的派发和监听者都是组件本身,和父组件无关
    45. */
    46. initEvents(vm)
    47. //解析组件的插槽信息,得到vm.$slot,处理渲染函数,得到vm$createElement方法,即h函数
    48. initRender(vm)
    49. //调用beforeCreate 钩子函数
    50. callHook(vm,'beforeCreate')
    51. //初始化组件的inject配置项,得到result[key]=val 形式的配置对象,然后对结果数据进行响应式处理,并代理每个key
    52. initInjections(vm)
    53. //数据响应式的重点,处理props,methods,data.computed,watch
    54. initState(vm)
    55. //解析组件配置项上的provide对象,将其挂载到vm._provided属性上
    56. initProvide(vm)
    57. //调用created钩子函数
    58. callHook(vm,'created')
    59. //如果发现配置项上有el选项,则自动调用 $mount 方法,也就是说有了el选项,就不需要再手动调用$mount,反之,没有el则必须手动调用$mount
    60. if(vm.$options.el){
    61. //调用$mount方法,进入挂载阶段
    62. vm.$mount(vm.$options.el)
    63. }
    64. }
    65. }

    resolveConstructorOptions

     /src/core/instance/init.js

    1. /**
    2. * 从组件构造函数中解析配置对象options,并合并基类选项
    3. * @param {*} Ctor
    4. * @returns
    5. */
    6. export function resolveConstructorOptions (Ctor: Class<Component>){
    7. //配置项目,获取Vue上面绑定的属性
    8. let options = Ctor.options
    9. if(Ctor.super){
    10. //存在基类,递归解析基类构造函数的选项
    11. const superOptions = resolveConstructorOptions(Ctor.super)
    12. const cachedSuperOptions = Ctor.superOptions
    13. if(superOptions !== cachedSuperOptions){
    14. //说明基类构造函数选项已经发生改变,需要重新设置
    15. Ctor.superOptions = superOptions
    16. //检查Ctor.superOptions上是否有任何后期修改/附件的选项
    17. const modifiedOptions = resolveModifiedOptions(Ctor)
    18. // 如果存在被修改或增加的选项,则合并两个选项
    19. if(modifiedOptions){
    20. extend(Ctor.extendOptions,modifiedOptions);
    21. }
    22. //选项合并,将合并结果赋值为Ctor.options
    23. options = Ctor.options = mergeOptions(superOptions,Ctor.extendOptions)
    24. if(options.name){
    25. //该组件构造函数的name为自身的组件构造函数时自己的constructor
    26. options.components[options.name] = Ctor
    27. }
    28. }
    29. }
    30. return options
    31. }

     resolveModifiedOptions

    /src/core/instance/init.js

    1. /**
    2. * 解析构造函数选项中后续被修改或者增加的选项
    3. */
    4. function resolveModifiedOptions (Ctor: Class<Component>): ?Object{
    5. let modified;
    6. //构造函数选项
    7. const lastest = Ctor.options
    8. //密封的构造函数选项,备份
    9. const sealed = Ctor.sealedOptions
    10. //对比两个选项,记录不一致的选项
    11. for(const key in latest){
    12. if(latest[key] !== sealed[key]{
    13. if(!modified) modified={}
    14. modified[key]=latest[key]
    15. }
    16. }
    17. return modified
    18. }

    mergeOptions

    /src/core/util/options.js

    1. /**
    2. * 合并两个选项,出现相同配置项时,子选项会覆盖父选项的配置
    3. */
    4. export function mergeOptions(
    5. parent: Object,
    6. child: Object,
    7. vm?: Component
    8. ):Object {
    9. if(process.env.NODE_ENV !== 'production'){
    10. checkComponents(child)
    11. }
    12. if(typeof child === 'function'){
    13. child = child.options
    14. }
    15. // 标准化props、inject、directive选项,方便后续程序的处理
    16. normalizeProps(child ,vm)
    17. normalizeInject(child ,vm)
    18. normalizeDirectives(child)
    19. //处理原始child对象上的extends和mixins,分别执行mergeOptions.将这些继承而来的选项合并到parent
    20. //mergeOptions 处理过的对象会含有_base属性
    21. if(!child._base){
    22. if(child.extends){
    23. parent = mergeOptions(parent, child.extends, vm)
    24. }
    25. if(child.mixins){
    26. for(let i=0,l = child.mixins.length; i < l; i++){
    27. parent = mergeOptions(parent, child.mixins[i],vm)
    28. }
    29. }
    30. }
    31. const options = {}
    32. let key
    33. //遍历 父选项
    34. for (key in parent){
    35. mergeField(key)
    36. }
    37. //遍历子选项,如果父选项不存在该配置,则合并,否则跳过,因为父子拥有同一个属性的情况在上面处理父选项时已经处理过了,用的子选项的值;
    38. for(key in child){
    39. if(!hasOwn(parent,key){
    40. mergeField(key)
    41. }
    42. }
    43. function mergeField(key){
    44. // strats = Object.create(null)
    45. const strat = strats[key] || defaultStrat
    46. // 值如果为childVal 存在则优先使用childVal,否则使用parentVal
    47. options[key] = strat(parent[key],child[key],vm,key)
    48. }
    49. return options
    50. }

     initInjections

    /src/core/instance/inject.js

     

    1. /**
    2. *初始化inject配置项
    3. * 1.得到 result[key]=val
    4. * 2.对结果数据进行响应式处理,代理每个key到vm实例
    5. */
    6. export function initInjections (vm:Component){
    7. //解析inject配置项,然后从祖代组件的配置中找到 配置项中每一个key对应的val,最后得到 result[key]=val的结果
    8. const result=resolveInject(vm.$options.inject,vm)
    9. //对 result做数据响应式处理,也有代理inject配置中每个key到vm实例的作用
    10. //不建议在子组件去更改这些数据,因为一旦祖代组件中 注入的provide发生更改,你在组件中做的更改就会被覆盖
    11. if(result){
    12. toggleObserving(false)
    13. Object.keys(result).forEach(key=>{
    14. /* istanbul ignore else */
    15. if(process.env.NODE_ENV !== 'production'){
    16. defineReactive(vm,key,result[key],()=>{
    17. warn(
    18. `Avoid mutating an injected value directly since the changes will be ` +`overwritten whenever the provided component re-renders. ` + `injection being mutated: "${key}"`,
    19. vm)
    20. })
    21. }else{
    22. defineReactive(vm,key,result[key]
    23. }
    24. })
    25. toggleObserving(true)
    26. }
    27. }

    resolveInject

    /src/core/instance/inject.js

    1. /**
    2. *解析 inject配置项,从祖代组件的provide配置中找到key对应的值,否则用默认值,最后得到result[key]=val
    3. *inject 对象肯定是以下这个结构,因为在合并选项时对组件配置对象做了标准化处理
    4. *@param{*}inject={
    5. * key:{
    6. * from:provideKey,
    7. * default:xx
    8. * }
    9. *}
    10. */
    11. export function resolveInject(inject:any,vm:Component): ?Object{
    12. if(inject){
    13. const result=Object.create(null)
    14. //inject 配置项的所有key
    15. const keys = hasSymbol ? Reflect.ownKeys(inject):Object.keys(inject)
    16. //遍历key
    17. for(let i=0;ilength;i++){
    18. const key=keys[i]
    19. //跳过__ob__对象
    20. if(key === '__ob__') continue
    21. //拿到provide中对应的key
    22. const provideKey=inject[key].from
    23. let source = vm
    24. //遍历所有的祖代组件,直到根组件,找到provide中对应的key的值,最后得到result[key]=provide[provideKey]
    25. while(source){
    26. if(source._provided && hasOwn(source._provided,provideKey)){
    27. result[key] = source._provided[provideKey)
    28. break
    29. }
    30. source = source.$parent
    31. }
    32. //如果上一个循环未找到,则采用inject[key].default,如果没有设置default值,则抛出错误
    33. if(!source){
    34. if('default' in inject[key]){
    35. const provideDefault = inject[key].default
    36. result[key] = typeof provideDefault === 'function' ? provideDefault.call(vm) : provideDefault
    37. }else if(process.env.NODE_ENV !== 'production'){
    38. warn(`Injection "${key}" not found`,vm)
    39. }
    40. }
    41. }
    42. return result
    43. }
    44. }

    initProvide 

    /src/core/instance/inject.js

    1. /**
    2. *解析组件配置项上的provide对象,将其挂载到vm._provided属性上
    3. */
    4. export function initProvide (vm: Component){
    5. const provide = vm.$options.provide
    6. if(provide){
    7. vm._provided = typeof provide === 'function' ? provide.call(vm):provide
    8. }
    9. }

     总结:

    Vue的初始化过程(new Vue(options))都做了什么?

    • 处理组件配置项
      • 初始化根组件时进行了选项合并操作,将全局配置合并到根组件的局部配置上(也就是将Vue构造函数原型上添加的配置项合并到vm实例化对象的身上)
      • 初始化每个子组件时做了一些性能优化,将组件配置对象上的一些深层次属性放到vm.$options选项中,以提高代码的执行效率
    • 初始化组件实例的关系属性,比如$parent,$children,$root,$refs等
    • 处理自定义事件
    • 调用beforeCreate钩子函数
    • 初始化组件的inject配置项,得到result[key]=val形式的配置对象,然后对该配置对象进行浅层的响应式处理(只处理了对象的第一层数据),并代理每个key到vm实例上;
    • 数据响应式,处理props,methods,data,computed,watch等选项
    • 解析组件配置项上的provide对象,将其挂载到vm._provided属性上
    • 调用created钩子函数
    • 如果发现配置项上有el选项,则自动调用$mount方法,也就是说有了el选项,就不需要再手动调用$mount方法,反之,没提供el选项则必须调用$mount
    • 接下来进入挂载阶段
  • 相关阅读:
    【自然语言处理】关系抽取 —— CoIn 讲解
    JavaScript 数组如何实现冒泡排序?
    分布式事务理论以及解决方案
    iOS ------ autoreleasePool
    RuoYi -Cloud开源框架-跨域配置
    选择排序--python(详解)
    若依前端部署后地址栏刷新后 出现 拦截提示
    自然语言处理学习笔记(十二)————语言模型
    Java基础15 内存分析和面向对象
    计算机毕业设计Python+django 网上外卖订餐系统(源码+系统+mysql数据库+Lw文档)
  • 原文地址:https://blog.csdn.net/weixin_44374938/article/details/127806366