npm package: @arkxio/ark-plugin
Description: 一个 Vue 运行时插件系统,用于管理组件扩展。它提供了一种灵活的方式来注册和渲染扩展点,允许插件/模块向应用程序的特定位置贡献组件。
Author: Christian González
Latest version: 0.1.6

# @arkxio/ark-plugin

一个 Vue 运行时插件系统,用于管理组件扩展。它提供了一种灵活的方式来注册和渲染扩展点,允许插件/模块向应用程序的特定位置贡献组件。

适用于需要在同一个位置收集不同组件的场景,这些组件由不同的插件或模块提供。

**注意**: 如果你只是需要同一个组件但数据不同,请使用 Vue 的 `v-for` 指令。

## 特性

- 🎯 **扩展点**: 在模板中定义命名的扩展点
- 🔌 **灵活注册**: 全局或按模块注册扩展
- ⚡ **权重排序**: 控制组件渲染顺序
- 🎨 **多 API 支持**: 支持 Vue 2/3 Options API 和 Composition API
- 📦 **多模块友好**: 完美支持 Monorepo 架构
- 🔧 **零配置**: 组件自行声明扩展点

## 安装

```bash
npm install @arkxio/ark-plugin
```

## 快速开始

### 1. 定义扩展点

在 Vue 模板中添加 `<ark-extend-point>` 标签:

```vue
<template>
    <div>
        <h3>扩展点示例</h3>
        <div class="extension-container">
            <ark-extend-point id="app.menu.items"/>
        </div>
    </div>
</template>
```

### 2. 注册扩展

**选项 A: 组件级注册(推荐)**

组件自行声明扩展点:

```vue
<!-- components/MenuItem.vue (Options API) -->
<script>
export default {
    name: 'MenuItem',
    arkExtendPointId: 'app.menu.items',
    arkWeight: 10
}
</script>

<template>
    <div class="menu-item">菜单项</div>
</template>
```

或使用 Vue 3 Setup:

```vue
<!-- components/MenuItem.vue (Composition API) -->
<script setup>
import { defineArkExtend } from '@arkxio/ark-plugin'

defineArkExtend('app.menu.items', { arkWeight: 10 })
</script>

<template>
    <div class="menu-item">菜单项</div>
</template>
```

然后在 main.js 中注册:

```javascript
import { createApp } from 'vue'
import App from './App.vue'
import ArkPlugin, { registerComponent } from '@arkxio/ark-plugin'
import MenuItem from './components/MenuItem.vue'

const app = createApp(App)
registerComponent(MenuItem)
app.use(ArkPlugin)
app.mount('#app')
```

**选项 B: 模块级注册**

```javascript
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import ExtensionPoints, { registerExtension } from '@arkxio/ark-plugin'

// 定义扩展
import menuItem from './components/MenuItem.vue'
import cardComponent from './components/CardComponent.vue'

const menuExtension = {
    arkExtends: {
        'app.menu.items': [
            { component: menuItem, arkWeight: 10 }
        ]
    }
}

const cardExtension = {
    arkExtends: {
        'app.card.items': [
            { component: cardComponent, arkWeight: 5 }
        ]
    }
}

const app = createApp(App)
registerExtends(menuExtension)
registerExtends(cardExtension)
app.use(ArkPlugin)
app.mount('#app')
```

## 使用模式

### 模式 1: 组件级注册(推荐)

组件使用 `arkExtendPoint` (Options API) 或 `defineArkExtend()` (Composition API) 自行声明扩展点。

#### Options API

```vue
<script>
export default {
    name: 'MyComponent',
    arkExtendPointId: 'my-extension-point',
    arkWeight: 1
}
</script>
```

#### Composition API (Vue 3 Setup)

```vue
<script setup>
import { defineArkExtend } from '@arkxio/ark-plugin'

defineArkExtend('my-extension-point', { arkWeight: 1 })
</script>
```

#### 注册方式

```javascript
import { registerComponent } from '@arkxio/ark-plugin'
import MyComponent from './components/MyComponent.vue'

registerComponent(MyComponent)
```

或批量自动注册:

```javascript
import { autoRegisterComponents } from '@arkxio/ark-plugin'
import * as components from './components'

autoRegisterComponents(components)
```

### 模式 2: 模块级注册(单模块)

适用于所有扩展集中在一个地方的小型项目:

```javascript
// extensions/index.ts
import Component1 from './components/Component1.vue'
import Component2 from './components/Component2.vue'

export default {
    arkExtends: {
        'point.a': [
            { component: Component1, arkWeight: 1 }
        ],
        'point.b': [
            { component: Component2, arkWeight: 2 }
        ]
    }
}
```

```javascript
// main.js
import ArkPlugin from '@arkxio/ark-plugin'
import AppExtensions from './extensions'

app.use(ArkPlugin, { arkExtends: AppExtensions })
```

### 模式 3: 多模块注册(Monorepo)

每个模块独立注册其扩展:

```javascript
// main.js
import ArkPlugin, { registerExtends } from '@arkxio/ark-plugin'

import moduleAExtensions from './packages/moduleA/extensions'
import moduleBExtensions from './packages/moduleB/extensions'

registerExtends(moduleAExtensions)
registerExtends(moduleBExtensions)

app.use(ArkPlugin)
```

## 扩展点

### 定义扩展点

在模板中添加 `<ark-extend-point>` 标签:

