理解插件的工作原理,是开发高质量插件的前提。本节深入探讨插件的内部机制和基本结构。
Vue 插件有两种定义方式:
const myPlugin = {
install(Vue, options) {
console.log('安装插件')
}
}
function myPlugin(Vue, options) {
console.log('安装插件')
}
两种形式效果相同,Vue.use() 会自动处理。
install 方法是插件的核心,接收两个参数:
const myPlugin = {
install(Vue, options = {}) {
const defaultOptions = {
prefix: 'my',
debug: false
}
const config = { ...defaultOptions, ...options }
if (config.debug) {
console.log('插件配置:', config)
}
}
}
Vue.use(myPlugin, { debug: true })
Vue.use 的工作原理:
Vue.use = function(plugin, ...options) {
const installedPlugins = this._installedPlugins || (this._installedPlugins = [])
if (installedPlugins.indexOf(plugin) > -1) {
return this
}
const args = [this, ...options]
if (typeof plugin.install === 'function') {
plugin.install.apply(plugin, args)
} else if (typeof plugin === 'function') {
plugin.apply(null, args)
}
installedPlugins.push(plugin)
return this
}
插件通常会在检测到全局 Vue 时自动安装:
if (typeof window !== 'undefined' && window.Vue) {
window.Vue.use(plugin)
}
这样用户可以通过 <script> 标签直接引入,无需手动调用 Vue.use()。
const utilsPlugin = {
install(Vue) {
Vue.utils = {
formatDate(date, format) {
return date.toLocaleDateString()
},
debounce(fn, delay) {
let timer = null
return function(...args) {
clearTimeout(timer)
timer = setTimeout(() => fn.apply(this, args), delay)
}
}
}
}
}
Vue.use(utilsPlugin)
console.log(Vue.utils.formatDate(new Date()))
const messagePlugin = {
install(Vue) {
Vue.prototype.$message = {
show(msg) {
alert(msg)
},
success(msg) {
this.show(`✓ ${msg}`)
},
error(msg) {
this.show(`✗ ${msg}`)
}
}
}
}
Vue.use(messagePlugin)
this.$message.success('操作成功')
const directivePlugin = {
install(Vue) {
Vue.directive('focus', {
inserted(el) {
el.focus()
}
})
Vue.directive('click-outside', {
bind(el, binding) {
el.__clickOutside__ = function(event) {
if (!(el === event.target || el.contains(event.target))) {
binding.value(event)
}
}
document.addEventListener('click', el.__clickOutside__)
},
unbind(el) {
document.removeEventListener('click', el.__clickOutside__)
delete el.__clickOutside__
}
})
}
}
const filterPlugin = {
install(Vue) {
Vue.filter('currency', function(value, symbol = '¥') {
return symbol + Number(value).toFixed(2)
})
Vue.filter('truncate', function(value, length = 50) {
if (value.length <= length) return value
return value.substring(0, length) + '...'
})
}
}
const mixinPlugin = {
install(Vue) {
Vue.mixin({
created() {
console.log(`[${this.$options.name}] created`)
}
})
}
}
谨慎使用全局混入
全局混入会影响所有组件,可能导致意外的副作用。只在确实需要时使用。
const componentsPlugin = {
install(Vue) {
const components = {
MyButton,
MyInput,
MyModal
}
Object.keys(components).forEach(name => {
Vue.component(name, components[name])
})
}
}
const plugin = {
install(Vue, options = {}) {
const defaultConfig = {
theme: 'light',
locale: 'zh-CN',
size: 'medium'
}
const config = { ...defaultConfig, ...options }
Vue.prototype.$config = config
}
}
Vue.use(plugin, {
theme: 'dark',
size: 'large'
})
const plugin = {
install(Vue, options = {}) {
const reactiveConfig = new Vue({
data() {
return {
theme: options.theme || 'light',
locale: options.locale || 'zh-CN'
}
}
})
Vue.prototype.$config = reactiveConfig
Vue.prototype.$setTheme = function(theme) {
this.$config.theme = theme
}
}
}
使用 $ 前缀区分 Vue 插件添加的方法和原生方法:
Vue.prototype.$http = axios
Vue.prototype.$loading = loadingService
Vue.prototype.$notify = notificationService
使用统一前缀避免冲突:
Vue.component('MyButton', Button)
Vue.component('MyInput', Input)
Vue.component('MyModal', Modal)
检查插件是否已安装:
if (Vue._installedPlugins && Vue._installedPlugins.includes(myPlugin)) {
console.log('插件已安装')
}
const storagePlugin = {
install(Vue, options = {}) {
const prefix = options.prefix || 'app_'
const storage = {
get(key) {
const value = localStorage.getItem(prefix + key)
try {
return JSON.parse(value)
} catch {
return value
}
},
set(key, value) {
const serialized = typeof value === 'object'
? JSON.stringify(value)
: String(value)
localStorage.setItem(prefix + key, serialized)
},
remove(key) {
localStorage.removeItem(prefix + key)
},
clear() {
Object.keys(localStorage)
.filter(k => k.startsWith(prefix))
.forEach(k => localStorage.removeItem(k))
}
}
Vue.prototype.$storage = storage
Vue.storage = storage
}
}
Vue.use(storagePlugin, { prefix: 'myapp_' })
this.$storage.set('user', { name: '张三', age: 25 })
const user = this.$storage.get('user')
插件基础要点:
$ 前缀和统一前缀