207 lines
4.5 KiB
Markdown
207 lines
4.5 KiB
Markdown
|
|
# Plugin Structure: Install Method Requirements
|
||
|
|
|
||
|
|
## Rule
|
||
|
|
|
||
|
|
A Vue plugin must be either an object with an `install()` method, or a function that serves as the install function. The install function receives the app instance and optional user-provided options.
|
||
|
|
|
||
|
|
## Why This Matters
|
||
|
|
|
||
|
|
1. **API contract**: Vue's `app.use()` expects a specific interface. Incorrect structure causes silent failures or errors.
|
||
|
|
|
||
|
|
2. **Options passing**: The install method receives options that users pass to `app.use()`, enabling plugin configuration.
|
||
|
|
|
||
|
|
3. **App access**: The install method receives the app instance, providing access to `app.component()`, `app.directive()`, `app.provide()`, etc.
|
||
|
|
|
||
|
|
## Plugin Structures
|
||
|
|
|
||
|
|
### Object with install method (recommended)
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// plugins/myPlugin.ts
|
||
|
|
import type { App } from 'vue'
|
||
|
|
|
||
|
|
interface PluginOptions {
|
||
|
|
prefix?: string
|
||
|
|
debug?: boolean
|
||
|
|
}
|
||
|
|
|
||
|
|
const myPlugin = {
|
||
|
|
install(app: App, options: PluginOptions = {}) {
|
||
|
|
const { prefix = 'my', debug = false } = options
|
||
|
|
|
||
|
|
if (debug) {
|
||
|
|
console.log('Installing myPlugin with prefix:', prefix)
|
||
|
|
}
|
||
|
|
|
||
|
|
app.provide('myPlugin', { prefix })
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export default myPlugin
|
||
|
|
|
||
|
|
// Usage
|
||
|
|
app.use(myPlugin, { prefix: 'custom', debug: true })
|
||
|
|
```
|
||
|
|
|
||
|
|
### Function as install (alternative)
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// plugins/simplePlugin.ts
|
||
|
|
import type { App } from 'vue'
|
||
|
|
|
||
|
|
function simplePlugin(app: App, options?: { message: string }) {
|
||
|
|
app.config.globalProperties.$greet = () => {
|
||
|
|
return options?.message ?? 'Hello!'
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export default simplePlugin
|
||
|
|
|
||
|
|
// Usage
|
||
|
|
app.use(simplePlugin, { message: 'Welcome!' })
|
||
|
|
```
|
||
|
|
|
||
|
|
### Factory function pattern (most flexible)
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// plugins/configuredPlugin.ts
|
||
|
|
import type { App, Plugin } from 'vue'
|
||
|
|
|
||
|
|
interface I18nOptions {
|
||
|
|
locale: string
|
||
|
|
messages: Record<string, Record<string, string>>
|
||
|
|
fallbackLocale?: string
|
||
|
|
}
|
||
|
|
|
||
|
|
export function createI18n(options: I18nOptions): Plugin {
|
||
|
|
return {
|
||
|
|
install(app: App) {
|
||
|
|
// Options are captured in closure - no need to pass through app.use()
|
||
|
|
const { locale, messages, fallbackLocale = 'en' } = options
|
||
|
|
|
||
|
|
const translate = (key: string): string => {
|
||
|
|
return messages[locale]?.[key]
|
||
|
|
?? messages[fallbackLocale]?.[key]
|
||
|
|
?? key
|
||
|
|
}
|
||
|
|
|
||
|
|
app.provide('i18n', { translate, locale })
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Usage - options passed to factory, not app.use()
|
||
|
|
const i18n = createI18n({
|
||
|
|
locale: 'fr',
|
||
|
|
messages: {
|
||
|
|
en: { hello: 'Hello' },
|
||
|
|
fr: { hello: 'Bonjour' }
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
app.use(i18n) // No second argument needed
|
||
|
|
```
|
||
|
|
|
||
|
|
## Common Plugin Capabilities
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const fullFeaturedPlugin = {
|
||
|
|
install(app: App, options: PluginOptions) {
|
||
|
|
// 1. Register global components
|
||
|
|
app.component('MyButton', MyButtonComponent)
|
||
|
|
app.component('MyInput', MyInputComponent)
|
||
|
|
|
||
|
|
// 2. Register global directives
|
||
|
|
app.directive('focus', focusDirective)
|
||
|
|
|
||
|
|
// 3. Provide injectable values (recommended)
|
||
|
|
app.provide('pluginConfig', options)
|
||
|
|
|
||
|
|
// 4. Add global properties (use sparingly)
|
||
|
|
app.config.globalProperties.$myHelper = helperFunction
|
||
|
|
|
||
|
|
// 5. Add global mixins (avoid if possible)
|
||
|
|
app.mixin({
|
||
|
|
created() {
|
||
|
|
// Runs for every component
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
// 6. Custom error handling
|
||
|
|
app.config.errorHandler = (err, vm, info) => {
|
||
|
|
// Handle errors
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## TypeScript: Plugin Type
|
||
|
|
|
||
|
|
Use the `Plugin` type for proper typing:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import type { App, Plugin } from 'vue'
|
||
|
|
|
||
|
|
// With options type parameter
|
||
|
|
interface MyOptions {
|
||
|
|
apiKey: string
|
||
|
|
}
|
||
|
|
|
||
|
|
const myPlugin: Plugin<[MyOptions]> = {
|
||
|
|
install(app: App, options: MyOptions) {
|
||
|
|
// options is typed as MyOptions
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Without options
|
||
|
|
const simplePlugin: Plugin = {
|
||
|
|
install(app: App) {
|
||
|
|
// No options expected
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Common Mistakes
|
||
|
|
|
||
|
|
### Missing install method
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// BAD - This is just an object, not a plugin
|
||
|
|
const notAPlugin = {
|
||
|
|
doSomething() { /* ... */ }
|
||
|
|
}
|
||
|
|
app.use(notAPlugin) // Error or silent failure
|
||
|
|
|
||
|
|
// GOOD
|
||
|
|
const actualPlugin = {
|
||
|
|
install(app) {
|
||
|
|
app.provide('service', { doSomething() { /* ... */ } })
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Forgetting to use the app parameter
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// BAD - Does nothing
|
||
|
|
const uselessPlugin = {
|
||
|
|
install(app, options) {
|
||
|
|
const service = createService(options)
|
||
|
|
// Forgot to register anything with app!
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// GOOD
|
||
|
|
const usefulPlugin = {
|
||
|
|
install(app, options) {
|
||
|
|
const service = createService(options)
|
||
|
|
app.provide('service', service) // Actually makes it available
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## References
|
||
|
|
|
||
|
|
- [Vue.js Plugins Documentation](https://vuejs.org/guide/reusability/plugins.html)
|
||
|
|
- [Vue.js Application API](https://vuejs.org/api/application.html)
|