阅读 8028

Vue中mixin和extend的区别和使用场景

Vue中有两个较为高级的静态方法mixin和extend,接下来我们来讨论下关于他们各自的原理和使用场景。

Mixin:

原理:

先来看看官网的介绍:

参数: {Object} mixin

用法:

混入也可以进行全局注册。使用时格外小心!一旦使用全局混入,它将影响每一个之后创建的 Vue 实例。使用恰当时,这可以用来为自定义选项注入处理逻辑。

     // 为自定义的选项 'myOption' 注入一个处理器。
     Vue.mixin({
       created: function () {
         var myOption = this.$options.myOption
         if (myOption) {
           console.log(myOption)
         }
       }
     })
     
     new Vue({
       myOption: 'hello!'
     })
     // => "hello!"
复制代码

我们知道,Vue.mixin传递的这个参数对象,在初始化Vue实例的时候会merge到options上,下面是Vue源码中对mixin的操作。

// src\core\global-api\mixin.js
 export function initMixin (Vue: GlobalAPI) {
   Vue.mixin = function (mixin: Object) {
     this.options = mergeOptions(this.options, mixin)
     return this
   }
 }
复制代码
 // src\core\instance\index.js
 function Vue (options) {
     if (process.env.NODE_ENV !== 'production' &&
     !(this instanceof Vue)
     ) {
     warn('Vue is a constructor and should be called with the `new` keyword')
     }
     this._init(options)
 }
 
 initMixin(Vue)
 ...
 
 export default Vue
复制代码

也就是说,mixin只是对我们在初始化Vue实例时传递的配置对象的一个扩展。

就像上面官网实例写的例子,我们在执行Vue.mixin方法时传递一个配置对象进去,对象里面有个created勾子函数,通过源码我们可以看到这个传递进来的对象最终会和我们在初始化实例也就是new Vue(options)时的这个options合并(通过上面源码中的mergeOptions方法),保存在option上。

使用场景:

当我们需要全局去注入一些methods,filter或者hooks时我们就可以使用mixin来做。 比如我们希望每一个Vue实例都有一个print方法,我们就可以这么做:

    Vue.mixin({
        methods: {
            print() {
                console.log(`我是一个通过mixin注入的方法!`)
            }
        }
    })
复制代码

或者我们想要去监听在什么阶段时什么组件被加载了,被卸载了等等,我们可以这么做:

    Vue.mixin({
        mounted() {
            console.log(`${this.$route.name} component mounted!`)
        },
        destroyed() {
            console.log(`${this.$route.name} component destroyed!`)
        }
    })
复制代码

如果我们并不想给每一个组件实例都混入这些配置options,而只是个别的组件,最好不要使用mixin,它可能会影响到我们组件的性能。

Extend:

原理:

先来看看官网的介绍:

参数: {Object} options

用法:

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

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

data必须是函数是为了防止各个实例的数据混乱,闭包的应用。

<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 Profile().$mount('#mount-point')
复制代码

再来看看源码里面关于Vue.extend的实现:

Vue.extend = function (extendOptions: Object): Function {
    extendOptions = extendOptions || {}
    const Super = this
    const SuperId = Super.cid
    const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
    if (cachedCtors[SuperId]) {
      return cachedCtors[SuperId]
    }

    const name = extendOptions.name || Super.options.name
    if (process.env.NODE_ENV !== 'production' && name) {
      validateComponentName(name)
    }

    const Sub = function VueComponent (options) {
      this._init(options)
    }
    Sub.prototype = Object.create(Super.prototype)
    Sub.prototype.constructor = Sub
    Sub.cid = cid++
    Sub.options = mergeOptions(
      Super.options,
      extendOptions
    )
    Sub['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) {
      initProps(Sub)
    }
    if (Sub.options.computed) {
      initComputed(Sub)
    }

    // allow further extension/mixin/plugin usage
    Sub.extend = Super.extend
    Sub.mixin = Super.mixin
    Sub.use = Super.use

    // 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)

    // cache constructor
    cachedCtors[SuperId] = Sub
    return Sub
  }
}
复制代码

首先我们可以看到,extend方法返回的Sub其实是一个构造函数,而且继承自Vue,也就是说extend方法返回的是Vue的一个子类。

    Sub.prototype = Object.create(Super.prototype)
    Sub.prototype.constructor = Sub
复制代码

这两行代码其实就是实现Sub对Vue的继承,源码中有一行是

    const Super = this
复制代码

所以这里的Super指的就是Vue。

Sub.options = mergeOptions(
     Super.options,
     extendOptions
)
复制代码

我们注意到在extend中也会对传进来的配置option和Vue原来的options做一个合并。

使用场景:

当我们不需要全局去混入一些配置,比如,我们想要获得一个component。我们可以使用Vue.component(),也可以使用Vue.extend()。

const ChildVue = Vue.extend({
    ...options
})

new ChildVue({
    ...options
})
复制代码

注意extend得到的是一个Vue的子类,也就是构造函数。

区别:

mixin是对Vue类的options进行混入。所有Vue的实例对象都会具备混入进来的配置行为。

extend是产生一个继承自Vue类的子类,只会影响这个子类的实例对象,不会对Vue类本身以及Vue类的实例对象产生影响

文章分类
前端
文章标签