=============== LIBRARY RULES =============== From library maintainers: - Use class-based services with dependency injection instead of centralized stores like Pinia/Vuex - All APIs are imported from @kaokei/use-vue-service; @kaokei/di is a required peer dependency but never imported directly - Three scope levels available: component-level (useService/declareProviders), global root (useRootService/declareRootProviders), app-level (useAppService/declareAppProviders) - Mark service classes with @Injectable() decorator to make them injectable via DI container - Retrieve reactive service instances via useService(), useRootService(), or useAppService() composables - Use declareProviders() in component setup to bind services to component scope — service lifecycle matches the component's - Decorators available: @Injectable, @Inject, @autobind, @Computed, @Raw, @RunInScope, @PostConstruct, @PreDestroy, @Self, @SkipSelf, @Optional, @LazyInject - For Nuxt projects, use @kaokei/nuxt-use-vue-service module for auto-imports instead of manual imports ### Install and Run Project Source: https://github.com/kaokei/use-vue-service/blob/main/examples/13-nuxt-decorators/README.md Commands to install dependencies and start the Nuxt development server. ```bash pnpm install pnpm run dev ``` -------------------------------- ### Install and Run Project Dependencies Source: https://github.com/kaokei/use-vue-service/blob/main/examples/04-computed-decorator/README.md Install project dependencies using pnpm and start the application. ```bash pnpm install pnpm start ``` -------------------------------- ### Install @kaokei/devtools-use-vue-service Source: https://github.com/kaokei/use-vue-service/blob/main/docs/ecosystem/devtools.md Install the package using npm or pnpm. ```bash npm install @kaokei/devtools-use-vue-service # 或 pnpm add @kaokei/devtools-use-vue-service ``` -------------------------------- ### Install @kaokei/nuxt-use-vue-service Source: https://github.com/kaokei/use-vue-service/blob/main/docs/ecosystem/nuxt-plugin.md Install the module using npm or pnpm. ```bash npm install @kaokei/nuxt-use-vue-service # 或 pnpm add @kaokei/nuxt-use-vue-service ``` -------------------------------- ### 组件级服务声明与获取 Source: https://github.com/kaokei/use-vue-service/blob/main/docs/guide/index.md 在组件的 `setup` 函数中调用 `declareProviders` 来声明服务,并使用 `useService` 来获取服务实例。这会在当前组件创建一个子容器。 ```ts import { declareProviders, useService } from '@kaokei/use-vue-service'; import { CountService } from './service.ts'; // 在组件的 setup 中调用 declareProviders([CountService]); const countService = useService(CountService); ``` -------------------------------- ### Start Development Server Source: https://github.com/kaokei/use-vue-service/blob/main/examples/14-tree-scope-demo/README.md Start the development server to run the application. This command compiles the code and serves the application locally, typically with hot-reloading enabled. ```bash pnpm start ``` -------------------------------- ### Install Dependencies Source: https://github.com/kaokei/use-vue-service/blob/main/examples/14-tree-scope-demo/README.md Install project dependencies using pnpm. This command fetches all the necessary packages listed in the package.json file. ```bash pnpm install ``` -------------------------------- ### Component Usage Example Source: https://github.com/kaokei/use-vue-service/blob/main/docs/examples/notification-queue.md Example of how to use the NotificationService within a Vue component to display and manage notifications. ```APIDOC ## Vue Component Usage ### Description This example demonstrates how to integrate the `NotificationService` into a Vue component using `@kaokei/use-vue-service`. It shows how to declare the service, inject it, and use its methods to trigger and display notifications. ### Setup 1. **Declare Service**: Use `declareProviders` to make the `NotificationService` available. 2. **Inject Service**: Use `useService` to get an instance of the `NotificationService`. ### Template - **Trigger Buttons**: Buttons to call `notif.success()`, `notif.error()`, `notif.warning()`, and `notif.info()`. - **Notification List**: A `TransitionGroup` to display notifications, iterating over `notif.notifications`. Each notification can be dismissed individually using `notif.dismiss(item.id)`. - **Clear All Button**: A button to call `notif.clearAll()`, conditionally shown if `notif.hasNotifications` is true. It displays the `notif.visibleCount`. ### Styling Includes basic CSS for positioning and styling the notification container and individual notifications. ``` -------------------------------- ### Using declareAppProvidersPlugin Source: https://github.com/kaokei/use-vue-service/blob/main/docs/api/index.md Example of how to import and use the `declareAppProvidersPlugin` to register services with the application. ```typescript import { createApp } from 'vue'; import { declareAppProvidersPlugin } from '@kaokei/use-vue-service'; import App from './App.vue'; const app = createApp(App); app.use(declareAppProvidersPlugin([ServiceA, ServiceB])); app.mount('#app'); ``` -------------------------------- ### Get App Service Instance Source: https://github.com/kaokei/use-vue-service/blob/main/docs/api/index.md Retrieves a service instance from the App container. It first looks in the App container and then falls back to the root container. This API is not suitable for component setup as it requires an explicit `app` instance. ```typescript function useAppService(token: CommonToken, app: App): T; ``` -------------------------------- ### Manually Setup Devtools Source: https://github.com/kaokei/use-vue-service/blob/main/docs/ecosystem/devtools.md Manually register the devtools in your main.ts. This is suitable for scenarios where Vite DevTools are not used or precise timing control is needed. For multiple App instances, call setupDevtools for each. ```typescript // main.ts import { createApp } from 'vue' import { setupDevtools } from '@kaokei/devtools-use-vue-service' import App from './App.vue' const app = createApp(App) setupDevtools(app) app.mount('#app') ``` ```typescript const app1 = createApp(App1) const app2 = createApp(App2) setupDevtools(app1) setupDevtools(app2) app1.mount('#app1') app2.mount('#app2') ``` -------------------------------- ### Writable Computed Example Source: https://github.com/kaokei/use-vue-service/blob/main/docs/api/index.md Example demonstrating a writable computed property using the `@Computed` decorator, including both getter and setter. ```typescript import { Computed } from '@kaokei/use-vue-service'; class UserService { public firstName = '张'; public lastName = '三'; @Computed public get fullName() { return this.firstName + this.lastName; } public set fullName(val: string) { this.firstName = val.slice(0, 1); this.lastName = val.slice(1); } } ``` -------------------------------- ### Use Product Service in a Vue Component Source: https://github.com/kaokei/use-vue-service/blob/main/docs/examples/product-list.md Demonstrates how to use the ProductService within a Vue component using ` ``` -------------------------------- ### useAppService Source: https://github.com/kaokei/use-vue-service/blob/main/docs/api/index.md Retrieves a service instance from the App container. It searches the App container first and falls back to the root container if not found. This API is not suitable for use within a component's setup function due to the explicit `app` instance requirement. ```APIDOC ## useAppService ```ts function useAppService(token: CommonToken, app: App): T; ``` ### Description Retrieves a service instance from the App container. It searches the App container first and falls back to the root container if not found. This API is not suitable for use within a component's setup function due to the explicit `app` instance requirement. Use `useService` within components instead. ``` -------------------------------- ### HttpService Definition Source: https://github.com/kaokei/use-vue-service/blob/main/docs/examples/http-service.md Defines a stateless HTTP service with methods for GET, POST, PUT, and DELETE requests. It supports setting a base URL and common headers, and uses `$fetch` for making requests. ```typescript import { Injectable, Raw } from '@kaokei/use-vue-service' interface FetchConfig { headers?: Record params?: Record } @Raw() @Injectable() export class HttpService { baseURL = '' commonHeaders: Record = {} setBaseURL(url: string): void { this.baseURL = url } setHeader(key: string, value: string): void { this.commonHeaders[key] = value } removeHeader(key: string): void { delete this.commonHeaders[key] } async get(url: string, config?: FetchConfig): Promise { return this.request(url, { method: 'GET', ...config }) } async post(url: string, body?: any, config?: FetchConfig): Promise { return this.request(url, { method: 'POST', body, ...config }) } async put(url: string, body?: any, config?: FetchConfig): Promise { return this.request(url, { method: 'PUT', body, ...config }) } async delete(url: string, config?: FetchConfig): Promise { return this.request(url, { method: 'DELETE', ...config }) } // 统一请求入口,合并公共请求头 private async request( url: string, config: FetchConfig & { method: string; body?: any } ): Promise { const fullURL = url.startsWith('http') ? url : `${this.baseURL}${url}` const mergedHeaders: Record = { ...this.commonHeaders, ...config.headers, } return $fetch(fullURL, { method: config.method as any, headers: mergedHeaders, body: config.body, params: config.params, }) } } ``` -------------------------------- ### Declare Global Services Source: https://github.com/kaokei/use-vue-service/blob/main/examples/14-tree-scope-demo/README.md Declare services at the root level to make them available globally. This setup ensures that services like LoggerService, SwitchService, CounterService, and CountdownService are accessible throughout the application unless overridden by a more specific binding in a child scope. ```ts declareProviders((container) => { container.bind(LoggerService).toSelf(); container.bind(SwitchService).toSelf(); container.bind(CounterService).toSelf(); container.bind(CountdownService).toSelf(); container.bind(COUNTER_THEME).toConstantValue('#69c0ff'); container.bind(COUNTDOWN_THEME).toConstantValue('#69c0ff'); }); ``` -------------------------------- ### Readonly Computed Example Source: https://github.com/kaokei/use-vue-service/blob/main/docs/api/index.md Example demonstrating a simple, read-only computed property using the `@Computed` decorator. ```typescript import { Computed } from '@kaokei/use-vue-service'; class CountService { public count = 1; @Computed public get doubleCount() { return this.count * 2; } } ``` -------------------------------- ### 全局根级服务声明与获取 Source: https://github.com/kaokei/use-vue-service/blob/main/docs/guide/index.md 使用 `declareRootProviders` 在全局根容器上声明服务,并用 `useRootService` 从根容器获取服务实例。这些服务在整个应用中全局共享。 ```ts import { declareRootProviders, useRootService } from '@kaokei/use-vue-service'; import { GlobalConfigService } from './service.ts'; declareRootProviders([GlobalConfigService]); const config = useRootService(GlobalConfigService); ``` -------------------------------- ### App 级服务声明与获取(插件形式) Source: https://github.com/kaokei/use-vue-service/blob/main/docs/guide/index.md 使用 `declareAppProvidersPlugin` 返回一个 Vue 插件,用于在 `main.ts` 中声明 App 级服务。组件内通过 `useService` 获取这些服务。 ```ts import { createApp } from 'vue'; import { declareAppProvidersPlugin } from '@kaokei/use-vue-service'; import { AppService } from './service.ts'; import App from './App.vue'; const app = createApp(App); // 以 Vue 插件形式声明 App 级服务 app.use(declareAppProvidersPlugin([AppService])); app.mount('#app'); ``` -------------------------------- ### @Computed Decorator Source: https://github.com/kaokei/use-vue-service/blob/main/docs/decorator/index.md The @Computed decorator transforms a class getter into a Vue computed reactive property. It supports both read-only and writable getters. If a setter exists on the prototype chain, it automatically creates a computed({ get, set }) property. ```APIDOC ## @Computed ### Description Transforms a class getter into a Vue computed reactive property. ### Usage #### Without Parentheses (Direct Call) ```ts @Computed public get doubleCount() { return this.count * 2; } ``` #### With Parentheses (Factory Call) ```ts @Computed() public get doubleCount() { return this.count * 2; } ``` ### Details - Supports both read-only and writable getters. - If a setter exists on the prototype chain, it automatically creates `computed({ get, set })`. - Refer to [detailed documentation](../api/index.md#computed) for more information. ``` -------------------------------- ### 安装 @kaokei/use-vue-service Source: https://github.com/kaokei/use-vue-service/blob/main/docs/guide/index.md 使用 npm 安装 `@kaokei/di` 和 `@kaokei/use-vue-service`。本库不依赖 `reflect-metadata`,TypeScript 5.0+ 默认支持装饰器。 ```sh npm install @kaokei/di @kaokei/use-vue-service ``` -------------------------------- ### App Service Retrieval Pseudocode Source: https://github.com/kaokei/use-vue-service/blob/main/docs/api/index.md Illustrates the internal mechanism for retrieving a service using `useAppService`, involving context running and container injection. ```typescript app.runWithContext(() => { // 通过inject获取最近父级组件/祖先组件关联的container对象 const container = inject(CONTAINER_TOKEN); // 通过container.get获取对应的服务 return container.get(token); }); ``` -------------------------------- ### Define Read-Only and Writable Computed Properties with @Computed Source: https://github.com/kaokei/use-vue-service/blob/main/examples/04-computed-decorator/README.md Use the @Computed decorator to transform getter properties into Vue's computed properties. This example shows both read-only getters and a getter/setter combination for writable computed properties. ```typescript export class UserService { public firstName = '张'; public lastName = '三'; @Computed public get fullName(): string { return `${this.firstName}${this.lastName}`; } @Computed public get writableFullName(): string { return `${this.firstName} ${this.lastName}`; } public set writableFullName(val: string) { this.firstName = val[0]; this.lastName = val.slice(1).trim(); } } ``` -------------------------------- ### @Computed Decorator for Vue Getters Source: https://github.com/kaokei/use-vue-service/blob/main/docs/decorator/index.md Use @Computed to transform class getters into Vue's reactive computed properties. Supports read-only and writable getters, automatically creating computed({ get, set }) if a setter exists on the prototype. ```typescript @Computed public get doubleCount() { return this.count * 2; } ``` ```typescript @Computed() public get doubleCount() { return this.count * 2; } ``` -------------------------------- ### Component Usage of HttpService Source: https://github.com/kaokei/use-vue-service/blob/main/docs/examples/http-service.md Demonstrates how to use the HttpService within a Vue component, managing loading, error, and data states independently for multiple concurrent requests. ```vue ``` -------------------------------- ### CountService with Decorators Source: https://github.com/kaokei/use-vue-service/blob/main/examples/13-nuxt-decorators/README.md A service demonstrating @Injectable, @Inject, and @Computed decorators for managing state and dependencies. ```typescript @Injectable() export class CountService { public count = 0; @Inject(LoggerService) public logger!: LoggerService; @Computed public get displayCount(): string { return `当前计数:${this.count}`; } } ``` -------------------------------- ### DataService with @PostConstruct Initialization Source: https://github.com/kaokei/use-vue-service/blob/main/examples/10-post-construct/README.md The @PostConstruct decorator marks the `init` method to be automatically called after the service instance is created and dependencies are injected. This method initializes the `data` and `logs` properties. ```typescript import { Injectable, PostConstruct } from '@nestjs/common'; @Injectable() export class DataService { public data: string[] = []; public initialized = false; public logs: string[] = ['constructor: 服务实例已创建']; @PostConstruct public init() { this.initialized = true; this.data = ['苹果', '香蕉', '橙子']; this.logs.push('PostConstruct: init() 已自动执行'); } } ``` -------------------------------- ### Map Model to API Parameters Source: https://github.com/kaokei/use-vue-service/blob/main/docs/examples/search-service.md Override `model2TOParserMap` to transform frontend form field names to backend API parameter names. Useful when a single frontend field maps to multiple backend parameters, like splitting a date range into start and end times. ```typescript override model2TOParserMap = { createTimeRange: (value: [string, string]) => ({ startTime: value[0], endTime: value[1], }), } ``` -------------------------------- ### Declare App-Level Service with Plugin Source: https://github.com/kaokei/use-vue-service/blob/main/examples/03-three-level-scope/README.md Declare services at the App level using a Vue plugin with `declareAppProvidersPlugin`. This allows for App-specific service configurations that override global ones. ```typescript app.use(declareAppProvidersPlugin((container) => { container.bind(ConfigService).toDynamicValue(() => { const s = new ConfigService(); s.scope = 'App 级'; return s; }); })); ``` -------------------------------- ### Declare Global Root Service Source: https://github.com/kaokei/use-vue-service/blob/main/examples/03-three-level-scope/README.md Declare services in the global root container using `declareRootProviders`. This sets up the base level for service availability. ```typescript declareRootProviders((container) => { container.bind(ConfigService).toDynamicValue(() => { const s = new ConfigService(); s.scope = '全局根级'; return s; }); }); ``` -------------------------------- ### Using Multiple Countdown Components Source: https://github.com/kaokei/use-vue-service/blob/main/docs/examples/countdown-service.md This template demonstrates how to use multiple CountdownText and CountdownBlocks components within a single Vue application. Each component instance manages its own countdown independently, ensuring that different timers do not interfere with each other. ```vue ``` -------------------------------- ### Use CartService in a Vue Component Source: https://github.com/kaokei/use-vue-service/blob/main/docs/examples/cart-service.md Demonstrates how to declare, use, and initialize the CartService within a Vue component. It loads data from localStorage on component mount. ```vue ``` -------------------------------- ### 定义和使用服务 Source: https://github.com/kaokei/use-vue-service/blob/main/docs/guide/index.md 定义 `LoggerService` 和 `CountService`,其中 `CountService` 依赖 `LoggerService`。在 Vue 组件中,使用 `declareProviders` 声明服务,并用 `useService` 获取服务实例。 ```ts // service.ts —— 定义了 2 个服务,LoggerService 和 CountService // 其中 CountService 依赖 LoggerService // @Inject 装饰器来自 @kaokei/di,通过 @kaokei/use-vue-service 重新导出 import { Inject } from '@kaokei/use-vue-service'; export class LoggerService { public log(...msg: any[]) { console.log('from logger service ==>', ...msg); } } export class CountService { public count = 0; @Inject(LoggerService) public accessor logger: LoggerService; public addOne() { this.count++; this.logger.log('addOne ==> ', this.count); } } ``` ```vue ``` -------------------------------- ### Equivalent Class Bindings: to vs toSelf Source: https://github.com/kaokei/use-vue-service/blob/main/examples/08-token-and-binding/README.md Demonstrates the equivalence of `to(Class)` and `toSelf()` for binding a service identifier to itself. `to(Class)` offers more flexibility for interface-to-implementation bindings. ```typescript // Both are equivalent: container.bind(CountService).to(CountService); container.bind(CountService).toSelf(); ``` -------------------------------- ### Configure Vite Plugin with appendTo option Source: https://github.com/kaokei/use-vue-service/blob/main/docs/ecosystem/devtools.md Use the appendTo option in useVueServiceDevtools to specify an entry module file. The virtual module import will be appended to the head of this file, suitable for SSR or non-HTML injection scenarios. ```typescript useVueServiceDevtools({ appendTo: 'src/main.ts' }) ``` -------------------------------- ### Find All Child Services within a Service Source: https://github.com/kaokei/use-vue-service/blob/main/docs/api/index.md Demonstrates how to inject and use `FIND_CHILDREN_SERVICES` within a service class to find all instances of another service in the descendant container tree. ```typescript class DemoService { @Inject(FIND_CHILDREN_SERVICES) public findAllService!: FindChildrenServices; public handleClickBtn() { const childServices = this.findAllService(ChildService); childServices.forEach(service => service.doSomething()); } } ``` -------------------------------- ### Vue Component Event Handling Source: https://github.com/kaokei/use-vue-service/blob/main/docs/examples/search-service.md Demonstrates correct event handling in Vue components to ensure the `this` context points to the reactive proxy. Use direct method references for simple cases or arrow functions/wrapper methods when passing arguments. ```vue