vue2源码分析-vue入口文件global-api分析_vuecli2多入口文件golb插件

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

源码目录

  • 在这里插入图片描述

global-api代码初始化

  • 代码路径 src\vue-2.6.14\src\core\global-api\index.js

代码分析

vue挂载的全局属性
  • vue.util 公共方法
    • warn 代码警告处理
    • extend 将源数据中的值拷贝到目标数据中
    • mergeOptions 合并选项
    • defineReactive 定义响应式数据
  • set set 方法,修改对象的某个属性,以更新视图
  • del del 方法,删除数组的某个索引下的数据 或者 对象的某个属性
  • nextTick nextTick方法
  • observable 调用observer类的observe方法,将对象变为响应式对象,并且返回该响应式对象
  • options vue的一些选项
初始化方法
  • extend
    • extend函数此处用于扩展,将源数据中的值拷贝到目标数据中,数据类型兼容到了 对象和数组
  • initUse
    • 初始化插件
  • initMixin
    • 初始化混入 逻辑有点多,后期详细补充
  • initExtend
    • 构造一个vue的子类
  • initAssetRegisters(Vue)
    • 注册或者获取全局组件、指令、过滤器
/\* @flow \*/

import config from '../config'
import { initUse } from './use'
import { initMixin } from './mixin'
import { initExtend } from './extend'
import { initAssetRegisters } from './assets'
import { set, del } from '../observer/index'
import { ASSET\_TYPES } from 'shared/constants'
import builtInComponents from '../components/index'
import { observe } from 'core/observer/index'

import {
  warn,
  extend,
  nextTick,
  mergeOptions,
  defineReactive
} from '../util/index'
export function initGlobalAPI(Vue: GlobalAPI) {
  // note 初始化全局api sjf-step2
  debugger
  const configDef = {}
  configDef.get = () => config
  // 初始化vue的全局config配置
  if (process.env.NODE\_ENV !== 'production') {
    configDef.set = () => {
      // set在修改对象的字段时触发,此处表示在设置 Vue的config属性时触发
      warn(
        'Do not replace the Vue.config object, set individual fields instead.'
      )
    }
    //在开发环境中,不允许替换整个Vue.config 对象,只允许设置单独的字段
  }
  Object.defineProperty(Vue, 'config', configDef)
  // 
  // exposed util methods.
  // NOTE: these are not considered part of the public API - avoid relying on
  // them unless you are aware of the risk.
  Vue.util = {//将全局方法,挂载到Vue的util方法上
    warn,
    extend,
    mergeOptions,
    defineReactive
  }

  Vue.set = set// set 方法,修改对象的某个属性,以更新视图
  Vue.delete = del // del 方法,删除数组的某个索引下的数据 或者 对象的某个属性
  Vue.nextTick = nextTick // nextTick方法
  // set delete nextTick 除了可以使用 this.$set,this.$delete,this.$nextTick调用,亦可以在Vue实例上直接调用

  // 2.6 explicit observable API
  Vue.observable = (obj) => {
    observe(obj)// 调用observer类的observe方法,将对象变为响应式对象
    return obj//并且返回该响应式对象
  }

  Vue.options = Object.create(null)
  //调用 Object.create(null) 创建一个没有原型链的空对象
  ASSET\_TYPES.forEach(type => {
    Vue.options[type + 's'] = Object.create(null)
  })
  // ASSET\_TYPES = ['component','directive','filter'] 
  // 遍历ASSET\_TYPES数组,给Vue.options添加三个属性,分别是components,directives,filters

  // this is used to identify the "base" constructor to extend all plain-object
  // components with in Weex's multi-instance scenarios.

  Vue.options._base = Vue
  // 将vue赋值给Vue.options.\_base,用于在weex的多实例场景下,标识“基础”构造函数,以扩展所有纯对象组件
  extend(Vue.options.components, builtInComponents)
  // 将builtInComponents对象的属性,复制到Vue.options.components上
  // extend函数此处用于扩展
  initUse(Vue)//初始化插件
  initMixin(Vue)//初始化混入 逻辑有点多,后期详细补充
  initExtend(Vue)//构造一个vue的子类 
  initAssetRegisters(Vue) // 注册或者获取全局组件、指令、过滤器
}



