Skip to content

Latest commit

 

History

History
269 lines (201 loc) · 9.51 KB

vue源码全局API.md

File metadata and controls

269 lines (201 loc) · 9.51 KB

全局API

  • Vue.use
  • Vue.mixin
  • Vue.component
  • Vue.filter
  • Vue.directive
  • Vue.extend
  • Vue.set
  • Vue.delete
  • Vue.nextTick

global-api 定义全局API

vm.$set(target, propertyName/index, value)

参数:
{Object | Array} target
{string | number} propertyName/index
{any} value

返回值:设置的值

用法:
这是全局Vue.set的别名。

Vue.set 向响应式对象中添加一个property,并确保这个新property同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新property,因为Vue无法探测普通的新增property,如:this.myObject.newProperty = 'hi'

Vue.extend(options)

参数: {Object} options

用法:

使用基础Vue构造器,创建一个“子类”,参数是一个包含组件选项的对象。

data选项是特例,需要注意,在Vue.extend() 中必须是函数

<div id="mount-point"></div>

// 创建构造器
var Profile = Vue.extend({
 template: `<p>{{firstName}} {{lastName}} aka {{alias}}</p>`,
 data: function() {
  return (
   firstName: 'Walter',
   lastName: 'White',
   alias: 'Heisenberg'
  )
 }
})
// 创建 Profile 实例,并挂载到一个元素上。
new Profie().$mount('$mount-point')

<p>Walter White aka Heisenberg</p>

Vue的众多全局API的实现大部分放在 /src/core/global-api 目录下,这些全局API源码阅读入口则在 index.js文件中.

// 初始化 Vue的众多全局API
// 默认配置 Vue.config
// 工具方法:Vue.util.xx
// Vue.set Vue.delete Vue.nextTick Vue.observable
// Vue.options.components Vue.options.directives Vue.options.filters
// Vue.options._base
// Vue.use Vue.extend Vue.mixin Vue.component Vue.directive Vue.filter
export function initGlobalAPI(Vue: GlobalAPI) {
 // config
 const configDef = {}
 // Vue的众多默认配置项
 configDef.get = {} => config

 if (process.env.NODE_ENV !== 'production') {
    configDef.set = () => {
      warn(
        'Do not replace the Vue.config object, set individual fields instead.'
      )
    }
  }

// Vue.config
Object.defineProperty(Vue, 'config', configDef)

// 暴露一些工具方法,轻易不要使用这些工具方法
Vue.util = {
 // 暴露日志
 warn,
 // 类似选项合并
 extend,
 // 合并选项
 mergeOptions,
 // 设置响应式
 defineReactive
}

 // Vue.set / delete / nextTick
 Vue.set = set
 Vue.delete = del
 Vue.nextTick = nextTick

 // 响应式方法
 Vue.observable = <T>{obj: T}: T => {
  observe(obj)
  return obj
 }

 // Vue.options.components/directives/filter
 Vue.options = Object.create(null)
 ASSET_TYPES.forEach(type => {
  Vue.options[type + 's'] = Object.create(null) 
 })

 // 将Vue构造函数挂载到Vue.options._base上
 Vue.options._base = Vue

 // 在Vue.options.components 中添加内置组件,比如keep-alive
 extend(Vue.options.components, builtInComponents)

 // Vue.use
 initUse(Vue)
 // Vue.mixin
 initMixin(Vue)
 // Vue.extend
 initExtend(Vue)
 // Vue.component/directive/filter
 initAssetRegisters(Vue)
}

Vue.use

// 定义Vue.use,负责为Vue安装插件,做了两件事
// 1.判断插件是否已经被安装,如果安装直接结束
// 2.安装插件,执行插件的install方法
// 返回Vue实例
Vue.use = function (plugin: Function | Object) {
 // 已经安装过的插件列表
 const installedPlugins = (this._installedPlugins || [this._installedPlugins = []])
 // 判断 plugin 是否已经安装,保证不重复安装
 if(installedPlugins.indexOf(plugin) > -1) {
  return this
 }

 // 将Vue实例放到第一个参数位置,然后将这些参数传递给install方法
 const args = toArray(arguments, 1)
 args.unshift(this)

 if(typeof plugin.install === 'function') {
  // plugin 是一个对象,则执行其install方法安装插件
  plugin.install.apply(plugin, args)
 } else if(typeof plugin === 'function') {
  // 执行直接plugin方法安装插件
  plugin.apply(null, args)
 }
 // 在插件列表中添加新安装的插件
 installedPlugins.push(plugin)
 return this
}

Vue.mixin

// 定义Vue.mixin,负责全局混入选项,影响之后所有创建的Vue实例,这些实例会合并全局混入的选项
// 返回Vue实例

Vue.mixin = function(mixin: Object) {
 // 在Vue的默认配置项上合并mixin对象
 this.options = mergeOptions(this.options, mixin)
 return this;
}

Vue.use(plugin)做了什么?