```vue
<template>
    <div>
        <!-- 所有注册到 'app.menu.items' 的组件将在此渲染 -->
        <ark-extend-point id="app.menu.items"/>

        <!-- 多个扩展点 -->
        <ark-extend-point id="app.toolbar.buttons"/>
        <ark-extend-point id="app.sidebar.items"/>
    </div>
</template>
```

### 扩展点命名

使用点号表示法进行分层组织:

```javascript
// 推荐: 使用 命名空间.类别.类型
'app.menu.items'        // 应用菜单项
'app.toolbar.buttons'   // 工具栏按钮
'module-a.card.item'    // 模块 A 卡片项
```

## 组件定义格式

### Options API

```javascript
export default {
    name: 'MyComponent',
    // 必需: 扩展点标识符
    arkExtendPointId: 'my.extension.point',
    // 可选: 渲染顺序(数值越小越靠前)
    arkWeight: 1,
    // 可选: 唯一标识符
    arkId: 'my-component-id'
}
```

### Composition API

```vue
<script setup>
import { defineArkExtend } from '@arkxio/ark-plugin'

defineArkExtend('my.extension.point', {
    arkWeight: 1,
    arkId: 'my-component-id'
})
</script>
```

## 模块级扩展格式

```javascript
export default {
    // 可选: 初始化函数
    initialize() {
        console.log('扩展模块已加载')
    },

    // 必需: 扩展点到组件的映射
    arkExtends: {
        'point.name': [
            {
                component: MyComponent,
                arkWeight: 1,  // 可选: 默认为 0
                arkId: 'unique-id'  // 可选: 用于 v-for key
            }
        ],
        'another.point': [
            { component: AnotherComponent, arkWeight: 2 },
            { component: ThirdComponent, arkWeight: 3 }
        ]
    },

    // 可选: Vue 路由
    routes: [{
        path: '/module',
        component: () => import('./layouts/ModuleLayout.vue'),
        children: [
            { path: '', component: () => import('./pages/ModuleIndex.vue') }
        ]
    }]
}
```

## API 参考

### 函数

#### `registerComponent(component)`

手动注册单个组件。

```javascript
import { registerComponent } from '@arkxio/ark-plugin'
import MyComponent from './MyComponent.vue'

registerComponent(MyComponent)
```

#### `autoRegisterComponents(components)`

从导入对象中自动发现并注册多个组件。

```javascript
import { autoRegisterComponents } from '@arkxio/ark-plugin'
import * as components from './components'

autoRegisterComponents(components)
```

#### `registerExtension(extension)`

注册包含多个扩展点的扩展模块。

```javascript
import { registerExtends } from '@arkxio/ark-plugin'

const extension = {
    arkExtends: {
        'point.a': [{ component: ComponentA }]
    }
}

registerExtends(extension)
```

#### `defineArkExtend(extendPoint, options)`

Vue 3 Composition API 辅助函数,用于声明扩展点。

```vue
<script setup>
import { defineArkExtend } from '@arkxio/ark-plugin'

defineArkExtend('my.point', { arkWeight: 1 })
</script>
```

### 组件: `<ark-extend-point>`

Props:
- `id` (String, 必需): 扩展点标识符

## TypeScript 支持

添加类型声明以获得更好的自动补全:

```typescript
// ark-plugin.d.ts
import { DefineComponent } from 'vue'

declare module 'vue' {
    interface ComponentCustomProperties {
        arkExtendPointId?: string
        arkWeight?: number
        arkId?: string
    }
}

export function defineArkExtend(extendPoint: string, options?: { arkWeight?: number; arkId?: string }): void
```

使用方式:

```vue
<script setup lang="ts">
import { defineArkExtend } from '@arkxio/ark-plugin'

defineArkExtend('my.point', { weight: 1 })
</script>
```

## 多模块项目示例

```javascript
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import ArkPlugin, { registerExtends } from '@arkxio/ark-plugin'

const app = createApp(App)

// 动态加载并注册模块
async function loadModules() {
    const modules = ['auth', 'dashboard', 'reports']

    for (const module of modules) {
        const moduleExtensions = await import(`./packages/${module}/extensions.js`)
        registerExtends(moduleExtensions.default)
    }
}

await loadModules()
app.use(ArkPlugin)
app.mount('#app')
```

## 架构

```
┌─────────────────────────────────────────────┐
│         主应用程序                           │
│                                             │
│  <ark-extend-point id="app.menu"/>          │
│         │                                   │
│         ├── Component1 (权重: 1)            │
│         ├── Component2 (权重: 5)            │
│         └── Component3 (权重: 10)           │
│                                             │
└─────────────────────────────────────────────┘
         │
         ├── 模块 A 扩展
         ├── 模块 B 扩展
         └── 模块 C 扩展
```

## 最佳实践

1. **命名约定**: 使用点号表示法: `命名空间.类别.类型`
2. **权重值**: 使用 5 或 10 的倍数,便于调整顺序
3. **文档**: 在 README 中记录扩展点
4. **类型安全**: 使用 TypeScript 提升开发体验
5. **懒加载**: 懒加载扩展组件以提升性能

## 故障排查

### 组件未渲染

- 检查 `arkExtendPointId` 是否正确设置
- 验证扩展点名称是否完全匹配
- 确保在挂载应用前安装插件

### 顺序错误

- 检查 `weight` 值(数值越小越靠前)
- 确保权重是数字而非字符串

### TypeScript 错误

- 添加类型声明(参见 TypeScript 支持部分)
- 对扩展点字符串使用 `as const`

## 许可证

MIT 许可证 - 详见 LICENSE 文件。

## 支持

如有问题、疑问或贡献,请访问项目仓库。