extend 扩展数据

  • 代码路径 src\vue-2.6.14\src\shared\util.js
  • extend函数此处用于扩展,将源数据中的值拷贝到目标数据中,数据类型兼容到了 对象和数组
  • 入参说明
    • to 目标值,_from 源数据
  • 核心代码说明
    • const key in _from
    • _from 可以是一个数组 ,也可以是一个对象
    • 如果是数组,则key是数组的索引
    • 如果是对象,则key是对象的键值
export function extend (to: Object, \_from: ?Object): Object {
  // sjf-note-best-code
  // 将源数据中的值拷贝到目标数据中,数据类型兼容到了 对象和数组
  for (const key in _from) {
    // const key in \_from 
    // \_from 可以是一个数组 ,也可以是一个对象
    // 如果是数组,则key是数组的索引
    // 如果是对象,则key是对象的键值
    to[key] = _from[key]
  }
  //当我们还在苦逼地用 Array.isArray 判断数据类型是否是数组来区分代码写两套逻辑时, vue源代码已经实现了,一个代码同时兼容了数组和对象的拷贝,简直不要太优秀啊
  return to
}

  • 优秀代码设计思想
    • 当我们还在苦逼地用 Array.isArray 判断数据类型是否是数组来区分代码写两套逻辑时, vue源代码已经实现了,一个代码同时兼容了数组和对象的拷贝,简直不要太优秀啊

initUse 初始化插件

  • 代码路径 src\vue-2.6.14\src\core\global-api\use.js
  • 入参说明
  • plugin 参数 有可能是函数也有能是对象
    • 当是对象时需要访问install属性并且使用apply修改this为plugin后调用
    • 当是函数时则直接调用
  • 核心代码说明
  • installedPlugins
    • 定义 installedPlugins 参数,
    • 读取vue实例上的_installedPlugins属性,如果读取不到则赋值为空数组
    • 缓存插件,将插件添加到已安装插件的数组中
  • 防止插件的重复加载
    • 如果插件已经安装,则将插件直接返回
import { toArray } from '../util/index'

export function initUse (Vue: GlobalAPI) {
  Vue.use = function (plugin: Function | Object) {
    const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
    // \_installedPlugins 函数内部的变量 用来存储已经注册的插件
    // \_installedPlugins 不存在时,初始化为一个空数组
    // vue源码中并没有声明 \_installedPlugins 并且此处是非箭头函数
    if (installedPlugins.indexOf(plugin) > -1) {
      // 防止插件的重复加载
      return this
    }
    const args = toArray(arguments, 1)
    // toArray 方法将参数转化为数组
    // arguments 是一个类数组对象,有length属性,但是没有数组的方法
    // 通过toArray方法将arguments转化为数组,并且去掉第一个参数
    args.unshift(this)//再将this添加到args数组的头部
    if (typeof plugin.install === 'function') {
      // 如果插件有install方法,则调用install方法
      plugin.install.apply(plugin, args)
    } else if (typeof plugin === 'function') {
      // 如果插件是一个函数,则直接调用
      plugin.apply(null, args)
    }
    installedPlugins.push(plugin)//缓存插件,将插件添加到已安装插件的数组中
    return this
  }
}

initMixin 初始化混入

  • 初始化混入 逻辑有点多,后期单独出一篇文章详细补充