负责安装plugin插件,其实就是执行插件提供的install方法

  • 首先判断该插件是否已经安装过
  • 如果没有,则执行插件提供的install方法安装插件,具体做什么由插件自己决定

Vue.mixin(options)做了什么?

负责在Vue的全局配置上合并options配置,然后在每个组件生成vnode时将全局配置合并到组件自身的配置上来

  • 标准化 options 对象上的props, inject, directive 选项的格式
  • 处理options上的extends和mixins,分别将他们合并到全局配置上
  • 然后将options配置和全局配置进行合并,选项冲突时options配置会覆盖全局配置

Vue.component(compName, Comp)做了什么?

负责注册全局组件,其实就是将组件配置注册到全局配置的components选项上

(options.components),然后各个子组件在 生成vnode时会将全局的components选项合并到局部的components配置项上。

  • 如果第二个参数为空,则表示获取compName的组件构造函数
  • 如果Comp是组件配置对象,则使用Vue.extend方法得到组件构造函数,否则直接进行下一步
  • 在全局配置上设置组件信息,this.options.components.compName = CompConstructor

Vue.directive('my-directive', {xx}) 做了什么?

在全局注册 my-directive 指令,然后每个子组件在生成 vnode 时会将全局的 directives 选项合并到局部的 directives 选项中,原理同 Vue.component方法:

  • 如果第二个参数为空,则获取指定指令的配置对象
  • 如果不为空,如果第二个参数时一个函数的话,则生成配置对象(bind:第二个参数,update: 第二个参数)
  • 然后将指令配置对象设置到全局配置上,this.options.directives['my-directive'] = {xxx}

Vue.filter('my-filter', function(val) {xxx}) 做了什么?

负责在全局注册过滤器my-filter,然后每个子组件在生成vnode时会将全局的 filters 选项合并到局部的 filters 选项中,原理时:

  • 如果没有提供第二个参数,则获取my-filter过滤器的回调函数
  • 如果提供了第二个参数,则时设置 this.options.filters['my-filter'] = function(val){xx}

Vue.extend(options) 做了什么?

Vue.extend 基于Vue创建一个子类,参数 options 会作为该子类的默认全局配置,就像Vue的默认全局配置一样。所以通过Vue.extend扩展一个子类,一大用处就是内置一些公共配置,供子类的子类使用。

  • 定义子类构造函数,这里和Vue一样,也是调用 _init(options)
  • 合并 Vue 的配置和 options,如果选项冲突,则options的选项会覆盖Vue的配置项
  • 给子类定义全局API,值为Vue的全局API,比如 Sub.extend=Super.extend,这样子类同样可以扩展出其它子类
  • 返回子类Sub

Vue.set(target, key, val) 做了什么

由于Vue无法探测普通的新增 property (比如this.myObject.newProperty='hi') , 所以通过Vue.set为响应式对象中添加一个property, 可以确保这个新property同样是响应式的,且触发视图更新。

  • 更新数组指定下标的元素:Vue.set(array, idx, val),内部通过 splice 方法实现响应式更新

  • 更新对象已有属性:Vue.set(obj, key, val),直接更新即可 => obj[key]=val

  • 不能向Vue实例或者 $data动态添加根级别的响应式数据

  • Vue.set(obj, key, val) ,如果obj不是响应式对象,会执行obj[key]=val,但是不会做响应式处理

  • Vue.set(obj, key, val),为响应式对象 obj 增加一个新的key,则通过defineReactive方法设置响应式,并触发依赖更新

Vue.delete(target, key) 做了什么

删除对象的property, 如果对象是响应式的,确保删除能触发更新视图。这个方法主要用于避开Vue不能检测到property被删除的限制,但是你应该很少会使用它。当然同样不能删除根级别的响应式属性。

  • Vue.delete(array, idx), 删除指定下标的元素,内部是通过 splice 方法实现的
  • 删除响应式对象上的某个属性: Vue.delete(obj, key), 内部是执行 delete obj.key, 然后执行依赖更新即可

Vue.nextTick(cb) 做了什么?

Vue.nextTick(cb)方法的作用是延迟回调函数 cb 的执行,一般用于 this.key = newVal 更改数据后,想立即获取更改过后的DOM数据。

this.key = 'xxx'
Vue.nextTick(function() {
 // DOM更新了
})

其内部的执行过程是:

  • this.key='xxx' ,触发依赖通知更新,将负责更新的watcher放入watcher队列
  • 将刷新 watcher 队列的按时放到 callbacks 数组中
  • 在浏览器的异步任务队列中放入一个刷新 callbacks 数组的函数
  • Vue.nextTick(cb)来插队,将cb函数放入 callbacks 数组
  • 待将来的某个时候执行刷新 callbacks 数组的函数
  • 然后执行 callbacks 数组中的众多函数,触发watcher.run的执行,更新DOM
  • 由于cb函数是在后面放到callbacks数组,所以这就是保证了先完成的DOM更新,再执行cb函数。