0%

Vue2.x 源码 02

全局 API

Vue.set

Vue.set(target, key, value)

由于 VUe 无法探测普通的新增 property(比如 this.myObj.newProp = 111),所以通过 Vue.set() 为响应式对象中添加一个 property,可以确保这个 property 是响应式的,并且触发视图更新

  1. 用于在运行时添加根级别属性,使该属性必须具有响应式能力
  2. 不能是 Vue 实例,或者 Vue 实例的根数据对象( 即 return {} )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
new Vue({
data() {
return {
a: 1,
str: '',
obj: {},
arr: [1, 2, 3, { key: val }]
}
},
methods: {
change() {
this.b = 2, // 不具有响应式
this.arr[0] = 111// 不具有响应式
this.arr[3].key = 'newVal' // 具有响应式
},
func() {
Vue.set(this, c, 3), // 具有响应式
Vue.set(this.arr, 0, 111) // 具有响应式
}
}
})
  • 不可在运行时直接 set 一个根级别属性( b: 2 ),这样不具有响应式
  • 若是初始化时设置 str 为空字符或者 obj 空对象,运行时再 set 是具有响应式的

Vue.use

  • Vue.use(plugin) 作用:负责安装插件,其实就是执行插件提供的 install 方法
  1. 首先会判断插件是否安装过
    1
    2
    3
    4
    5
    6
    const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
    // 是否存在组件
    // 防止重复注册
    if (installedPlugins.indexOf(plugin) > -1) {
    return this
    }
  2. 未安装过则执行插件提供的 install 方法,具体做什么由插件自己决定
    1
    2
    3
    4
    5
    6
    7
    // plugin 是 对象,直接执行 install 方法
    if (typeof plugin.install === 'function') {
    plugin.install.apply(plugin, args)
    } else if (typeof plugin === 'function') {
    // plugin 是函数
    plugin.apply(null, args)
    }

Vue.mixin

  • 负责在全局的配置上合并 options 选项,然后在每个组件生成 vnode 时将全局配置合并到自己的 options 上来
1
2
3
4
5
// 核心 mergeOptions,合并选项
Vue.mixin = function (mixin: Object) {
this.options = mergeOptions(this.options, mixin)
return this
}

Vue.component | Vue.directive(‘my-directive’, {}) | Vue.filter(‘my-filter’, {})

  • 负责注册全局组件
  • 其实就是将组件配置注册到全局配置的 components 选项上,然后各个子组件在生成 vnode 时会将全局的 components 选项合并到 局部的 components 配置上
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    if (type === 'component' && isPlainObject(definition)) {
    // 设置组件名
    definition.name = definition.name || id
    // Vue.extend 方法,基于 definition 扩展一个新的组件子类
    // 后面可以直接 new definition() 实例化一个组件
    definition = this.options._base.extend(definition)
    }
    if (type === 'directive' && typeof definition === 'function') {
    definition = { bind: definition, update: definition }
    }
    // 放入全局配置上
    // this.options[components] = { CompName: definition }
    this.options[type + 's'][id] = definition
    return definition

Vue.extend(options)

  • Vue.extends 基于 Vue 创建一个子类,参数 options 会作为该子类的默认全局配置,如同 Vue 的默认全局配置一般;
  • 所以通过 Vue.extend 扩展一个子类,一大用处就是内置一些公共配置,供子类的子类使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 重点
// 定义一个 Vue 子类,调用 _init() 方法
const Sub = function VueComponent (options) {
this._init(options)
}
// 设置子类的原型对象
Sub.prototype = Object.create(Super.prototype)
// 子类的构造函数,指回子类自己
Sub.prototype.constructor = Sub
Sub.cid = cid++
// 合并 options
// 使用 Vue.extentd 方法定义一个子类,预设一些配置项,就相当于直接使用 Vue 构造函数时的默认配置的一样
Sub.options = mergeOptions(
Super.options,
extendOptions
)

// 将全局方法复制到子类上
// 允许子类继续向下扩展
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use

Vue.delete(target, key)

  • 删除对象的 property,如果对象是响应式的,确保删除能触发视图更新,
  • 这个方法主要用于避开 Vue 不能监测到 property 被删除的限制
  • 当然不能删除根级别的响应式属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 数组利用 splice 删除
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.splice(key, 1)
return
}
const ob = (target).__ob__
// 删除对象上的属性 delete 操作符
if (!hasOwn(target, key)) {
return
}
delete target[key]
if (!ob) {
return
}
// 触发依赖通知更新
ob.dep.notify()

Vue.nextTick(cb)

  • Vue.nextTick(cb) 方法的作用是延迟回调函数 cb 的执行,一般用于 this.key = newVal 更改数据后,想要立即获取更改后的 DOM 数据
    1
    2
    3
    4
    5
    6
    // 执行 1, 2, 3
    this.key = 'newVal'

    Vue.nextTick(function() {
    // DOM 更新了
    })
    其内部执行过程:
  1. this.key = ‘newVal’ 触发依赖通知更新,将负责更新的 watcher 放入 watcher 队列
  2. 将刷新 watcher 的函数放入 callbacks 数组中
  3. 在浏览器的异步任务队列中放入一个刷新 callbacks 数组的函数
  4. Vue.nextTick(cb) 插队,将 cb 函数放入 callbacks 数组
  5. 待将来的某个时刻执行刷新 callbacks 数组的函数
  6. 然后执行 callbacks 数组的众多函数,触发 watcher.run() 的执行 更新 DOM
  7. 由于 cb 函数是在后面放到 callbacks 数组,这就保证了先完成的 DOM 更新,再执行 cb 函数
-------------本文结束感谢您的阅读-------------