initExtend 初始化vue组件对象

  • 代码路径 src\vue-2.6.14\src\core\global-api\extend.js
    函数说明
  • 基于构造函数Vue,创建了一个Vue组件对象,并初始化vue选项式api
    核心代码说明
  • 获取父类的cid,也就是组件的id,即组件的标识
  • cachedCtors,定义一个缓存对象
    • 如果缓存对象中已经有了当前类的id,则表明在此操作前已经创建过了
    • 通过id访问到缓存对象中的子类,并直接返回
    • 这样做可以提高性能,避免重复的子类创建
  • 获取到组件的name属性
    • 如果当前传入的组件选项中有name属性则直接读取,否则访问Super(父类的)name属性
    • name是组件的标识,一般确实永不到,但是组件递归的时候必须要写,否则无法调用子组件
  • 调用validateComponentName校验name属性是否合法
  • sub类的处理
    • 通过函数的方式创建一个sub类,并将父类的原型链继承到子类中(原型链继承方式),同时还需要修正constructor的指向
    • 将父类的options字段和传入组件对象选项进行合并,并保存在子类sub的options字段中
    • 将父类保存到子类的 super 字段中,确保子类能拿到父类
  • 初始化 props
    • props 是当前组件的props属性
    • const in props 遍历对象的属性
    • 将 组件传入的props属性代理到组件实例的_props属性上
      • 初始化 computed
    • computed 是当前组件的computed属性
    • const in props 遍历对象的属性
    • defineComputed 逻辑暂时没看,后期单独出一期文章补充
  • extend,mixin,use等api添加到子类中
  • 新增 superOptions,extendOptions,sealedOptions等选项
  • 将子类返回
/\* @flow \*/

import { ASSET\_TYPES } from 'shared/constants'
import { defineComputed, proxy } from '../instance/state'
import { extend, mergeOptions, validateComponentName } from '../util/index'

export function initExtend (Vue: GlobalAPI) {
  /\*\*
 \* Each instance constructor, including Vue, has a unique
 \* cid. This enables us to create wrapped "child
 \* constructors" for prototypal inheritance and cache them.
 \*/
  // sjf-note-best-code
  Vue.cid = 0//初始化cid为0 可以理解为组件的标识
  let cid = 1 

  /\*\*
 \* Class inheritance
 \*/
  Vue.extend = function (extendOptions: Object): Function {
    // 基于构造函数Vue,创建了一个Vue组件对对象,并初始化vue选项式api
    extendOptions = extendOptions || {}
    // extendOptions是一个对象,如果没有传入,则初始化为空对象
    // extendOptions 是组件传入的选项
    const Super = this//将this(vue的实例)赋值给Super
    const SuperId = Super.cid

    const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
    //定义一个缓存对象,用生成的id作为属性键,如果cachedCtors[SuperId]存在则表明该组件的构造函数已经生成,直接返回即可
    if (cachedCtors[SuperId]) {
      return cachedCtors[SuperId]
    }

    const name = extendOptions.name || Super.options.name
    // extendOptions 当前组件的选项 的 
    // Super 是 Vue的实例 options 是Vue的选项
    // 如果传入了extendOptions.name 则使用extendOptions.name 否则使用Super.options.name
    if (process.env.NODE\_ENV !== 'production' && name) {
      //判断是否是开发环境
      validateComponentName(name)
      //校验组件名称是否合法, 如果不合法会抛出异常
    }
    const Sub = function VueComponent (options) {

      this.\_init(options)
      //此处的this是vue实例 ,调用了\_init方法,初始化组件实例
    }
    Sub.prototype = Object.create(Super.prototype)
    // Super.prototype 是Vue的原型对象
    // Object.create(Super.prototype) 创建了一个新的对象,该对象的原型是Super.prototype
    // Object.create() 方法创建一个新对象,不会有原型链上的属性和方法
    
    Sub.prototype.constructor = Sub// consturctor指向自身 sjf-todo
    // 通过函数的方式创建一个sub类,并将父类的原型链继承到子类中(原型链继承方式),同时还需要修正constructor的指向
    Sub.cid = cid++//组件的cid自增1

    Sub.options = mergeOptions(
      //将父类的options字段和传入组件对象选项进行合并,并保存在子类sub的options字段中
      Super.options,//Vue的选项
      extendOptions//当前组件的选项
    )
    Sub['super'] = Super//将Super(根组件的实例)赋值给当前组件的super属性
// 将父类保存到子类的 super 字段中,确保子类能拿到父类
    // For props and computed properties, we define the proxy getters on
    // the Vue instances at extension time, on the extended prototype. This
    // avoids Object.defineProperty calls for each instance created.
    if (Sub.options.props) {
      //如果当前组件有props属性,则初始化props
      initProps(Sub)  
    }
    if (Sub.options.computed) {
      //如果当前组件有computed属性,则初始化computed
      initComputed(Sub)
    }

    // allow further extension/mixin/plugin usage
    Sub.extend = Super.extend
    Sub.mixin = Super.mixin
    Sub.use = Super.use
// Vue的一些原生API 等扩展到子构造器上边
    // create asset registers, so extended classes
    // can have their private assets too.
    ASSET\_TYPES.forEach(function (type) {
      Sub[type] = Super[type]
    })
    // enable recursive self-lookup
    if (name) {
      Sub.options.components[name] = Sub
    }

    // keep a reference to the super options at extension time.
    // later at instantiation we can check if Super's options have
    // been updated.
    Sub.superOptions = Super.options
    Sub.extendOptions = extendOptions
    Sub.sealedOptions = extend({}, Sub.options)
// Vue的一些原生API 等扩展到子构造器上边
    // cache constructor
    cachedCtors[SuperId] = Sub
    return Sub
  }
}

