Vue.mixin 源码深入理解

一. 了解 Vue.mixin

介绍:该 API 就是为了给组件提供一些可复用的属性,以此来扩展组件的功能。

二. 实际应用

在了解它实现原理之前,我们先来看下它的实际应用。
假如我们需要给所有组件都添加一个 token 属性,并且在 beforeCreate 中执行某些相同的操作,我们可以执行如下操作,这样一来所有组件中都默认拥有这些属性和方法。(下边实例为全局注册,局部注册比较容易理解,可自己查看文档)

    Vue.mixin({
      data(){
        return {
          token: 'token'
        }
      },
      beforeCreate(){
        console.log('执行方法')
      }
    })

我们将组件实例打印一下,可以看到 token 已经被添加到组件中。

1651330778(1).jpg
这里只是举了一个小例子,具体使用需要结合场景去用,但是它确实可以将组件中重复的代码抽离出来,实现一个很好的复用。

三. mixin 是如何做到给所有组件都同时添加属性的呢?

我们来分析一下,所有的子组件都来自于 VueComponent构造函数的实例,而Vuecomponent 又继承至根组件Vue的实例,并且在继承时使用mergeOptions(后面会讲解)方法将VueComponentVue中的options进行合并,所以想给每个组件都实现混入,只要给Vueoptions中添加要混入的对象即可。那我们来看看 Vue中是如何实现的吧。

1. Vue.mixin 函数的实现
该方法的逻辑很简单,就是将 Vue自身的options和传入的options作为参数调用 mergeOptions方法,并将返回值重新写入Vue的options

Vue.mixin = function (mixin: Object) {
    this.options = mergeOptions(this.options, mixin)
    return this
  }

2. mergeOptions 函数的实现
以下是实现mergeOption的源码,实现比较容易理解,难懂的地方我写了上注释。

function mergeOptions (
  parent: Object,
  child: Object,
  vm?: Component,
  uid,
  isRoot
): Object {

  normalizeProps(child, vm, uid) // 将child 中的 props属性规范化,props我们在使用时有两种写法,数组和对象的形式,规范之后统一变为对象的形式。
  normalizeInject(child, vm, uid) // 同上
  normalizeDirectives(child, uid)

  if (!child._base) {
    if (child.extends) {
      parent = mergeOptions(parent, child.extends, vm)
    }
    if (child.mixins) {
      for (let i = 0, l = child.mixins.length; i < l; i++) {
        parent = mergeOptions(parent, child.mixins[i], vm)
      }
    }
  }

  const options = {}
  let key
  for (key in parent) {

    mergeField(key)
  }
  for (key in child) {
    if (!hasOwn(parent, key)) {
      mergeField(key)
    }
  }
 
  function mergeField (key) {  // 该方法是真正实现合并的地方,这里用了一个策略模式,对不同的属性,使用与其对应的方法进行合并。将父子options中的各个属性进行合并。
    const strat = strats[key] || defaultStrat
    options[key] = strat(parent[key], child[key], vm, key)
  }
  return options
}

看了源码可能还是有点懵,我来举个例子。其实就是将两个options

parentOptions = {
    // 当然可能还有其他属性,如props,生命周期hook等等
    data(){
        return{
           name: '石头山',
           age: '12'
        }
    }
}
childOptions = {
    data(){
        return{
           name: '前端开发',
           gender: '男'
        }
    }
}
// 合并之后的 options
mergeOptions = {
    data(){
        return {
           name: '石头山',
           age: '12',
           gender: '男'
        }
    }
}

2. 为每一个实例上合并 mixin 的属性
看了上边代码,小伙伴们有没有想提出这么一个问题,上边不是只给 Vueoptions上进行了合并吗,但是组件的实例 vm 上是怎么拥有这些属性的呢?这里就需要明白两个点了,请往下看。

  1. Vue.extend(VueComponent 的由来)
    这里可以看到,VueComponent它是继承至Vue,并且合并了Vueoptions,因此,VueComponent.options便有了我们使用 mixin 混入进来的对象属性。
// 详细代码在讲 extend API 时讲,这里只讲合并
Vue.extend = function (extendOptions: Object): Function {
    const Sub = function VueComponent (options) {
      this._init(options)
    }
    Sub.options = mergeOptions(
      Super.options,
      extendOptions
    )
}
  1. this._init()
    该方法是执行初始化时调用的方法,也是Vue执行的入口,实现原理看代码中的注释
Vue.prototype._init = function (options?: Object) {
// 省略详细代码
    if (options && options._isComponent) { // 如果该实例是组件实例执行
      initInternalComponent(vm, options) // 该方法往下看
    } else {  // 根实例执行
      vm.$options = mergeOptions( // 根实例vm.$options是合并了Vue和我们传入的options
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm,
        uid,
        true
      )
    }
}
function initInternalComponent (vm: Component, options: InternalComponentOptions) {
  const opts = vm.$options = Object.create(vm.constructor.options) // 组件实例vm.$options 是将原型链指向了 VueComponent.options
}

四. 结尾

通过全局API Vue.mixin源码的学习,我们可以了解作者的构建思路,更好的把握对该API的使用,并且学习编程代码优秀的写法和设计模式,在这个API中使用了策略模式,也展现了原型链实现继承的巧妙。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

石头山_S

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值