深入淺析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類的實例對象產生影響。

總結

 

Vue中有兩個較為高級的靜態方法mixin和extend,接下來我們來討論下關於他們各自的原理和使用場景。

Mixin:

原理:

先來看看官網的介紹:

參數:{Object} mixin

用法:

混入也可以進行全局注冊。使用時格外小心!一旦使用全局混入,它將影響每一個之后創建的 Vue 實例。使用恰當時,這可以用來為自定義選項注入處理邏輯。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 為自定義的選項 '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的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 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方法,我們就可以這么做:

1
2
3
4
5
6
7
Vue.mixin({
     methods: {
       print() {
         console.log(`我是一個通過mixin注入的方法!`)
       }
     }
   })

或者我們想要去監聽在什么階段時什么組件被加載了,被卸載了等等,我們可以這么做:

1
2
3
4
5
6
7
8
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必須是函數是為了防止各個實例的數據混亂,閉包的應用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<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的實現:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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的一個子類。

1
2
Sub.prototype = Object.create(Super.prototype)
   Sub.prototype.constructor = Sub

這兩行代碼其實就是實現Sub對Vue的繼承,源碼中有一行是

const Super = this

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

1
2
3
4
Sub.options = mergeOptions(
    Super.options,
    extendOptions
)

我們注意到在extend中也會對傳進來的配置option和Vue原來的options做一個合並。

使用場景:

當我們不需要全局去混入一些配置,比如,我們想要獲得一個component。我們可以使用Vue.component(),也可以使用Vue.extend()。

1
2
3
4
5
6
7
const ChildVue = Vue.extend({
   ...options
})
 
new ChildVue({
   ...options
})

注意extend得到的是一個Vue的子類,也就是構造函數。

區別:

mixin是對Vue類的options進行混入。所有Vue的實例對象都會具備混入進來的配置行為。

extend是產生一個繼承自Vue類的子類,只會影響這個子類的實例對象,不會對Vue類本身以及Vue類的實例對象產生影響。

總結

以上所述是小編給大家介紹的Vue中mixin和extend的區別和使用場景,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網站的支持!
如果你覺得本文對你有幫助,歡迎轉載,煩請注明出處,謝謝!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM