feat(deploy): production deployment setup and fixes
- Debian-based api image (bookworm-slim), docker/debian mirrors, prisma binaryTargets for openssl 3.0 - nginx: admin SPA under /admin, TLS via acme.sh (ZeroSSL) + auto-renewal cron, http->https redirect - prisma: add origin_goods.delisted migration, sync missing schema (good_image/tag_font_color/good_tags), fix users.createdAt Timestamptz - api: CORS wildcard reflection, helmet CORP cross-origin, price backfill in persistProductDetail, categoryIcon ancestor fallback, mediaByColor per-color gallery in public goods detail - admin: /admin base path (vite + router) - import-data.mjs: udt_name casting, serial sequence advance fix
This commit is contained in:
@@ -1,203 +0,0 @@
|
||||
---
|
||||
title: Provide/Inject Has Limited DevTools Support - Plan for Debugging
|
||||
impact: LOW
|
||||
impactDescription: Unlike props and state, provided values are harder to trace in Vue DevTools, making debugging more challenging
|
||||
type: gotcha
|
||||
tags: [vue3, provide-inject, debugging, devtools, architecture]
|
||||
---
|
||||
|
||||
# Provide/Inject Has Limited DevTools Support - Plan for Debugging
|
||||
|
||||
**Impact: LOW** - While provide/inject is powerful for avoiding prop drilling, it creates less visible data flow than props. Provided values are not as easily inspectable in Vue DevTools, and tracing where a value comes from requires navigating the component tree manually.
|
||||
|
||||
## Task Checklist
|
||||
|
||||
- [ ] Document provided values at the provider component level
|
||||
- [ ] Use descriptive Symbol descriptions for easier identification
|
||||
- [ ] Consider adding development-only logging for provided state changes
|
||||
- [ ] Keep provide/inject chains shallow when possible
|
||||
- [ ] Prefer Pinia for complex state that needs DevTools integration
|
||||
|
||||
## The Challenge
|
||||
|
||||
Unlike props which are clearly visible in Vue DevTools for each component, provided values:
|
||||
|
||||
1. Don't show which ancestor provided them
|
||||
2. Require manual navigation to find the provider
|
||||
3. Don't show in the standard props/data panels
|
||||
4. Can be shadowed by closer ancestors using the same key
|
||||
|
||||
## Strategies for Better Debugging
|
||||
|
||||
### 1. Use Descriptive Symbol Names
|
||||
|
||||
```js
|
||||
// injection-keys.js
|
||||
|
||||
// BETTER: Descriptive names appear in errors and debugging
|
||||
export const UserAuthKey = Symbol('UserAuthenticationState')
|
||||
export const ThemeConfigKey = Symbol('ThemeConfiguration')
|
||||
export const FormContextKey = Symbol('FormValidationContext')
|
||||
|
||||
// WORSE: Generic names are harder to trace
|
||||
export const UserKey = Symbol()
|
||||
export const ThemeKey = Symbol('theme')
|
||||
```
|
||||
|
||||
### 2. Document Providers Clearly
|
||||
|
||||
```vue
|
||||
<!-- AuthProvider.vue -->
|
||||
<script setup>
|
||||
/**
|
||||
* Authentication Provider
|
||||
*
|
||||
* Provides:
|
||||
* - UserAuthKey: Current user state (Ref<User | null>)
|
||||
* - AuthActionsKey: { login, logout, refresh }
|
||||
*
|
||||
* Must wrap any component that needs authentication state.
|
||||
*/
|
||||
import { provide, ref, readonly } from 'vue'
|
||||
import { UserAuthKey, AuthActionsKey } from '@/injection-keys'
|
||||
|
||||
const user = ref(null)
|
||||
|
||||
// ... implementation
|
||||
|
||||
provide(UserAuthKey, readonly(user))
|
||||
provide(AuthActionsKey, { login, logout, refresh })
|
||||
</script>
|
||||
```
|
||||
|
||||
### 3. Development-Only Logging
|
||||
|
||||
```js
|
||||
// composables/useProvideWithLogging.js
|
||||
import { provide, watch, getCurrentInstance } from 'vue'
|
||||
|
||||
export function useProvideWithLogging(key, value, name) {
|
||||
provide(key, value)
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
const instance = getCurrentInstance()
|
||||
const componentName = instance?.type?.name || 'Unknown'
|
||||
|
||||
console.log(`[Provide] ${name} provided by <${componentName}>`)
|
||||
|
||||
// Log reactive changes
|
||||
if (value && typeof value === 'object' && 'value' in value) {
|
||||
watch(value, (newVal) => {
|
||||
console.log(`[Provide] ${name} changed:`, newVal)
|
||||
}, { deep: true })
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useProvideWithLogging } from '@/composables/useProvideWithLogging'
|
||||
import { ThemeKey } from '@/injection-keys'
|
||||
|
||||
const theme = ref('dark')
|
||||
|
||||
// In development, logs when provided and when changed
|
||||
useProvideWithLogging(ThemeKey, theme, 'Theme')
|
||||
</script>
|
||||
```
|
||||
|
||||
### 4. Inject with Missing Provider Warnings
|
||||
|
||||
```js
|
||||
// composables/useSafeInject.js
|
||||
import { inject, getCurrentInstance } from 'vue'
|
||||
|
||||
export function useSafeInject(key, fallback, keyName) {
|
||||
const value = inject(key, undefined)
|
||||
|
||||
if (value === undefined) {
|
||||
const instance = getCurrentInstance()
|
||||
const componentName = instance?.type?.name || 'Unknown'
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
console.warn(
|
||||
`[Inject] ${keyName || String(key)} not provided. ` +
|
||||
`Component <${componentName}> is using fallback value. ` +
|
||||
`Ensure a provider exists in the ancestor chain.`
|
||||
)
|
||||
}
|
||||
|
||||
return typeof fallback === 'function' ? fallback() : fallback
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
```
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { useSafeInject } from '@/composables/useSafeInject'
|
||||
import { ThemeKey } from '@/injection-keys'
|
||||
|
||||
// Warns in dev if no provider found
|
||||
const theme = useSafeInject(ThemeKey, () => ({ mode: 'light' }), 'ThemeConfig')
|
||||
</script>
|
||||
```
|
||||
|
||||
### 5. Create Provider Registry for Complex Apps
|
||||
|
||||
```js
|
||||
// utils/provider-registry.js
|
||||
const providerRegistry = new Map()
|
||||
|
||||
export function registerProvider(key, componentName, value) {
|
||||
if (import.meta.env.DEV) {
|
||||
providerRegistry.set(key, {
|
||||
componentName,
|
||||
value,
|
||||
timestamp: Date.now()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function getProviderInfo(key) {
|
||||
return providerRegistry.get(key)
|
||||
}
|
||||
|
||||
// For DevTools custom plugin or debugging
|
||||
export function getAllProviders() {
|
||||
return Object.fromEntries(providerRegistry)
|
||||
}
|
||||
|
||||
// Expose to window for console debugging
|
||||
if (import.meta.env.DEV) {
|
||||
window.__VUE_PROVIDERS__ = {
|
||||
getAll: getAllProviders,
|
||||
get: getProviderInfo
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## When to Use Pinia Instead
|
||||
|
||||
If you find yourself needing extensive debugging for state:
|
||||
|
||||
| Use Provide/Inject | Use Pinia |
|
||||
|-------------------|-----------|
|
||||
| Component library internals | Application-wide state |
|
||||
| Theme/locale configuration | User session data |
|
||||
| Form context | Shopping cart |
|
||||
| Simple parent-child sharing | Complex state with actions |
|
||||
| Plugin configuration | State that needs time-travel debugging |
|
||||
|
||||
Pinia provides excellent DevTools integration with:
|
||||
- State inspection
|
||||
- Time-travel debugging
|
||||
- Action logging
|
||||
- Hot module replacement
|
||||
|
||||
## Reference
|
||||
- [Vue DevTools](https://devtools.vuejs.org/)
|
||||
- [Pinia DevTools](https://pinia.vuejs.org/core-concepts/index.html#devtools)
|
||||
Reference in New Issue
Block a user