function initProps (Comp) {
  const props = Comp.options.props
  // props 是当前组件的props属性
  for (const key in props) {  
    // const in props 遍历对象的属性
    proxy(Comp.prototype, `\_props`, key)
    // proxy 代理数据
    // 此处是将 组件传入的props属性代理到组件实例的\_props属性上
  }
}

function initComputed (Comp) {
  const computed = Comp.options.computed
  // computed 是当前组件的computed属性
  for (const key in computed) {
    // const in computed 遍历对象的属性
    defineComputed(Comp.prototype, key, computed[key])
    // defineComputed 代理数据
    // 此处是将 组件传入的computed属性代理到组件实例的\_props属性上
  }
}



initAssetRegisters 注册或者获取全局组件、指令、过滤器

  • src\vue-2.6.14\src\core\global-api\assets.js
    函数说明
  • 注册或者获取全局 component 组件、directive指令、 filter 过滤器
    函数入参说明
  • id 标识名称id
  • definition 定义类型是函数或者对象
    • 当type 是directive或者filter的时候,是函数
    • 当type是components的时候是,对象
      核心代码说明
  • 判断是是否有传入 definition 参数
    • 无则表明不是注册全局组件、指令、过滤器,而直接读取,直接通过标识名称id读取返回即可
    • 有则标明是注册
  • 校验组件名是否合规
    • 非生产环境 并且type值是component组件名称,则需要校验组件名是否合规
    • 如果不合规则在 validateComponentName函数中抛出异常,下文有说明
  • 根据type类型分别做逻辑处理
  • component
  • type为 component,并且 isPlainObject 判断 definition类型是 object,则执行下边逻辑
    • 读取 name 属性,如果读不到definition的name属性,则使用标识名称id
    • 调用 vue 的extend扩展定义,并重新赋值
  • directive
    • type值为 directive ,并且 definition的类型为 function
    • 定义一个对象有bindupdate属性,赋值为definition
  • 以上逻辑处理好了directive和component选项下的definition,filter选项不需要特殊处理
  • 根据选项式api对象读取到指定id的component 组件、directive指令、 filter 过滤器并赋值为 definition
  • 将 definition返回给调用者
/\* @flow \*/

import { ASSET\_TYPES } from 'shared/constants'
import { isPlainObject, validateComponentName } from '../util/index'



![img](https://img-blog.csdnimg.cn/img_convert/c2a0f66c3cb9920a96defe00a5440e2e.png)
![img](https://img-blog.csdnimg.cn/img_convert/974f23ed21a3870e0ef3e42ffbd2d4ec.png)
![img](https://img-blog.csdnimg.cn/img_convert/f12870f2854b636a499598ecdcbec994.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618658159)**

\_TYPES } from 'shared/constants'
import { isPlainObject, validateComponentName } from '../util/index'



[外链图片转存中...(img-awbs0LsA-1715532294656)]
[外链图片转存中...(img-pEL2rzLj-1715532294657)]
[外链图片转存中...(img-Sp6M82Pz-1715532294657)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618658159)**

  • 30
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值