全域註冊的方法,意思是每個 Vue 元件都能使用的方法。在 Vue 2 會有以下方法:
Vue.prototype
Vue 3 同樣有以上方法,但注意是語法會有點不同。主要是不再在 Vue 的原型上建立,而是在 app 上建立,因此,你會發現以前的 Vue.mixin()
現在改為 app.mixin()
,或者 Vue.use()
改為 app.use()
等等。另外,Vue 3 也新增了 provide
和 inject
,作用可取代 app.config.globalProperties
,即是 Vue 2 的 Vue.prototype
。
這不是常見面試題,但想藉此整理自己對全域註冊方法的理解,因此以下會再詳細說明這些語法和使用情景。
避免直接在全域建立變數或函式,我們可以在 Vue 的原型上註冊,並在每個 Vue 實例裏使用。如果幾乎每個元件都會用到此方法的話,此手法會比 mixin 方便,因為不用寫 import xxx from ...
然後又寫一行 mixins: [...]
。
看看 Vue 官方例子:
main.js
import Vue from 'vue'
import App from './App.vue'
...
// 定義全域變數
Vue.prototype.$username = 'Alysa'
// 定義全域函式
Vue.prototype.$reverseText = function (propertyName) {
this[propertyName] = this[propertyName]
.split('')
.reverse()
.join('')
}
new Vue({
router,
render: h => h(App)
}).$mount('#app')
在元件裏使用:
export default {
data() {
return {
// 使用全域變數
username: this.$username
}
},
created() {
// 使用全域函式
this.$reverseText('username')
console.log(this.username) // asylA
}
}
要注意:
undefined
的錯誤。$
符號代表全域變數或方法。稍為解釋第一點,因為 this
需要指向 Vue 實體,否則取不到資料。箭頭函式會繼承上一層函式所指向的 this
。相反,傳統函式的 this
會取決於呼叫的物件,因此當我們在 created 裏呼叫 this.$reverseText
,$reverseText
裏的 this
就會指向 Vue 實體。
官方還有舉例一個實際常見用法:在全域註冊 axios 方法。
main.js
import axios from 'axios'
Vue.prototype.$http = axios
再在某元件使用:
created() {
this.$http('https://randomuser.me/api/')
.then( res => console.log(res.data)) // {results:...}
}
這樣每次開發時就不用下載 vue-axios
這種插件了,反正自己寫幾行就搞定了。
使用 Vue.prototype 的缺點是,當你是團隊開發,可能有些開發者不太熟 Vue,以為 this.$xxx
全都是 Vue 本身的方法,例如是 this.$http
這樣的寫法,會讓人誤會是 Vue 本身的方法。
除了載入第三方插件,我們也可以建立自己的 plugin。有幾點要注意:
new Vue ({})
前使用 Vue.use()
載入 plugin。使用 Vue CLI 時,我之前在這個教學影片學習過的做法是,先建立一個叫 libs
的資料夾,裏面再開一個資料夾 MyPlugin
,並且建立 index.js
檔案。
src/ libs/ MyPlugin/ index.js
let MyPlugin = {}
// 需要使用 install 方法
MyPlugin.install = function(Vue, options) {
// 注意!不能在 new Vue() 後使用這些方法
Vue.greeting = function () {
console.log('來自 plugin 的 message')
}
// 建立指令
Vue.directive('greeting', {
bind(el, binding, vnode, oldVnode) {
console.log('來自 plugin 的 directive')
}
})
// 建立 mixin
Vue.mixin({
created() {
console.log('來自 plugin 的 mixin')
}
})
// 使用 Vue prototype
Vue.prototype.$greeting = function() {
console.log('來自 plugin 的 $greeting 方法')
}
}
export default MyPlugin
再在 main.js 引入此 plugin:
import MyPlugin from '@/libs/MyPlugin'
Vue.use(MyPlugin)
new Vue({
router,
render: h => h(App)
}).$mount('#app')
注意,在 plugin 裏使用Vue.xxx
註冊的方法,無法在建立 Vue 實體,即是 new Vue()
之後使用。因此無法在元件使用。否則會報錯:
created() {
this.greeting()
}
// this.greeting is not a function
以上明顯看到,plugin 比第一個提到的方法更強大,可以同時在裏面使用 Vue.prototype
、mixin、directive。就如字面 plugin 的意思一樣,此做法通常是用來開發插件時會用到。
相信大家對 mixin 都不陌生了,在此就不累贅重複。簡單講,mixin 的作用就把重用的功能抽出來,讓我們在不同元件裏,可以重用此功能。
最常見做法是,把會重用的功能拆出來,獨立成為一個 JS 檔案,然後把裏面的程式碼 export 出去。最後在元件裏把它 import 進來。
一個常用到的例子,就是當打 API 出錯時,就會彈跳出 error。
mixins/ showAlert.js
export default {
methods: {
showError() {
alert('載入資料失敗')
}
},
}
Home.vue 裏使用 showAlert 這個 mixin
import showAlert from '@/mixins/showAlert.js'
export default {
// 引入 mixin
mixins: [showAlert],
created() {
this.showError()
}
}
以上看到,在 Home.vue 裏,即使沒有建立 showError
這個 methods,但我們透過 mixins,把 showError
這個函式,合併到 Home.vue 的 methods 裏,因此 Home.vue 現在其實是這樣:
export default {
methods: {
showError() {
alert('載入資料失敗')
}
}
created() {
this.showError()
}
}
除了 methods,也可以用 data、created、mounted 等,如同寫 Vue 元件時一樣。
跟 Vue.prototype 的效果一樣,如果全域註冊 mixin的話,每個元件都會用到此 mixin,不用每次都要自行 import 進來才用到。
main.js
Vue.mixin({
methods: {
showError() {
alert('載入資料失敗')
}
}
})
升級到 Vue 3 後,現在是使用 createApp()
來建立 Vue 應用程式,因此不再把全局方法註冊在 Vue 身上,而是在 app 上建立。
import { createApp } from 'vue'
const app = createApp({})
截圖自官方文件:
以前 Vue 2 是直接在 Vue 的原型上使用這些全域 API,即是 Vue.use()
、Vue.mixin()
。因此會出現 Vue 實體被污染的情況,因為所有 Vue 實體都是由 new Vue()
此建構函式來建立,因此每一個實體也有共享了這些全域方法。
官方說明:
// 这会影响到所有根实例
Vue.mixin({
/* ... */
})
const app1 = new Vue({ el: '#app-1' })
const app2 = new Vue({ el: '#app-2' })
為了避免此問題,Vue 使用了 createApp 的手法來建立 Vue 的實體,而所有全域註冊的 mixin、plugin 等,全都會在該 app 上,而不是 Vue 原型上。
所以,當你建立一個 Vue CLI 專案時,你會發現 router 是註冊在某個 app 上。
const { createApp } = Vue
const app = createApp({})
app.use(router).mount('#app')
除了以上的變更,Vue 3 新增了 provide / inject
語法,官方更表示可以代替使用 app.config.globalProperties
。
通常我們會在跨元件傳資料時,才會用到 provide
和 inject
,但其實同樣可以用來實現全域註冊:
main.js
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.provide('message', function() {
console.log('來自全域的 provide')
})
app.use(router).mount('#app')
Home.vue
export default {
inject: {
msg: {
from: 'message'
}
},
created() {
this.msg() // 來自全域的 provide
}
}
這應該是相對常用的方法,特別是跨元件傳資料時就非常方便。不用一直不斷用 props 往下傳資料。
舉例說: Home
--> ProductInfo
--> ProductComments
。
要把在 Home 的資料傳到 ProductComments。
Home.vue
export default {
...,
data() {
return {
comments: ['Nice product', 'Cool!']
}
},
provide() {
return {
comments: this.comments
}
}
}
ProductComments.vue
export default {
inject: ['comments'],
created() {
console.log(this.comments) // ['Nice product', 'Cool!']
}
}
Vue.prototype
、plugin 和 mixin 全域註冊方法。Vue3 的資料狀態管理,provide / inject、vuex、props
Global API
Vue中如何使用插件?(Plugins)【Vue面试题】