chore(h5): 移除项目内 vendored 技能库及锁文件
删除 software/h5-uniapp/.agents/skills 与 software/h5-uniapp/agent/skills(各 629 文件,来源 uni-helper/skills 的 Vue/uni-app 技能包),并删除 skills-lock.json。两份技能库内容除 9 个 SKILL.md 的前置元数据格式外完全重复,属仓库内冗余副本;开发期技能改由全局技能目录加载。
This commit is contained in:
parent
b553c4e519
commit
9829e9dcb1
@ -1,5 +0,0 @@
|
|||||||
# Generation Info
|
|
||||||
|
|
||||||
- **Source:** `sources/pinia`
|
|
||||||
- **Git SHA:** `55dbfc5c20d4461748996aa74d8c0913e89fb98e`
|
|
||||||
- **Generated:** 2026-01-28
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2025-PRESENT Anthony Fu <https://github.com/antfu>
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
---
|
|
||||||
name: pinia
|
|
||||||
description: Pinia official Vue state management library, type-safe and extensible. Use when defining stores, working with state/getters/actions, or implementing store patterns in Vue apps.
|
|
||||||
metadata:
|
|
||||||
author: Anthony Fu
|
|
||||||
version: "2026.1.28"
|
|
||||||
source: Generated from https://github.com/vuejs/pinia, scripts located at https://github.com/antfu/skills
|
|
||||||
---
|
|
||||||
|
|
||||||
# Pinia
|
|
||||||
|
|
||||||
Pinia is the official state management library for Vue, designed to be intuitive and type-safe. It supports both Options API and Composition API styles, with first-class TypeScript support and devtools integration.
|
|
||||||
|
|
||||||
> The skill is based on Pinia v3.0.4, generated at 2026-01-28.
|
|
||||||
|
|
||||||
## Core References
|
|
||||||
|
|
||||||
| Topic | Description | Reference |
|
|
||||||
|-------|-------------|-----------|
|
|
||||||
| Stores | Defining stores, state, getters, actions, storeToRefs, subscriptions | [core-stores](references/core-stores.md) |
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
### Extensibility
|
|
||||||
|
|
||||||
| Topic | Description | Reference |
|
|
||||||
|-------|-------------|-----------|
|
|
||||||
| Plugins | Extend stores with custom properties, state, and behavior | [features-plugins](references/features-plugins.md) |
|
|
||||||
|
|
||||||
### Composability
|
|
||||||
|
|
||||||
| Topic | Description | Reference |
|
|
||||||
|-------|-------------|-----------|
|
|
||||||
| Composables | Using Vue composables within stores (VueUse, etc.) | [features-composables](references/features-composables.md) |
|
|
||||||
| Composing Stores | Store-to-store communication, avoiding circular dependencies | [features-composing-stores](references/features-composing-stores.md) |
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
| Topic | Description | Reference |
|
|
||||||
|-------|-------------|-----------|
|
|
||||||
| Testing | Unit testing with @pinia/testing, mocking, stubbing | [best-practices-testing](references/best-practices-testing.md) |
|
|
||||||
| Outside Components | Using stores in navigation guards, plugins, middlewares | [best-practices-outside-component](references/best-practices-outside-component.md) |
|
|
||||||
|
|
||||||
## Advanced
|
|
||||||
|
|
||||||
| Topic | Description | Reference |
|
|
||||||
|-------|-------------|-----------|
|
|
||||||
| SSR | Server-side rendering, state hydration | [advanced-ssr](references/advanced-ssr.md) |
|
|
||||||
| Nuxt | Nuxt integration, auto-imports, SSR best practices | [advanced-nuxt](references/advanced-nuxt.md) |
|
|
||||||
| HMR | Hot module replacement for development | [advanced-hmr](references/advanced-hmr.md) |
|
|
||||||
|
|
||||||
## Key Recommendations
|
|
||||||
|
|
||||||
- **Prefer Setup Stores** for complex logic, composables, and watchers
|
|
||||||
- **Use `storeToRefs()`** when destructuring state/getters to preserve reactivity
|
|
||||||
- **Actions can be destructured directly** - they're bound to the store
|
|
||||||
- **Call stores inside functions** not at module scope, especially for SSR
|
|
||||||
- **Add HMR support** to each store for better development experience
|
|
||||||
- **Use `@pinia/testing`** for component tests with mocked stores
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
# Sync Info
|
|
||||||
|
|
||||||
- **Source:** `vendor/antfu/skills/pinia`
|
|
||||||
- **Git SHA:** `4086f59fa12d308fb05592ca8ce1704fbb940aa3`
|
|
||||||
- **Synced:** 2026-01-30
|
|
||||||
@ -1,61 +0,0 @@
|
|||||||
---
|
|
||||||
name: hot-module-replacement
|
|
||||||
description: Enable HMR to preserve store state during development
|
|
||||||
---
|
|
||||||
|
|
||||||
# Hot Module Replacement (HMR)
|
|
||||||
|
|
||||||
Pinia supports HMR to edit stores without page reload, preserving existing state.
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
Add this snippet after each store definition:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineStore, acceptHMRUpdate } from 'pinia'
|
|
||||||
|
|
||||||
export const useAuth = defineStore('auth', {
|
|
||||||
// store options...
|
|
||||||
})
|
|
||||||
|
|
||||||
if (import.meta.hot) {
|
|
||||||
import.meta.hot.accept(acceptHMRUpdate(useAuth, import.meta.hot))
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Setup Store Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineStore, acceptHMRUpdate } from 'pinia'
|
|
||||||
|
|
||||||
export const useCounterStore = defineStore('counter', () => {
|
|
||||||
const count = ref(0)
|
|
||||||
const increment = () => count.value++
|
|
||||||
return { count, increment }
|
|
||||||
})
|
|
||||||
|
|
||||||
if (import.meta.hot) {
|
|
||||||
import.meta.hot.accept(acceptHMRUpdate(useCounterStore, import.meta.hot))
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Bundler Support
|
|
||||||
|
|
||||||
- **Vite:** Officially supported via `import.meta.hot`
|
|
||||||
- **Webpack:** Uses `import.meta.webpackHot`
|
|
||||||
- Any bundler implementing the `import.meta.hot` spec should work
|
|
||||||
|
|
||||||
## Nuxt
|
|
||||||
|
|
||||||
With `@pinia/nuxt`, `acceptHMRUpdate` is auto-imported but you still need to add the HMR snippet manually.
|
|
||||||
|
|
||||||
## Benefits
|
|
||||||
|
|
||||||
- Edit store logic without losing state
|
|
||||||
- Add/remove state, actions, and getters on the fly
|
|
||||||
- Faster development iteration
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://pinia.vuejs.org/cookbook/hot-module-replacement.html
|
|
||||||
-->
|
|
||||||
@ -1,119 +0,0 @@
|
|||||||
---
|
|
||||||
name: nuxt-integration
|
|
||||||
description: Using Pinia with Nuxt - auto-imports, SSR, and best practices
|
|
||||||
---
|
|
||||||
|
|
||||||
# Nuxt Integration
|
|
||||||
|
|
||||||
Pinia works seamlessly with Nuxt 3/4, handling SSR, serialization, and XSS protection automatically.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npx nuxi@latest module add pinia
|
|
||||||
```
|
|
||||||
|
|
||||||
This installs both `@pinia/nuxt` and `pinia`. If `pinia` isn't installed, add it manually.
|
|
||||||
|
|
||||||
> **npm users:** If you get `ERESOLVE unable to resolve dependency tree`, add to `package.json`:
|
|
||||||
> ```json
|
|
||||||
> "overrides": { "vue": "latest" }
|
|
||||||
> ```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// nuxt.config.ts
|
|
||||||
export default defineNuxtConfig({
|
|
||||||
modules: ['@pinia/nuxt'],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Auto Imports
|
|
||||||
|
|
||||||
These are automatically available:
|
|
||||||
- `usePinia()` - get pinia instance
|
|
||||||
- `defineStore()` - define stores
|
|
||||||
- `storeToRefs()` - extract reactive refs
|
|
||||||
- `acceptHMRUpdate()` - HMR support
|
|
||||||
|
|
||||||
**All stores in `app/stores/` (Nuxt 4) or `stores/` are auto-imported.**
|
|
||||||
|
|
||||||
### Custom Store Directories
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// nuxt.config.ts
|
|
||||||
export default defineNuxtConfig({
|
|
||||||
modules: ['@pinia/nuxt'],
|
|
||||||
pinia: {
|
|
||||||
storesDirs: ['./stores/**', './custom-folder/stores/**'],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Fetching Data in Pages
|
|
||||||
|
|
||||||
Use `callOnce()` for SSR-friendly data fetching:
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
const store = useStore()
|
|
||||||
|
|
||||||
// Run once, data persists across navigations
|
|
||||||
await callOnce('user', () => store.fetchUser())
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Refetch on Navigation
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
const store = useStore()
|
|
||||||
|
|
||||||
// Refetch on every navigation (like useFetch)
|
|
||||||
await callOnce('user', () => store.fetchUser(), { mode: 'navigation' })
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Using Stores Outside Components
|
|
||||||
|
|
||||||
In navigation guards, middlewares, or other stores, pass the `pinia` instance:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// middleware/auth.ts
|
|
||||||
export default defineNuxtRouteMiddleware((to) => {
|
|
||||||
const nuxtApp = useNuxtApp()
|
|
||||||
const store = useStore(nuxtApp.$pinia)
|
|
||||||
|
|
||||||
if (to.meta.requiresAuth && !store.isLoggedIn) {
|
|
||||||
return navigateTo('/login')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Most of the time, you don't need this - just use stores in components or other injection-aware contexts.
|
|
||||||
|
|
||||||
## Pinia Plugins with Nuxt
|
|
||||||
|
|
||||||
Create a Nuxt plugin:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// plugins/myPiniaPlugin.ts
|
|
||||||
import { PiniaPluginContext } from 'pinia'
|
|
||||||
|
|
||||||
function MyPiniaPlugin({ store }: PiniaPluginContext) {
|
|
||||||
store.$subscribe((mutation) => {
|
|
||||||
console.log(`[🍍 ${mutation.storeId}]: ${mutation.type}`)
|
|
||||||
})
|
|
||||||
return { creationTime: new Date() }
|
|
||||||
}
|
|
||||||
|
|
||||||
export default defineNuxtPlugin(({ $pinia }) => {
|
|
||||||
$pinia.use(MyPiniaPlugin)
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://pinia.vuejs.org/ssr/nuxt.html
|
|
||||||
-->
|
|
||||||
@ -1,121 +0,0 @@
|
|||||||
---
|
|
||||||
name: server-side-rendering
|
|
||||||
description: SSR setup, state hydration, and avoiding cross-request state pollution
|
|
||||||
---
|
|
||||||
|
|
||||||
# Server Side Rendering (SSR)
|
|
||||||
|
|
||||||
Pinia works with SSR when stores are called at the top of `setup`, getters, or actions.
|
|
||||||
|
|
||||||
> **Using Nuxt?** See the [Nuxt integration](advanced-nuxt.md) instead.
|
|
||||||
|
|
||||||
## Basic Usage
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
// ✅ Works - pinia knows the app context in setup
|
|
||||||
const main = useMainStore()
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Using Store Outside setup()
|
|
||||||
|
|
||||||
Pass the `pinia` instance explicitly:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const pinia = createPinia()
|
|
||||||
const app = createApp(App)
|
|
||||||
app.use(router)
|
|
||||||
app.use(pinia)
|
|
||||||
|
|
||||||
router.beforeEach((to) => {
|
|
||||||
// ✅ Pass pinia for correct SSR context
|
|
||||||
const main = useMainStore(pinia)
|
|
||||||
|
|
||||||
if (to.meta.requiresAuth && !main.isLoggedIn) {
|
|
||||||
return '/login'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## serverPrefetch()
|
|
||||||
|
|
||||||
Access pinia via `this.$pinia`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default {
|
|
||||||
serverPrefetch() {
|
|
||||||
const store = useStore(this.$pinia)
|
|
||||||
return store.fetchData()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## onServerPrefetch()
|
|
||||||
|
|
||||||
Works normally:
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
const store = useStore()
|
|
||||||
|
|
||||||
onServerPrefetch(async () => {
|
|
||||||
await store.fetchData()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## State Hydration
|
|
||||||
|
|
||||||
Serialize state on server and hydrate on client.
|
|
||||||
|
|
||||||
### Server Side
|
|
||||||
|
|
||||||
Use [devalue](https://github.com/Rich-Harris/devalue) for XSS-safe serialization:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import devalue from 'devalue'
|
|
||||||
import { createPinia } from 'pinia'
|
|
||||||
|
|
||||||
const pinia = createPinia()
|
|
||||||
const app = createApp(App)
|
|
||||||
app.use(router)
|
|
||||||
app.use(pinia)
|
|
||||||
|
|
||||||
// After rendering, state is available
|
|
||||||
const serializedState = devalue(pinia.state.value)
|
|
||||||
// Inject into HTML as global variable
|
|
||||||
```
|
|
||||||
|
|
||||||
### Client Side
|
|
||||||
|
|
||||||
Hydrate before any `useStore()` call:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const pinia = createPinia()
|
|
||||||
const app = createApp(App)
|
|
||||||
app.use(pinia)
|
|
||||||
|
|
||||||
// Hydrate from serialized state (e.g., from window.__pinia)
|
|
||||||
if (typeof window !== 'undefined') {
|
|
||||||
pinia.state.value = JSON.parse(window.__pinia)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## SSR Examples
|
|
||||||
|
|
||||||
- [Vitesse template](https://github.com/antfu/vitesse/blob/main/src/modules/pinia.ts)
|
|
||||||
- [vite-plugin-ssr](https://vite-plugin-ssr.com/pinia)
|
|
||||||
|
|
||||||
## Key Points
|
|
||||||
|
|
||||||
1. Call stores inside functions, not at module scope
|
|
||||||
2. Pass `pinia` instance when using stores outside components in SSR
|
|
||||||
3. Hydrate state before calling any `useStore()`
|
|
||||||
4. Use `devalue` or similar for safe serialization
|
|
||||||
5. Avoid cross-request state pollution by creating fresh pinia per request
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://pinia.vuejs.org/ssr/
|
|
||||||
-->
|
|
||||||
@ -1,115 +0,0 @@
|
|||||||
---
|
|
||||||
name: using-stores-outside-components
|
|
||||||
description: Correctly using stores in navigation guards, plugins, and other non-component contexts
|
|
||||||
---
|
|
||||||
|
|
||||||
# Using Stores Outside Components
|
|
||||||
|
|
||||||
Stores need the `pinia` instance, which is automatically injected in components. Outside components, you may need to provide it manually.
|
|
||||||
|
|
||||||
## Single Page Applications
|
|
||||||
|
|
||||||
Call stores **after** pinia is installed:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { useUserStore } from '@/stores/user'
|
|
||||||
import { createPinia } from 'pinia'
|
|
||||||
import { createApp } from 'vue'
|
|
||||||
import App from './App.vue'
|
|
||||||
|
|
||||||
// ❌ Fails - pinia not created yet
|
|
||||||
const userStore = useUserStore()
|
|
||||||
|
|
||||||
const pinia = createPinia()
|
|
||||||
const app = createApp(App)
|
|
||||||
app.use(pinia)
|
|
||||||
|
|
||||||
// ✅ Works - pinia is active
|
|
||||||
const userStore = useUserStore()
|
|
||||||
```
|
|
||||||
|
|
||||||
## Navigation Guards
|
|
||||||
|
|
||||||
**Wrong:** Call at module level
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { createRouter } from 'vue-router'
|
|
||||||
const router = createRouter({ /* ... */ })
|
|
||||||
|
|
||||||
// ❌ May fail depending on import order
|
|
||||||
const store = useUserStore()
|
|
||||||
|
|
||||||
router.beforeEach((to) => {
|
|
||||||
if (store.isLoggedIn) { /* ... */ }
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
**Correct:** Call inside the guard
|
|
||||||
|
|
||||||
```ts
|
|
||||||
router.beforeEach((to) => {
|
|
||||||
// ✅ Called after pinia is installed
|
|
||||||
const store = useUserStore()
|
|
||||||
|
|
||||||
if (to.meta.requiresAuth && !store.isLoggedIn) {
|
|
||||||
return '/login'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## SSR Applications
|
|
||||||
|
|
||||||
Always pass the `pinia` instance to `useStore()`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const pinia = createPinia()
|
|
||||||
const app = createApp(App)
|
|
||||||
app.use(router)
|
|
||||||
app.use(pinia)
|
|
||||||
|
|
||||||
router.beforeEach((to) => {
|
|
||||||
// ✅ Pass pinia instance
|
|
||||||
const main = useMainStore(pinia)
|
|
||||||
|
|
||||||
if (to.meta.requiresAuth && !main.isLoggedIn) {
|
|
||||||
return '/login'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## serverPrefetch()
|
|
||||||
|
|
||||||
Access pinia via `this.$pinia`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default {
|
|
||||||
serverPrefetch() {
|
|
||||||
const store = useStore(this.$pinia)
|
|
||||||
return store.fetchData()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## onServerPrefetch()
|
|
||||||
|
|
||||||
Works normally in `<script setup>`:
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
const store = useStore()
|
|
||||||
|
|
||||||
onServerPrefetch(async () => {
|
|
||||||
// ✅ Just works
|
|
||||||
await store.fetchData()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Key Takeaway
|
|
||||||
|
|
||||||
Defer `useStore()` calls to functions that run after pinia is installed, rather than calling at module scope.
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://pinia.vuejs.org/core-concepts/outside-component-usage.html
|
|
||||||
-->
|
|
||||||
@ -1,212 +0,0 @@
|
|||||||
---
|
|
||||||
name: testing
|
|
||||||
description: Unit testing stores and components with @pinia/testing
|
|
||||||
---
|
|
||||||
|
|
||||||
# Testing Stores
|
|
||||||
|
|
||||||
## Unit Testing Stores
|
|
||||||
|
|
||||||
Create a fresh pinia instance for each test:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { setActivePinia, createPinia } from 'pinia'
|
|
||||||
import { useCounterStore } from '../src/stores/counter'
|
|
||||||
|
|
||||||
describe('Counter Store', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
setActivePinia(createPinia())
|
|
||||||
})
|
|
||||||
|
|
||||||
it('increments', () => {
|
|
||||||
const counter = useCounterStore()
|
|
||||||
expect(counter.n).toBe(0)
|
|
||||||
counter.increment()
|
|
||||||
expect(counter.n).toBe(1)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Plugins
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { setActivePinia, createPinia } from 'pinia'
|
|
||||||
import { createApp } from 'vue'
|
|
||||||
import { somePlugin } from '../src/stores/plugin'
|
|
||||||
|
|
||||||
const app = createApp({})
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
const pinia = createPinia().use(somePlugin)
|
|
||||||
app.use(pinia)
|
|
||||||
setActivePinia(pinia)
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing Components
|
|
||||||
|
|
||||||
Install `@pinia/testing`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm i -D @pinia/testing
|
|
||||||
```
|
|
||||||
|
|
||||||
Use `createTestingPinia()`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { mount } from '@vue/test-utils'
|
|
||||||
import { createTestingPinia } from '@pinia/testing'
|
|
||||||
import { useSomeStore } from '@/stores/myStore'
|
|
||||||
|
|
||||||
const wrapper = mount(Counter, {
|
|
||||||
global: {
|
|
||||||
plugins: [createTestingPinia()],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const store = useSomeStore()
|
|
||||||
|
|
||||||
// Manipulate state directly
|
|
||||||
store.name = 'new name'
|
|
||||||
store.$patch({ name: 'new name' })
|
|
||||||
|
|
||||||
// Actions are stubbed by default
|
|
||||||
store.someAction()
|
|
||||||
expect(store.someAction).toHaveBeenCalledTimes(1)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Initial State
|
|
||||||
|
|
||||||
Set initial state for tests:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const wrapper = mount(Counter, {
|
|
||||||
global: {
|
|
||||||
plugins: [
|
|
||||||
createTestingPinia({
|
|
||||||
initialState: {
|
|
||||||
counter: { n: 20 }, // Store name → initial state
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Action Stubbing
|
|
||||||
|
|
||||||
### Execute Real Actions
|
|
||||||
|
|
||||||
```ts
|
|
||||||
createTestingPinia({ stubActions: false })
|
|
||||||
```
|
|
||||||
|
|
||||||
### Selective Stubbing
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Only stub specific actions
|
|
||||||
createTestingPinia({
|
|
||||||
stubActions: ['increment', 'reset'],
|
|
||||||
})
|
|
||||||
|
|
||||||
// Or use a function
|
|
||||||
createTestingPinia({
|
|
||||||
stubActions: (actionName, store) => {
|
|
||||||
if (actionName.startsWith('set')) return true
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Mock Action Return Values
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import type { Mock } from 'vitest'
|
|
||||||
|
|
||||||
// After getting store
|
|
||||||
store.someAction.mockResolvedValue('mocked value')
|
|
||||||
```
|
|
||||||
|
|
||||||
## Mocking Getters
|
|
||||||
|
|
||||||
Getters are writable in tests:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const pinia = createTestingPinia()
|
|
||||||
const counter = useCounterStore(pinia)
|
|
||||||
|
|
||||||
counter.double = 3 // Override computed value
|
|
||||||
|
|
||||||
// Reset to default behavior
|
|
||||||
counter.double = undefined
|
|
||||||
counter.double // Now computed normally
|
|
||||||
```
|
|
||||||
|
|
||||||
## Custom Spy Function
|
|
||||||
|
|
||||||
If not using Jest/Vitest with globals:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { vi } from 'vitest'
|
|
||||||
|
|
||||||
createTestingPinia({
|
|
||||||
createSpy: vi.fn,
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
With Sinon:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import sinon from 'sinon'
|
|
||||||
|
|
||||||
createTestingPinia({
|
|
||||||
createSpy: sinon.spy,
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Pinia Plugins in Tests
|
|
||||||
|
|
||||||
Pass plugins to `createTestingPinia()`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { somePlugin } from '../src/stores/plugin'
|
|
||||||
|
|
||||||
createTestingPinia({
|
|
||||||
stubActions: false,
|
|
||||||
plugins: [somePlugin],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
**Don't use** `testingPinia.use(MyPlugin)` - pass plugins in options.
|
|
||||||
|
|
||||||
## Type-Safe Mocked Store
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import type { Mock } from 'vitest'
|
|
||||||
import type { Store, StoreDefinition } from 'pinia'
|
|
||||||
|
|
||||||
function mockedStore<TStoreDef extends () => unknown>(
|
|
||||||
useStore: TStoreDef
|
|
||||||
): TStoreDef extends StoreDefinition<infer Id, infer State, infer Getters, infer Actions>
|
|
||||||
? Store<Id, State, Record<string, never>, {
|
|
||||||
[K in keyof Actions]: Actions[K] extends (...args: any[]) => any
|
|
||||||
? Mock<Actions[K]>
|
|
||||||
: Actions[K]
|
|
||||||
}>
|
|
||||||
: ReturnType<TStoreDef> {
|
|
||||||
return useStore() as any
|
|
||||||
}
|
|
||||||
|
|
||||||
// Usage
|
|
||||||
const store = mockedStore(useSomeStore)
|
|
||||||
store.someAction.mockResolvedValue('value') // Typed!
|
|
||||||
```
|
|
||||||
|
|
||||||
## E2E Tests
|
|
||||||
|
|
||||||
No special handling needed - Pinia works normally.
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://pinia.vuejs.org/cookbook/testing.html
|
|
||||||
-->
|
|
||||||
@ -1,389 +0,0 @@
|
|||||||
---
|
|
||||||
name: stores
|
|
||||||
description: Defining stores, state, getters, and actions in Pinia
|
|
||||||
---
|
|
||||||
|
|
||||||
# Pinia Stores
|
|
||||||
|
|
||||||
Stores are defined using `defineStore()` with a unique name. Each store has three core concepts: **state**, **getters**, and **actions**.
|
|
||||||
|
|
||||||
## Defining Stores
|
|
||||||
|
|
||||||
### Option Stores
|
|
||||||
|
|
||||||
Similar to Vue's Options API:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineStore } from 'pinia'
|
|
||||||
|
|
||||||
export const useCounterStore = defineStore('counter', {
|
|
||||||
state: () => ({
|
|
||||||
count: 0,
|
|
||||||
name: 'Eduardo',
|
|
||||||
}),
|
|
||||||
getters: {
|
|
||||||
doubleCount: (state) => state.count * 2,
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
increment() {
|
|
||||||
this.count++
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Think of `state` as `data`, `getters` as `computed`, and `actions` as `methods`.
|
|
||||||
|
|
||||||
### Setup Stores (Recommended)
|
|
||||||
|
|
||||||
Uses Composition API syntax - more flexible and powerful:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { ref, computed } from 'vue'
|
|
||||||
import { defineStore } from 'pinia'
|
|
||||||
|
|
||||||
export const useCounterStore = defineStore('counter', () => {
|
|
||||||
const count = ref(0)
|
|
||||||
const name = ref('Eduardo')
|
|
||||||
const doubleCount = computed(() => count.value * 2)
|
|
||||||
|
|
||||||
function increment() {
|
|
||||||
count.value++
|
|
||||||
}
|
|
||||||
|
|
||||||
return { count, name, doubleCount, increment }
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
In Setup Stores: `ref()` → state, `computed()` → getters, `function()` → actions.
|
|
||||||
|
|
||||||
**Important:** You must return all state properties for Pinia to track them.
|
|
||||||
|
|
||||||
### Using Stores
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
import { useCounterStore } from '@/stores/counter'
|
|
||||||
|
|
||||||
const store = useCounterStore()
|
|
||||||
// Access: store.count, store.doubleCount, store.increment()
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Destructuring with storeToRefs
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
import { storeToRefs } from 'pinia'
|
|
||||||
import { useCounterStore } from '@/stores/counter'
|
|
||||||
|
|
||||||
const store = useCounterStore()
|
|
||||||
|
|
||||||
// ❌ Breaks reactivity
|
|
||||||
const { name, doubleCount } = store
|
|
||||||
|
|
||||||
// ✅ Preserves reactivity for state/getters
|
|
||||||
const { name, doubleCount } = storeToRefs(store)
|
|
||||||
|
|
||||||
// ✅ Actions can be destructured directly
|
|
||||||
const { increment } = store
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## State
|
|
||||||
|
|
||||||
State is defined as a function returning the initial state.
|
|
||||||
|
|
||||||
### TypeScript
|
|
||||||
|
|
||||||
Type inference works automatically. For complex types:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
interface UserInfo {
|
|
||||||
name: string
|
|
||||||
age: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useUserStore = defineStore('user', {
|
|
||||||
state: () => ({
|
|
||||||
userList: [] as UserInfo[],
|
|
||||||
user: null as UserInfo | null,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Or use an interface for the return type:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
interface State {
|
|
||||||
userList: UserInfo[]
|
|
||||||
user: UserInfo | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useUserStore = defineStore('user', {
|
|
||||||
state: (): State => ({
|
|
||||||
userList: [],
|
|
||||||
user: null,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Accessing and Modifying
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const store = useStore()
|
|
||||||
store.count++
|
|
||||||
```
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<input v-model="store.count" type="number" />
|
|
||||||
```
|
|
||||||
|
|
||||||
### Mutating with $patch
|
|
||||||
|
|
||||||
Apply multiple changes at once:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Object syntax
|
|
||||||
store.$patch({
|
|
||||||
count: store.count + 1,
|
|
||||||
name: 'DIO',
|
|
||||||
})
|
|
||||||
|
|
||||||
// Function syntax (for complex mutations)
|
|
||||||
store.$patch((state) => {
|
|
||||||
state.items.push({ name: 'shoes', quantity: 1 })
|
|
||||||
state.hasChanged = true
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Resetting State
|
|
||||||
|
|
||||||
Option Stores have built-in `$reset()`. For Setup Stores, implement your own:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export const useCounterStore = defineStore('counter', () => {
|
|
||||||
const count = ref(0)
|
|
||||||
|
|
||||||
function $reset() {
|
|
||||||
count.value = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return { count, $reset }
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Subscribing to State Changes
|
|
||||||
|
|
||||||
```ts
|
|
||||||
cartStore.$subscribe((mutation, state) => {
|
|
||||||
mutation.type // 'direct' | 'patch object' | 'patch function'
|
|
||||||
mutation.storeId // 'cart'
|
|
||||||
mutation.payload // patch object (only for 'patch object')
|
|
||||||
|
|
||||||
localStorage.setItem('cart', JSON.stringify(state))
|
|
||||||
})
|
|
||||||
|
|
||||||
// Options
|
|
||||||
cartStore.$subscribe(callback, { flush: 'sync' }) // Immediate
|
|
||||||
cartStore.$subscribe(callback, { detached: true }) // Keep after unmount
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Getters
|
|
||||||
|
|
||||||
Getters are computed values, equivalent to Vue's `computed()`.
|
|
||||||
|
|
||||||
### Basic Getters
|
|
||||||
|
|
||||||
```ts
|
|
||||||
getters: {
|
|
||||||
doubleCount: (state) => state.count * 2,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Accessing Other Getters
|
|
||||||
|
|
||||||
Use `this` with explicit return type:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
getters: {
|
|
||||||
doubleCount: (state) => state.count * 2,
|
|
||||||
doublePlusOne(): number {
|
|
||||||
return this.doubleCount + 1
|
|
||||||
},
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||
### Getters with Arguments
|
|
||||||
|
|
||||||
Return a function (note: loses caching):
|
|
||||||
|
|
||||||
```ts
|
|
||||||
getters: {
|
|
||||||
getUserById: (state) => {
|
|
||||||
return (userId: string) => state.users.find((user) => user.id === userId)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||
Cache within parameterized getters:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
getters: {
|
|
||||||
getActiveUserById(state) {
|
|
||||||
const activeUsers = state.users.filter((user) => user.active)
|
|
||||||
return (userId: string) => activeUsers.find((user) => user.id === userId)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||
### Accessing Other Stores in Getters
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { useOtherStore } from './other-store'
|
|
||||||
|
|
||||||
getters: {
|
|
||||||
combined(state) {
|
|
||||||
const otherStore = useOtherStore()
|
|
||||||
return state.localData + otherStore.data
|
|
||||||
},
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Actions
|
|
||||||
|
|
||||||
Actions are methods for business logic. Unlike getters, they can be asynchronous.
|
|
||||||
|
|
||||||
### Defining Actions
|
|
||||||
|
|
||||||
```ts
|
|
||||||
actions: {
|
|
||||||
increment() {
|
|
||||||
this.count++
|
|
||||||
},
|
|
||||||
randomizeCounter() {
|
|
||||||
this.count = Math.round(100 * Math.random())
|
|
||||||
},
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||
### Async Actions
|
|
||||||
|
|
||||||
```ts
|
|
||||||
actions: {
|
|
||||||
async registerUser(login: string, password: string) {
|
|
||||||
try {
|
|
||||||
this.userData = await api.post({ login, password })
|
|
||||||
} catch (error) {
|
|
||||||
return error
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||
### Accessing Other Stores in Actions
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { useAuthStore } from './auth-store'
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
async fetchUserPreferences() {
|
|
||||||
const auth = useAuthStore()
|
|
||||||
if (auth.isAuthenticated) {
|
|
||||||
this.preferences = await fetchPreferences()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||
**SSR:** Call all `useStore()` before any `await`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
async orderCart() {
|
|
||||||
// ✅ Call stores before await
|
|
||||||
const user = useUserStore()
|
|
||||||
|
|
||||||
await apiOrderCart(user.token, this.items)
|
|
||||||
// ❌ Don't call useStore() after await in SSR
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Subscribing to Actions
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const unsubscribe = someStore.$onAction(
|
|
||||||
({ name, store, args, after, onError }) => {
|
|
||||||
const startTime = Date.now()
|
|
||||||
console.log(`Start "${name}" with params [${args.join(', ')}]`)
|
|
||||||
|
|
||||||
after((result) => {
|
|
||||||
console.log(`Finished "${name}" after ${Date.now() - startTime}ms`)
|
|
||||||
})
|
|
||||||
|
|
||||||
onError((error) => {
|
|
||||||
console.warn(`Failed "${name}": ${error}`)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
unsubscribe() // Cleanup
|
|
||||||
```
|
|
||||||
|
|
||||||
Keep subscription after component unmount:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
someStore.$onAction(callback, true)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Options API Helpers
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { mapState, mapWritableState, mapActions } from 'pinia'
|
|
||||||
import { useCounterStore } from '../stores/counter'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
computed: {
|
|
||||||
// Readonly state/getters
|
|
||||||
...mapState(useCounterStore, ['count', 'doubleCount']),
|
|
||||||
// Writable state
|
|
||||||
...mapWritableState(useCounterStore, ['count']),
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
...mapActions(useCounterStore, ['increment']),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Accessing Global Providers in Setup Stores
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { inject } from 'vue'
|
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { defineStore } from 'pinia'
|
|
||||||
|
|
||||||
export const useSearchFilters = defineStore('search-filters', () => {
|
|
||||||
const route = useRoute()
|
|
||||||
const appProvided = inject('appProvided')
|
|
||||||
|
|
||||||
// Don't return these - access them directly in components
|
|
||||||
return { /* ... */ }
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://pinia.vuejs.org/core-concepts/
|
|
||||||
- https://pinia.vuejs.org/core-concepts/state.html
|
|
||||||
- https://pinia.vuejs.org/core-concepts/getters.html
|
|
||||||
- https://pinia.vuejs.org/core-concepts/actions.html
|
|
||||||
-->
|
|
||||||
@ -1,114 +0,0 @@
|
|||||||
---
|
|
||||||
name: composables-in-stores
|
|
||||||
description: Using Vue composables within Pinia stores
|
|
||||||
---
|
|
||||||
|
|
||||||
# Composables in Stores
|
|
||||||
|
|
||||||
Pinia stores can leverage Vue composables for reusable stateful logic.
|
|
||||||
|
|
||||||
## Option Stores
|
|
||||||
|
|
||||||
Call composables inside the `state` property, but only those returning writable refs:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineStore } from 'pinia'
|
|
||||||
import { useLocalStorage } from '@vueuse/core'
|
|
||||||
|
|
||||||
export const useAuthStore = defineStore('auth', {
|
|
||||||
state: () => ({
|
|
||||||
user: useLocalStorage('pinia/auth/login', 'bob'),
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
**Works:** Composables returning `ref()`:
|
|
||||||
- `useLocalStorage`
|
|
||||||
- `useAsyncState`
|
|
||||||
|
|
||||||
**Doesn't work in Option Stores:**
|
|
||||||
- Composables exposing functions
|
|
||||||
- Composables exposing readonly data
|
|
||||||
|
|
||||||
## Setup Stores
|
|
||||||
|
|
||||||
More flexible - can use almost any composable:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineStore } from 'pinia'
|
|
||||||
import { useMediaControls } from '@vueuse/core'
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
export const useVideoPlayer = defineStore('video', () => {
|
|
||||||
const videoElement = ref<HTMLVideoElement>()
|
|
||||||
const src = ref('/data/video.mp4')
|
|
||||||
const { playing, volume, currentTime, togglePictureInPicture } =
|
|
||||||
useMediaControls(videoElement, { src })
|
|
||||||
|
|
||||||
function loadVideo(element: HTMLVideoElement, newSrc: string) {
|
|
||||||
videoElement.value = element
|
|
||||||
src.value = newSrc
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
src,
|
|
||||||
playing,
|
|
||||||
volume,
|
|
||||||
currentTime,
|
|
||||||
loadVideo,
|
|
||||||
togglePictureInPicture,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** Don't return non-serializable DOM refs like `videoElement` - they're internal implementation details.
|
|
||||||
|
|
||||||
## SSR Considerations
|
|
||||||
|
|
||||||
### Option Stores with hydrate()
|
|
||||||
|
|
||||||
Define a `hydrate()` function to handle client-side hydration:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineStore } from 'pinia'
|
|
||||||
import { useLocalStorage } from '@vueuse/core'
|
|
||||||
|
|
||||||
export const useAuthStore = defineStore('auth', {
|
|
||||||
state: () => ({
|
|
||||||
user: useLocalStorage('pinia/auth/login', 'bob'),
|
|
||||||
}),
|
|
||||||
|
|
||||||
hydrate(state, initialState) {
|
|
||||||
// Ignore server state, read from browser
|
|
||||||
state.user = useLocalStorage('pinia/auth/login', 'bob')
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Setup Stores with skipHydrate()
|
|
||||||
|
|
||||||
Mark state that shouldn't hydrate from server:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineStore, skipHydrate } from 'pinia'
|
|
||||||
import { useEyeDropper, useLocalStorage } from '@vueuse/core'
|
|
||||||
|
|
||||||
export const useColorStore = defineStore('colors', () => {
|
|
||||||
const { isSupported, open, sRGBHex } = useEyeDropper()
|
|
||||||
const lastColor = useLocalStorage('lastColor', sRGBHex)
|
|
||||||
|
|
||||||
return {
|
|
||||||
// Skip hydration for client-only state
|
|
||||||
lastColor: skipHydrate(lastColor),
|
|
||||||
open, // Function - no hydration needed
|
|
||||||
isSupported, // Boolean - not reactive
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
`skipHydrate()` only applies to state properties (refs), not functions or non-reactive values.
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://pinia.vuejs.org/cookbook/composables.html
|
|
||||||
-->
|
|
||||||
@ -1,134 +0,0 @@
|
|||||||
---
|
|
||||||
name: composing-stores
|
|
||||||
description: Store-to-store communication and avoiding circular dependencies
|
|
||||||
---
|
|
||||||
|
|
||||||
# Composing Stores
|
|
||||||
|
|
||||||
Stores can use each other for shared state and logic.
|
|
||||||
|
|
||||||
## Rule: Avoid Circular Dependencies
|
|
||||||
|
|
||||||
Two stores cannot directly read each other's state during setup:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// ❌ Infinite loop
|
|
||||||
const useX = defineStore('x', () => {
|
|
||||||
const y = useY()
|
|
||||||
y.name // Don't read here!
|
|
||||||
return { name: ref('X') }
|
|
||||||
})
|
|
||||||
|
|
||||||
const useY = defineStore('y', () => {
|
|
||||||
const x = useX()
|
|
||||||
x.name // Don't read here!
|
|
||||||
return { name: ref('Y') }
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
**Solution:** Read in getters, computed, or actions:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const useX = defineStore('x', () => {
|
|
||||||
const y = useY()
|
|
||||||
|
|
||||||
// ✅ Read in computed/actions
|
|
||||||
function doSomething() {
|
|
||||||
const yName = y.name
|
|
||||||
}
|
|
||||||
|
|
||||||
return { name: ref('X'), doSomething }
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Setup Stores: Use Store at Top
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineStore } from 'pinia'
|
|
||||||
import { useUserStore } from './user'
|
|
||||||
|
|
||||||
export const useCartStore = defineStore('cart', () => {
|
|
||||||
const user = useUserStore()
|
|
||||||
const list = ref([])
|
|
||||||
|
|
||||||
const summary = computed(() => {
|
|
||||||
return `Hi ${user.name}, you have ${list.value.length} items`
|
|
||||||
})
|
|
||||||
|
|
||||||
function purchase() {
|
|
||||||
return apiPurchase(user.id, list.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return { list, summary, purchase }
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Shared Getters
|
|
||||||
|
|
||||||
Call `useStore()` inside a getter:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { useUserStore } from './user'
|
|
||||||
|
|
||||||
export const useCartStore = defineStore('cart', {
|
|
||||||
getters: {
|
|
||||||
summary(state) {
|
|
||||||
const user = useUserStore()
|
|
||||||
return `Hi ${user.name}, you have ${state.list.length} items`
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Shared Actions
|
|
||||||
|
|
||||||
Call `useStore()` inside an action:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { useUserStore } from './user'
|
|
||||||
import { apiOrderCart } from './api'
|
|
||||||
|
|
||||||
export const useCartStore = defineStore('cart', {
|
|
||||||
actions: {
|
|
||||||
async orderCart() {
|
|
||||||
const user = useUserStore()
|
|
||||||
|
|
||||||
try {
|
|
||||||
await apiOrderCart(user.token, this.items)
|
|
||||||
this.emptyCart()
|
|
||||||
} catch (err) {
|
|
||||||
displayError(err)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## SSR: Call Stores Before Await
|
|
||||||
|
|
||||||
In async actions, call all stores before any `await`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
actions: {
|
|
||||||
async orderCart() {
|
|
||||||
// ✅ All useStore() calls before await
|
|
||||||
const user = useUserStore()
|
|
||||||
const analytics = useAnalyticsStore()
|
|
||||||
|
|
||||||
try {
|
|
||||||
await apiOrderCart(user.token, this.items)
|
|
||||||
// ❌ Don't call useStore() after await (SSR issue)
|
|
||||||
// const otherStore = useOtherStore()
|
|
||||||
} catch (err) {
|
|
||||||
displayError(err)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This ensures the correct Pinia instance is used during SSR.
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://pinia.vuejs.org/cookbook/composing-stores.html
|
|
||||||
-->
|
|
||||||
@ -1,203 +0,0 @@
|
|||||||
---
|
|
||||||
name: plugins
|
|
||||||
description: Extend stores with custom properties, methods, and behavior
|
|
||||||
---
|
|
||||||
|
|
||||||
# Plugins
|
|
||||||
|
|
||||||
Plugins extend all stores with custom properties, methods, or behavior.
|
|
||||||
|
|
||||||
## Basic Plugin
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { createPinia } from 'pinia'
|
|
||||||
|
|
||||||
function SecretPiniaPlugin() {
|
|
||||||
return { secret: 'the cake is a lie' }
|
|
||||||
}
|
|
||||||
|
|
||||||
const pinia = createPinia()
|
|
||||||
pinia.use(SecretPiniaPlugin)
|
|
||||||
|
|
||||||
// In any store
|
|
||||||
const store = useStore()
|
|
||||||
store.secret // 'the cake is a lie'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Plugin Context
|
|
||||||
|
|
||||||
Plugins receive a context object:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { PiniaPluginContext } from 'pinia'
|
|
||||||
|
|
||||||
export function myPiniaPlugin(context: PiniaPluginContext) {
|
|
||||||
context.pinia // pinia instance
|
|
||||||
context.app // Vue app instance
|
|
||||||
context.store // store being augmented
|
|
||||||
context.options // store definition options
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Adding Properties
|
|
||||||
|
|
||||||
Return an object to add properties (tracked in devtools):
|
|
||||||
|
|
||||||
```ts
|
|
||||||
pinia.use(() => ({ hello: 'world' }))
|
|
||||||
```
|
|
||||||
|
|
||||||
Or set directly on store:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
pinia.use(({ store }) => {
|
|
||||||
store.hello = 'world'
|
|
||||||
// For devtools visibility in dev mode
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
|
||||||
store._customProperties.add('hello')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Adding State
|
|
||||||
|
|
||||||
Add to both `store` and `store.$state` for SSR/devtools:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { toRef, ref } from 'vue'
|
|
||||||
|
|
||||||
pinia.use(({ store }) => {
|
|
||||||
if (!store.$state.hasOwnProperty('hasError')) {
|
|
||||||
const hasError = ref(false)
|
|
||||||
store.$state.hasError = hasError
|
|
||||||
}
|
|
||||||
store.hasError = toRef(store.$state, 'hasError')
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Adding External Properties
|
|
||||||
|
|
||||||
Wrap non-reactive objects with `markRaw()`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { markRaw } from 'vue'
|
|
||||||
import { router } from './router'
|
|
||||||
|
|
||||||
pinia.use(({ store }) => {
|
|
||||||
store.router = markRaw(router)
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Custom Store Options
|
|
||||||
|
|
||||||
Define custom options consumed by plugins:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Store definition
|
|
||||||
defineStore('search', {
|
|
||||||
actions: {
|
|
||||||
searchContacts() { /* ... */ },
|
|
||||||
},
|
|
||||||
debounce: {
|
|
||||||
searchContacts: 300,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// Plugin reads custom option
|
|
||||||
import debounce from 'lodash/debounce'
|
|
||||||
|
|
||||||
pinia.use(({ options, store }) => {
|
|
||||||
if (options.debounce) {
|
|
||||||
return Object.keys(options.debounce).reduce((acc, action) => {
|
|
||||||
acc[action] = debounce(store[action], options.debounce[action])
|
|
||||||
return acc
|
|
||||||
}, {})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
For Setup Stores, pass options as third argument:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
defineStore(
|
|
||||||
'search',
|
|
||||||
() => { /* ... */ },
|
|
||||||
{
|
|
||||||
debounce: { searchContacts: 300 },
|
|
||||||
}
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## TypeScript Augmentation
|
|
||||||
|
|
||||||
### Custom Properties
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import 'pinia'
|
|
||||||
import type { Router } from 'vue-router'
|
|
||||||
|
|
||||||
declare module 'pinia' {
|
|
||||||
export interface PiniaCustomProperties {
|
|
||||||
router: Router
|
|
||||||
hello: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom State
|
|
||||||
|
|
||||||
```ts
|
|
||||||
declare module 'pinia' {
|
|
||||||
export interface PiniaCustomStateProperties<S> {
|
|
||||||
hasError: boolean
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom Options
|
|
||||||
|
|
||||||
```ts
|
|
||||||
declare module 'pinia' {
|
|
||||||
export interface DefineStoreOptionsBase<S, Store> {
|
|
||||||
debounce?: Partial<Record<keyof StoreActions<Store>, number>>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Subscribe in Plugins
|
|
||||||
|
|
||||||
```ts
|
|
||||||
pinia.use(({ store }) => {
|
|
||||||
store.$subscribe(() => {
|
|
||||||
// React to state changes
|
|
||||||
})
|
|
||||||
store.$onAction(() => {
|
|
||||||
// React to actions
|
|
||||||
})
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Nuxt Plugin
|
|
||||||
|
|
||||||
Create a Nuxt plugin to add Pinia plugins:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// plugins/myPiniaPlugin.ts
|
|
||||||
import { PiniaPluginContext } from 'pinia'
|
|
||||||
|
|
||||||
function MyPiniaPlugin({ store }: PiniaPluginContext) {
|
|
||||||
store.$subscribe((mutation) => {
|
|
||||||
console.log(`[🍍 ${mutation.storeId}]: ${mutation.type}`)
|
|
||||||
})
|
|
||||||
return { creationTime: new Date() }
|
|
||||||
}
|
|
||||||
|
|
||||||
export default defineNuxtPlugin(({ $pinia }) => {
|
|
||||||
$pinia.use(MyPiniaPlugin)
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://pinia.vuejs.org/core-concepts/plugins.html
|
|
||||||
-->
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
# Generation Info
|
|
||||||
|
|
||||||
- **Source:** `sources/uniapp`
|
|
||||||
- **Git SHA:** `91423a240a5b0714fc5e7e13bd3f93a79b8cc8d7`
|
|
||||||
- **Generated:** 2026-01-30
|
|
||||||
|
|
||||||
## Generated Skills
|
|
||||||
|
|
||||||
| Category | File | Description |
|
|
||||||
|----------|------|-------------|
|
|
||||||
| Core | core-framework.md | Framework basics, platform support, condition compilation |
|
|
||||||
| Core | core-view-components.md | Container components: view, scroll-view, swiper, movable-area |
|
|
||||||
| Core | core-form-components.md | Form controls: input, textarea, picker, checkbox, radio |
|
|
||||||
| Features | feature-media-components.md | Media: image, video, camera, map, live-player |
|
|
||||||
| Features | feature-navigation.md | Navigation: navigator, routing APIs |
|
|
||||||
| Features | feature-network.md | Network: request, upload, download, WebSocket |
|
|
||||||
| Features | feature-storage.md | Storage: localStorage, file system |
|
|
||||||
| Features | feature-system-info.md | System: device info, network, screen, vibration |
|
|
||||||
| Features | feature-file-operations.md | Files: image/video selection, file operations |
|
|
||||||
| Features | feature-location.md | Location: geolocation, map component |
|
|
||||||
| Features | feature-ui-feedback.md | UI: toast, modal, loading, action sheet |
|
|
||||||
| Features | feature-lifecycle.md | Lifecycle: app and page hooks |
|
|
||||||
| Config | config-pages.md | pages.json configuration |
|
|
||||||
| Config | config-manifest.md | manifest.json configuration |
|
|
||||||
|
|
||||||
## Source Documentation Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
sources/uniapp/docs/
|
|
||||||
├── README.md # Framework overview
|
|
||||||
├── api/ # API documentation
|
|
||||||
│ ├── README.md # API overview
|
|
||||||
│ ├── request/ # Network APIs
|
|
||||||
│ ├── storage/ # Storage APIs
|
|
||||||
│ ├── media/ # Media APIs
|
|
||||||
│ ├── file/ # File APIs
|
|
||||||
│ ├── location/ # Location APIs
|
|
||||||
│ ├── ui/ # UI APIs
|
|
||||||
│ └── system/ # System APIs
|
|
||||||
├── component/ # Component documentation
|
|
||||||
├── collocation/ # Configuration
|
|
||||||
│ ├── pages.md # pages.json
|
|
||||||
│ ├── manifest.md # manifest.json
|
|
||||||
│ └── manifest-app.md # App-specific manifest
|
|
||||||
├── tutorial/ # Tutorials
|
|
||||||
└── quickstart.md # Getting started
|
|
||||||
```
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- Skills focus on practical usage patterns and code examples
|
|
||||||
- Platform-specific differences are highlighted where relevant
|
|
||||||
- Configuration files include common use cases for each platform
|
|
||||||
- API skills include both callback and Promise usage patterns
|
|
||||||
@ -1,113 +0,0 @@
|
|||||||
---
|
|
||||||
name: uni-app
|
|
||||||
description: Comprehensive skill reference for uni-app cross-platform development framework
|
|
||||||
metadata:
|
|
||||||
author: FlippeDround
|
|
||||||
version: "2026.1.30"
|
|
||||||
source: Generated from https://gitcode.com/dcloud/unidocs-zh, skills located at https://github.com/antfu/skills
|
|
||||||
---
|
|
||||||
|
|
||||||
> The skill is based on uni-app documentation, generated at 2026-01-30.
|
|
||||||
|
|
||||||
uni-app is a Vue.js-based cross-platform framework for developing applications that run on iOS, Android, HarmonyOS, Web (responsive), and various mini-program platforms (WeChat/Alipay/Baidu/Douyin/Feishu/QQ/Kuaishou/DingTalk/Taobao/Jingdong/Xiaohongshu).
|
|
||||||
|
|
||||||
## Core
|
|
||||||
|
|
||||||
| Topic | Description | Reference |
|
|
||||||
|-------|-------------|-----------|
|
|
||||||
| Core Framework | Project structure, platform support, condition compilation | [core-framework](references/core-framework.md) |
|
|
||||||
| View Components | view, scroll-view, swiper, movable-area, cover-view | [core-view-components](references/core-view-components.md) |
|
|
||||||
| Form Components | input, textarea, picker, checkbox, radio, switch, slider | [core-form-components](references/core-form-components.md) |
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
### UI Components
|
|
||||||
|
|
||||||
| Topic | Description | Reference |
|
|
||||||
|-------|-------------|-----------|
|
|
||||||
| Media Components | image, video, camera, live-player, map | [feature-media-components](references/feature-media-components.md) |
|
|
||||||
| Navigation | navigator, routing, page navigation | [feature-navigation](references/feature-navigation.md) |
|
|
||||||
| UI Feedback | toast, modal, loading, action sheet, pull refresh | [feature-ui-feedback](references/feature-ui-feedback.md) |
|
|
||||||
|
|
||||||
### APIs
|
|
||||||
|
|
||||||
| Topic | Description | Reference |
|
|
||||||
|-------|-------------|-----------|
|
|
||||||
| Network | HTTP requests, file upload/download, WebSocket | [feature-network](references/feature-network.md) |
|
|
||||||
| Storage | Local storage, file system, caching | [feature-storage](references/feature-storage.md) |
|
|
||||||
| System Info | Device info, network status, screen, vibration | [feature-system-info](references/feature-system-info.md) |
|
|
||||||
| File Operations | Image/video selection, file system operations | [feature-file-operations](references/feature-file-operations.md) |
|
|
||||||
| Location | Geolocation, map component, address selection | [feature-location](references/feature-location.md) |
|
|
||||||
| Lifecycle | App and page lifecycle hooks | [feature-lifecycle](references/feature-lifecycle.md) |
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
| Topic | Description | Reference |
|
|
||||||
|-------|-------------|-----------|
|
|
||||||
| pages.json | Page routing, tab bar, global styles, sub-packages | [config-pages](references/config-pages.md) |
|
|
||||||
| manifest.json | App config, permissions, platform settings | [config-manifest](references/config-manifest.md) |
|
|
||||||
|
|
||||||
## Platform Support
|
|
||||||
|
|
||||||
| Platform | Support Level |
|
|
||||||
|----------|---------------|
|
|
||||||
| iOS App | Full support |
|
|
||||||
| Android App | Full support |
|
|
||||||
| HarmonyOS Next | Full support |
|
|
||||||
| H5/Web | Full support |
|
|
||||||
| WeChat Mini Program | Full support |
|
|
||||||
| Alipay Mini Program | Full support |
|
|
||||||
| Baidu Smart Program | Full support |
|
|
||||||
| Douyin Mini Program | Full support |
|
|
||||||
| QQ Mini Program | Full support |
|
|
||||||
| Kuaishou Mini Program | Full support |
|
|
||||||
| Feishu Mini Program | Full support |
|
|
||||||
| JD Mini Program | Full support |
|
|
||||||
| HarmonyOS Meta Service | Full support |
|
|
||||||
|
|
||||||
## Key Concepts
|
|
||||||
|
|
||||||
### Condition Compilation
|
|
||||||
|
|
||||||
Use special comments to write platform-specific code:
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<!-- #ifdef APP-PLUS -->
|
|
||||||
<view>App only</view>
|
|
||||||
<!-- #endif -->
|
|
||||||
|
|
||||||
<!-- #ifdef MP-WEIXIN -->
|
|
||||||
<view>WeChat only</view>
|
|
||||||
<!-- #endif -->
|
|
||||||
```
|
|
||||||
|
|
||||||
### API Promise Support
|
|
||||||
|
|
||||||
Most uni-app APIs support Promise when no callback is provided:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const res = await uni.request({ url: 'https://api.example.com' })
|
|
||||||
```
|
|
||||||
|
|
||||||
### Cross-Platform Best Practices
|
|
||||||
|
|
||||||
1. Use uni-app components and APIs instead of platform-specific ones
|
|
||||||
2. Use condition compilation for platform-specific features
|
|
||||||
3. Test on all target platforms
|
|
||||||
4. Use rpx for responsive layouts
|
|
||||||
5. Handle platform differences in manifest.json
|
|
||||||
6. must use uni-helper tools
|
|
||||||
|
|
||||||
|
|
||||||
### MCP扩展
|
|
||||||
|
|
||||||
当需要查询 uni-app 官方文档时,优先调用 `search-docs-by-Uniapp-official` MCP 工具搜索相关 API 文档和使用示例。
|
|
||||||
|
|
||||||
**使用场景:**
|
|
||||||
- 用户询问特定 API 的详细用法
|
|
||||||
- 需要官方文档中的代码示例
|
|
||||||
- 查询组件的属性和事件
|
|
||||||
- 了解 API 的平台兼容性
|
|
||||||
|
|
||||||
**工具安装:**
|
|
||||||
如果检测到该 MCP 工具不可用,引导用户访问 https://github.com/uni-helper/mcp 进行安装。
|
|
||||||
@ -1,370 +0,0 @@
|
|||||||
---
|
|
||||||
name: Manifest.json Configuration
|
|
||||||
description: App configuration, permissions, and platform-specific settings
|
|
||||||
---
|
|
||||||
|
|
||||||
# Manifest.json Configuration
|
|
||||||
|
|
||||||
`manifest.json` is the application configuration file, defining app name, icon, permissions, and platform-specific settings.
|
|
||||||
|
|
||||||
## Basic Configuration
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "My App",
|
|
||||||
"appid": "__UNI__XXXXXXX",
|
|
||||||
"description": "App description",
|
|
||||||
"versionName": "1.0.0",
|
|
||||||
"versionCode": 100,
|
|
||||||
"locale": "auto",
|
|
||||||
"debug": false
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Basic Properties
|
|
||||||
|
|
||||||
| Property | Type | Description |
|
|
||||||
|----------|------|-------------|
|
|
||||||
| name | String | App name |
|
|
||||||
| appid | String | DCloud app ID |
|
|
||||||
| description | String | App description |
|
|
||||||
| versionName | String | Version name (1.0.0) |
|
|
||||||
| versionCode | Number | Version code (integer) |
|
|
||||||
| locale | String | Default language |
|
|
||||||
| debug | Boolean | Debug mode |
|
|
||||||
| networkTimeout | Object | Network timeout settings |
|
|
||||||
| uniStatistics | Object | Statistics configuration |
|
|
||||||
|
|
||||||
## Network Timeout
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"networkTimeout": {
|
|
||||||
"request": 60000,
|
|
||||||
"connectSocket": 60000,
|
|
||||||
"uploadFile": 60000,
|
|
||||||
"downloadFile": 60000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## App Configuration (app-plus)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"app-plus": {
|
|
||||||
"splashscreen": {
|
|
||||||
"alwaysShowBeforeRender": true,
|
|
||||||
"autoclose": true,
|
|
||||||
"waiting": true
|
|
||||||
},
|
|
||||||
"screenOrientation": ["portrait-primary"],
|
|
||||||
"modules": {
|
|
||||||
"OAuth": {},
|
|
||||||
"Payment": {},
|
|
||||||
"Push": {}
|
|
||||||
},
|
|
||||||
"distribute": {
|
|
||||||
"android": {
|
|
||||||
"permissions": [
|
|
||||||
"<uses-permission android:name=\"android.permission.INTERNET\" />"
|
|
||||||
],
|
|
||||||
"minSdkVersion": 21,
|
|
||||||
"targetSdkVersion": 30
|
|
||||||
},
|
|
||||||
"ios": {
|
|
||||||
"capabilities": {
|
|
||||||
"entitlements": {
|
|
||||||
"com.apple.developer.push": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sdkConfigs": {
|
|
||||||
"payment": {
|
|
||||||
"alipay": {},
|
|
||||||
"weixin": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"optimization": {
|
|
||||||
"subPackages": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Splash Screen
|
|
||||||
|
|
||||||
| Property | Type | Description |
|
|
||||||
|----------|------|-------------|
|
|
||||||
| alwaysShowBeforeRender | Boolean | Show until first page renders |
|
|
||||||
| autoclose | Boolean | Auto close splash screen |
|
|
||||||
| waiting | Boolean | Show loading indicator |
|
|
||||||
|
|
||||||
## Mini-Program Configuration
|
|
||||||
|
|
||||||
### WeChat (mp-weixin)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"mp-weixin": {
|
|
||||||
"appid": "wx1234567890",
|
|
||||||
"setting": {
|
|
||||||
"urlCheck": false,
|
|
||||||
"es6": true,
|
|
||||||
"postcss": true,
|
|
||||||
"minified": true
|
|
||||||
},
|
|
||||||
"usingComponents": true,
|
|
||||||
"permission": {
|
|
||||||
"scope.userLocation": {
|
|
||||||
"desc": "Your location is needed"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"requiredPrivateInfos": [
|
|
||||||
"getLocation"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Alipay (mp-alipay)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"mp-alipay": {
|
|
||||||
"appid": "2021...",
|
|
||||||
"allowsAlignRight": true,
|
|
||||||
"component2": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Baidu (mp-baidu)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"mp-baidu": {
|
|
||||||
"appid": "12345678",
|
|
||||||
"navigationBarForceEnable": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Douyin (mp-toutiao)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"mp-toutiao": {
|
|
||||||
"appid": "tt...",
|
|
||||||
"setting": {
|
|
||||||
"es6": true,
|
|
||||||
"minified": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## H5 Configuration
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"h5": {
|
|
||||||
"title": "My App",
|
|
||||||
"template": "index.html",
|
|
||||||
"router": {
|
|
||||||
"mode": "hash",
|
|
||||||
"base": "./"
|
|
||||||
},
|
|
||||||
"optimization": {
|
|
||||||
"treeShaking": {
|
|
||||||
"enable": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"publicPath": "./",
|
|
||||||
"devServer": {
|
|
||||||
"port": 8080,
|
|
||||||
"disableHostCheck": true
|
|
||||||
},
|
|
||||||
"sdkConfigs": {
|
|
||||||
"maps": {
|
|
||||||
"qqmap": {
|
|
||||||
"key": "..."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Vue Configuration
|
|
||||||
|
|
||||||
### Vue 2
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"vueVersion": "2",
|
|
||||||
"sassImplementationName": "dart-sass"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Vue 3
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"vueVersion": "3"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Permission Configuration
|
|
||||||
|
|
||||||
### Android Permissions
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"app-plus": {
|
|
||||||
"distribute": {
|
|
||||||
"android": {
|
|
||||||
"permissions": [
|
|
||||||
"<uses-permission android:name=\"android.permission.INTERNET\" />",
|
|
||||||
"<uses-permission android:name=\"android.permission.CAMERA\" />",
|
|
||||||
"<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />",
|
|
||||||
"<uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" />",
|
|
||||||
"<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### iOS Privacy Descriptions
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"app-plus": {
|
|
||||||
"distribute": {
|
|
||||||
"ios": {
|
|
||||||
"privacyDescription": {
|
|
||||||
"NSCameraUsageDescription": "Camera access is needed for scanning QR codes",
|
|
||||||
"NSPhotoLibraryUsageDescription": "Photo access is needed for uploading images",
|
|
||||||
"NSLocationWhenInUseUsageDescription": "Location is needed for finding nearby stores"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Common Module Configuration
|
|
||||||
|
|
||||||
### OAuth (Login)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"app-plus": {
|
|
||||||
"modules": {
|
|
||||||
"OAuth": {}
|
|
||||||
},
|
|
||||||
"distribute": {
|
|
||||||
"sdkConfigs": {
|
|
||||||
"oauth": {
|
|
||||||
"weixin": {
|
|
||||||
"appid": "wx...",
|
|
||||||
"appsecret": "...",
|
|
||||||
"UniversalLinks": "https://..."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Payment
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"app-plus": {
|
|
||||||
"modules": {
|
|
||||||
"Payment": {}
|
|
||||||
},
|
|
||||||
"distribute": {
|
|
||||||
"sdkConfigs": {
|
|
||||||
"payment": {
|
|
||||||
"alipay": {},
|
|
||||||
"weixin": {
|
|
||||||
"appid": "wx..."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Push Notifications
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"app-plus": {
|
|
||||||
"modules": {
|
|
||||||
"Push": {}
|
|
||||||
},
|
|
||||||
"distribute": {
|
|
||||||
"sdkConfigs": {
|
|
||||||
"push": {
|
|
||||||
"unipush": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Share
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"app-plus": {
|
|
||||||
"modules": {
|
|
||||||
"Share": {}
|
|
||||||
},
|
|
||||||
"distribute": {
|
|
||||||
"sdkConfigs": {
|
|
||||||
"share": {
|
|
||||||
"weixin": {
|
|
||||||
"appid": "wx..."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Statistics Configuration
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"uniStatistics": {
|
|
||||||
"enable": true
|
|
||||||
},
|
|
||||||
"app-plus": {
|
|
||||||
"uniStatistics": {
|
|
||||||
"enable": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mp-weixin": {
|
|
||||||
"uniStatistics": {
|
|
||||||
"enable": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/collocation/manifest.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/collocation/manifest-app.md
|
|
||||||
-->
|
|
||||||
@ -1,270 +0,0 @@
|
|||||||
---
|
|
||||||
name: Pages.json Configuration
|
|
||||||
description: Page routing, tab bar, and global style configuration
|
|
||||||
---
|
|
||||||
|
|
||||||
# Pages.json Configuration
|
|
||||||
|
|
||||||
`pages.json` is the global configuration file for uni-app, defining page routes, window styles, native navigation bar, and tab bar.
|
|
||||||
|
|
||||||
## Basic Structure
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"pages": [
|
|
||||||
{
|
|
||||||
"path": "pages/index/index",
|
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "Home"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"globalStyle": {
|
|
||||||
"navigationBarTextStyle": "black",
|
|
||||||
"navigationBarBackgroundColor": "#F8F8F8"
|
|
||||||
},
|
|
||||||
"tabBar": {
|
|
||||||
"list": [
|
|
||||||
{ "pagePath": "pages/index/index", "text": "Home" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Root Properties
|
|
||||||
|
|
||||||
| Property | Type | Required | Description |
|
|
||||||
|----------|------|----------|-------------|
|
|
||||||
| pages | Array | Yes | Page routes |
|
|
||||||
| globalStyle | Object | No | Default window style |
|
|
||||||
| tabBar | Object | No | Tab bar configuration |
|
|
||||||
| condition | Object | No | Launch mode (dev only) |
|
|
||||||
| subPackages | Array | No | Sub-packages |
|
|
||||||
| preloadRule | Object | No | Preload rules (MP) |
|
|
||||||
| easycom | Object | No | Auto component import |
|
|
||||||
| leftWindow/topWindow/rightWindow | Object | No | Multi-window (H5) |
|
|
||||||
|
|
||||||
## Pages Configuration
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"pages": [
|
|
||||||
{
|
|
||||||
"path": "pages/index/index",
|
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "Home",
|
|
||||||
"enablePullDownRefresh": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "pages/detail/detail",
|
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "Detail",
|
|
||||||
"navigationStyle": "custom"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Page Style Options
|
|
||||||
|
|
||||||
| Property | Type | Description |
|
|
||||||
|----------|------|-------------|
|
|
||||||
| navigationBarTitleText | String | Navigation title |
|
|
||||||
| navigationBarTextStyle | String | Title color: black/white |
|
|
||||||
| navigationBarBackgroundColor | HexColor | Nav background |
|
|
||||||
| navigationStyle | String | default/custom |
|
|
||||||
| enablePullDownRefresh | Boolean | Enable pull refresh |
|
|
||||||
| backgroundColor | HexColor | Background color |
|
|
||||||
| backgroundTextStyle | String | dark/light |
|
|
||||||
| onReachBottomDistance | Number | Bottom distance (px) |
|
|
||||||
| disableScroll | Boolean | Disable scroll (MP) |
|
|
||||||
| usingComponents | Object | Using custom components |
|
|
||||||
|
|
||||||
## Global Style
|
|
||||||
|
|
||||||
Applied to all pages unless overridden.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"globalStyle": {
|
|
||||||
"navigationBarTextStyle": "black",
|
|
||||||
"navigationBarTitleText": "My App",
|
|
||||||
"navigationBarBackgroundColor": "#F8F8F8",
|
|
||||||
"backgroundColor": "#F8F8F8",
|
|
||||||
"backgroundTextStyle": "dark",
|
|
||||||
"enablePullDownRefresh": false,
|
|
||||||
"onReachBottomDistance": 50,
|
|
||||||
"rpxCalcMaxDeviceWidth": 960,
|
|
||||||
"rpxCalcBaseDeviceWidth": 375,
|
|
||||||
"rpxCalcIncludeWidth": 750
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Tab Bar Configuration
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"tabBar": {
|
|
||||||
"color": "#7A7E83",
|
|
||||||
"selectedColor": "#3cc51f",
|
|
||||||
"backgroundColor": "#ffffff",
|
|
||||||
"borderStyle": "black",
|
|
||||||
"list": [
|
|
||||||
{
|
|
||||||
"pagePath": "pages/index/index",
|
|
||||||
"text": "Home",
|
|
||||||
"iconPath": "static/image/home.png",
|
|
||||||
"selectedIconPath": "static/image/home-active.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pagePath": "pages/category/category",
|
|
||||||
"text": "Category",
|
|
||||||
"iconPath": "static/image/cat.png",
|
|
||||||
"selectedIconPath": "static/image/cat-active.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pagePath": "pages/cart/cart",
|
|
||||||
"text": "Cart",
|
|
||||||
"iconPath": "static/image/cart.png",
|
|
||||||
"selectedIconPath": "static/image/cart-active.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pagePath": "pages/user/user",
|
|
||||||
"text": "User",
|
|
||||||
"iconPath": "static/image/user.png",
|
|
||||||
"selectedIconPath": "static/image/user-active.png"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Tab Bar Properties
|
|
||||||
|
|
||||||
| Property | Type | Description |
|
|
||||||
|----------|------|-------------|
|
|
||||||
| color | HexColor | Unselected text color |
|
|
||||||
| selectedColor | HexColor | Selected text color |
|
|
||||||
| backgroundColor | HexColor | Background color |
|
|
||||||
| borderStyle | String | black/white |
|
|
||||||
| list | Array | Tab items (2-5 items) |
|
|
||||||
| position | String | bottom/top |
|
|
||||||
|
|
||||||
### Tab Item Properties
|
|
||||||
|
|
||||||
| Property | Type | Required | Description |
|
|
||||||
|----------|------|----------|-------------|
|
|
||||||
| pagePath | String | Yes | Page path |
|
|
||||||
| text | String | Yes | Tab text |
|
|
||||||
| iconPath | String | No | Icon path (81x81px) |
|
|
||||||
| selectedIconPath | String | No | Selected icon |
|
|
||||||
|
|
||||||
### Custom Tab Bar (WeChat/QQ/Douyin)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"tabBar": {
|
|
||||||
"custom": true,
|
|
||||||
"list": [
|
|
||||||
{ "pagePath": "pages/index/index", "text": "Home" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Create `custom-tab-bar/index` component in root.
|
|
||||||
|
|
||||||
## Sub-packages
|
|
||||||
|
|
||||||
Split app into smaller chunks for faster loading.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"subPackages": [
|
|
||||||
{
|
|
||||||
"root": "packageA",
|
|
||||||
"pages": [
|
|
||||||
{ "path": "pages/cat/cat" },
|
|
||||||
{ "path": "pages/dog/dog" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"root": "packageB",
|
|
||||||
"pages": [
|
|
||||||
{ "path": "pages/apple/apple" },
|
|
||||||
{ "path": "pages/banana/banana" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"preloadRule": {
|
|
||||||
"pages/index/index": {
|
|
||||||
"network": "all",
|
|
||||||
"packages": ["packageA"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## EasyCom (Auto Component Import)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"easycom": {
|
|
||||||
"autoscan": true,
|
|
||||||
"custom": {
|
|
||||||
"^u--(.*)": "uview-plus/components/u-$1/u-$1.vue",
|
|
||||||
"^up-(.*)": "uview-plus/components/u-$1/u-$1.vue",
|
|
||||||
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
With this config, components are auto-imported without registration.
|
|
||||||
|
|
||||||
## Conditional Compilation in Config
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"pages": [
|
|
||||||
{
|
|
||||||
"path": "pages/index/index",
|
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "Home"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"globalStyle": {
|
|
||||||
"navigationBarTextStyle": "black"
|
|
||||||
},
|
|
||||||
"condition": {
|
|
||||||
"current": 0,
|
|
||||||
"list": [
|
|
||||||
{
|
|
||||||
"name": "test",
|
|
||||||
"path": "pages/test/test",
|
|
||||||
"query": "id=1"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"mp-weixin": {
|
|
||||||
"appid": "wx...",
|
|
||||||
"setting": {
|
|
||||||
"urlCheck": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"app-plus": {
|
|
||||||
"splashscreen": {
|
|
||||||
"alwaysShowBeforeRender": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/collocation/pages.md
|
|
||||||
-->
|
|
||||||
@ -1,250 +0,0 @@
|
|||||||
---
|
|
||||||
name: Form Components
|
|
||||||
description: Input, selection, and form control components
|
|
||||||
---
|
|
||||||
|
|
||||||
# Form Components
|
|
||||||
|
|
||||||
## input
|
|
||||||
|
|
||||||
Single-line text input.
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<input
|
|
||||||
v-model="inputValue"
|
|
||||||
type="text"
|
|
||||||
placeholder="Enter text"
|
|
||||||
:maxlength="100"
|
|
||||||
:focus="true"
|
|
||||||
@input="onInput"
|
|
||||||
@focus="onFocus"
|
|
||||||
@blur="onBlur"
|
|
||||||
@confirm="onConfirm"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
**Properties:**
|
|
||||||
|
|
||||||
| Property | Type | Default | Description |
|
|
||||||
|----------|------|---------|-------------|
|
|
||||||
| value | String | | Input value |
|
|
||||||
| type | String | text | Input type: text/number/idcard/digit/tel |
|
|
||||||
| password | Boolean | false | Password input |
|
|
||||||
| placeholder | String | | Placeholder text |
|
|
||||||
| placeholder-style | String | | Placeholder inline styles |
|
|
||||||
| placeholder-class | String | | Placeholder CSS class |
|
|
||||||
| maxlength | Number | 140 | Max character length |
|
|
||||||
| cursor-spacing | Number | 0 | Cursor distance from keyboard bottom (px) |
|
|
||||||
| focus | Boolean | false | Auto focus |
|
|
||||||
| confirm-type | String | done | Return key type: send/search/next/go/done |
|
|
||||||
| confirm-hold | Boolean | false | Keep keyboard open after confirm |
|
|
||||||
|
|
||||||
**Events:**
|
|
||||||
- `@input` - Input value change
|
|
||||||
- `@focus` - Input focused
|
|
||||||
- `@blur` - Input blurred
|
|
||||||
- `@confirm` - Confirm button clicked
|
|
||||||
- `@keyboardheightchange` - Keyboard height changed
|
|
||||||
|
|
||||||
## textarea
|
|
||||||
|
|
||||||
Multi-line text input.
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<textarea
|
|
||||||
v-model="content"
|
|
||||||
placeholder="Enter content"
|
|
||||||
:maxlength="500"
|
|
||||||
:auto-height="true"
|
|
||||||
:show-confirm-bar="false"
|
|
||||||
@linechange="onLineChange"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
**Additional Properties:**
|
|
||||||
|
|
||||||
| Property | Type | Default | Description |
|
|
||||||
|----------|------|---------|-------------|
|
|
||||||
| auto-height | Boolean | false | Auto adjust height |
|
|
||||||
| fixed | Boolean | false | Fixed position when scrolling |
|
|
||||||
| cursor | Number | | Cursor position |
|
|
||||||
| show-confirm-bar | Boolean | true | Show recommendation bar (WeChat) |
|
|
||||||
| selection-start | Number | -1 | Selection start position |
|
|
||||||
| selection-end | Number | -1 | Selection end position |
|
|
||||||
|
|
||||||
## picker
|
|
||||||
|
|
||||||
Selection from predefined options.
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<!-- Selector mode -->
|
|
||||||
<picker mode="selector" :range="options" :value="selected" @change="onChange">
|
|
||||||
<view>Selected: {{ options[selected] }}</view>
|
|
||||||
</picker>
|
|
||||||
|
|
||||||
<!-- Multi-selector -->
|
|
||||||
<picker mode="multiSelector" :range="multiOptions" @change="onMultiChange">
|
|
||||||
<view>Multi-select</view>
|
|
||||||
</picker>
|
|
||||||
|
|
||||||
<!-- Date picker -->
|
|
||||||
<picker mode="date" :value="date" :start="startDate" :end="endDate" @change="onDateChange">
|
|
||||||
<view>Date: {{ date }}</view>
|
|
||||||
</picker>
|
|
||||||
|
|
||||||
<!-- Region picker -->
|
|
||||||
<picker mode="region" :value="region" @change="onRegionChange">
|
|
||||||
<view>Region: {{ region.join('-') }}</view>
|
|
||||||
</picker>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
options: ['Option 1', 'Option 2', 'Option 3'],
|
|
||||||
selected: 0,
|
|
||||||
multiOptions: [['A', 'B'], ['1', '2', '3']],
|
|
||||||
date: '2024-01-01',
|
|
||||||
startDate: '2020-01-01',
|
|
||||||
endDate: '2025-12-31',
|
|
||||||
region: ['Beijing', 'Beijing', 'Dongcheng']
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onChange(e) {
|
|
||||||
this.selected = e.detail.value
|
|
||||||
},
|
|
||||||
onDateChange(e) {
|
|
||||||
this.date = e.detail.value
|
|
||||||
},
|
|
||||||
onRegionChange(e) {
|
|
||||||
this.region = e.detail.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
**Mode-specific Properties:**
|
|
||||||
|
|
||||||
| Mode | Properties |
|
|
||||||
|------|------------|
|
|
||||||
| selector | range, range-key |
|
|
||||||
| multiSelector | range, range-key |
|
|
||||||
| time | start, end |
|
|
||||||
| date | start, end, fields (year/month/day) |
|
|
||||||
| region | custom-item, disable-sub-districts |
|
|
||||||
|
|
||||||
## picker-view
|
|
||||||
|
|
||||||
Embedded picker view (inline, not popup).
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<picker-view :value="value" @change="onChange">
|
|
||||||
<picker-view-column>
|
|
||||||
<view v-for="item in years" :key="item">{{ item }}年</view>
|
|
||||||
</picker-view-column>
|
|
||||||
<picker-view-column>
|
|
||||||
<view v-for="item in months" :key="item">{{ item }}月</view>
|
|
||||||
</picker-view-column>
|
|
||||||
</picker-view>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
## radio / checkbox
|
|
||||||
|
|
||||||
Selection controls.
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<!-- Radio group -->
|
|
||||||
<radio-group @change="onRadioChange">
|
|
||||||
<label v-for="item in items" :key="item.value">
|
|
||||||
<radio :value="item.value" :checked="item.checked" color="#007AFF" />
|
|
||||||
{{ item.name }}
|
|
||||||
</label>
|
|
||||||
</radio-group>
|
|
||||||
|
|
||||||
<!-- Checkbox group -->
|
|
||||||
<checkbox-group @change="onCheckboxChange">
|
|
||||||
<label v-for="item in items" :key="item.value">
|
|
||||||
<checkbox :value="item.value" :checked="item.checked" color="#007AFF" />
|
|
||||||
{{ item.name }}
|
|
||||||
</label>
|
|
||||||
</checkbox-group>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
**radio/checkbox Properties:**
|
|
||||||
|
|
||||||
| Property | Type | Default | Description |
|
|
||||||
|----------|------|---------|-------------|
|
|
||||||
| value | String | | Identifier value |
|
|
||||||
| checked | Boolean | false | Selected state |
|
|
||||||
| disabled | Boolean | false | Disabled state |
|
|
||||||
| color | Color | | Selected color |
|
|
||||||
|
|
||||||
## switch
|
|
||||||
|
|
||||||
Toggle switch.
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<switch
|
|
||||||
:checked="isOn"
|
|
||||||
:disabled="false"
|
|
||||||
type="switch"
|
|
||||||
color="#007AFF"
|
|
||||||
@change="onSwitchChange"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
**Properties:**
|
|
||||||
|
|
||||||
| Property | Type | Default | Description |
|
|
||||||
|----------|------|---------|-------------|
|
|
||||||
| checked | Boolean | false | Checked state |
|
|
||||||
| disabled | Boolean | false | Disabled state |
|
|
||||||
| type | String | switch | Style: switch/checkbox |
|
|
||||||
| color | Color | | Switch color when on |
|
|
||||||
|
|
||||||
## slider
|
|
||||||
|
|
||||||
Range slider input.
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<slider
|
|
||||||
:value="50"
|
|
||||||
:min="0"
|
|
||||||
:max="100"
|
|
||||||
:step="1"
|
|
||||||
:show-value="true"
|
|
||||||
:disabled="false"
|
|
||||||
activeColor="#007AFF"
|
|
||||||
backgroundColor="#e9e9e9"
|
|
||||||
block-size="28"
|
|
||||||
@change="onChange"
|
|
||||||
@changing="onChanging"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/component/input.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/component/textarea.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/component/picker.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/component/radio.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/component/checkbox.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/component/switch.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/component/slider.md
|
|
||||||
-->
|
|
||||||
@ -1,133 +0,0 @@
|
|||||||
---
|
|
||||||
name: Core Framework
|
|
||||||
description: UniApp core framework concepts, project structure, and platform overview
|
|
||||||
---
|
|
||||||
|
|
||||||
# UniApp Core Framework
|
|
||||||
|
|
||||||
UniApp is a Vue.js-based cross-platform framework for developing applications that run on iOS, Android, HarmonyOS, Web, and various mini-program platforms (WeChat, Alipay, Baidu, Douyin, etc.).
|
|
||||||
|
|
||||||
## Project Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
project-root/
|
|
||||||
├── pages/ # Page files
|
|
||||||
│ └── index/
|
|
||||||
│ └── index.vue # Page component
|
|
||||||
├── components/ # Reusable components
|
|
||||||
├── static/ # Static assets (images, fonts)
|
|
||||||
├── App.vue # Application root component
|
|
||||||
├── main.js # Application entry
|
|
||||||
├── manifest.json # App configuration
|
|
||||||
├── pages.json # Page routing config
|
|
||||||
└── uni.scss # Global SCSS variables
|
|
||||||
```
|
|
||||||
|
|
||||||
## Platform Support
|
|
||||||
|
|
||||||
| Platform | Value | Description |
|
|
||||||
|----------|-------|-------------|
|
|
||||||
| App | APP-PLUS | iOS/Android native apps |
|
|
||||||
| App nvue | APP-PLUS-NVUE | Native rendering pages |
|
|
||||||
| H5 | H5 / WEB | Web applications |
|
|
||||||
| WeChat Mini Program | MP-WEIXIN | WeChat MP |
|
|
||||||
| Alipay | MP-ALIPAY | Alipay MP |
|
|
||||||
| Baidu | MP-BAIDU | Baidu Smart Program |
|
|
||||||
| Douyin | MP-TOUTIAO | ByteDance MP |
|
|
||||||
| QQ | MP-QQ | QQ Mini Program |
|
|
||||||
| Kuaishou | MP-KUAISHOU | Kuaishou MP |
|
|
||||||
| HarmonyOS | APP-HARMONY | HarmonyOS Next |
|
|
||||||
|
|
||||||
## Condition Compilation
|
|
||||||
|
|
||||||
Handle platform differences using special comment syntax:
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<view>
|
|
||||||
<!-- #ifdef APP-PLUS -->
|
|
||||||
<text>App only content</text>
|
|
||||||
<!-- #endif -->
|
|
||||||
|
|
||||||
<!-- #ifdef MP-WEIXIN -->
|
|
||||||
<text>WeChat MP only</text>
|
|
||||||
<!-- #endif -->
|
|
||||||
|
|
||||||
<!-- #ifndef H5 -->
|
|
||||||
<text>All platforms except H5</text>
|
|
||||||
<!-- #endif -->
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
methods: {
|
|
||||||
getPlatform() {
|
|
||||||
// #ifdef APP-PLUS
|
|
||||||
return 'App'
|
|
||||||
// #endif
|
|
||||||
// #ifdef H5
|
|
||||||
return 'Web'
|
|
||||||
// #endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
/* #ifdef APP-PLUS */
|
|
||||||
.app-style { padding: 20px; }
|
|
||||||
/* #endif */
|
|
||||||
</style>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Platform Values Reference
|
|
||||||
|
|
||||||
| Value | Description |
|
|
||||||
|-------|-------------|
|
|
||||||
| VUE3 / VUE2 | Vue version |
|
|
||||||
| UNI-APP-X | UniApp X project |
|
|
||||||
| APP-PLUS | App (JS engine) |
|
|
||||||
| APP-PLUS-NVUE / APP-NVUE | App nvue pages |
|
|
||||||
| APP-ANDROID | Android platform |
|
|
||||||
| APP-IOS | iOS platform |
|
|
||||||
| APP-HARMONY | HarmonyOS Next |
|
|
||||||
| H5 / WEB | Web platform |
|
|
||||||
| MP-WEIXIN | WeChat Mini Program |
|
|
||||||
| MP-ALIPAY | Alipay Mini Program |
|
|
||||||
| MP-BAIDU | Baidu Smart Program |
|
|
||||||
| MP-TOUTIAO | Douyin Mini Program |
|
|
||||||
| MP-LARK | Feishu Mini Program |
|
|
||||||
| MP-QQ | QQ Mini Program |
|
|
||||||
| MP-KUAISHOU | Kuaishou Mini Program |
|
|
||||||
| MP-HARMONY | HarmonyOS Meta Service |
|
|
||||||
|
|
||||||
## API Promise Support
|
|
||||||
|
|
||||||
UniApp APIs support Promise when no callback is provided:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Promise style
|
|
||||||
uni.request({ url: 'https://api.example.com' })
|
|
||||||
.then(res => console.log(res))
|
|
||||||
.catch(err => console.error(err))
|
|
||||||
|
|
||||||
// Async/await
|
|
||||||
async function fetchData() {
|
|
||||||
try {
|
|
||||||
const res = await uni.request({ url: 'https://api.example.com' })
|
|
||||||
return res.data
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** Synchronous APIs (ending in `Sync`), `create*` methods, and `*Manager` methods do not support Promise.
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/README.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/tutorial/platform.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/api/README.md
|
|
||||||
-->
|
|
||||||
@ -1,202 +0,0 @@
|
|||||||
---
|
|
||||||
name: View Components
|
|
||||||
description: Core container components for layout and structure
|
|
||||||
---
|
|
||||||
|
|
||||||
# View Components
|
|
||||||
|
|
||||||
## view
|
|
||||||
|
|
||||||
Basic container component, similar to HTML `<div>`.
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<view class="container">
|
|
||||||
<view class="flex-row">
|
|
||||||
<view class="item">A</view>
|
|
||||||
<view class="item">B</view>
|
|
||||||
</view>
|
|
||||||
<view class="flex-column">
|
|
||||||
<view class="item">C</view>
|
|
||||||
<view class="item">D</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
**Properties:**
|
|
||||||
|
|
||||||
| Property | Type | Default | Description |
|
|
||||||
|----------|------|---------|-------------|
|
|
||||||
| hover-class | String | none | CSS class when pressed |
|
|
||||||
| hover-stop-propagation | Boolean | false | Stop propagation to ancestor nodes |
|
|
||||||
| hover-start-time | Number | 50 | Delay before hover state (ms) |
|
|
||||||
| hover-stay-time | Number | 400 | Duration to keep hover state (ms) |
|
|
||||||
|
|
||||||
**Tips:**
|
|
||||||
- `<div>` is auto-converted to `<view>` in mini-programs
|
|
||||||
- Use `<text>` to wrap text in nvue pages
|
|
||||||
|
|
||||||
## scroll-view
|
|
||||||
|
|
||||||
Scrollable container with enhanced scroll capabilities.
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<!-- Vertical scroll -->
|
|
||||||
<scroll-view
|
|
||||||
scroll-y
|
|
||||||
class="scroll-container"
|
|
||||||
@scroll="onScroll"
|
|
||||||
@scrolltolower="loadMore"
|
|
||||||
:scroll-top="scrollTop"
|
|
||||||
:scroll-into-view="targetId"
|
|
||||||
>
|
|
||||||
<view id="item1">Item 1</view>
|
|
||||||
<view id="item2">Item 2</view>
|
|
||||||
</scroll-view>
|
|
||||||
|
|
||||||
<!-- Horizontal scroll -->
|
|
||||||
<scroll-view scroll-x class="horizontal-scroll">
|
|
||||||
<view class="scroll-item">1</view>
|
|
||||||
<view class="scroll-item">2</view>
|
|
||||||
</scroll-view>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
**Properties:**
|
|
||||||
|
|
||||||
| Property | Type | Description |
|
|
||||||
|----------|------|-------------|
|
|
||||||
| scroll-x | Boolean | Enable horizontal scroll |
|
|
||||||
| scroll-y | Boolean | Enable vertical scroll |
|
|
||||||
| upper-threshold | Number | Distance from top to trigger scrolltoupper (px) |
|
|
||||||
| lower-threshold | Number | Distance from bottom to trigger scrolltolower (px) |
|
|
||||||
| scroll-top | Number | Vertical scroll position |
|
|
||||||
| scroll-left | Number | Horizontal scroll position |
|
|
||||||
| scroll-into-view | String | Element ID to scroll into view |
|
|
||||||
| scroll-with-animation | Boolean | Enable smooth scroll animation |
|
|
||||||
| enable-back-to-top | Boolean | iOS: tap status bar to scroll top |
|
|
||||||
|
|
||||||
**Events:**
|
|
||||||
- `@scroll` - Scroll event
|
|
||||||
- `@scrolltoupper` - Reached top
|
|
||||||
- `@scrolltolower` - Reached bottom
|
|
||||||
|
|
||||||
## swiper
|
|
||||||
|
|
||||||
Carousel/slider container for switching content.
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<swiper
|
|
||||||
:current="currentIndex"
|
|
||||||
:autoplay="true"
|
|
||||||
:interval="3000"
|
|
||||||
:duration="500"
|
|
||||||
:circular="true"
|
|
||||||
:indicator-dots="true"
|
|
||||||
@change="onSwiperChange"
|
|
||||||
>
|
|
||||||
<swiper-item>
|
|
||||||
<view class="slide">Slide 1</view>
|
|
||||||
</swiper-item>
|
|
||||||
<swiper-item>
|
|
||||||
<view class="slide">Slide 2</view>
|
|
||||||
</swiper-item>
|
|
||||||
</swiper>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
currentIndex: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onSwiperChange(e) {
|
|
||||||
this.currentIndex = e.detail.current
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
**swiper Properties:**
|
|
||||||
|
|
||||||
| Property | Type | Default | Description |
|
|
||||||
|----------|------|---------|-------------|
|
|
||||||
| indicator-dots | Boolean | false | Show pagination dots |
|
|
||||||
| indicator-color | Color | rgba(0,0,0,.3) | Inactive dot color |
|
|
||||||
| indicator-active-color | Color | #000000 | Active dot color |
|
|
||||||
| autoplay | Boolean | false | Auto-play slides |
|
|
||||||
| current | Number | 0 | Current slide index |
|
|
||||||
| interval | Number | 5000 | Auto-play interval (ms) |
|
|
||||||
| duration | Number | 500 | Transition duration (ms) |
|
|
||||||
| circular | Boolean | false | Circular sliding |
|
|
||||||
| vertical | Boolean | false | Vertical sliding |
|
|
||||||
| previous-margin | String | 0px | Previous slide margin |
|
|
||||||
| next-margin | String | 0px | Next slide margin |
|
|
||||||
|
|
||||||
## movable-area / movable-view
|
|
||||||
|
|
||||||
Draggable and scalable container.
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<movable-area class="move-area">
|
|
||||||
<movable-view
|
|
||||||
:x="x"
|
|
||||||
:y="y"
|
|
||||||
direction="all"
|
|
||||||
:scale="true"
|
|
||||||
:scale-min="0.5"
|
|
||||||
:scale-max="4"
|
|
||||||
@change="onChange"
|
|
||||||
@scale="onScale"
|
|
||||||
>
|
|
||||||
Draggable content
|
|
||||||
</movable-view>
|
|
||||||
</movable-area>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
**movable-view Properties:**
|
|
||||||
|
|
||||||
| Property | Type | Default | Description |
|
|
||||||
|----------|------|---------|-------------|
|
|
||||||
| direction | String | none | Move direction: all/vertical/horizontal/none |
|
|
||||||
| x | Number | | Initial X position |
|
|
||||||
| y | Number | | Initial Y position |
|
|
||||||
| scale | Boolean | false | Enable scaling |
|
|
||||||
| scale-min | Number | 0.5 | Min scale |
|
|
||||||
| scale-max | Number | 10 | Max scale |
|
|
||||||
| scale-value | Number | 1 | Initial scale |
|
|
||||||
|
|
||||||
## cover-view / cover-image
|
|
||||||
|
|
||||||
Native overlay components that can cover native components (map, video, canvas).
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<map class="map">
|
|
||||||
<cover-view class="overlay">Overlay text</cover-view>
|
|
||||||
<cover-image class="marker" src="/static/marker.png" />
|
|
||||||
</map>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
**Limitations:**
|
|
||||||
- Only supported in WeChat MP, App, and H5
|
|
||||||
- Limited styling capabilities
|
|
||||||
- Cannot nest regular components inside
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/component/view.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/component/scroll-view.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/component/swiper.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/component/movable-area.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/component/cover-view.md
|
|
||||||
-->
|
|
||||||
@ -1,391 +0,0 @@
|
|||||||
---
|
|
||||||
name: File Operations
|
|
||||||
description: File system operations, image/video selection, and file management
|
|
||||||
---
|
|
||||||
|
|
||||||
# File Operations
|
|
||||||
|
|
||||||
## Image Selection
|
|
||||||
|
|
||||||
### uni.chooseImage
|
|
||||||
|
|
||||||
Select images from album or camera.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Select from album
|
|
||||||
uni.chooseImage({
|
|
||||||
count: 9, // Max 9
|
|
||||||
sizeType: ['original', 'compressed'],
|
|
||||||
sourceType: ['album'],
|
|
||||||
success: (res) => {
|
|
||||||
console.log('Selected:', res.tempFilePaths)
|
|
||||||
// tempFilePaths: ['blob:xxx', 'blob:xxx']
|
|
||||||
// tempFiles: [{ path, size }]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Take photo
|
|
||||||
uni.chooseImage({
|
|
||||||
count: 1,
|
|
||||||
sourceType: ['camera'],
|
|
||||||
success: (res) => {
|
|
||||||
const tempPath = res.tempFilePaths[0]
|
|
||||||
this.uploadImage(tempPath)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Both album and camera
|
|
||||||
uni.chooseImage({
|
|
||||||
count: 5,
|
|
||||||
sizeType: ['compressed'], // Compressed only
|
|
||||||
sourceType: ['album', 'camera'],
|
|
||||||
success: (res) => {
|
|
||||||
res.tempFilePaths.forEach(path => {
|
|
||||||
this.previewImage(path)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
|
|
||||||
| Parameter | Type | Default | Description |
|
|
||||||
|-----------|------|---------|-------------|
|
|
||||||
| count | Number | 9 | Max number of images |
|
|
||||||
| sizeType | Array | ['original', 'compressed'] | original/compressed |
|
|
||||||
| sourceType | Array | ['album', 'camera'] | album/camera |
|
|
||||||
|
|
||||||
## Video Selection
|
|
||||||
|
|
||||||
### uni.chooseVideo
|
|
||||||
|
|
||||||
Select or record video.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.chooseVideo({
|
|
||||||
sourceType: ['album', 'camera'],
|
|
||||||
compressed: true,
|
|
||||||
maxDuration: 60,
|
|
||||||
camera: 'back', // front/back
|
|
||||||
success: (res) => {
|
|
||||||
console.log('Path:', res.tempFilePath)
|
|
||||||
console.log('Duration:', res.duration) // seconds
|
|
||||||
console.log('Size:', res.size) // bytes
|
|
||||||
console.log('Height:', res.height)
|
|
||||||
console.log('Width:', res.width)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### uni.chooseMedia (WeChat/QQ)
|
|
||||||
|
|
||||||
Choose mixed media types.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.chooseMedia({
|
|
||||||
count: 9,
|
|
||||||
mediaType: ['image', 'video'],
|
|
||||||
sourceType: ['album', 'camera'],
|
|
||||||
maxDuration: 30,
|
|
||||||
camera: 'back',
|
|
||||||
success: (res) => {
|
|
||||||
console.log(res.tempFiles)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## File Selection
|
|
||||||
|
|
||||||
### uni.chooseFile
|
|
||||||
|
|
||||||
Select any file type.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.chooseFile({
|
|
||||||
count: 1,
|
|
||||||
type: 'all', // all/video/image/file
|
|
||||||
extension: ['.pdf', '.doc', '.docx'], // Filter by extension
|
|
||||||
success: (res) => {
|
|
||||||
console.log(res.tempFilePaths)
|
|
||||||
console.log(res.tempFiles)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## File System Operations (App)
|
|
||||||
|
|
||||||
### Get File System Manager
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const fs = uni.getFileSystemManager()
|
|
||||||
```
|
|
||||||
|
|
||||||
### Read File
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Read as text
|
|
||||||
fs.readFile({
|
|
||||||
filePath: `${uni.env.USER_DATA_PATH}/data.txt`,
|
|
||||||
encoding: 'utf8',
|
|
||||||
success: (res) => {
|
|
||||||
console.log(res.data)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Read as binary
|
|
||||||
fs.readFile({
|
|
||||||
filePath: tempFilePath,
|
|
||||||
encoding: 'binary',
|
|
||||||
success: (res) => {
|
|
||||||
console.log(res.data)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Write File
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
fs.writeFile({
|
|
||||||
filePath: `${uni.env.USER_DATA_PATH}/config.json`,
|
|
||||||
data: JSON.stringify({ theme: 'dark' }),
|
|
||||||
encoding: 'utf8',
|
|
||||||
success: () => {
|
|
||||||
console.log('File written')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Append to File
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
fs.appendFile({
|
|
||||||
filePath: `${uni.env.USER_DATA_PATH}/log.txt`,
|
|
||||||
data: '\nNew log entry',
|
|
||||||
encoding: 'utf8',
|
|
||||||
success: () => {
|
|
||||||
console.log('Appended')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Delete File
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
fs.unlink({
|
|
||||||
filePath: `${uni.env.USER_DATA_PATH}/temp.txt`,
|
|
||||||
success: () => {
|
|
||||||
console.log('File deleted')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Check File Exists
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
fs.access({
|
|
||||||
path: `${uni.env.USER_DATA_PATH}/data.json`,
|
|
||||||
success: () => {
|
|
||||||
console.log('File exists')
|
|
||||||
},
|
|
||||||
fail: () => {
|
|
||||||
console.log('File not found')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Create Directory
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
fs.mkdir({
|
|
||||||
dirPath: `${uni.env.USER_DATA_PATH}/downloads`,
|
|
||||||
recursive: true, // Create parent directories
|
|
||||||
success: () => {
|
|
||||||
console.log('Directory created')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Read Directory
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
fs.readdir({
|
|
||||||
dirPath: `${uni.env.USER_DATA_PATH}/downloads`,
|
|
||||||
success: (res) => {
|
|
||||||
console.log('Files:', res.files)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Get File Info
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
fs.getFileInfo({
|
|
||||||
filePath: tempFilePath,
|
|
||||||
success: (res) => {
|
|
||||||
console.log('Size:', res.size)
|
|
||||||
console.log('Create time:', res.createTime)
|
|
||||||
console.log('Last access:', res.lastAccessedTime)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Save and Open Files
|
|
||||||
|
|
||||||
### Save File
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.saveFile({
|
|
||||||
tempFilePath: res.tempFilePath,
|
|
||||||
success: (res) => {
|
|
||||||
const savedPath = res.savedFilePath
|
|
||||||
console.log('Saved to:', savedPath)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Get Saved File List
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.getSavedFileList({
|
|
||||||
success: (res) => {
|
|
||||||
console.log('Files:', res.fileList)
|
|
||||||
// [{ filePath, createTime, size }, ...]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Get Saved File Info
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.getSavedFileInfo({
|
|
||||||
filePath: savedFilePath,
|
|
||||||
success: (res) => {
|
|
||||||
console.log('Size:', res.size)
|
|
||||||
console.log('Create time:', res.createTime)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Remove Saved File
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.removeSavedFile({
|
|
||||||
filePath: savedFilePath,
|
|
||||||
success: () => {
|
|
||||||
console.log('File removed')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Open Document
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.openDocument({
|
|
||||||
filePath: filePath,
|
|
||||||
fileType: 'pdf', // Optional hint
|
|
||||||
showMenu: true, // Show share menu (WeChat)
|
|
||||||
success: () => {
|
|
||||||
console.log('Document opened')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Image Operations
|
|
||||||
|
|
||||||
### Preview Image
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.previewImage({
|
|
||||||
current: currentImage, // Current image URL
|
|
||||||
urls: imageList, // All image URLs
|
|
||||||
indicator: 'default',
|
|
||||||
loop: false,
|
|
||||||
longPressActions: {
|
|
||||||
itemList: ['Save Image', 'Share'],
|
|
||||||
success: (data) => {
|
|
||||||
console.log('Selected:', data.tapIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Get Image Info
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.getImageInfo({
|
|
||||||
src: imagePath,
|
|
||||||
success: (res) => {
|
|
||||||
console.log('Width:', res.width)
|
|
||||||
console.log('Height:', res.height)
|
|
||||||
console.log('Path:', res.path)
|
|
||||||
console.log('Orientation:', res.orientation)
|
|
||||||
console.log('Type:', res.type)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Compress Image
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.compressImage({
|
|
||||||
src: originalPath,
|
|
||||||
quality: 80, // 0-100
|
|
||||||
success: (res) => {
|
|
||||||
console.log('Compressed:', res.tempFilePath)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Save Image to Album
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.saveImageToPhotosAlbum({
|
|
||||||
filePath: tempFilePath,
|
|
||||||
success: () => {
|
|
||||||
uni.showToast({ title: 'Saved to album' })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Video Operations
|
|
||||||
|
|
||||||
### Save Video to Album
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.saveVideoToPhotosAlbum({
|
|
||||||
filePath: videoPath,
|
|
||||||
success: () => {
|
|
||||||
uni.showToast({ title: 'Video saved' })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Get Video Info
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.getVideoInfo({
|
|
||||||
src: videoPath,
|
|
||||||
success: (res) => {
|
|
||||||
console.log('Duration:', res.duration)
|
|
||||||
console.log('Size:', res.size)
|
|
||||||
console.log('Bitrate:', res.bitrate)
|
|
||||||
console.log('FPS:', res.fps)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## File Paths Reference
|
|
||||||
|
|
||||||
| Path Type | Example | Description |
|
|
||||||
|-----------|---------|-------------|
|
|
||||||
| tempFilePath | `blob:xxx` or `_doc/uniapp_temp/xxx` | Temporary file |
|
|
||||||
| savedFilePath | `_doc/uniapp_save/xxx` | Saved file |
|
|
||||||
| USER_DATA_PATH | `_doc/` | App data directory |
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/api/media/image.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/api/media/file.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/api/file/file.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/api/file/getFileSystemManager.md
|
|
||||||
-->
|
|
||||||
@ -1,423 +0,0 @@
|
|||||||
---
|
|
||||||
name: Page Lifecycle
|
|
||||||
description: Page and application lifecycle hooks
|
|
||||||
---
|
|
||||||
|
|
||||||
# Page Lifecycle
|
|
||||||
|
|
||||||
## Application Lifecycle
|
|
||||||
|
|
||||||
In `App.vue`:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
export default {
|
|
||||||
globalData: {
|
|
||||||
userInfo: null,
|
|
||||||
theme: 'light'
|
|
||||||
},
|
|
||||||
|
|
||||||
onLaunch(options) {
|
|
||||||
// App launched (only once)
|
|
||||||
console.log('App launched', options)
|
|
||||||
this.checkUpdate()
|
|
||||||
},
|
|
||||||
|
|
||||||
onShow(options) {
|
|
||||||
// App shown/foreground
|
|
||||||
console.log('App shown', options)
|
|
||||||
},
|
|
||||||
|
|
||||||
onHide() {
|
|
||||||
// App hidden/background
|
|
||||||
console.log('App hidden')
|
|
||||||
},
|
|
||||||
|
|
||||||
onError(msg) {
|
|
||||||
// Global error handler
|
|
||||||
console.error('App error:', msg)
|
|
||||||
},
|
|
||||||
|
|
||||||
onUnhandledRejection(err) {
|
|
||||||
// Unhandled promise rejection
|
|
||||||
console.error('Unhandled rejection:', err)
|
|
||||||
},
|
|
||||||
|
|
||||||
onPageNotFound(res) {
|
|
||||||
// 404 page not found
|
|
||||||
console.error('Page not found:', res.path)
|
|
||||||
uni.redirectTo({
|
|
||||||
url: '/pages/404/404'
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
checkUpdate() {
|
|
||||||
// Check for app updates
|
|
||||||
const updateManager = uni.getUpdateManager()
|
|
||||||
updateManager.onCheckForUpdate((res) => {
|
|
||||||
if (res.hasUpdate) {
|
|
||||||
console.log('New version available')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Page Lifecycle
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
// === Page Load ===
|
|
||||||
onLoad(options) {
|
|
||||||
// Page loaded with query parameters
|
|
||||||
console.log('Page loaded', options)
|
|
||||||
// options contains URL query params
|
|
||||||
// e.g., /pages/detail?id=123 -> options = { id: '123' }
|
|
||||||
this.id = options.id
|
|
||||||
this.loadData()
|
|
||||||
},
|
|
||||||
|
|
||||||
// === Page Show ===
|
|
||||||
onShow() {
|
|
||||||
// Page shown (every time)
|
|
||||||
console.log('Page shown')
|
|
||||||
// Good for refreshing data when returning
|
|
||||||
},
|
|
||||||
|
|
||||||
// === Page Ready ===
|
|
||||||
onReady() {
|
|
||||||
// Page ready, DOM rendered
|
|
||||||
console.log('Page ready')
|
|
||||||
// Safe to access DOM elements
|
|
||||||
this.initChart()
|
|
||||||
},
|
|
||||||
|
|
||||||
// === Page Hide ===
|
|
||||||
onHide() {
|
|
||||||
// Page hidden (navigated away)
|
|
||||||
console.log('Page hidden')
|
|
||||||
// Pause videos, timers, etc.
|
|
||||||
},
|
|
||||||
|
|
||||||
// === Page Unload ===
|
|
||||||
onUnload() {
|
|
||||||
// Page destroyed
|
|
||||||
console.log('Page unloaded')
|
|
||||||
// Clean up resources, remove listeners
|
|
||||||
clearInterval(this.timer)
|
|
||||||
},
|
|
||||||
|
|
||||||
// === Pull Down Refresh ===
|
|
||||||
onPullDownRefresh() {
|
|
||||||
// User pulled down
|
|
||||||
console.log('Pull down refresh')
|
|
||||||
this.refreshData().finally(() => {
|
|
||||||
uni.stopPullDownRefresh()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// === Reach Bottom ===
|
|
||||||
onReachBottom() {
|
|
||||||
// Scrolled to bottom
|
|
||||||
console.log('Reach bottom')
|
|
||||||
this.loadMore()
|
|
||||||
},
|
|
||||||
|
|
||||||
// === Page Scroll ===
|
|
||||||
onPageScroll(e) {
|
|
||||||
// Page scrolled
|
|
||||||
// e.scrollTop: scroll position
|
|
||||||
this.scrollTop = e.scrollTop
|
|
||||||
},
|
|
||||||
|
|
||||||
// === Resize ===
|
|
||||||
onResize(e) {
|
|
||||||
// Page resized (e.g., rotation)
|
|
||||||
console.log('Page resized', e.size)
|
|
||||||
},
|
|
||||||
|
|
||||||
// === Share ===
|
|
||||||
onShareAppMessage(res) {
|
|
||||||
// Native share (MP)
|
|
||||||
if (res.from === 'button') {
|
|
||||||
// From share button
|
|
||||||
console.log(res.target)
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
title: 'Share Title',
|
|
||||||
path: '/pages/index/index',
|
|
||||||
imageUrl: '/static/share.png'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// === Timeline Share ===
|
|
||||||
onShareTimeline() {
|
|
||||||
// Share to timeline (WeChat)
|
|
||||||
return {
|
|
||||||
title: 'Timeline Title',
|
|
||||||
query: 'id=123',
|
|
||||||
imageUrl: '/static/share.png'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// === Add to Favorites ===
|
|
||||||
onAddToFavorites() {
|
|
||||||
// Add to MP favorites
|
|
||||||
return {
|
|
||||||
title: 'Favorite Title',
|
|
||||||
imageUrl: '/static/fav.png',
|
|
||||||
query: 'id=123'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
id: null,
|
|
||||||
scrollTop: 0,
|
|
||||||
timer: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
loadData() {
|
|
||||||
// Load page data
|
|
||||||
},
|
|
||||||
refreshData() {
|
|
||||||
// Refresh data
|
|
||||||
},
|
|
||||||
loadMore() {
|
|
||||||
// Load more data
|
|
||||||
},
|
|
||||||
initChart() {
|
|
||||||
// Initialize chart after DOM ready
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Component Lifecycle (Vue 2)
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
// === Creation ===
|
|
||||||
beforeCreate() {
|
|
||||||
// Instance initialized
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
// Instance created, data observed
|
|
||||||
// Good for initial data loading
|
|
||||||
},
|
|
||||||
|
|
||||||
// === Mounting ===
|
|
||||||
beforeMount() {
|
|
||||||
// Before DOM mount
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
// DOM mounted
|
|
||||||
// Good for DOM operations
|
|
||||||
},
|
|
||||||
|
|
||||||
// === Updating ===
|
|
||||||
beforeUpdate() {
|
|
||||||
// Before data update
|
|
||||||
},
|
|
||||||
|
|
||||||
updated() {
|
|
||||||
// After data update
|
|
||||||
},
|
|
||||||
|
|
||||||
// === Destruction ===
|
|
||||||
beforeDestroy() {
|
|
||||||
// Before instance destroyed
|
|
||||||
// Clean up here
|
|
||||||
},
|
|
||||||
|
|
||||||
destroyed() {
|
|
||||||
// Instance destroyed
|
|
||||||
},
|
|
||||||
|
|
||||||
// === Keep-alive ===
|
|
||||||
activated() {
|
|
||||||
// Component activated (kept-alive)
|
|
||||||
},
|
|
||||||
|
|
||||||
deactivated() {
|
|
||||||
// Component deactivated (kept-alive)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Component Lifecycle (Vue 3)
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
import {
|
|
||||||
onBeforeMount,
|
|
||||||
onMounted,
|
|
||||||
onBeforeUpdate,
|
|
||||||
onUpdated,
|
|
||||||
onBeforeUnmount,
|
|
||||||
onUnmounted,
|
|
||||||
onActivated,
|
|
||||||
onDeactivated
|
|
||||||
} from 'vue'
|
|
||||||
|
|
||||||
// Setup runs before beforeCreate
|
|
||||||
console.log('setup')
|
|
||||||
|
|
||||||
onBeforeMount(() => {
|
|
||||||
console.log('onBeforeMount')
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
console.log('onMounted')
|
|
||||||
})
|
|
||||||
|
|
||||||
onBeforeUpdate(() => {
|
|
||||||
console.log('onBeforeUpdate')
|
|
||||||
})
|
|
||||||
|
|
||||||
onUpdated(() => {
|
|
||||||
console.log('onUpdated')
|
|
||||||
})
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
console.log('onBeforeUnmount')
|
|
||||||
})
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
console.log('onUnmounted')
|
|
||||||
})
|
|
||||||
|
|
||||||
onActivated(() => {
|
|
||||||
console.log('onActivated')
|
|
||||||
})
|
|
||||||
|
|
||||||
onDeactivated(() => {
|
|
||||||
console.log('onDeactivated')
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Lifecycle Comparison
|
|
||||||
|
|
||||||
| Scenario | UniApp Page | Vue Component |
|
|
||||||
|----------|-------------|---------------|
|
|
||||||
| Initial load | onLoad | created |
|
|
||||||
| DOM ready | onReady | mounted |
|
|
||||||
| Page show | onShow | - |
|
|
||||||
| Page hide | onHide | - |
|
|
||||||
| Page destroy | onUnload | destroyed/unmounted |
|
|
||||||
| Data refresh | onPullDownRefresh | - |
|
|
||||||
| Infinite scroll | onReachBottom | - |
|
|
||||||
| Scroll position | onPageScroll | - |
|
|
||||||
|
|
||||||
## App Update Manager
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// In App.vue onLaunch
|
|
||||||
onLaunch() {
|
|
||||||
const updateManager = uni.getUpdateManager()
|
|
||||||
|
|
||||||
updateManager.onCheckForUpdate((res) => {
|
|
||||||
console.log('Has update:', res.hasUpdate)
|
|
||||||
})
|
|
||||||
|
|
||||||
updateManager.onUpdateReady(() => {
|
|
||||||
uni.showModal({
|
|
||||||
title: 'Update Ready',
|
|
||||||
content: 'New version downloaded. Restart to apply?',
|
|
||||||
success: (res) => {
|
|
||||||
if (res.confirm) {
|
|
||||||
updateManager.applyUpdate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
updateManager.onUpdateFailed(() => {
|
|
||||||
console.error('Update failed')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
### Data Loading Pattern
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
loading: false,
|
|
||||||
error: null,
|
|
||||||
data: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onLoad(options) {
|
|
||||||
this.fetchData(options.id)
|
|
||||||
},
|
|
||||||
|
|
||||||
onPullDownRefresh() {
|
|
||||||
this.fetchData(this.id).finally(() => {
|
|
||||||
uni.stopPullDownRefresh()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
async fetchData(id) {
|
|
||||||
this.loading = true
|
|
||||||
this.error = null
|
|
||||||
try {
|
|
||||||
this.data = await api.getDetail(id)
|
|
||||||
} catch (err) {
|
|
||||||
this.error = err.message
|
|
||||||
} finally {
|
|
||||||
this.loading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Scroll Performance
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
scrollTop: 0,
|
|
||||||
showBackTop: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Throttle scroll events
|
|
||||||
onPageScroll: throttle(function(e) {
|
|
||||||
this.scrollTop = e.scrollTop
|
|
||||||
this.showBackTop = e.scrollTop > 500
|
|
||||||
}, 200),
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
scrollToTop() {
|
|
||||||
uni.pageScrollTo({
|
|
||||||
scrollTop: 0,
|
|
||||||
duration: 300
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/collocation/App.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/api/lifecycle.md
|
|
||||||
-->
|
|
||||||
@ -1,417 +0,0 @@
|
|||||||
---
|
|
||||||
name: Location Services
|
|
||||||
description: Geolocation, map operations, and location-based services
|
|
||||||
---
|
|
||||||
|
|
||||||
# Location Services
|
|
||||||
|
|
||||||
## Get Current Location
|
|
||||||
|
|
||||||
### uni.getLocation
|
|
||||||
|
|
||||||
Get current geographic location.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.getLocation({
|
|
||||||
type: 'wgs84', // wgs84/gcj02 (gcj02 for map display)
|
|
||||||
altitude: false, // Include altitude
|
|
||||||
geocode: false, // Include address info (App)
|
|
||||||
highAccuracyExpireTime: 3000, // High accuracy timeout
|
|
||||||
success: (res) => {
|
|
||||||
console.log('Latitude:', res.latitude)
|
|
||||||
console.log('Longitude:', res.longitude)
|
|
||||||
console.log('Speed:', res.speed)
|
|
||||||
console.log('Accuracy:', res.accuracy)
|
|
||||||
// App only:
|
|
||||||
console.log('Altitude:', res.altitude)
|
|
||||||
console.log('Address:', res.address)
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
console.error('Location failed:', err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
**Response Properties:**
|
|
||||||
|
|
||||||
| Property | Type | Description |
|
|
||||||
|----------|------|-------------|
|
|
||||||
| latitude | Number | Latitude |
|
|
||||||
| longitude | Number | Longitude |
|
|
||||||
| speed | Number | Speed (m/s) |
|
|
||||||
| accuracy | Number | Accuracy (meters) |
|
|
||||||
| altitude | Number | Altitude (meters) |
|
|
||||||
| verticalAccuracy | Number | Vertical accuracy |
|
|
||||||
| horizontalAccuracy | Number | Horizontal accuracy |
|
|
||||||
| address | Object | Address info (App) |
|
|
||||||
|
|
||||||
### uni.getFuzzyLocation (WeChat)
|
|
||||||
|
|
||||||
Get approximate location (better privacy, faster).
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.getFuzzyLocation({
|
|
||||||
type: 'wgs84',
|
|
||||||
success: (res) => {
|
|
||||||
console.log(res.latitude, res.longitude)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Choose Location
|
|
||||||
|
|
||||||
### uni.chooseLocation
|
|
||||||
|
|
||||||
Open map to select location.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.chooseLocation({
|
|
||||||
latitude: 39.9,
|
|
||||||
longitude: 116.4,
|
|
||||||
keyword: 'restaurant',
|
|
||||||
success: (res) => {
|
|
||||||
console.log('Name:', res.name)
|
|
||||||
console.log('Address:', res.address)
|
|
||||||
console.log('Latitude:', res.latitude)
|
|
||||||
console.log('Longitude:', res.longitude)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Open Location
|
|
||||||
|
|
||||||
### uni.openLocation
|
|
||||||
|
|
||||||
Open external map app.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.openLocation({
|
|
||||||
latitude: 39.9,
|
|
||||||
longitude: 116.4,
|
|
||||||
name: 'Destination Name',
|
|
||||||
address: 'Full address here',
|
|
||||||
scale: 18
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Location Change Monitoring
|
|
||||||
|
|
||||||
### uni.startLocationUpdate
|
|
||||||
|
|
||||||
Start background location updates.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.startLocationUpdate({
|
|
||||||
type: 'gcj02',
|
|
||||||
success: () => {
|
|
||||||
console.log('Location updates started')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### uni.startLocationUpdateBackground
|
|
||||||
|
|
||||||
Start background location (requires permission).
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.startLocationUpdateBackground({
|
|
||||||
type: 'gcj02',
|
|
||||||
success: () => {
|
|
||||||
console.log('Background location started')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Listen for Location Changes
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.onLocationChange((res) => {
|
|
||||||
console.log('Location updated:', res.latitude, res.longitude)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Stop listening
|
|
||||||
uni.offLocationChange(callback)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Stop Location Updates
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.stopLocationUpdate({
|
|
||||||
success: () => {
|
|
||||||
console.log('Location updates stopped')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Map Component
|
|
||||||
|
|
||||||
### Basic Map
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<map
|
|
||||||
id="myMap"
|
|
||||||
style="width: 100%; height: 300px;"
|
|
||||||
:latitude="latitude"
|
|
||||||
:longitude="longitude"
|
|
||||||
:scale="14"
|
|
||||||
:markers="markers"
|
|
||||||
:polyline="polyline"
|
|
||||||
:circles="circles"
|
|
||||||
:show-location="true"
|
|
||||||
@markertap="onMarkerTap"
|
|
||||||
@regionchange="onRegionChange"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
latitude: 39.909,
|
|
||||||
longitude: 116.39742,
|
|
||||||
markers: [{
|
|
||||||
id: 1,
|
|
||||||
latitude: 39.909,
|
|
||||||
longitude: 116.39742,
|
|
||||||
title: 'Marker 1',
|
|
||||||
iconPath: '/static/marker.png',
|
|
||||||
width: 30,
|
|
||||||
height: 30,
|
|
||||||
callout: {
|
|
||||||
content: 'Hello',
|
|
||||||
color: '#000',
|
|
||||||
fontSize: 14,
|
|
||||||
borderRadius: 5,
|
|
||||||
padding: 10,
|
|
||||||
display: 'BYCLICK'
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
polyline: [{
|
|
||||||
points: [
|
|
||||||
{ latitude: 39.909, longitude: 116.39742 },
|
|
||||||
{ latitude: 39.91, longitude: 116.4 }
|
|
||||||
],
|
|
||||||
color: '#FF0000',
|
|
||||||
width: 2,
|
|
||||||
dottedLine: false
|
|
||||||
}],
|
|
||||||
circles: [{
|
|
||||||
latitude: 39.909,
|
|
||||||
longitude: 116.39742,
|
|
||||||
radius: 100,
|
|
||||||
strokeWidth: 2,
|
|
||||||
fillColor: '#FF000020'
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Map Context Operations
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
export default {
|
|
||||||
onReady() {
|
|
||||||
this.mapContext = uni.createMapContext('myMap')
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
// Get center location
|
|
||||||
getCenter() {
|
|
||||||
this.mapContext.getCenterLocation({
|
|
||||||
success: (res) => {
|
|
||||||
console.log('Center:', res.latitude, res.longitude)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// Move to location
|
|
||||||
moveToLocation() {
|
|
||||||
this.mapContext.moveToLocation({
|
|
||||||
latitude: 39.9,
|
|
||||||
longitude: 116.4
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// Translate marker
|
|
||||||
translateMarker() {
|
|
||||||
this.mapContext.translateMarker({
|
|
||||||
markerId: 1,
|
|
||||||
destination: {
|
|
||||||
latitude: 39.91,
|
|
||||||
longitude: 116.41
|
|
||||||
},
|
|
||||||
autoRotate: true,
|
|
||||||
rotate: 0,
|
|
||||||
duration: 1000
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// Include points in view
|
|
||||||
includePoints() {
|
|
||||||
this.mapContext.includePoints({
|
|
||||||
points: [
|
|
||||||
{ latitude: 39.9, longitude: 116.4 },
|
|
||||||
{ latitude: 39.91, longitude: 116.41 }
|
|
||||||
],
|
|
||||||
padding: [10, 10, 10, 10]
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// Get region
|
|
||||||
getRegion() {
|
|
||||||
this.mapContext.getRegion({
|
|
||||||
success: (res) => {
|
|
||||||
console.log('Southwest:', res.southwest)
|
|
||||||
console.log('Northeast:', res.northeast)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// Add markers
|
|
||||||
addMarkers() {
|
|
||||||
this.mapContext.addMarkers({
|
|
||||||
markers: [{
|
|
||||||
id: 2,
|
|
||||||
latitude: 39.91,
|
|
||||||
longitude: 116.41,
|
|
||||||
title: 'New Marker'
|
|
||||||
}],
|
|
||||||
clear: false // Don't clear existing
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// Remove markers
|
|
||||||
removeMarkers() {
|
|
||||||
this.mapContext.removeMarkers({
|
|
||||||
markerIds: [1, 2]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Coordinate Systems
|
|
||||||
|
|
||||||
| System | Description | Usage |
|
|
||||||
|--------|-------------|-------|
|
|
||||||
| WGS84 | GPS coordinates | International standard |
|
|
||||||
| GCJ02 | Mars coordinates | China national standard |
|
|
||||||
| BD09 | Baidu coordinates | Baidu Map only |
|
|
||||||
|
|
||||||
**Note:** For map display in China, use `gcj02`.
|
|
||||||
|
|
||||||
## Permission Configuration
|
|
||||||
|
|
||||||
### Mini-Program
|
|
||||||
|
|
||||||
Add to `manifest.json`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"mp-weixin": {
|
|
||||||
"permission": {
|
|
||||||
"scope.userLocation": {
|
|
||||||
"desc": "Your location is needed to find nearby stores"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"requiredPrivateInfos": [
|
|
||||||
"getLocation",
|
|
||||||
"chooseLocation"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### App (Android)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"app-plus": {
|
|
||||||
"distribute": {
|
|
||||||
"android": {
|
|
||||||
"permissions": [
|
|
||||||
"<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />",
|
|
||||||
"<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\" />"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### App (iOS)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"app-plus": {
|
|
||||||
"distribute": {
|
|
||||||
"ios": {
|
|
||||||
"privacyDescription": {
|
|
||||||
"NSLocationWhenInUseUsageDescription": "Location is needed to find nearby stores",
|
|
||||||
"NSLocationAlwaysUsageDescription": "Background location is needed for navigation"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
### Permission Handling
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
async function getLocationWithPermission() {
|
|
||||||
try {
|
|
||||||
// Check permission
|
|
||||||
const setting = await uni.getSetting()
|
|
||||||
if (!setting.authSetting['scope.userLocation']) {
|
|
||||||
// Request permission
|
|
||||||
await uni.authorize({ scope: 'scope.userLocation' })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get location
|
|
||||||
const res = await uni.getLocation({ type: 'gcj02' })
|
|
||||||
return res
|
|
||||||
} catch (err) {
|
|
||||||
if (err.errMsg.includes('auth deny')) {
|
|
||||||
uni.showModal({
|
|
||||||
title: 'Permission Required',
|
|
||||||
content: 'Please enable location permission in settings',
|
|
||||||
success: (res) => {
|
|
||||||
if (res.confirm) {
|
|
||||||
uni.openSetting()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Distance Calculation
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
function calculateDistance(lat1, lon1, lat2, lon2) {
|
|
||||||
const R = 6371 // Earth's radius in km
|
|
||||||
const dLat = (lat2 - lat1) * Math.PI / 180
|
|
||||||
const dLon = (lon2 - lon1) * Math.PI / 180
|
|
||||||
const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
|
|
||||||
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
|
|
||||||
Math.sin(dLon/2) * Math.sin(dLon/2)
|
|
||||||
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
|
|
||||||
return R * c // Distance in km
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/api/location/location.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/api/location/location-change.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/api/location/open-location.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/component/map.md
|
|
||||||
-->
|
|
||||||
@ -1,314 +0,0 @@
|
|||||||
---
|
|
||||||
name: Media Components
|
|
||||||
description: Image, video, audio, and camera components
|
|
||||||
---
|
|
||||||
|
|
||||||
# Media Components
|
|
||||||
|
|
||||||
## image
|
|
||||||
|
|
||||||
Display images with various mode options.
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<!-- Basic usage -->
|
|
||||||
<image src="/static/logo.png" mode="aspectFit" />
|
|
||||||
|
|
||||||
<!-- With event handling -->
|
|
||||||
<image
|
|
||||||
:src="imageUrl"
|
|
||||||
mode="aspectFill"
|
|
||||||
:lazy-load="true"
|
|
||||||
:show-menu-by-longpress="true"
|
|
||||||
@load="onImageLoad"
|
|
||||||
@error="onImageError"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
**Properties:**
|
|
||||||
|
|
||||||
| Property | Type | Default | Description |
|
|
||||||
|----------|------|---------|-------------|
|
|
||||||
| src | String | | Image source URL |
|
|
||||||
| mode | String | scaleToFill | Display mode (see below) |
|
|
||||||
| lazy-load | Boolean | false | Lazy load image |
|
|
||||||
| show-menu-by-longpress | Boolean | false | Show menu on long press |
|
|
||||||
| webp | Boolean | false | Parse WebP format (Android) |
|
|
||||||
|
|
||||||
**Mode Values:**
|
|
||||||
|
|
||||||
| Mode | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| scaleToFill | Fill container, may distort |
|
|
||||||
| aspectFit | Contain within container |
|
|
||||||
| aspectFill | Cover container, may clip |
|
|
||||||
| widthFix | Width fixed, height auto |
|
|
||||||
| heightFix | Height fixed, width auto |
|
|
||||||
| top / bottom / center / left / right | Align to position |
|
|
||||||
| top left / top right / bottom left / bottom right | Corner alignment |
|
|
||||||
|
|
||||||
**Events:**
|
|
||||||
- `@load` - Image loaded successfully
|
|
||||||
- `@error` - Image failed to load
|
|
||||||
|
|
||||||
## video
|
|
||||||
|
|
||||||
Video player component.
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<video
|
|
||||||
id="myVideo"
|
|
||||||
src="https://example.com/video.mp4"
|
|
||||||
:controls="true"
|
|
||||||
:autoplay="false"
|
|
||||||
:loop="false"
|
|
||||||
:muted="false"
|
|
||||||
initial-time="30"
|
|
||||||
:duration="300"
|
|
||||||
poster="/static/poster.jpg"
|
|
||||||
object-fit="contain"
|
|
||||||
@play="onPlay"
|
|
||||||
@pause="onPause"
|
|
||||||
@ended="onEnded"
|
|
||||||
@timeupdate="onTimeUpdate"
|
|
||||||
@fullscreenchange="onFullscreenChange"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
onReady() {
|
|
||||||
this.videoContext = uni.createVideoContext('myVideo')
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
play() {
|
|
||||||
this.videoContext.play()
|
|
||||||
},
|
|
||||||
pause() {
|
|
||||||
this.videoContext.pause()
|
|
||||||
},
|
|
||||||
seek(time) {
|
|
||||||
this.videoContext.seek(time)
|
|
||||||
},
|
|
||||||
sendDanmu(danmu) {
|
|
||||||
this.videoContext.sendDanmu({
|
|
||||||
text: danmu.text,
|
|
||||||
color: danmu.color
|
|
||||||
})
|
|
||||||
},
|
|
||||||
playbackRate(rate) {
|
|
||||||
this.videoContext.playbackRate(rate)
|
|
||||||
},
|
|
||||||
onFullscreenChange(e) {
|
|
||||||
console.log('Fullscreen:', e.detail.fullScreen)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
**Properties:**
|
|
||||||
|
|
||||||
| Property | Type | Default | Description |
|
|
||||||
|----------|------|---------|-------------|
|
|
||||||
| src | String | | Video source URL |
|
|
||||||
| controls | Boolean | true | Show controls |
|
|
||||||
| autoplay | Boolean | false | Auto play |
|
|
||||||
| loop | Boolean | false | Loop playback |
|
|
||||||
| muted | Boolean | false | Muted |
|
|
||||||
| initial-time | Number | 0 | Start time (s) |
|
|
||||||
| duration | Number | | Total duration (s) |
|
|
||||||
| poster | String | | Poster image URL |
|
|
||||||
| object-fit | String | contain | contain/cover/fill |
|
|
||||||
| danmu-list | Array | | Danmu list |
|
|
||||||
| danmu-btn | Boolean | false | Show danmu button |
|
|
||||||
| enable-danmu | Boolean | false | Enable danmu |
|
|
||||||
| show-center-play-btn | Boolean | true | Show center play button |
|
|
||||||
| show-play-btn | Boolean | true | Show play button |
|
|
||||||
| show-fullscreen-btn | Boolean | true | Show fullscreen button |
|
|
||||||
| page-gesture | Boolean | false | Enable page gesture |
|
|
||||||
| enable-progress-gesture | Boolean | true | Enable progress gesture |
|
|
||||||
|
|
||||||
## audio
|
|
||||||
|
|
||||||
Audio player (deprecated, use `uni.getBackgroundAudioManager` instead).
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<audio
|
|
||||||
:src="audioSrc"
|
|
||||||
:poster="posterUrl"
|
|
||||||
:name="audioName"
|
|
||||||
:author="author"
|
|
||||||
:controls="true"
|
|
||||||
:loop="false"
|
|
||||||
@play="onPlay"
|
|
||||||
@pause="onPause"
|
|
||||||
@ended="onEnded"
|
|
||||||
@timeupdate="onTimeUpdate"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
## camera
|
|
||||||
|
|
||||||
Camera component for capturing photos/videos.
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<camera
|
|
||||||
device-position="back"
|
|
||||||
flash="auto"
|
|
||||||
resolution="high"
|
|
||||||
frame-size="large"
|
|
||||||
@stop="onCameraStop"
|
|
||||||
@error="onCameraError"
|
|
||||||
@initdone="onCameraReady"
|
|
||||||
/>
|
|
||||||
<button @click="takePhoto">Take Photo</button>
|
|
||||||
<button @click="startRecord">Start Record</button>
|
|
||||||
<button @click="stopRecord">Stop Record</button>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
onReady() {
|
|
||||||
this.cameraContext = uni.createCameraContext()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
takePhoto() {
|
|
||||||
this.cameraContext.takePhoto({
|
|
||||||
quality: 'high',
|
|
||||||
success: (res) => {
|
|
||||||
console.log(res.tempImagePath)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
startRecord() {
|
|
||||||
this.cameraContext.startRecord({
|
|
||||||
success: () => console.log('Recording started')
|
|
||||||
})
|
|
||||||
},
|
|
||||||
stopRecord() {
|
|
||||||
this.cameraContext.stopRecord({
|
|
||||||
success: (res) => {
|
|
||||||
console.log(res.tempVideoPath)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
**Properties:**
|
|
||||||
|
|
||||||
| Property | Type | Default | Description |
|
|
||||||
|----------|------|---------|-------------|
|
|
||||||
| mode | String | normal | normal/scanCode |
|
|
||||||
| resolution | String | medium | low/medium/high |
|
|
||||||
| device-position | String | back | front/back |
|
|
||||||
| flash | String | auto | auto/on/off |
|
|
||||||
| frame-size | String | large | small/medium/large |
|
|
||||||
|
|
||||||
## live-player / live-pusher
|
|
||||||
|
|
||||||
Live streaming components (mini-program only).
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<!-- Live player -->
|
|
||||||
<live-player
|
|
||||||
src="rtmp://example.com/live/stream"
|
|
||||||
mode="live"
|
|
||||||
:autoplay="true"
|
|
||||||
:muted="false"
|
|
||||||
orientation="vertical"
|
|
||||||
object-fit="contain"
|
|
||||||
@statechange="onStateChange"
|
|
||||||
@error="onError"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
## map
|
|
||||||
|
|
||||||
Map component for displaying maps and markers.
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<map
|
|
||||||
id="myMap"
|
|
||||||
style="width: 100%; height: 300px;"
|
|
||||||
:latitude="latitude"
|
|
||||||
:longitude="longitude"
|
|
||||||
:scale="14"
|
|
||||||
:markers="markers"
|
|
||||||
:polyline="polyline"
|
|
||||||
:circles="circles"
|
|
||||||
:controls="controls"
|
|
||||||
:show-location="true"
|
|
||||||
@markertap="onMarkerTap"
|
|
||||||
@regionchange="onRegionChange"
|
|
||||||
@tap="onMapTap"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
latitude: 39.909,
|
|
||||||
longitude: 116.39742,
|
|
||||||
markers: [{
|
|
||||||
id: 1,
|
|
||||||
latitude: 39.909,
|
|
||||||
longitude: 116.39742,
|
|
||||||
title: 'Marker 1',
|
|
||||||
iconPath: '/static/marker.png',
|
|
||||||
width: 30,
|
|
||||||
height: 30
|
|
||||||
}],
|
|
||||||
polyline: [{
|
|
||||||
points: [
|
|
||||||
{ latitude: 39.909, longitude: 116.39742 },
|
|
||||||
{ latitude: 39.91, longitude: 116.4 }
|
|
||||||
],
|
|
||||||
color: '#FF0000',
|
|
||||||
width: 2
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onReady() {
|
|
||||||
this.mapContext = uni.createMapContext('myMap')
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
moveToLocation() {
|
|
||||||
this.mapContext.moveToLocation({
|
|
||||||
latitude: 39.91,
|
|
||||||
longitude: 116.4
|
|
||||||
})
|
|
||||||
},
|
|
||||||
getCenterLocation() {
|
|
||||||
this.mapContext.getCenterLocation({
|
|
||||||
success: (res) => {
|
|
||||||
console.log(res.latitude, res.longitude)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/component/image.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/component/video.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/component/audio.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/component/camera.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/component/live-player.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/component/map.md
|
|
||||||
-->
|
|
||||||
@ -1,257 +0,0 @@
|
|||||||
---
|
|
||||||
name: Navigation
|
|
||||||
description: Navigation, routing, and page navigation components
|
|
||||||
---
|
|
||||||
|
|
||||||
# Navigation
|
|
||||||
|
|
||||||
## navigator
|
|
||||||
|
|
||||||
Page navigation component.
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<!-- Basic navigation -->
|
|
||||||
<navigator url="/pages/detail/detail" hover-class="navigator-hover">
|
|
||||||
Go to Detail
|
|
||||||
</navigator>
|
|
||||||
|
|
||||||
<!-- Open in new page -->
|
|
||||||
<navigator url="/pages/detail/detail" open-type="navigate">
|
|
||||||
Navigate (default)
|
|
||||||
</navigator>
|
|
||||||
|
|
||||||
<!-- Redirect (no back button) -->
|
|
||||||
<navigator url="/pages/login/login" open-type="redirect">
|
|
||||||
Login (redirect)
|
|
||||||
</navigator>
|
|
||||||
|
|
||||||
<!-- Switch to tab page -->
|
|
||||||
<navigator url="/pages/index/index" open-type="switchTab">
|
|
||||||
Go to Home
|
|
||||||
</navigator>
|
|
||||||
|
|
||||||
<!-- Re-launch app -->
|
|
||||||
<navigator url="/pages/start/start" open-type="reLaunch">
|
|
||||||
Restart
|
|
||||||
</navigator>
|
|
||||||
|
|
||||||
<!-- Navigate back -->
|
|
||||||
<navigator open-type="navigateBack" :delta="1">
|
|
||||||
Go Back
|
|
||||||
</navigator>
|
|
||||||
|
|
||||||
<!-- Exit app (mini-program) -->
|
|
||||||
<navigator open-type="exit" target="miniProgram">
|
|
||||||
Exit
|
|
||||||
</navigator>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
**Properties:**
|
|
||||||
|
|
||||||
| Property | Type | Default | Description |
|
|
||||||
|----------|------|---------|-------------|
|
|
||||||
| url | String | | Target page path |
|
|
||||||
| open-type | String | navigate | navigate/redirect/switchTab/reLaunch/navigateBack/exit |
|
|
||||||
| delta | Number | 1 | Back level when open-type is navigateBack |
|
|
||||||
| hover-class | String | navigator-hover | Hover state class |
|
|
||||||
| hover-stop-propagation | Boolean | false | Stop hover propagation |
|
|
||||||
| target | String | self | self/miniProgram (mini-program only) |
|
|
||||||
|
|
||||||
## Programmatic Navigation
|
|
||||||
|
|
||||||
### uni.navigateTo
|
|
||||||
|
|
||||||
Navigate to new page.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Basic navigation
|
|
||||||
uni.navigateTo({
|
|
||||||
url: '/pages/detail/detail'
|
|
||||||
})
|
|
||||||
|
|
||||||
// With query parameters
|
|
||||||
uni.navigateTo({
|
|
||||||
url: '/pages/detail/detail?id=123&name=test'
|
|
||||||
})
|
|
||||||
|
|
||||||
// With events (Vue 2 only)
|
|
||||||
uni.navigateTo({
|
|
||||||
url: '/pages/detail/detail',
|
|
||||||
events: {
|
|
||||||
acceptDataFromOpenedPage(data) {
|
|
||||||
console.log(data)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
success(res) {
|
|
||||||
res.eventChannel.emit('acceptDataFromOpenerPage', { data: 'test' })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### uni.redirectTo
|
|
||||||
|
|
||||||
Redirect to new page (closes current page).
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.redirectTo({
|
|
||||||
url: '/pages/login/login'
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### uni.reLaunch
|
|
||||||
|
|
||||||
Re-launch application to specified page.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.reLaunch({
|
|
||||||
url: '/pages/index/index'
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### uni.switchTab
|
|
||||||
|
|
||||||
Switch to tab bar page.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.switchTab({
|
|
||||||
url: '/pages/home/home'
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### uni.navigateBack
|
|
||||||
|
|
||||||
Navigate back to previous page.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Go back one page
|
|
||||||
uni.navigateBack()
|
|
||||||
|
|
||||||
// Go back multiple pages
|
|
||||||
uni.navigateBack({
|
|
||||||
delta: 2
|
|
||||||
})
|
|
||||||
|
|
||||||
// With animation (App only)
|
|
||||||
uni.navigateBack({
|
|
||||||
delta: 1,
|
|
||||||
animationType: 'pop-out',
|
|
||||||
animationDuration: 300
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### uni.preloadPage
|
|
||||||
|
|
||||||
Preload page for faster navigation (App only).
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.preloadPage({
|
|
||||||
url: '/pages/detail/detail'
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Getting Page Information
|
|
||||||
|
|
||||||
### getCurrentPages
|
|
||||||
|
|
||||||
Get current page stack.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const pages = getCurrentPages()
|
|
||||||
const currentPage = pages[pages.length - 1]
|
|
||||||
console.log(currentPage.route) // Current page path
|
|
||||||
```
|
|
||||||
|
|
||||||
### getApp
|
|
||||||
|
|
||||||
Get app instance.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const app = getApp()
|
|
||||||
console.log(app.globalData)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Page Event Channel (Vue 2)
|
|
||||||
|
|
||||||
Communicate between pages using event channel.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Page A: Open page B
|
|
||||||
uni.navigateTo({
|
|
||||||
url: '/pages/pageB/pageB',
|
|
||||||
success(res) {
|
|
||||||
// Listen for events from page B
|
|
||||||
res.eventChannel.on('acceptDataFromPageB', (data) => {
|
|
||||||
console.log(data)
|
|
||||||
})
|
|
||||||
// Send data to page B
|
|
||||||
res.eventChannel.emit('acceptDataFromPageA', { data: 'hello' })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Page B: Receive and send data
|
|
||||||
export default {
|
|
||||||
onLoad() {
|
|
||||||
const eventChannel = this.getOpenerEventChannel()
|
|
||||||
// Listen for events from page A
|
|
||||||
eventChannel.on('acceptDataFromPageA', (data) => {
|
|
||||||
console.log(data)
|
|
||||||
})
|
|
||||||
// Send data back to page A
|
|
||||||
eventChannel.emit('acceptDataFromPageB', { data: 'world' })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Navigation Bar
|
|
||||||
|
|
||||||
### Custom Navigation Bar
|
|
||||||
|
|
||||||
Configure in `pages.json`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"pages": [{
|
|
||||||
"path": "pages/index/index",
|
|
||||||
"style": {
|
|
||||||
"navigationStyle": "custom"
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### uni.setNavigationBarTitle
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.setNavigationBarTitle({
|
|
||||||
title: 'New Title'
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### uni.setNavigationBarColor
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.setNavigationBarColor({
|
|
||||||
frontColor: '#ffffff',
|
|
||||||
backgroundColor: '#000000',
|
|
||||||
animation: {
|
|
||||||
duration: 400,
|
|
||||||
timingFunc: 'easeIn'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### uni.showNavigationBarLoading
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.showNavigationBarLoading()
|
|
||||||
// ...loading operation
|
|
||||||
uni.hideNavigationBarLoading()
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/component/navigator.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/api/router.md
|
|
||||||
-->
|
|
||||||
@ -1,345 +0,0 @@
|
|||||||
---
|
|
||||||
name: Network Requests
|
|
||||||
description: HTTP requests, file upload/download, and WebSocket
|
|
||||||
---
|
|
||||||
|
|
||||||
# Network Requests
|
|
||||||
|
|
||||||
## uni.request
|
|
||||||
|
|
||||||
Make HTTP requests to backend APIs.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// GET request
|
|
||||||
uni.request({
|
|
||||||
url: 'https://api.example.com/data',
|
|
||||||
method: 'GET',
|
|
||||||
data: { id: 123 },
|
|
||||||
header: {
|
|
||||||
'content-type': 'application/json',
|
|
||||||
'Authorization': 'Bearer token'
|
|
||||||
},
|
|
||||||
timeout: 30000,
|
|
||||||
success: (res) => {
|
|
||||||
console.log(res.data)
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
console.error(err)
|
|
||||||
},
|
|
||||||
complete: () => {
|
|
||||||
console.log('Request complete')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// POST request with JSON
|
|
||||||
uni.request({
|
|
||||||
url: 'https://api.example.com/submit',
|
|
||||||
method: 'POST',
|
|
||||||
data: {
|
|
||||||
name: 'John',
|
|
||||||
age: 30
|
|
||||||
},
|
|
||||||
header: {
|
|
||||||
'content-type': 'application/json'
|
|
||||||
},
|
|
||||||
success: (res) => {
|
|
||||||
if (res.statusCode === 200) {
|
|
||||||
console.log('Success:', res.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Using Promise
|
|
||||||
uni.request({
|
|
||||||
url: 'https://api.example.com/data',
|
|
||||||
method: 'GET'
|
|
||||||
}).then(res => {
|
|
||||||
console.log(res.data)
|
|
||||||
}).catch(err => {
|
|
||||||
console.error(err)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Async/await
|
|
||||||
async function fetchData() {
|
|
||||||
try {
|
|
||||||
const res = await uni.request({
|
|
||||||
url: 'https://api.example.com/data'
|
|
||||||
})
|
|
||||||
return res.data
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Failed to fetch:', err)
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
|
|
||||||
| Parameter | Type | Required | Description |
|
|
||||||
|-----------|------|----------|-------------|
|
|
||||||
| url | String | Yes | Request URL |
|
|
||||||
| data | Object/String/Array/ArrayBuffer | No | Request data |
|
|
||||||
| header | Object | No | Request headers |
|
|
||||||
| method | String | No | HTTP method (default: GET) |
|
|
||||||
| timeout | Number | No | Timeout in ms (default: 60000) |
|
|
||||||
| dataType | String | No | Response data type (default: json) |
|
|
||||||
| responseType | String | No | Response type (default: text) |
|
|
||||||
| sslVerify | Boolean | No | Verify SSL certificate (default: true) |
|
|
||||||
|
|
||||||
**Response Object:**
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
data: Object | String | ArrayBuffer, // Response data
|
|
||||||
statusCode: Number, // HTTP status code
|
|
||||||
header: Object, // Response headers
|
|
||||||
cookies: Array // Response cookies
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## uni.uploadFile
|
|
||||||
|
|
||||||
Upload files to server.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.chooseImage({
|
|
||||||
count: 1,
|
|
||||||
success: (chooseRes) => {
|
|
||||||
const tempFilePath = chooseRes.tempFilePaths[0]
|
|
||||||
|
|
||||||
const uploadTask = uni.uploadFile({
|
|
||||||
url: 'https://api.example.com/upload',
|
|
||||||
filePath: tempFilePath,
|
|
||||||
name: 'file',
|
|
||||||
formData: {
|
|
||||||
user: 'test',
|
|
||||||
description: 'Image upload'
|
|
||||||
},
|
|
||||||
header: {
|
|
||||||
'Authorization': 'Bearer token'
|
|
||||||
},
|
|
||||||
success: (res) => {
|
|
||||||
console.log('Upload success:', JSON.parse(res.data))
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
console.error('Upload failed:', err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Track upload progress
|
|
||||||
uploadTask.onProgressUpdate((res) => {
|
|
||||||
console.log('Progress:', res.progress)
|
|
||||||
console.log('Uploaded bytes:', res.totalBytesSent)
|
|
||||||
console.log('Total bytes:', res.totalBytesExpectedToSend)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Abort upload if needed
|
|
||||||
// uploadTask.abort()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
**Upload Task Methods:**
|
|
||||||
- `onProgressUpdate(callback)` - Listen for progress updates
|
|
||||||
- `abort()` - Cancel the upload
|
|
||||||
- `offProgressUpdate(callback)` - Remove progress listener
|
|
||||||
|
|
||||||
## uni.downloadFile
|
|
||||||
|
|
||||||
Download files from server.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const downloadTask = uni.downloadFile({
|
|
||||||
url: 'https://example.com/file.pdf',
|
|
||||||
success: (res) => {
|
|
||||||
if (res.statusCode === 200) {
|
|
||||||
console.log('Downloaded to:', res.tempFilePath)
|
|
||||||
|
|
||||||
// Save to local (App only)
|
|
||||||
uni.saveFile({
|
|
||||||
tempFilePath: res.tempFilePath,
|
|
||||||
success: (saveRes) => {
|
|
||||||
console.log('Saved to:', saveRes.savedFilePath)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Track download progress
|
|
||||||
downloadTask.onProgressUpdate((res) => {
|
|
||||||
console.log('Progress:', res.progress)
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## WebSocket
|
|
||||||
|
|
||||||
Real-time bidirectional communication.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Connect to WebSocket
|
|
||||||
const socketTask = uni.connectSocket({
|
|
||||||
url: 'wss://api.example.com/ws',
|
|
||||||
protocols: ['protocol1'],
|
|
||||||
header: {
|
|
||||||
'Authorization': 'Bearer token'
|
|
||||||
},
|
|
||||||
success: () => {
|
|
||||||
console.log('WebSocket connecting...')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Listen for connection open
|
|
||||||
uni.onSocketOpen((res) => {
|
|
||||||
console.log('WebSocket connected')
|
|
||||||
|
|
||||||
// Send message
|
|
||||||
uni.sendSocketMessage({
|
|
||||||
data: JSON.stringify({
|
|
||||||
type: 'message',
|
|
||||||
content: 'Hello server'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// Listen for messages
|
|
||||||
uni.onSocketMessage((res) => {
|
|
||||||
console.log('Received:', res.data)
|
|
||||||
const data = JSON.parse(res.data)
|
|
||||||
// Handle message...
|
|
||||||
})
|
|
||||||
|
|
||||||
// Listen for errors
|
|
||||||
uni.onSocketError((err) => {
|
|
||||||
console.error('WebSocket error:', err)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Listen for close
|
|
||||||
uni.onSocketClose((res) => {
|
|
||||||
console.log('WebSocket closed:', res)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Close connection
|
|
||||||
function closeSocket() {
|
|
||||||
uni.closeSocket({
|
|
||||||
code: 1000,
|
|
||||||
reason: 'User logout',
|
|
||||||
success: () => {
|
|
||||||
console.log('Socket closed successfully')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Socket Task Methods:**
|
|
||||||
- `close(options)` - Close connection
|
|
||||||
- `send(options)` - Send message
|
|
||||||
- `onOpen(callback)` - Connection opened
|
|
||||||
- `onMessage(callback)` - Message received
|
|
||||||
- `onClose(callback)` - Connection closed
|
|
||||||
- `onError(callback)` - Error occurred
|
|
||||||
|
|
||||||
## Request Interceptors
|
|
||||||
|
|
||||||
Use `uni.addInterceptor` to intercept requests.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Add request interceptor
|
|
||||||
uni.addInterceptor('request', {
|
|
||||||
invoke(args) {
|
|
||||||
// Before request
|
|
||||||
console.log('Request:', args)
|
|
||||||
|
|
||||||
// Add auth token
|
|
||||||
args.header = args.header || {}
|
|
||||||
args.header.Authorization = `Bearer ${getToken()}`
|
|
||||||
|
|
||||||
return args
|
|
||||||
},
|
|
||||||
success(res) {
|
|
||||||
// After success
|
|
||||||
console.log('Response:', res)
|
|
||||||
return res
|
|
||||||
},
|
|
||||||
fail(err) {
|
|
||||||
// After failure
|
|
||||||
console.error('Request failed:', err)
|
|
||||||
return err
|
|
||||||
},
|
|
||||||
complete(res) {
|
|
||||||
// Always executed
|
|
||||||
console.log('Request complete')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Remove interceptor
|
|
||||||
uni.removeInterceptor('request')
|
|
||||||
```
|
|
||||||
|
|
||||||
## Domain Configuration
|
|
||||||
|
|
||||||
### Mini-Program
|
|
||||||
|
|
||||||
Configure request domains in mini-program developer console:
|
|
||||||
- `request` domain: For `uni.request`
|
|
||||||
- `uploadFile` domain: For `uni.uploadFile`
|
|
||||||
- `downloadFile` domain: For `uni.downloadFile`
|
|
||||||
- `websocket` domain: For `uni.connectSocket`
|
|
||||||
|
|
||||||
### H5
|
|
||||||
|
|
||||||
H5 uses browser's same-origin policy. Configure CORS on server or use proxy in development.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// vite.config.js proxy configuration
|
|
||||||
export default {
|
|
||||||
server: {
|
|
||||||
proxy: {
|
|
||||||
'/api': {
|
|
||||||
target: 'https://api.example.com',
|
|
||||||
changeOrigin: true,
|
|
||||||
rewrite: (path) => path.replace(/^\/api/, '')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Create a request wrapper
|
|
||||||
const request = (options) => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
uni.request({
|
|
||||||
...options,
|
|
||||||
success: (res) => {
|
|
||||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
||||||
resolve(res.data)
|
|
||||||
} else if (res.statusCode === 401) {
|
|
||||||
// Handle unauthorized
|
|
||||||
uni.redirectTo({ url: '/pages/login/login' })
|
|
||||||
reject(new Error('Unauthorized'))
|
|
||||||
} else {
|
|
||||||
reject(new Error(res.data.message || 'Request failed'))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
fail: reject
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Usage
|
|
||||||
const api = {
|
|
||||||
getUser: () => request({ url: '/user' }),
|
|
||||||
updateUser: (data) => request({ url: '/user', method: 'PUT', data })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/api/request/request.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/api/request/network-file.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/api/request/websocket.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/api/interceptor.md
|
|
||||||
-->
|
|
||||||
@ -1,279 +0,0 @@
|
|||||||
---
|
|
||||||
name: Storage
|
|
||||||
description: Local data storage and caching APIs
|
|
||||||
---
|
|
||||||
|
|
||||||
# Storage
|
|
||||||
|
|
||||||
## Synchronous Storage (Recommended for small data)
|
|
||||||
|
|
||||||
### uni.setStorageSync
|
|
||||||
|
|
||||||
Store data synchronously.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Store simple value
|
|
||||||
uni.setStorageSync('username', 'John')
|
|
||||||
|
|
||||||
// Store object
|
|
||||||
uni.setStorageSync('userInfo', {
|
|
||||||
name: 'John',
|
|
||||||
age: 30,
|
|
||||||
email: 'john@example.com'
|
|
||||||
})
|
|
||||||
|
|
||||||
// Store array
|
|
||||||
uni.setStorageSync('tags', ['vue', 'uniapp', 'javascript'])
|
|
||||||
```
|
|
||||||
|
|
||||||
### uni.getStorageSync
|
|
||||||
|
|
||||||
Retrieve data synchronously.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Get simple value
|
|
||||||
const username = uni.getStorageSync('username')
|
|
||||||
console.log(username) // 'John'
|
|
||||||
|
|
||||||
// Get object
|
|
||||||
const userInfo = uni.getStorageSync('userInfo')
|
|
||||||
console.log(userInfo.name) // 'John'
|
|
||||||
|
|
||||||
// Check if exists
|
|
||||||
const value = uni.getStorageSync('nonexistent')
|
|
||||||
console.log(value) // '' (empty string if not found)
|
|
||||||
```
|
|
||||||
|
|
||||||
### uni.removeStorageSync
|
|
||||||
|
|
||||||
Remove specific key.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.removeStorageSync('username')
|
|
||||||
```
|
|
||||||
|
|
||||||
### uni.clearStorageSync
|
|
||||||
|
|
||||||
Clear all storage.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.clearStorageSync()
|
|
||||||
```
|
|
||||||
|
|
||||||
## Asynchronous Storage (Recommended for large data)
|
|
||||||
|
|
||||||
### uni.setStorage
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.setStorage({
|
|
||||||
key: 'userData',
|
|
||||||
data: {
|
|
||||||
id: 123,
|
|
||||||
preferences: { theme: 'dark', language: 'zh' }
|
|
||||||
},
|
|
||||||
success: () => {
|
|
||||||
console.log('Storage saved')
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
console.error('Save failed:', err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Promise style
|
|
||||||
uni.setStorage({
|
|
||||||
key: 'config',
|
|
||||||
data: { debug: true }
|
|
||||||
}).then(() => {
|
|
||||||
console.log('Config saved')
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### uni.getStorage
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.getStorage({
|
|
||||||
key: 'userData',
|
|
||||||
success: (res) => {
|
|
||||||
console.log('Data:', res.data)
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
console.log('Key not found')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Promise style
|
|
||||||
uni.getStorage({ key: 'userData' })
|
|
||||||
.then(res => console.log(res.data))
|
|
||||||
.catch(() => console.log('Not found'))
|
|
||||||
```
|
|
||||||
|
|
||||||
### uni.removeStorage
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.removeStorage({
|
|
||||||
key: 'tempData',
|
|
||||||
success: () => {
|
|
||||||
console.log('Removed successfully')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### uni.getStorageInfo
|
|
||||||
|
|
||||||
Get storage information.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.getStorageInfo({
|
|
||||||
success: (res) => {
|
|
||||||
console.log('Keys:', res.keys)
|
|
||||||
console.log('Current size:', res.currentSize, 'KB')
|
|
||||||
console.log('Limit size:', res.limitSize, 'KB')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Storage Limits
|
|
||||||
|
|
||||||
| Platform | Limit |
|
|
||||||
|----------|-------|
|
|
||||||
| Mini Program | 10 MB (single) / 200+ MB total |
|
|
||||||
| App | No hard limit (device dependent) |
|
|
||||||
| H5 | ~5-10 MB (browser dependent) |
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
### Data Persistence Helper
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const storage = {
|
|
||||||
// Set with expiration (days)
|
|
||||||
setWithExpiry(key, value, days) {
|
|
||||||
const item = {
|
|
||||||
value,
|
|
||||||
expiry: Date.now() + days * 24 * 60 * 60 * 1000
|
|
||||||
}
|
|
||||||
uni.setStorageSync(key, item)
|
|
||||||
},
|
|
||||||
|
|
||||||
// Get with expiration check
|
|
||||||
getWithExpiry(key) {
|
|
||||||
const item = uni.getStorageSync(key)
|
|
||||||
if (!item) return null
|
|
||||||
|
|
||||||
if (Date.now() > item.expiry) {
|
|
||||||
uni.removeStorageSync(key)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return item.value
|
|
||||||
},
|
|
||||||
|
|
||||||
// Safe get with default
|
|
||||||
get(key, defaultValue = null) {
|
|
||||||
try {
|
|
||||||
const value = uni.getStorageSync(key)
|
|
||||||
return value !== '' ? value : defaultValue
|
|
||||||
} catch (e) {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Batch operations
|
|
||||||
setBatch(data) {
|
|
||||||
Object.entries(data).forEach(([key, value]) => {
|
|
||||||
uni.setStorageSync(key, value)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
uni.clearStorageSync()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Usage
|
|
||||||
storage.setWithExpiry('token', 'abc123', 7) // Expires in 7 days
|
|
||||||
const token = storage.getWithExpiry('token')
|
|
||||||
```
|
|
||||||
|
|
||||||
### User Session Management
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const session = {
|
|
||||||
setToken(token) {
|
|
||||||
uni.setStorageSync('access_token', token)
|
|
||||||
},
|
|
||||||
|
|
||||||
getToken() {
|
|
||||||
return uni.getStorageSync('access_token')
|
|
||||||
},
|
|
||||||
|
|
||||||
clearToken() {
|
|
||||||
uni.removeStorageSync('access_token')
|
|
||||||
},
|
|
||||||
|
|
||||||
setUserInfo(info) {
|
|
||||||
uni.setStorageSync('user_info', info)
|
|
||||||
},
|
|
||||||
|
|
||||||
getUserInfo() {
|
|
||||||
return uni.getStorageSync('user_info')
|
|
||||||
},
|
|
||||||
|
|
||||||
isLoggedIn() {
|
|
||||||
return !!this.getToken()
|
|
||||||
},
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
this.clearToken()
|
|
||||||
uni.removeStorageSync('user_info')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## File Storage (App only)
|
|
||||||
|
|
||||||
### Local File System
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Get file system manager
|
|
||||||
const fs = uni.getFileSystemManager()
|
|
||||||
|
|
||||||
// Write file
|
|
||||||
fs.writeFile({
|
|
||||||
filePath: `${uni.env.USER_DATA_PATH}/data.json`,
|
|
||||||
data: JSON.stringify({ name: 'test' }),
|
|
||||||
encoding: 'utf8',
|
|
||||||
success: () => console.log('File written')
|
|
||||||
})
|
|
||||||
|
|
||||||
// Read file
|
|
||||||
fs.readFile({
|
|
||||||
filePath: `${uni.env.USER_DATA_PATH}/data.json`,
|
|
||||||
encoding: 'utf8',
|
|
||||||
success: (res) => {
|
|
||||||
const data = JSON.parse(res.data)
|
|
||||||
console.log(data)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Check if file exists
|
|
||||||
fs.access({
|
|
||||||
path: `${uni.env.USER_DATA_PATH}/data.json`,
|
|
||||||
success: () => console.log('File exists'),
|
|
||||||
fail: () => console.log('File not found')
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Storage Comparison
|
|
||||||
|
|
||||||
| Method | Data Type | Size Limit | Async | Use Case |
|
|
||||||
|--------|-----------|------------|-------|----------|
|
|
||||||
| StorageSync | Any | ~10MB | No | Small config data |
|
|
||||||
| Storage | Any | ~10MB | Yes | Large data objects |
|
|
||||||
| File System | Binary/Text | Large | Yes | Files, images |
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/api/storage/storage.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/api/file/file.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/api/file/getFileSystemManager.md
|
|
||||||
-->
|
|
||||||
@ -1,300 +0,0 @@
|
|||||||
---
|
|
||||||
name: System Information
|
|
||||||
description: Device info, system info, and environment detection APIs
|
|
||||||
---
|
|
||||||
|
|
||||||
# System Information
|
|
||||||
|
|
||||||
## uni.getSystemInfo / getSystemInfoSync
|
|
||||||
|
|
||||||
Get device and system information.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Synchronous (faster, recommended)
|
|
||||||
const info = uni.getSystemInfoSync()
|
|
||||||
console.log(info)
|
|
||||||
|
|
||||||
// Asynchronous
|
|
||||||
uni.getSystemInfo({
|
|
||||||
success: (res) => {
|
|
||||||
console.log(res)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Promise
|
|
||||||
uni.getSystemInfo().then(res => {
|
|
||||||
console.log(res)
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### System Info Properties
|
|
||||||
|
|
||||||
| Property | Type | Description |
|
|
||||||
|----------|------|-------------|
|
|
||||||
| brand | String | Device brand |
|
|
||||||
| model | String | Device model |
|
|
||||||
| pixelRatio | Number | Device pixel ratio |
|
|
||||||
| screenWidth | Number | Screen width (px) |
|
|
||||||
| screenHeight | Number | Screen height (px) |
|
|
||||||
| windowWidth | Number | Window width (px) |
|
|
||||||
| windowHeight | Number | Window height (px) |
|
|
||||||
| statusBarHeight | Number | Status bar height (px) |
|
|
||||||
| language | String | Language |
|
|
||||||
| system | String | OS version |
|
|
||||||
| version | String | WeChat/Runtime version |
|
|
||||||
| platform | String | Platform: ios/android/windows/mac/devtools |
|
|
||||||
| SDKVersion | String | Client base library version |
|
|
||||||
| appId | String | App ID (DCloud) |
|
|
||||||
| appName | String | App name |
|
|
||||||
| appVersion | String | App version |
|
|
||||||
| appCodeName | String | App code name |
|
|
||||||
| uniPlatform | String | uni-app platform |
|
|
||||||
| uniCompileVersion | String | Compilation version |
|
|
||||||
| uniRuntimeVersion | String | Runtime version |
|
|
||||||
| deviceId | String | Device ID |
|
|
||||||
| deviceBrand | String | Device brand |
|
|
||||||
| deviceModel | String | Device model |
|
|
||||||
| deviceType | String | Device type: phone/pad |
|
|
||||||
| osName | String | OS name |
|
|
||||||
| osVersion | String | OS version |
|
|
||||||
| osLanguage | String | OS language |
|
|
||||||
| osTheme | String | OS theme: light/dark |
|
|
||||||
| batteryLevel | Number | Battery level (0-100) |
|
|
||||||
|
|
||||||
## uni.getAppBaseInfo
|
|
||||||
|
|
||||||
Get app base information.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const info = uni.getAppBaseInfo()
|
|
||||||
// Returns: appId, appName, appVersion, appVersionCode, etc.
|
|
||||||
```
|
|
||||||
|
|
||||||
## uni.getDeviceInfo
|
|
||||||
|
|
||||||
Get device hardware information.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const info = uni.getDeviceInfo()
|
|
||||||
// Returns: brand, model, deviceId, deviceBrand, deviceModel, deviceType
|
|
||||||
```
|
|
||||||
|
|
||||||
## uni.getWindowInfo
|
|
||||||
|
|
||||||
Get window information.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const info = uni.getWindowInfo()
|
|
||||||
// Returns: pixelRatio, screenWidth, screenHeight, windowWidth, windowHeight,
|
|
||||||
// statusBarHeight, safeArea, screenTop
|
|
||||||
```
|
|
||||||
|
|
||||||
## Safe Area
|
|
||||||
|
|
||||||
Handle notched devices and safe areas.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const info = uni.getSystemInfoSync()
|
|
||||||
|
|
||||||
// Safe area info
|
|
||||||
const safeArea = info.safeArea
|
|
||||||
console.log(safeArea) // { top, left, right, bottom, width, height }
|
|
||||||
|
|
||||||
// Check if device has notch (unsafe area at top)
|
|
||||||
const hasNotch = info.safeAreaInsets && info.safeAreaInsets.top > 0
|
|
||||||
```
|
|
||||||
|
|
||||||
### Safe Area CSS Variables (App/H5)
|
|
||||||
|
|
||||||
```css
|
|
||||||
.safe-area-bottom {
|
|
||||||
padding-bottom: constant(safe-area-inset-bottom); /* iOS 11.0 */
|
|
||||||
padding-bottom: env(safe-area-inset-bottom); /* iOS 11.2+ */
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Network Status
|
|
||||||
|
|
||||||
### uni.getNetworkType
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.getNetworkType({
|
|
||||||
success: (res) => {
|
|
||||||
// res.networkType: wifi/2g/3g/4g/5g/unknown/none
|
|
||||||
console.log('Network:', res.networkType)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### uni.onNetworkStatusChange
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Listen for network changes
|
|
||||||
uni.onNetworkStatusChange((res) => {
|
|
||||||
console.log('Network type:', res.networkType)
|
|
||||||
console.log('Is connected:', res.isConnected)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Remove listener
|
|
||||||
uni.offNetworkStatusChange(callback)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Battery Info
|
|
||||||
|
|
||||||
### uni.getBatteryInfo
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.getBatteryInfo({
|
|
||||||
success: (res) => {
|
|
||||||
console.log('Level:', res.level) // 0-100
|
|
||||||
console.log('Is charging:', res.isCharging)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Device Orientation
|
|
||||||
|
|
||||||
### uni.onDeviceMotionChange
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.startDeviceMotionListening({
|
|
||||||
interval: 'normal', // game/ui/normal
|
|
||||||
success: () => {
|
|
||||||
uni.onDeviceMotionChange((res) => {
|
|
||||||
console.log('Alpha:', res.alpha) // 0-360
|
|
||||||
console.log('Beta:', res.beta) // -180 to 180
|
|
||||||
console.log('Gamma:', res.gamma) // -90 to 90
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Stop listening
|
|
||||||
uni.stopDeviceMotionListening()
|
|
||||||
```
|
|
||||||
|
|
||||||
### Screen Orientation
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Get current orientation
|
|
||||||
const info = uni.getSystemInfoSync()
|
|
||||||
const isLandscape = info.screenWidth > info.screenHeight
|
|
||||||
|
|
||||||
// Lock orientation (App only)
|
|
||||||
plus.screen.lockOrientation('portrait-primary')
|
|
||||||
// Options: portrait-primary/portrait-secondary/landscape-primary/landscape-secondary
|
|
||||||
```
|
|
||||||
|
|
||||||
## Screen Brightness
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Set brightness (0-1)
|
|
||||||
uni.setScreenBrightness({
|
|
||||||
value: 0.8
|
|
||||||
})
|
|
||||||
|
|
||||||
// Get brightness
|
|
||||||
uni.getScreenBrightness({
|
|
||||||
success: (res) => {
|
|
||||||
console.log('Brightness:', res.value)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Keep screen on
|
|
||||||
uni.setKeepScreenOn({
|
|
||||||
keepScreenOn: true
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Vibration
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Short vibration (15ms)
|
|
||||||
uni.vibrateShort()
|
|
||||||
|
|
||||||
// Long vibration (400ms)
|
|
||||||
uni.vibrateLong()
|
|
||||||
|
|
||||||
// Pattern vibration (App only)
|
|
||||||
uni.vibrateLong() // or custom pattern
|
|
||||||
```
|
|
||||||
|
|
||||||
## Clipboard
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Set clipboard
|
|
||||||
uni.setClipboardData({
|
|
||||||
data: 'Text to copy',
|
|
||||||
success: () => {
|
|
||||||
uni.showToast({ title: 'Copied' })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Get clipboard
|
|
||||||
uni.getClipboardData({
|
|
||||||
success: (res) => {
|
|
||||||
console.log('Clipboard:', res.data)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Phone Call
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.makePhoneCall({
|
|
||||||
phoneNumber: '13800138000'
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Scan Code
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.scanCode({
|
|
||||||
onlyFromCamera: false, // Allow from album
|
|
||||||
scanType: ['qrCode', 'barCode'], // Types to scan
|
|
||||||
success: (res) => {
|
|
||||||
console.log('Result:', res.result)
|
|
||||||
console.log('Type:', res.scanType)
|
|
||||||
console.log('CharSet:', res.charSet)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Platform Detection
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const info = uni.getSystemInfoSync()
|
|
||||||
|
|
||||||
// Platform checks
|
|
||||||
const isIOS = info.platform === 'ios'
|
|
||||||
const isAndroid = info.platform === 'android'
|
|
||||||
const isWindows = info.platform === 'windows'
|
|
||||||
const isMac = info.platform === 'mac'
|
|
||||||
const isDevtools = info.platform === 'devtools'
|
|
||||||
|
|
||||||
// App platform checks
|
|
||||||
const isApp = info.uniPlatform === 'app'
|
|
||||||
const isH5 = info.uniPlatform === 'web'
|
|
||||||
const isWeixinMP = info.uniPlatform === 'mp-weixin'
|
|
||||||
|
|
||||||
// Safe area calculation
|
|
||||||
const safeAreaTop = info.statusBarHeight + (isApp ? 44 : 0) // 44px for nav bar
|
|
||||||
```
|
|
||||||
|
|
||||||
## Storage Info
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.getStorageInfo({
|
|
||||||
success: (res) => {
|
|
||||||
console.log('Keys:', res.keys)
|
|
||||||
console.log('Current size:', res.currentSize)
|
|
||||||
console.log('Limit size:', res.limitSize)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/api/system/info.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/api/system/network.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/api/system/phone.md
|
|
||||||
-->
|
|
||||||
@ -1,382 +0,0 @@
|
|||||||
---
|
|
||||||
name: UI Feedback
|
|
||||||
description: Toast, modal, loading, and action sheet APIs
|
|
||||||
---
|
|
||||||
|
|
||||||
# UI Feedback
|
|
||||||
|
|
||||||
## Toast Messages
|
|
||||||
|
|
||||||
### uni.showToast
|
|
||||||
|
|
||||||
Display success/error messages.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Success toast
|
|
||||||
uni.showToast({
|
|
||||||
title: 'Success!',
|
|
||||||
icon: 'success',
|
|
||||||
duration: 2000,
|
|
||||||
mask: false
|
|
||||||
})
|
|
||||||
|
|
||||||
// Loading toast
|
|
||||||
uni.showToast({
|
|
||||||
title: 'Loading...',
|
|
||||||
icon: 'loading',
|
|
||||||
duration: 10000
|
|
||||||
})
|
|
||||||
|
|
||||||
// Text only (no icon)
|
|
||||||
uni.showToast({
|
|
||||||
title: 'Please wait',
|
|
||||||
icon: 'none',
|
|
||||||
duration: 2000
|
|
||||||
})
|
|
||||||
|
|
||||||
// Error toast
|
|
||||||
uni.showToast({
|
|
||||||
title: 'Failed!',
|
|
||||||
icon: 'error'
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
|
|
||||||
| Parameter | Type | Default | Description |
|
|
||||||
|-----------|------|---------|-------------|
|
|
||||||
| title | String | Required | Message text |
|
|
||||||
| icon | String | success | success/loading/none/error |
|
|
||||||
| duration | Number | 1500 | Duration in ms |
|
|
||||||
| mask | Boolean | false | Prevent touch during display |
|
|
||||||
| position | String | | top/center/bottom (App) |
|
|
||||||
|
|
||||||
### uni.hideToast
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.hideToast()
|
|
||||||
```
|
|
||||||
|
|
||||||
## Loading
|
|
||||||
|
|
||||||
### uni.showLoading
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.showLoading({
|
|
||||||
title: 'Loading...',
|
|
||||||
mask: true // Prevent interaction
|
|
||||||
})
|
|
||||||
|
|
||||||
// Hide after operation
|
|
||||||
setTimeout(() => {
|
|
||||||
uni.hideLoading()
|
|
||||||
}, 2000)
|
|
||||||
```
|
|
||||||
|
|
||||||
### uni.hideLoading
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.hideLoading()
|
|
||||||
```
|
|
||||||
|
|
||||||
## Modal Dialogs
|
|
||||||
|
|
||||||
### uni.showModal
|
|
||||||
|
|
||||||
Alert and confirm dialogs.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Alert (single button)
|
|
||||||
uni.showModal({
|
|
||||||
title: 'Notice',
|
|
||||||
content: 'Operation completed',
|
|
||||||
showCancel: false
|
|
||||||
})
|
|
||||||
|
|
||||||
// Confirm (two buttons)
|
|
||||||
uni.showModal({
|
|
||||||
title: 'Confirm',
|
|
||||||
content: 'Are you sure?',
|
|
||||||
cancelText: 'Cancel',
|
|
||||||
cancelColor: '#999',
|
|
||||||
confirmText: 'Confirm',
|
|
||||||
confirmColor: '#007AFF',
|
|
||||||
success: (res) => {
|
|
||||||
if (res.confirm) {
|
|
||||||
console.log('User confirmed')
|
|
||||||
} else if (res.cancel) {
|
|
||||||
console.log('User cancelled')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Editable modal
|
|
||||||
uni.showModal({
|
|
||||||
title: 'Input',
|
|
||||||
content: 'Enter your name',
|
|
||||||
editable: true,
|
|
||||||
placeholderText: 'Name',
|
|
||||||
success: (res) => {
|
|
||||||
if (res.confirm) {
|
|
||||||
console.log('Input:', res.content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Action Sheet
|
|
||||||
|
|
||||||
### uni.showActionSheet
|
|
||||||
|
|
||||||
Bottom action menu.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.showActionSheet({
|
|
||||||
itemList: ['Take Photo', 'Choose from Album', 'Cancel'],
|
|
||||||
itemColor: '#000000',
|
|
||||||
success: (res) => {
|
|
||||||
// res.tapIndex: 0, 1, 2...
|
|
||||||
console.log('Selected:', res.tapIndex)
|
|
||||||
switch (res.tapIndex) {
|
|
||||||
case 0:
|
|
||||||
this.takePhoto()
|
|
||||||
break
|
|
||||||
case 1:
|
|
||||||
this.chooseFromAlbum()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
console.log('Cancelled')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Pull to Refresh
|
|
||||||
|
|
||||||
### Enable in pages.json
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"pages": [{
|
|
||||||
"path": "pages/index/index",
|
|
||||||
"style": {
|
|
||||||
"enablePullDownRefresh": true,
|
|
||||||
"backgroundTextStyle": "dark"
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Handle in Page
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
export default {
|
|
||||||
onPullDownRefresh() {
|
|
||||||
console.log('Pull down triggered')
|
|
||||||
this.refreshData().finally(() => {
|
|
||||||
uni.stopPullDownRefresh()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Programmatic Control
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Start pull refresh
|
|
||||||
uni.startPullDownRefresh()
|
|
||||||
|
|
||||||
// Stop pull refresh
|
|
||||||
uni.stopPullDownRefresh()
|
|
||||||
```
|
|
||||||
|
|
||||||
## Navigation Bar Loading
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Show loading in nav bar
|
|
||||||
uni.showNavigationBarLoading()
|
|
||||||
|
|
||||||
// Hide loading
|
|
||||||
uni.hideNavigationBarLoading()
|
|
||||||
```
|
|
||||||
|
|
||||||
## Tab Bar Operations
|
|
||||||
|
|
||||||
### Show/Hide Tab Bar
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Hide tab bar
|
|
||||||
uni.hideTabBar({
|
|
||||||
animation: true
|
|
||||||
})
|
|
||||||
|
|
||||||
// Show tab bar
|
|
||||||
uni.showTabBar({
|
|
||||||
animation: true
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Set Tab Bar Style
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.setTabBarStyle({
|
|
||||||
color: '#999',
|
|
||||||
selectedColor: '#007AFF',
|
|
||||||
backgroundColor: '#fff',
|
|
||||||
borderStyle: 'black'
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Set Tab Bar Item
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.setTabBarItem({
|
|
||||||
index: 0,
|
|
||||||
text: 'Home',
|
|
||||||
iconPath: '/static/home.png',
|
|
||||||
selectedIconPath: '/static/home-active.png'
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Add/Remove Tab Bar Badge
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Show badge
|
|
||||||
uni.showTabBarRedDot({
|
|
||||||
index: 2 // Tab index
|
|
||||||
})
|
|
||||||
|
|
||||||
// Hide badge
|
|
||||||
uni.hideTabBarRedDot({
|
|
||||||
index: 2
|
|
||||||
})
|
|
||||||
|
|
||||||
// Set badge text
|
|
||||||
uni.setTabBarBadge({
|
|
||||||
index: 2,
|
|
||||||
text: '5'
|
|
||||||
})
|
|
||||||
|
|
||||||
// Remove badge text
|
|
||||||
uni.removeTabBarBadge({
|
|
||||||
index: 2
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Preview Image
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.previewImage({
|
|
||||||
current: 'https://example.com/1.jpg', // Current image
|
|
||||||
urls: [
|
|
||||||
'https://example.com/1.jpg',
|
|
||||||
'https://example.com/2.jpg',
|
|
||||||
'https://example.com/3.jpg'
|
|
||||||
],
|
|
||||||
indicator: 'default', // default/number/none
|
|
||||||
loop: false,
|
|
||||||
longPressActions: {
|
|
||||||
itemList: ['Save Image', 'Share'],
|
|
||||||
success: (data) => {
|
|
||||||
console.log('Long press:', data.tapIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Save Image to Photos
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
uni.saveImageToPhotosAlbum({
|
|
||||||
filePath: 'temp://path/to/image.jpg',
|
|
||||||
success: () => {
|
|
||||||
uni.showToast({ title: 'Saved' })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
### Toast Helper
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const toast = {
|
|
||||||
success(message, duration = 2000) {
|
|
||||||
uni.showToast({ title: message, icon: 'success', duration })
|
|
||||||
},
|
|
||||||
error(message, duration = 2000) {
|
|
||||||
uni.showToast({ title: message, icon: 'error', duration })
|
|
||||||
},
|
|
||||||
loading(message = 'Loading...') {
|
|
||||||
uni.showLoading({ title: message, mask: true })
|
|
||||||
},
|
|
||||||
hide() {
|
|
||||||
uni.hideLoading()
|
|
||||||
uni.hideToast()
|
|
||||||
},
|
|
||||||
text(message, duration = 2000) {
|
|
||||||
uni.showToast({ title: message, icon: 'none', duration })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Usage
|
|
||||||
toast.loading()
|
|
||||||
fetchData()
|
|
||||||
.then(() => toast.success('Loaded'))
|
|
||||||
.catch(() => toast.error('Failed'))
|
|
||||||
.finally(() => toast.hide())
|
|
||||||
```
|
|
||||||
|
|
||||||
### Modal Helper
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const modal = {
|
|
||||||
confirm(title, content) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
uni.showModal({
|
|
||||||
title,
|
|
||||||
content,
|
|
||||||
success: (res) => resolve(res.confirm)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
alert(title, content) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
uni.showModal({
|
|
||||||
title,
|
|
||||||
content,
|
|
||||||
showCancel: false,
|
|
||||||
success: () => resolve()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
action(items) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
uni.showActionSheet({
|
|
||||||
itemList: items,
|
|
||||||
success: (res) => resolve(res.tapIndex),
|
|
||||||
fail: reject
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Usage
|
|
||||||
async function deleteItem(id) {
|
|
||||||
const confirmed = await modal.confirm('Delete', 'Are you sure?')
|
|
||||||
if (confirmed) {
|
|
||||||
await api.delete(id)
|
|
||||||
toast.success('Deleted')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/api/ui/prompt.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/api/ui/navigationbar.md
|
|
||||||
- https://gitcode.com/dcloud/unidocs-zh/blob/main/docs/api/ui/tabbar.md
|
|
||||||
-->
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
# Generation Info
|
|
||||||
|
|
||||||
- **Source:** `sources/uni-helper`
|
|
||||||
- **Git SHA:** `286d8656dc077a7d9d6a50e647284ff1ea48ac6b`
|
|
||||||
- **Generated:** 2026-01-30
|
|
||||||
@ -1,88 +0,0 @@
|
|||||||
---
|
|
||||||
name: uni-helper
|
|
||||||
description: Comprehensive skill reference for uni-helper ecosystem - AI-powered development tools for uni-app
|
|
||||||
metadata:
|
|
||||||
author: FlippeDround
|
|
||||||
version: "2026.1.30"
|
|
||||||
source: Generated from https://github.com/uni-helper/website, skills located at https://github.com/uni-helper/ai-tools
|
|
||||||
---
|
|
||||||
|
|
||||||
> The skill is based on uni-helper documentation, generated at 2026-01-30.
|
|
||||||
|
|
||||||
uni-helper is an ecosystem of AI-powered development tools for uni-app, providing Vite plugins, utility libraries, TypeScript support, and development tools to enhance the uni-app development experience.
|
|
||||||
|
|
||||||
## Vite Plugins
|
|
||||||
|
|
||||||
| Topic | Description | Reference |
|
|
||||||
|-------|-------------|-----------|
|
|
||||||
| vite-plugin-uni-pages | File-based routing system for uni-app with auto page discovery | [plugin-pages](references/plugin-pages.md) |
|
|
||||||
| vite-plugin-uni-layouts | Nuxt-like layouts system for uni-app | [plugin-layouts](references/plugin-layouts.md) |
|
|
||||||
| vite-plugin-uni-components | On-demand automatic component imports | [plugin-components](references/plugin-components.md) |
|
|
||||||
| vite-plugin-uni-manifest | Write manifest.json in TypeScript | [plugin-manifest](references/plugin-manifest.md) |
|
|
||||||
| vite-plugin-uni-platform | File-based platform compilation (*.h5|mp-weixin|app.*) | [plugin-platform](references/plugin-platform.md) |
|
|
||||||
| vite-plugin-uni-platform-modifier | Platform modifiers for attributes/directives | [plugin-platform-modifier](references/plugin-platform-modifier.md) |
|
|
||||||
| vite-plugin-uni-middleware | Middleware support for uni-app routing | [plugin-middleware](references/plugin-middleware.md) |
|
|
||||||
|
|
||||||
## Libraries
|
|
||||||
|
|
||||||
| Topic | Description | Reference |
|
|
||||||
|-------|-------------|-----------|
|
|
||||||
| uni-use | VueUse-style composable utilities for uni-app | [lib-uni-use](references/lib-uni-use.md) |
|
|
||||||
| uni-network | Promise-based HTTP client for uni-app | [lib-uni-network](references/lib-uni-network.md) |
|
|
||||||
| uni-promises | Promise wrappers for uni-app APIs | [lib-uni-promises](references/lib-uni-promises.md) |
|
|
||||||
| uni-typed | TypeScript type definitions for uni-app templates | [lib-uni-typed](references/lib-uni-typed.md) |
|
|
||||||
|
|
||||||
## Utilities
|
|
||||||
|
|
||||||
| Topic | Description | Reference |
|
|
||||||
|-------|-------------|-----------|
|
|
||||||
| uni-env | Environment detection utilities for uni-app | [util-uni-env](references/util-uni-env.md) |
|
|
||||||
| unocss-preset-uni | UnoCSS preset for uni-app | [util-unocss-preset](references/util-unocss-preset.md) |
|
|
||||||
|
|
||||||
## Project Starters
|
|
||||||
|
|
||||||
| Topic | Description | Reference |
|
|
||||||
|-------|-------------|-----------|
|
|
||||||
| create-uni | CLI scaffolding tool for uni-app projects | [starter-create-uni](references/starter-create-uni.md) |
|
|
||||||
| vitesse-uni-app | Vite-powered uni-app starter template | [starter-vitesse](references/starter-vitesse.md) |
|
|
||||||
|
|
||||||
## Plugin Order Best Practices
|
|
||||||
|
|
||||||
When using multiple uni-helper Vite plugins, the recommended order is:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite.config.ts
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [
|
|
||||||
UniComponents(), // 1. Component auto-import
|
|
||||||
UniPages(), // 2. File-based routing
|
|
||||||
UniLayouts(), // 3. Layout system
|
|
||||||
UniManifest(), // 4. Manifest generation
|
|
||||||
UniPlatform(), // 5. Platform-specific files
|
|
||||||
UniPlatformModifier(), // 6. Platform modifiers
|
|
||||||
UniMiddleware(), // 7. Route middleware
|
|
||||||
Uni(), // 8. Official uni-app plugin (always last)
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
Create a new uni-app project with create-uni:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# npm 7+, extra double-dash is needed
|
|
||||||
npm create uni@latest
|
|
||||||
|
|
||||||
# pnpm
|
|
||||||
pnpm create uni
|
|
||||||
|
|
||||||
# yarn
|
|
||||||
yarn create uni
|
|
||||||
```
|
|
||||||
|
|
||||||
## Official Resources
|
|
||||||
|
|
||||||
- Website: https://uni-helper.js.org
|
|
||||||
- GitHub: https://github.com/uni-helper
|
|
||||||
- NPM Scope: @uni-helper
|
|
||||||
@ -1,221 +0,0 @@
|
|||||||
---
|
|
||||||
name: uni-network
|
|
||||||
description: Promise-based HTTP client for uni-app - axios-inspired API with uni.request as default transport
|
|
||||||
---
|
|
||||||
|
|
||||||
# uni-network
|
|
||||||
|
|
||||||
Promise-based HTTP client for uni-app, inspired by axios@0.27.2. Uses uni.request/uni.uploadFile/uni.downloadFile as the underlying transport.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm i @uni-helper/uni-network
|
|
||||||
```
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- Promise-based API
|
|
||||||
- Request/response interceptors
|
|
||||||
- Request cancellation
|
|
||||||
- TypeScript support
|
|
||||||
- Composition API integration
|
|
||||||
- Upload/download support
|
|
||||||
|
|
||||||
## Basic Usage
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { un } from '@uni-helper/uni-network'
|
|
||||||
|
|
||||||
// GET request
|
|
||||||
un('/user/123')
|
|
||||||
|
|
||||||
// POST request
|
|
||||||
un({
|
|
||||||
method: 'POST',
|
|
||||||
url: '/user/123',
|
|
||||||
data: {
|
|
||||||
firstName: 'Fred',
|
|
||||||
lastName: 'Flintstone',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Request Aliases
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { un } from '@uni-helper/uni-network'
|
|
||||||
|
|
||||||
// HTTP verbs
|
|
||||||
un.get('/users')
|
|
||||||
un.post('/users', { name: 'John' })
|
|
||||||
un.put('/users/1', { name: 'Jane' })
|
|
||||||
un.patch('/users/1', { age: 30 })
|
|
||||||
un.delete('/users/1')
|
|
||||||
|
|
||||||
// Upload/download
|
|
||||||
un.upload({
|
|
||||||
url: '/upload',
|
|
||||||
filePath: tempFilePath,
|
|
||||||
name: 'file',
|
|
||||||
})
|
|
||||||
|
|
||||||
un.download({
|
|
||||||
url: '/files/report.pdf',
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Creating an Instance
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { createUniNetwork } from '@uni-helper/uni-network'
|
|
||||||
|
|
||||||
const http = createUniNetwork({
|
|
||||||
baseURL: 'https://api.example.com',
|
|
||||||
timeout: 10000,
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// Use the instance
|
|
||||||
http.get('/users')
|
|
||||||
```
|
|
||||||
|
|
||||||
## Interceptors
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { un } from '@uni-helper/uni-network'
|
|
||||||
|
|
||||||
// Request interceptor
|
|
||||||
un.interceptors.request.use(
|
|
||||||
(config) => {
|
|
||||||
// Add auth token
|
|
||||||
const token = uni.getStorageSync('token')
|
|
||||||
if (token) {
|
|
||||||
config.headers = config.headers || {}
|
|
||||||
config.headers.Authorization = `Bearer ${token}`
|
|
||||||
}
|
|
||||||
return config
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
return Promise.reject(error)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Response interceptor
|
|
||||||
un.interceptors.response.use(
|
|
||||||
(response) => {
|
|
||||||
// Transform response data
|
|
||||||
return response.data
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
// Handle errors globally
|
|
||||||
if (error.statusCode === 401) {
|
|
||||||
uni.redirectTo({ url: '/pages/login/index' })
|
|
||||||
}
|
|
||||||
return Promise.reject(error)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
```ts
|
|
||||||
interface UniNetworkConfig {
|
|
||||||
// URL
|
|
||||||
url?: string
|
|
||||||
// Base URL
|
|
||||||
baseURL?: string
|
|
||||||
// Method
|
|
||||||
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | ...
|
|
||||||
// Headers
|
|
||||||
headers?: Record<string, string>
|
|
||||||
// Request data
|
|
||||||
data?: any
|
|
||||||
// Query params
|
|
||||||
params?: Record<string, any>
|
|
||||||
// Timeout in ms
|
|
||||||
timeout?: number
|
|
||||||
// Response type
|
|
||||||
responseType?: 'text' | 'arraybuffer'
|
|
||||||
// Data type
|
|
||||||
dataType?: 'json' | 'string'
|
|
||||||
// Enable http2
|
|
||||||
enableHttp2?: boolean
|
|
||||||
// Enable quic
|
|
||||||
enableQuic?: boolean
|
|
||||||
// Enable cache
|
|
||||||
enableCache?: boolean
|
|
||||||
// SSL verify
|
|
||||||
sslVerify?: boolean
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Request Cancellation
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { un } from '@uni-helper/uni-network'
|
|
||||||
|
|
||||||
const controller = new AbortController()
|
|
||||||
|
|
||||||
un.get('/long-request', {
|
|
||||||
signal: controller.signal,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Cancel after 5 seconds
|
|
||||||
setTimeout(() => {
|
|
||||||
controller.abort()
|
|
||||||
}, 5000)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { un, UniNetworkError } from '@uni-helper/uni-network'
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await un.get('/users')
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof UniNetworkError) {
|
|
||||||
console.log('Request failed:', error.message)
|
|
||||||
console.log('Status:', error.statusCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Composition API
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { useUniNetwork } from '@uni-helper/uni-network'
|
|
||||||
|
|
||||||
const { data, error, loading, execute } = useUniNetwork({
|
|
||||||
url: '/users',
|
|
||||||
method: 'GET',
|
|
||||||
})
|
|
||||||
|
|
||||||
// Execute manually
|
|
||||||
await execute()
|
|
||||||
```
|
|
||||||
|
|
||||||
## TypeScript Support
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { un } from '@uni-helper/uni-network'
|
|
||||||
|
|
||||||
interface User {
|
|
||||||
id: number
|
|
||||||
name: string
|
|
||||||
email: string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Typed response
|
|
||||||
const { data } = await un.get<User>('/users/1')
|
|
||||||
console.log(data.name) // Type-safe access
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://github.com/uni-helper/uni-network
|
|
||||||
- https://uni-helper.js.org/uni-network
|
|
||||||
-->
|
|
||||||
@ -1,197 +0,0 @@
|
|||||||
---
|
|
||||||
name: uni-promises
|
|
||||||
description: Promise wrappers for uni-app APIs - convert callback-based APIs to Promise-based
|
|
||||||
---
|
|
||||||
|
|
||||||
# uni-promises
|
|
||||||
|
|
||||||
Promise wrappers for uni-app's callback-based APIs. Convert uni-app APIs to return Promises for better async/await support.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm i @uni-helper/uni-promises
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
Instead of using callbacks:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Original callback style
|
|
||||||
uni.request({
|
|
||||||
url: 'https://api.example.com',
|
|
||||||
success: (res) => {
|
|
||||||
console.log(res.data)
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
console.error(err)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Use Promises:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { request } from '@uni-helper/uni-promises'
|
|
||||||
|
|
||||||
// Promise style
|
|
||||||
const res = await request({
|
|
||||||
url: 'https://api.example.com',
|
|
||||||
})
|
|
||||||
console.log(res.data)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Available APIs
|
|
||||||
|
|
||||||
Most uni-app APIs are wrapped:
|
|
||||||
|
|
||||||
### Network
|
|
||||||
- `request` - HTTP request
|
|
||||||
- `uploadFile` - File upload
|
|
||||||
- `downloadFile` - File download
|
|
||||||
- `connectSocket` - WebSocket
|
|
||||||
|
|
||||||
### Storage
|
|
||||||
- `setStorage` - Set storage
|
|
||||||
- `getStorage` - Get storage
|
|
||||||
- `removeStorage` - Remove storage
|
|
||||||
- `clearStorage` - Clear all storage
|
|
||||||
- `getStorageInfo` - Storage info
|
|
||||||
|
|
||||||
### Media
|
|
||||||
- `chooseImage` - Select images
|
|
||||||
- `chooseVideo` - Select video
|
|
||||||
- `chooseMedia` - Select media
|
|
||||||
- `previewImage` - Preview images
|
|
||||||
- `saveImageToPhotosAlbum` - Save image
|
|
||||||
- `compressImage` - Compress image
|
|
||||||
|
|
||||||
### File
|
|
||||||
- `saveFile` - Save file
|
|
||||||
- `getFileInfo` - File info
|
|
||||||
- `getSavedFileList` - Saved files list
|
|
||||||
- `removeSavedFile` - Remove saved file
|
|
||||||
- `openDocument` - Open document
|
|
||||||
|
|
||||||
### Location
|
|
||||||
- `getLocation` - Get location
|
|
||||||
- `chooseLocation` - Choose location
|
|
||||||
- `openLocation` - Open location
|
|
||||||
|
|
||||||
### Device
|
|
||||||
- `getSystemInfo` - System info
|
|
||||||
- `getNetworkType` - Network type
|
|
||||||
- `scanCode` - QR/barcode scan
|
|
||||||
- `setClipboardData` - Copy to clipboard
|
|
||||||
- `getClipboardData` - Get clipboard
|
|
||||||
|
|
||||||
### UI
|
|
||||||
- `showToast` - Show toast
|
|
||||||
- `showModal` - Show modal
|
|
||||||
- `showActionSheet` - Show action sheet
|
|
||||||
- `showLoading` - Show loading
|
|
||||||
- `hideLoading` - Hide loading
|
|
||||||
- `showNavigationBarLoading` - Nav loading
|
|
||||||
- `hideNavigationBarLoading` - Hide nav loading
|
|
||||||
- `setNavigationBarTitle` - Set nav title
|
|
||||||
- `setNavigationBarColor` - Set nav color
|
|
||||||
- `pageScrollTo` - Scroll to position
|
|
||||||
|
|
||||||
### Navigation
|
|
||||||
- `navigateTo` - Navigate to page
|
|
||||||
- `redirectTo` - Redirect to page
|
|
||||||
- `reLaunch` - Re-launch app
|
|
||||||
- `switchTab` - Switch tab
|
|
||||||
- `navigateBack` - Go back
|
|
||||||
|
|
||||||
### Login
|
|
||||||
- `login` - User login
|
|
||||||
- `checkSession` - Check session
|
|
||||||
- `getUserProfile` - Get user profile
|
|
||||||
- `getUserInfo` - Get user info
|
|
||||||
|
|
||||||
### Payment
|
|
||||||
- `requestPayment` - Request payment
|
|
||||||
|
|
||||||
## Example Usage
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import {
|
|
||||||
showModal,
|
|
||||||
showToast,
|
|
||||||
getStorage,
|
|
||||||
navigateTo,
|
|
||||||
getSystemInfo,
|
|
||||||
chooseImage,
|
|
||||||
uploadFile,
|
|
||||||
} from '@uni-helper/uni-promises'
|
|
||||||
|
|
||||||
async function handleUserAction() {
|
|
||||||
// Show confirmation
|
|
||||||
const { confirm } = await showModal({
|
|
||||||
title: 'Confirm',
|
|
||||||
content: 'Are you sure?',
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!confirm) return
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Get cached data
|
|
||||||
const { data: token } = await getStorage({ key: 'token' })
|
|
||||||
|
|
||||||
// Get system info
|
|
||||||
const systemInfo = await getSystemInfo()
|
|
||||||
console.log('Platform:', systemInfo.platform)
|
|
||||||
|
|
||||||
// Select image
|
|
||||||
const { tempFilePaths } = await chooseImage({
|
|
||||||
count: 1,
|
|
||||||
sizeType: ['compressed'],
|
|
||||||
})
|
|
||||||
|
|
||||||
// Upload file
|
|
||||||
const uploadRes = await uploadFile({
|
|
||||||
url: 'https://api.example.com/upload',
|
|
||||||
filePath: tempFilePaths[0],
|
|
||||||
name: 'file',
|
|
||||||
})
|
|
||||||
|
|
||||||
await showToast({
|
|
||||||
title: 'Success!',
|
|
||||||
icon: 'success',
|
|
||||||
})
|
|
||||||
|
|
||||||
// Navigate
|
|
||||||
await navigateTo({
|
|
||||||
url: '/pages/success/index',
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
await showToast({
|
|
||||||
title: error.message || 'Failed',
|
|
||||||
icon: 'error',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
All wrapped APIs reject with standard Error objects:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { request } from '@uni-helper/uni-promises'
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await request({ url: 'https://api.example.com' })
|
|
||||||
} catch (error) {
|
|
||||||
// error.message contains the fail callback message
|
|
||||||
console.error('Request failed:', error.message)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://github.com/uni-helper/uni-promises
|
|
||||||
- https://uni-helper.js.org/uni-promises
|
|
||||||
-->
|
|
||||||
@ -1,200 +0,0 @@
|
|||||||
---
|
|
||||||
name: uni-typed
|
|
||||||
description: TypeScript type definitions for uni-app templates - complete type safety for uni-app components
|
|
||||||
---
|
|
||||||
|
|
||||||
# uni-typed
|
|
||||||
|
|
||||||
TypeScript type definitions for uni-app template sections. Provides complete type safety and IntelliSense for uni-app components.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# For uni-app
|
|
||||||
npm i -D @uni-helper/uni-app-types
|
|
||||||
|
|
||||||
# For uni-ui
|
|
||||||
npm i -D @uni-helper/uni-ui-types
|
|
||||||
|
|
||||||
# For uni-cloud
|
|
||||||
npm i -D @uni-helper/uni-cloud-types
|
|
||||||
```
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
### Uni-app Types
|
|
||||||
|
|
||||||
```json
|
|
||||||
// tsconfig.json
|
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"types": ["@uni-helper/uni-app-types"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Uni-ui Types
|
|
||||||
|
|
||||||
```json
|
|
||||||
// tsconfig.json
|
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"types": [
|
|
||||||
"@uni-helper/uni-app-types",
|
|
||||||
"@uni-helper/uni-ui-types"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- Type-safe template event handlers
|
|
||||||
- Component prop type checking
|
|
||||||
- Custom event type definitions
|
|
||||||
- Ref type inference for components
|
|
||||||
|
|
||||||
## Component Types
|
|
||||||
|
|
||||||
### Template Type Safety
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
const inputValue = ref('')
|
|
||||||
const count = ref(0)
|
|
||||||
|
|
||||||
const handleInput = (e: UniHelper.InputOnInputEvent) => {
|
|
||||||
inputValue.value = e.detail.value
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleChange = (e: UniHelper.SwitchOnChangeEvent) => {
|
|
||||||
console.log(e.detail.value)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<!-- Type-safe component usage -->
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
:value="inputValue"
|
|
||||||
@input="handleInput"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<switch :checked="true" @change="handleChange" />
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Component Ref Types
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, onMounted } from 'vue'
|
|
||||||
|
|
||||||
// Type-safe component refs
|
|
||||||
const videoRef = ref<UniHelper.VideoInstance>()
|
|
||||||
const mapRef = ref<UniHelper.MapInstance>()
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
// IntelliSense for component methods
|
|
||||||
videoRef.value?.play()
|
|
||||||
videoRef.value?.pause()
|
|
||||||
videoRef.value?.seek(100)
|
|
||||||
|
|
||||||
mapRef.value?.moveToLocation({
|
|
||||||
latitude: 39.9,
|
|
||||||
longitude: 116.4,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<video ref="videoRef" src="..." />
|
|
||||||
<map ref="mapRef" />
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Event Types
|
|
||||||
|
|
||||||
### Common Event Types
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Input events
|
|
||||||
UniHelper.InputOnInputEvent
|
|
||||||
UniHelper.InputOnBlurEvent
|
|
||||||
UniHelper.InputOnFocusEvent
|
|
||||||
|
|
||||||
// Form events
|
|
||||||
UniHelper.SwitchOnChangeEvent
|
|
||||||
UniHelper.CheckboxOnChangeEvent
|
|
||||||
UniHelper.RadioOnChangeEvent
|
|
||||||
UniHelper.PickerOnChangeEvent
|
|
||||||
UniHelper.SliderOnChangeEvent
|
|
||||||
|
|
||||||
// Media events
|
|
||||||
UniHelper.VideoOnPlayEvent
|
|
||||||
UniHelper.VideoOnPauseEvent
|
|
||||||
UniHelper.VideoOnEndedEvent
|
|
||||||
UniHelper.VideoOnTimeUpdateEvent
|
|
||||||
|
|
||||||
// Touch events
|
|
||||||
UniHelper.ViewOnTouchstartEvent
|
|
||||||
UniHelper.ViewOnTouchmoveEvent
|
|
||||||
UniHelper.ViewOnTouchendEvent
|
|
||||||
UniHelper.ViewOnTapEvent
|
|
||||||
UniHelper.ViewOnLongpressEvent
|
|
||||||
|
|
||||||
// Scroll events
|
|
||||||
UniHelper.ScrollViewOnScrollEvent
|
|
||||||
UniHelper.ScrollViewOnScrolltolowerEvent
|
|
||||||
UniHelper.ScrollViewOnScrolltoupperEvent
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom Events
|
|
||||||
|
|
||||||
Define custom event types for your components:
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup lang="ts">
|
|
||||||
// Child component
|
|
||||||
const emit = defineEmits<{
|
|
||||||
change: [value: string]
|
|
||||||
submit: [data: { id: number; name: string }]
|
|
||||||
}>()
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup lang="ts">
|
|
||||||
// Parent component
|
|
||||||
import type { MyComponentOnChangeEvent } from './types'
|
|
||||||
|
|
||||||
const handleChange = (e: MyComponentOnChangeEvent) => {
|
|
||||||
console.log(e.detail.value)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<my-component @change="handleChange" />
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Why uni-typed?
|
|
||||||
|
|
||||||
DCloud's `@dcloudio/types` only provides TypeScript support for script sections. uni-typed fills the gap for template section type safety, providing:
|
|
||||||
|
|
||||||
- Component prop validation
|
|
||||||
- Event handler type checking
|
|
||||||
- Method IntelliSense on component refs
|
|
||||||
- Better IDE support (VS Code, WebStorm)
|
|
||||||
|
|
||||||
## Migration from v0 to v1
|
|
||||||
|
|
||||||
See the [migration guide](https://uni-helper.js.org/uni-typed/other/migrate-v0-to-v1) for upgrading instructions.
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://github.com/uni-helper/uni-typed
|
|
||||||
- https://uni-helper.js.org/uni-typed
|
|
||||||
-->
|
|
||||||
@ -1,185 +0,0 @@
|
|||||||
---
|
|
||||||
name: uni-use
|
|
||||||
description: VueUse-style composable utilities for uni-app development
|
|
||||||
---
|
|
||||||
|
|
||||||
# uni-use
|
|
||||||
|
|
||||||
VueUse-style composable utilities specifically designed for uni-app. Provides reactive wrappers around uni-app APIs.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm i @uni-helper/uni-use @vueuse/core@9
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { tryOnLoad, useClipboard, useSystemInfo } from '@uni-helper/uni-use'
|
|
||||||
|
|
||||||
tryOnLoad(() => {
|
|
||||||
console.log('Page loaded')
|
|
||||||
})
|
|
||||||
|
|
||||||
const { copy, text } = useClipboard()
|
|
||||||
const { systemInfo } = useSystemInfo()
|
|
||||||
```
|
|
||||||
|
|
||||||
## Auto Import Setup
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite.config.ts
|
|
||||||
import { uniuseAutoImports } from '@uni-helper/uni-use'
|
|
||||||
import autoImport from 'unplugin-auto-import/vite'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [
|
|
||||||
autoImport({
|
|
||||||
imports: [uniuseAutoImports()],
|
|
||||||
}),
|
|
||||||
uni(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Available Composables
|
|
||||||
|
|
||||||
### Lifecycle
|
|
||||||
|
|
||||||
| Composable | Description |
|
|
||||||
|------------|-------------|
|
|
||||||
| `tryOnLoad` | onLoad with try-catch |
|
|
||||||
| `tryOnShow` | onShow with try-catch |
|
|
||||||
| `tryOnHide` | onHide with try-catch |
|
|
||||||
| `tryOnReady` | onReady with try-catch |
|
|
||||||
| `tryOnUnload` | onUnload with try-catch |
|
|
||||||
| `tryOnPullDownRefresh` | onPullDownRefresh with try-catch |
|
|
||||||
| `tryOnReachBottom` | onReachBottom with try-catch |
|
|
||||||
| `tryOnPageScroll` | onPageScroll with try-catch |
|
|
||||||
| `tryOnTabItemTap` | onTabItemTap with try-catch |
|
|
||||||
|
|
||||||
### System
|
|
||||||
|
|
||||||
| Composable | Description |
|
|
||||||
|------------|-------------|
|
|
||||||
| `useSystemInfo` | Reactive system information |
|
|
||||||
| `useDeviceInfo` | Device information |
|
|
||||||
| `useWindowInfo` | Window dimensions |
|
|
||||||
| `useAppTheme` | App theme (dark/light) |
|
|
||||||
| `useNetworkStatus` | Network connection status |
|
|
||||||
| `useBattery` | Battery status |
|
|
||||||
|
|
||||||
### Storage
|
|
||||||
|
|
||||||
| Composable | Description |
|
|
||||||
|------------|-------------|
|
|
||||||
| `useStorage` | Reactive local storage |
|
|
||||||
| `useStorageAsync` | Async storage operations |
|
|
||||||
|
|
||||||
### UI
|
|
||||||
|
|
||||||
| Composable | Description |
|
|
||||||
|------------|-------------|
|
|
||||||
| `useClipboard` | Clipboard operations |
|
|
||||||
| `useToast` | Toast notifications |
|
|
||||||
| `useModal` | Modal dialogs |
|
|
||||||
| `useActionSheet` | Action sheet |
|
|
||||||
| `useLoading` | Loading indicator |
|
|
||||||
| `useNavigationBar` | Navigation bar control |
|
|
||||||
| `usePullDownRefresh` | Pull-to-refresh |
|
|
||||||
|
|
||||||
### Location
|
|
||||||
|
|
||||||
| Composable | Description |
|
|
||||||
|------------|-------------|
|
|
||||||
| `useLocation` | Current location |
|
|
||||||
| `useChooseLocation` | Location picker |
|
|
||||||
|
|
||||||
### Media
|
|
||||||
|
|
||||||
| Composable | Description |
|
|
||||||
|------------|-------------|
|
|
||||||
| `useImage` | Image operations |
|
|
||||||
| `useCamera` | Camera access |
|
|
||||||
| `useRecorder` | Audio recording |
|
|
||||||
|
|
||||||
## Example Usage
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { useClipboard, useSystemInfo, useStorage, tryOnLoad } from '@uni-helper/uni-use'
|
|
||||||
|
|
||||||
// System info
|
|
||||||
const { systemInfo, safeArea } = useSystemInfo()
|
|
||||||
|
|
||||||
// Clipboard
|
|
||||||
const { text, copy, isSupported } = useClipboard()
|
|
||||||
const handleCopy = async () => {
|
|
||||||
await copy('Hello uni-app')
|
|
||||||
uni.showToast({ title: 'Copied!' })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Storage
|
|
||||||
const userToken = useStorage('token', '')
|
|
||||||
const saveToken = (token: string) => {
|
|
||||||
userToken.value = token
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lifecycle
|
|
||||||
tryOnLoad((options) => {
|
|
||||||
console.log('Page options:', options)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="page">
|
|
||||||
<text>Platform: {{ systemInfo.platform }}</text>
|
|
||||||
<text>Safe area top: {{ safeArea?.top }}px</text>
|
|
||||||
<button @click="handleCopy">Copy to clipboard</button>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Build Configuration
|
|
||||||
|
|
||||||
For Vite + Vue3 projects targeting ES6:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite.config.ts
|
|
||||||
export default defineConfig({
|
|
||||||
build: {
|
|
||||||
target: 'es6',
|
|
||||||
cssTarget: 'chrome61',
|
|
||||||
},
|
|
||||||
optimizeDeps: {
|
|
||||||
exclude: ['vue-demi'],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Add polyfills in `main.ts` if needed:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// main.ts
|
|
||||||
import 'core-js/actual/array/iterator'
|
|
||||||
import 'core-js/actual/promise'
|
|
||||||
import 'core-js/actual/object/assign'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Limitations
|
|
||||||
|
|
||||||
- Mini Program and App environments lack some globals (window, navigator)
|
|
||||||
- Cannot use top-level await
|
|
||||||
- Some VueUse features may not work in uni-app
|
|
||||||
|
|
||||||
## See Also
|
|
||||||
|
|
||||||
- Full API docs: https://uni-helper.js.org/uni-use/apis
|
|
||||||
- VueUse: https://vueuse.org/
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://github.com/uni-helper/uni-use
|
|
||||||
- https://uni-helper.js.org/uni-use
|
|
||||||
-->
|
|
||||||
@ -1,93 +0,0 @@
|
|||||||
---
|
|
||||||
name: vite-plugin-uni-components
|
|
||||||
description: On-demand automatic component imports for uni-app - derived from unplugin-vue-components
|
|
||||||
---
|
|
||||||
|
|
||||||
# vite-plugin-uni-components
|
|
||||||
|
|
||||||
Automatic on-demand component imports for uni-app. Derived from unplugin-vue-components with uni-app specific adaptations.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm i -D @uni-helper/vite-plugin-uni-components
|
|
||||||
```
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite.config.ts
|
|
||||||
import Uni from '@dcloudio/vite-plugin-uni'
|
|
||||||
import Components from '@uni-helper/vite-plugin-uni-components'
|
|
||||||
import { defineConfig } from 'vite'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [
|
|
||||||
Components(), // Must be before Uni()
|
|
||||||
Uni(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- Automatic component imports from `src/components`
|
|
||||||
- Supports UI library resolvers
|
|
||||||
- Generates TypeScript declarations
|
|
||||||
- Handles `default` export for third-party UI libraries
|
|
||||||
|
|
||||||
## Supported UI Libraries
|
|
||||||
|
|
||||||
Built-in resolvers for:
|
|
||||||
|
|
||||||
| Library | Import |
|
|
||||||
|---------|--------|
|
|
||||||
| Ano UI | `AnoUIResolver()` |
|
|
||||||
| uni-ui | `UniUIResolver()` |
|
|
||||||
| wot-design-uni | `WotDesignUniResolver()` |
|
|
||||||
| uv-uni | `UvUniResolver()` |
|
|
||||||
| uview-pro | `UviewProResolver()` |
|
|
||||||
| z-paging | `ZPagingResolver()` |
|
|
||||||
|
|
||||||
## TypeScript Declarations
|
|
||||||
|
|
||||||
For pnpm users, create `.npmrc`:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
shamefully-hoist = true
|
|
||||||
```
|
|
||||||
|
|
||||||
Generated `components.d.ts` example:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
declare module 'vue' {
|
|
||||||
export interface GlobalComponents {
|
|
||||||
AButton: typeof import('ano-ui/components/AButton/AButton.vue')['default']
|
|
||||||
UniCalendar: typeof import('@dcloudio/uni-ui/lib/uni-calendar/uni-calendar.vue')['default']
|
|
||||||
MyComponent: typeof import('./src/components/MyComponent.vue')['default']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Third-Party Component Handling
|
|
||||||
|
|
||||||
For third-party components like `@dcloudio/uni-ui` and `ano-ui`, the plugin automatically adds the `default` property to handle H5 compatibility issues:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
declare module 'vue' {
|
|
||||||
export interface GlobalComponents {
|
|
||||||
- AButton: typeof import('ano-ui/components/AButton/AButton.vue')['AButton']
|
|
||||||
+ AButton: typeof import('ano-ui/components/AButton/AButton.vue')['default']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Full Configuration
|
|
||||||
|
|
||||||
See [unplugin-vue-components documentation](https://github.com/antfu/unplugin-vue-components) for all options.
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://github.com/uni-helper/vite-plugin-uni-components
|
|
||||||
- https://uni-helper.js.org/vite-plugin-uni-components
|
|
||||||
-->
|
|
||||||
@ -1,143 +0,0 @@
|
|||||||
---
|
|
||||||
name: vite-plugin-uni-layouts
|
|
||||||
description: Nuxt-like layouts system for Vite-powered uni-app projects
|
|
||||||
---
|
|
||||||
|
|
||||||
# vite-plugin-uni-layouts
|
|
||||||
|
|
||||||
Provides a Nuxt-like layout system for uni-app. Wraps pages with reusable layout components.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm i -D @uni-helper/vite-plugin-uni-layouts
|
|
||||||
```
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite.config.ts
|
|
||||||
import uni from '@dcloudio/vite-plugin-uni'
|
|
||||||
import UniLayouts from '@uni-helper/vite-plugin-uni-layouts'
|
|
||||||
import { defineConfig } from 'vite'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [UniLayouts(), uni()],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Creating Layouts
|
|
||||||
|
|
||||||
Create layout files in `src/layouts/` directory:
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<!-- src/layouts/default.vue -->
|
|
||||||
<template>
|
|
||||||
<view class="layout">
|
|
||||||
<slot name="header" />
|
|
||||||
<slot>main content</slot>
|
|
||||||
<slot name="footer" />
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Applying Layouts
|
|
||||||
|
|
||||||
### Via pages.json (traditional)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"pages": [{
|
|
||||||
"path": "pages/index/index",
|
|
||||||
"layout": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "pages/about/index",
|
|
||||||
"layout": false // Disable layout
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Via definePage (recommended)
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
definePage({
|
|
||||||
layout: 'default',
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Dynamic Layouts
|
|
||||||
|
|
||||||
Use the `<uni-layout>` component for dynamic layout switching:
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
const layoutName = ref('default')
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<!-- Disable page layout first -->
|
|
||||||
<uni-layout :name="layoutName">
|
|
||||||
<template #header>
|
|
||||||
<text>Header content</text>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
Main content here
|
|
||||||
|
|
||||||
<template #footer>
|
|
||||||
<text>Footer content</text>
|
|
||||||
</template>
|
|
||||||
</uni-layout>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Accessing Layout Ref
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
const uniLayout = ref()
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
console.log(uniLayout.value) // Access layout component
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<uni-layout ref="uniLayout">
|
|
||||||
Content
|
|
||||||
</uni-layout>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
Options API:
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
onLoad() {
|
|
||||||
console.log(this.$refs.uniLayout)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
The plugin does two things:
|
|
||||||
|
|
||||||
1. Auto-scans and globally registers components from `layouts/` directory
|
|
||||||
2. Wraps pages with the specified layout component
|
|
||||||
|
|
||||||
> **Note:** WeChat Mini Program limitation - If using `web-view` component, layout wrapping may not work properly.
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
See [type definitions](https://github.com/uni-helper/vite-plugin-uni-layouts/blob/main/src/types.ts) for all options.
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://github.com/uni-helper/vite-plugin-uni-layouts
|
|
||||||
- https://uni-helper.js.org/vite-plugin-uni-layouts
|
|
||||||
-->
|
|
||||||
@ -1,103 +0,0 @@
|
|||||||
---
|
|
||||||
name: vite-plugin-uni-manifest
|
|
||||||
description: Write uni-app manifest.json in TypeScript with type safety and IntelliSense
|
|
||||||
---
|
|
||||||
|
|
||||||
# vite-plugin-uni-manifest
|
|
||||||
|
|
||||||
Write your uni-app `manifest.json` configuration in TypeScript with full type safety and IntelliSense support.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm i -D @uni-helper/vite-plugin-uni-manifest
|
|
||||||
```
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite.config.ts
|
|
||||||
import Uni from '@dcloudio/vite-plugin-uni'
|
|
||||||
import UniManifest from '@uni-helper/vite-plugin-uni-manifest'
|
|
||||||
import { defineConfig } from 'vite'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [UniManifest(), Uni()],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration File
|
|
||||||
|
|
||||||
Create `manifest.config.ts` (or .js/.json):
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// manifest.config.ts
|
|
||||||
import { defineManifestConfig } from '@uni-helper/vite-plugin-uni-manifest'
|
|
||||||
|
|
||||||
export default defineManifestConfig({
|
|
||||||
name: 'my-uni-app',
|
|
||||||
appid: '__UNI__XXXXXXX',
|
|
||||||
description: 'My awesome uni-app project',
|
|
||||||
versionName: '1.0.0',
|
|
||||||
versionCode: '100',
|
|
||||||
transformPx: false,
|
|
||||||
app: {
|
|
||||||
// App-specific config
|
|
||||||
},
|
|
||||||
mp-weixin: {
|
|
||||||
// WeChat Mini Program config
|
|
||||||
},
|
|
||||||
mp-alipay: {
|
|
||||||
// Alipay Mini Program config
|
|
||||||
},
|
|
||||||
h5: {
|
|
||||||
// H5 config
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Benefits
|
|
||||||
|
|
||||||
- **Type Safety**: Full TypeScript intellisense for all manifest properties
|
|
||||||
- **Code Reuse**: Import and reuse configuration values
|
|
||||||
- **Comments**: Add comments to explain configuration choices
|
|
||||||
- **Environment-based Config**: Use conditionals for different environments
|
|
||||||
|
|
||||||
## Example with Dynamic Config
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineManifestConfig } from '@uni-helper/vite-plugin-uni-manifest'
|
|
||||||
import { version } from './package.json'
|
|
||||||
|
|
||||||
export default defineManifestConfig({
|
|
||||||
name: 'My App',
|
|
||||||
versionName: version,
|
|
||||||
versionCode: version.replace(/\./g, ''),
|
|
||||||
|
|
||||||
// Platform-specific config
|
|
||||||
'mp-weixin': {
|
|
||||||
appid: process.env.WX_APPID || '',
|
|
||||||
setting: {
|
|
||||||
es6: true,
|
|
||||||
enhance: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
h5: {
|
|
||||||
title: 'My H5 App',
|
|
||||||
router: {
|
|
||||||
mode: 'hash',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration Options
|
|
||||||
|
|
||||||
See [types.ts](https://github.com/uni-helper/vite-plugin-uni-manifest/tree/main/packages/core/src/types.ts) for all available options.
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://github.com/uni-helper/vite-plugin-uni-manifest
|
|
||||||
- https://uni-helper.js.org/vite-plugin-uni-manifest
|
|
||||||
-->
|
|
||||||
@ -1,181 +0,0 @@
|
|||||||
---
|
|
||||||
name: vite-plugin-uni-middleware
|
|
||||||
description: Route middleware support for uni-app - implement guards, auth, and page lifecycle hooks
|
|
||||||
---
|
|
||||||
|
|
||||||
# vite-plugin-uni-middleware
|
|
||||||
|
|
||||||
Add Express-style middleware support to uni-app routing. Implement authentication guards, page analytics, and custom route logic.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm i -D @uni-helper/vite-plugin-uni-middleware
|
|
||||||
```
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite.config.ts
|
|
||||||
import Uni from '@dcloudio/vite-plugin-uni'
|
|
||||||
import UniMiddleware from '@uni-helper/vite-plugin-uni-middleware'
|
|
||||||
import { defineConfig } from 'vite'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [UniMiddleware(), Uni()],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Creating Middleware
|
|
||||||
|
|
||||||
Create middleware files in `src/middleware/` directory:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// src/middleware/auth.ts
|
|
||||||
import { defineMiddleware } from '@uni-helper/vite-plugin-middleware'
|
|
||||||
|
|
||||||
export default defineMiddleware((to, from, next) => {
|
|
||||||
const token = uni.getStorageSync('token')
|
|
||||||
|
|
||||||
if (!token && to.path !== '/pages/login/index') {
|
|
||||||
// Redirect to login
|
|
||||||
next('/pages/login/index')
|
|
||||||
} else {
|
|
||||||
// Continue navigation
|
|
||||||
next()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// src/middleware/analytics.ts
|
|
||||||
export default defineMiddleware((to, from, next) => {
|
|
||||||
// Track page view
|
|
||||||
uni.report('page_view', {
|
|
||||||
path: to.path,
|
|
||||||
from: from.path,
|
|
||||||
timestamp: Date.now(),
|
|
||||||
})
|
|
||||||
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// src/middleware/logger.ts
|
|
||||||
export default defineMiddleware((to, from, next) => {
|
|
||||||
console.log(`[Router] ${from.path} -> ${to.path}`)
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Applying Middleware
|
|
||||||
|
|
||||||
### Via definePage (recommended)
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
definePage({
|
|
||||||
middlewares: ['auth', 'analytics'],
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Via pages.json
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"pages": [{
|
|
||||||
"path": "pages/profile/index",
|
|
||||||
"middlewares": ["auth"]
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Global Middleware
|
|
||||||
|
|
||||||
Create `src/middleware/index.ts` for global middleware:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// src/middleware/index.ts
|
|
||||||
export default defineMiddleware((to, from, next) => {
|
|
||||||
// Runs on every navigation
|
|
||||||
console.log('Global middleware')
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Middleware Context
|
|
||||||
|
|
||||||
```ts
|
|
||||||
interface MiddlewareContext {
|
|
||||||
to: {
|
|
||||||
path: string
|
|
||||||
query: Record<string, string>
|
|
||||||
meta?: Record<string, any>
|
|
||||||
}
|
|
||||||
from: {
|
|
||||||
path: string
|
|
||||||
query: Record<string, string>
|
|
||||||
}
|
|
||||||
next: (path?: string) => void
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Middleware Order
|
|
||||||
|
|
||||||
Middleware executes in declaration order:
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
definePage({
|
|
||||||
// Runs: logger -> auth -> analytics
|
|
||||||
middlewares: ['logger', 'auth', 'analytics'],
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Navigation Control
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineMiddleware((to, from, next) => {
|
|
||||||
// Continue to route
|
|
||||||
next()
|
|
||||||
|
|
||||||
// Redirect to different route
|
|
||||||
next('/pages/other/index')
|
|
||||||
|
|
||||||
// Redirect with query
|
|
||||||
next({
|
|
||||||
path: '/pages/login/index',
|
|
||||||
query: { redirect: to.path },
|
|
||||||
})
|
|
||||||
|
|
||||||
// Cancel navigation (stay on current page)
|
|
||||||
next(false)
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Async Middleware
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineMiddleware(async (to, from, next) => {
|
|
||||||
try {
|
|
||||||
const user = await fetchUserInfo()
|
|
||||||
|
|
||||||
if (user.isBanned) {
|
|
||||||
next('/pages/banned/index')
|
|
||||||
} else {
|
|
||||||
next()
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
next('/pages/error/index')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://github.com/uni-helper/vite-plugin-uni-middleware
|
|
||||||
- https://uni-helper.js.org/vite-plugin-uni-middleware
|
|
||||||
-->
|
|
||||||
@ -1,186 +0,0 @@
|
|||||||
---
|
|
||||||
name: vite-plugin-uni-pages
|
|
||||||
description: File-based routing system for Vite-powered uni-app projects with automatic page discovery and TypeScript support
|
|
||||||
---
|
|
||||||
|
|
||||||
# vite-plugin-uni-pages
|
|
||||||
|
|
||||||
Provides file-based routing for uni-app projects using Vite. Automatically generates `pages.json` configuration from your file structure.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm i -D @uni-helper/vite-plugin-uni-pages
|
|
||||||
```
|
|
||||||
|
|
||||||
## Basic Setup
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite.config.ts
|
|
||||||
import Uni from '@dcloudio/vite-plugin-uni'
|
|
||||||
import UniPages from '@uni-helper/vite-plugin-uni-pages'
|
|
||||||
import { defineConfig } from 'vite'
|
|
||||||
|
|
||||||
// Place UniPages before Uni
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [UniPages(), Uni()],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## TypeScript Support
|
|
||||||
|
|
||||||
Add types to `tsconfig.json`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"types": ["@uni-helper/vite-plugin-uni-pages"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Global Configuration
|
|
||||||
|
|
||||||
Create `pages.config.ts` (or .js/.json) for global settings:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// pages.config.ts
|
|
||||||
import { defineUniPages } from '@uni-helper/vite-plugin-uni-pages'
|
|
||||||
|
|
||||||
export default defineUniPages({
|
|
||||||
// Optional: manual pages have highest priority
|
|
||||||
pages: [],
|
|
||||||
globalStyle: {
|
|
||||||
navigationBarTextStyle: 'black',
|
|
||||||
navigationBarTitleText: '@uni-helper',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Virtual Module
|
|
||||||
|
|
||||||
Access page metadata via virtual module:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
/// <reference types="@uni-helper/vite-plugin-uni-pages/client" />
|
|
||||||
import { pages } from 'virtual:uni-pages'
|
|
||||||
|
|
||||||
console.log(pages)
|
|
||||||
```
|
|
||||||
|
|
||||||
## definePage Macro
|
|
||||||
|
|
||||||
Define page configuration directly in SFC files:
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup lang="ts">
|
|
||||||
// Object form
|
|
||||||
definePage({
|
|
||||||
style: {
|
|
||||||
navigationBarTitleText: 'hello world',
|
|
||||||
},
|
|
||||||
middlewares: ['auth'],
|
|
||||||
})
|
|
||||||
|
|
||||||
// Function form (supports TypeScript, async)
|
|
||||||
definePage(() => {
|
|
||||||
return {
|
|
||||||
style: {
|
|
||||||
navigationBarTitleText: 'dynamic title',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Async form
|
|
||||||
definePage(async () => {
|
|
||||||
const title = await fetchTitle()
|
|
||||||
return {
|
|
||||||
style: { navigationBarTitleText: title },
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Important Notes for definePage
|
|
||||||
|
|
||||||
- Write inside `<script>` block
|
|
||||||
- `definePage` runs in a different scope than SFC (no access to SFC variables)
|
|
||||||
- Page path is auto-generated from file path
|
|
||||||
- Only one `definePage` per file
|
|
||||||
- Supports importing external libraries (pure JS only)
|
|
||||||
- Supports TypeScript type imports (types are stripped)
|
|
||||||
|
|
||||||
### Using with Condition Compile
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
import { isH5 } from '@uni-helper/uni-env'
|
|
||||||
|
|
||||||
definePage(() => {
|
|
||||||
const title = isH5 ? 'H5 Environment' : 'Other Platform'
|
|
||||||
return {
|
|
||||||
style: { navigationBarTitleText: title },
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration Options
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export interface Options {
|
|
||||||
/** Generate TypeScript declarations */
|
|
||||||
dts?: boolean | string // default: true (outputs uni-pages.d.ts)
|
|
||||||
|
|
||||||
/** Config file pattern */
|
|
||||||
configSource: string // default: 'pages.config.(ts|mts|cts|js|cjs|mjs|json)'
|
|
||||||
|
|
||||||
/** Default home page */
|
|
||||||
homePage: string // default: 'pages/index' or 'pages/index/index'
|
|
||||||
|
|
||||||
/** Merge with existing pages.json */
|
|
||||||
mergePages: boolean // default: true
|
|
||||||
|
|
||||||
/** Pages directory */
|
|
||||||
dir: string // default: 'src/pages'
|
|
||||||
|
|
||||||
/** Sub-packages directories */
|
|
||||||
subPackages: string[] // e.g., ['src/pages-sub']
|
|
||||||
|
|
||||||
/** Output directory for pages.json */
|
|
||||||
outDir: string // default: 'src'
|
|
||||||
|
|
||||||
/** Exclude patterns */
|
|
||||||
exclude: string[] // default: []
|
|
||||||
|
|
||||||
/** Route block language in SFC */
|
|
||||||
routeBlockLang: 'json5' | 'jsonc' | 'json' | 'yaml' | 'yml' // default: 'json5'
|
|
||||||
|
|
||||||
// Lifecycle hooks
|
|
||||||
onBeforeLoadUserConfig?: (ctx: PageContext) => void
|
|
||||||
onAfterLoadUserConfig?: (ctx: PageContext) => void
|
|
||||||
onBeforeScanPages?: (ctx: PageContext) => void
|
|
||||||
onAfterScanPages?: (ctx: PageContext) => void
|
|
||||||
onBeforeMergePageMetaData?: (ctx: PageContext) => void
|
|
||||||
onAfterMergePageMetaData?: (ctx: PageContext) => void
|
|
||||||
onBeforeWriteFile?: (ctx: PageContext) => void
|
|
||||||
onAfterWriteFile?: (ctx: PageContext) => void
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage with Layouts
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
definePage({
|
|
||||||
layout: 'default', // Specify layout
|
|
||||||
middlewares: ['auth'],
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://github.com/uni-helper/vite-plugin-uni-pages
|
|
||||||
- https://uni-helper.js.org/vite-plugin-uni-pages
|
|
||||||
-->
|
|
||||||
@ -1,119 +0,0 @@
|
|||||||
---
|
|
||||||
name: vite-plugin-uni-platform-modifier
|
|
||||||
description: Platform modifiers for attributes and directives in uni-app - optimize bundle size by removing unused platform code
|
|
||||||
---
|
|
||||||
|
|
||||||
# vite-plugin-uni-platform-modifier
|
|
||||||
|
|
||||||
Add platform modifiers to attributes and directives for conditional compilation at the template level. Reduces bundle size by removing code for other platforms.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm i -D @uni-helper/vite-plugin-uni-platform-modifier
|
|
||||||
```
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite.config.ts
|
|
||||||
import Uni from '@dcloudio/vite-plugin-uni'
|
|
||||||
import UniPlatformModifier from '@uni-helper/vite-plugin-uni-platform-modifier'
|
|
||||||
import { defineConfig } from 'vite'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [UniPlatformModifier(), Uni()],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Platform Modifiers
|
|
||||||
|
|
||||||
### Directive Modifiers
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<!-- Only render on H5 -->
|
|
||||||
<view v-if.h5="condition">H5 only content</view>
|
|
||||||
|
|
||||||
<!-- Only render on WeChat -->
|
|
||||||
<button v-if.mp-weixin="true">WeChat specific</button>
|
|
||||||
|
|
||||||
<!-- Only render on App -->
|
|
||||||
<native-view v-if.app="true">Native view</native-view>
|
|
||||||
|
|
||||||
<!-- Platform-specific event -->
|
|
||||||
<view @click.h5="handleH5Click" @click.mp-weixin="handleWeChatClick">
|
|
||||||
Platform-specific click
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Attribute Modifiers
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<!-- Different classes per platform -->
|
|
||||||
<view class.h5="h5-container" class.mp-weixin="wechat-container">
|
|
||||||
Content
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- Platform-specific styles -->
|
|
||||||
<view :style.h5="{ padding: '20px' }" :style.app="{ padding: '10px' }">
|
|
||||||
Styled content
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Supported Platforms
|
|
||||||
|
|
||||||
| Modifier | Platform |
|
|
||||||
|----------|----------|
|
|
||||||
| `.h5` | H5/Web |
|
|
||||||
| `.mp-weixin` | WeChat Mini Program |
|
|
||||||
| `.mp-alipay` | Alipay Mini Program |
|
|
||||||
| `.mp-baidu` | Baidu Smart Program |
|
|
||||||
| `.mp-toutiao` | Douyin Mini Program |
|
|
||||||
| `.mp-qq` | QQ Mini Program |
|
|
||||||
| `.app` | iOS/Android App |
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
The plugin transforms templates at build time:
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<!-- Source -->
|
|
||||||
<view v-if.h5="show">H5 Only</view>
|
|
||||||
|
|
||||||
<!-- Transformed for H5 build -->
|
|
||||||
<view v-if="show">H5 Only</view>
|
|
||||||
|
|
||||||
<!-- Removed for non-H5 builds -->
|
|
||||||
```
|
|
||||||
|
|
||||||
## Use Cases
|
|
||||||
|
|
||||||
- Platform-specific UI components
|
|
||||||
- Different interactions per platform
|
|
||||||
- Platform-optimized styles
|
|
||||||
- Conditional event handlers
|
|
||||||
|
|
||||||
## Combining with Other Plugins
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite.config.ts
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [
|
|
||||||
UniComponents(),
|
|
||||||
UniPages(),
|
|
||||||
UniPlatform(), // File-based platform
|
|
||||||
UniPlatformModifier(), // Directive/attribute modifiers
|
|
||||||
Uni(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://github.com/uni-helper/vite-plugin-uni-platform-modifier
|
|
||||||
- https://uni-helper.js.org/vite-plugin-uni-platform-modifier
|
|
||||||
-->
|
|
||||||
@ -1,105 +0,0 @@
|
|||||||
---
|
|
||||||
name: vite-plugin-uni-platform
|
|
||||||
description: File-based platform compilation for uni-app - compile different files based on target platform
|
|
||||||
---
|
|
||||||
|
|
||||||
# vite-plugin-uni-platform
|
|
||||||
|
|
||||||
Compile different files based on target platform using file naming conventions. Supports platform-specific implementations without conditional compilation.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm i -D @uni-helper/vite-plugin-uni-platform
|
|
||||||
```
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite.config.ts
|
|
||||||
import Uni from '@dcloudio/vite-plugin-uni'
|
|
||||||
import UniPlatform from '@uni-helper/vite-plugin-uni-platform'
|
|
||||||
import { defineConfig } from 'vite'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [UniPlatform(), Uni()],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## File Naming Convention
|
|
||||||
|
|
||||||
Use platform suffixes in filenames:
|
|
||||||
|
|
||||||
```
|
|
||||||
src/
|
|
||||||
utils/
|
|
||||||
request.ts # Default implementation
|
|
||||||
request.h5.ts # H5-specific
|
|
||||||
request.mp-weixin.ts # WeChat Mini Program
|
|
||||||
request.app.ts # App (iOS/Android)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Supported Platforms
|
|
||||||
|
|
||||||
| Suffix | Platform |
|
|
||||||
|--------|----------|
|
|
||||||
| `.h5.ts` | H5/Web |
|
|
||||||
| `.mp-weixin.ts` | WeChat Mini Program |
|
|
||||||
| `.mp-alipay.ts` | Alipay Mini Program |
|
|
||||||
| `.mp-baidu.ts` | Baidu Smart Program |
|
|
||||||
| `.mp-toutiao.ts` | Douyin Mini Program |
|
|
||||||
| `.mp-qq.ts` | QQ Mini Program |
|
|
||||||
| `.mp-kuaishou.ts` | Kuaishou Mini Program |
|
|
||||||
| `.app.ts` | iOS/Android App |
|
|
||||||
| `.app-plus.ts` | App (alternative) |
|
|
||||||
| `.harmony` | HarmonyOS |
|
|
||||||
|
|
||||||
## Import Usage
|
|
||||||
|
|
||||||
Import without the platform suffix:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// This automatically resolves to the correct platform version
|
|
||||||
import { request } from './utils/request'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// utils/request.ts - Default implementation
|
|
||||||
export const request = (options: RequestOptions) => {
|
|
||||||
return uni.request(options)
|
|
||||||
}
|
|
||||||
|
|
||||||
// utils/request.h5.ts - H5 with axios
|
|
||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
export const request = (options: RequestOptions) => {
|
|
||||||
return axios.request(options)
|
|
||||||
}
|
|
||||||
|
|
||||||
// utils/request.mp-weixin.ts - WeChat specific
|
|
||||||
export const request = (options: RequestOptions) => {
|
|
||||||
// Add WeChat-specific headers
|
|
||||||
return uni.request({
|
|
||||||
...options,
|
|
||||||
header: {
|
|
||||||
...options.header,
|
|
||||||
'X-Platform': 'weixin',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Benefits
|
|
||||||
|
|
||||||
- Cleaner code without `#ifdef` conditions
|
|
||||||
- Type-safe platform implementations
|
|
||||||
- Tree-shaking removes unused platform code
|
|
||||||
- Better IDE support and navigation
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://github.com/uni-helper/vite-plugin-uni-platform
|
|
||||||
- https://uni-helper.js.org/vite-plugin-uni-platform
|
|
||||||
-->
|
|
||||||
@ -1,153 +0,0 @@
|
|||||||
---
|
|
||||||
name: create-uni
|
|
||||||
description: CLI scaffolding tool for creating uni-app projects with best practices
|
|
||||||
---
|
|
||||||
|
|
||||||
# create-uni
|
|
||||||
|
|
||||||
Interactive CLI tool for scaffolding uni-app projects. Quickly create projects with your choice of templates, plugins, and UI libraries.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Create new project
|
|
||||||
npm create uni@latest
|
|
||||||
|
|
||||||
# With project name
|
|
||||||
npm create uni@latest my-project
|
|
||||||
|
|
||||||
# With options
|
|
||||||
npm create uni@latest my-project --ts --template vitesse -m pinia -u wot
|
|
||||||
```
|
|
||||||
|
|
||||||
## CLI Options
|
|
||||||
|
|
||||||
| Option | Shorthand | Description |
|
|
||||||
|--------|-----------|-------------|
|
|
||||||
| `--template` | `-t` | Select project template |
|
|
||||||
| `--typescript` | `--ts` | Enable TypeScript |
|
|
||||||
| `--eslint` | `-e` | Add ESLint |
|
|
||||||
| `--plugin` | `-p` | Add Vite plugins |
|
|
||||||
| `--module` | `-m` | Add functional modules |
|
|
||||||
| `--ui` | `-u` | Add UI component library |
|
|
||||||
| `--gui` | - | Launch GUI interface |
|
|
||||||
| `--info` | - | Show project info |
|
|
||||||
|
|
||||||
## Templates
|
|
||||||
|
|
||||||
| Template | Description | Value |
|
|
||||||
|----------|-------------|-------|
|
|
||||||
| Vitesse Uni App | Vite + uni-app starter with best practices | `vitesse` |
|
|
||||||
| Wot Starter | Based on vitesse with wot-ui | `wot-starter` |
|
|
||||||
| Wot Starter Retail | Retail/e-commerce template | `wot-starter-retail` |
|
|
||||||
| Unisave | Web-first uni-app development | `unisave` |
|
|
||||||
| TMUI 3.2 | Enterprise component library | `tmui32` |
|
|
||||||
| uView Pro Starter | uView Pro quick start | `uview-pro-starter` |
|
|
||||||
| uView Pro Demo | uView Pro full demo | `uview-pro-demo` |
|
|
||||||
|
|
||||||
## Available Plugins
|
|
||||||
|
|
||||||
| Plugin | CLI Value | Description |
|
|
||||||
|--------|-----------|-------------|
|
|
||||||
| vite-plugin-uni-components | `import` | Auto component import |
|
|
||||||
| vite-plugin-uni-pages | `pages` | File-based routing |
|
|
||||||
| vite-plugin-uni-layouts | `layouts` | Nuxt-like layouts |
|
|
||||||
| vite-plugin-uni-manifest | `manifest` | TypeScript manifest |
|
|
||||||
| vite-plugin-uni-platform | `filePlatform` | Platform-specific files |
|
|
||||||
| vite-plugin-uni-platform-modifier | `platformModifier` | Platform modifiers |
|
|
||||||
| vite-plugin-uni-middleware | `middleware` | Route middleware |
|
|
||||||
| uni-ku-root | `root` | Virtual root component |
|
|
||||||
|
|
||||||
## Available Modules
|
|
||||||
|
|
||||||
| Module | CLI Value | Description |
|
|
||||||
|--------|-----------|-------------|
|
|
||||||
| Pinia | `pinia` | State management |
|
|
||||||
| UnoCSS | `unocss` | Atomic CSS |
|
|
||||||
| uni-network | `uniNetwork` | HTTP client |
|
|
||||||
| uni-use | `uniUse` | Composable utilities |
|
|
||||||
| uni-promises | `uniPromises` | Promise wrappers |
|
|
||||||
| Uni ECharts | `uniEcharts` | Chart component |
|
|
||||||
| z-paging | `zPaging` | Pagination component |
|
|
||||||
|
|
||||||
## UI Libraries
|
|
||||||
|
|
||||||
| UI Library | CLI Value | Description |
|
|
||||||
|------------|-----------|-------------|
|
|
||||||
| Uni UI | `uni` | Official DCloud UI |
|
|
||||||
| Wot Design Uni | `wot` | Lightweight, beautiful |
|
|
||||||
| uView Pro | `uview-pro` | Vue 3 + TypeScript |
|
|
||||||
| NutUI Uniapp | `nut` | JD style, e-commerce |
|
|
||||||
| Skiyee UI | `skiyee` | Unique design language |
|
|
||||||
| UV UI | `uv` | Multi-platform |
|
|
||||||
| Ano UI | `ano` | UnoCSS-based |
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Basic TypeScript Project
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm create uni@latest my-app --ts
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Vitesse Template
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm create uni@latest my-app --ts --template vitesse
|
|
||||||
```
|
|
||||||
|
|
||||||
### Full-Featured Setup
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm create uni@latest my-app \
|
|
||||||
--ts \
|
|
||||||
--template vitesse \
|
|
||||||
-p pages -p layouts -p import \
|
|
||||||
-m pinia -m unocss -m uniUse \
|
|
||||||
-u wot \
|
|
||||||
-e
|
|
||||||
```
|
|
||||||
|
|
||||||
### GUI Mode
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm create uni@latest --gui
|
|
||||||
```
|
|
||||||
|
|
||||||
## Project Structure
|
|
||||||
|
|
||||||
Created projects follow this structure:
|
|
||||||
|
|
||||||
```
|
|
||||||
my-project/
|
|
||||||
├── src/
|
|
||||||
│ ├── components/ # Vue components
|
|
||||||
│ ├── composables/ # Composable functions
|
|
||||||
│ ├── layouts/ # Layout components (if layouts plugin)
|
|
||||||
│ ├── pages/ # Page components
|
|
||||||
│ ├── static/ # Static assets
|
|
||||||
│ ├── stores/ # Pinia stores (if pinia)
|
|
||||||
│ ├── App.vue
|
|
||||||
│ ├── main.ts
|
|
||||||
│ ├── manifest.config.ts # (if manifest plugin)
|
|
||||||
│ └── pages.config.ts # (if pages plugin)
|
|
||||||
├── index.html
|
|
||||||
├── package.json
|
|
||||||
├── tsconfig.json
|
|
||||||
├── vite.config.ts
|
|
||||||
└── uno.config.ts # (if unocss)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Post-Creation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd my-project
|
|
||||||
npm install
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://github.com/uni-helper/create-uni
|
|
||||||
- https://uni-helper.js.org/create-uni
|
|
||||||
-->
|
|
||||||
@ -1,263 +0,0 @@
|
|||||||
---
|
|
||||||
name: vitesse-uni-app
|
|
||||||
description: Vite-powered opinionated uni-app starter template with best practices
|
|
||||||
---
|
|
||||||
|
|
||||||
# vitesse-uni-app
|
|
||||||
|
|
||||||
Opinionated uni-app starter template powered by Vite. Includes best practices, pre-configured tooling, and modern development experience.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- ⚡️ **Vite** - Fast development and building
|
|
||||||
- 🌍 **uni-app** - Cross-platform development
|
|
||||||
- 🖖 **Vue 3** - Composition API, `<script setup>`
|
|
||||||
- 🔷 **TypeScript** - Type safety
|
|
||||||
- 🎨 **UnoCSS** - Atomic CSS
|
|
||||||
- 📦 **Auto Import** - Components and composables
|
|
||||||
- 🗂 **File-based Routing** - vite-plugin-uni-pages
|
|
||||||
- 📐 **Layout System** - Nuxt-like layouts
|
|
||||||
- 🔧 **ESLint** - Code linting
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Create with create-uni
|
|
||||||
npm create uni@latest my-app --template vitesse
|
|
||||||
|
|
||||||
# Or clone directly
|
|
||||||
git clone https://github.com/uni-helper/vitesse-uni-app.git my-app
|
|
||||||
```
|
|
||||||
|
|
||||||
## Project Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
vitesse-uni-app/
|
|
||||||
├── src/
|
|
||||||
│ ├── components/ # Auto-imported components
|
|
||||||
│ ├── composables/ # Auto-imported composables
|
|
||||||
│ ├── layouts/ # Layout components
|
|
||||||
│ │ ├── default.vue
|
|
||||||
│ │ └── home.vue
|
|
||||||
│ ├── pages/ # File-based routing
|
|
||||||
│ │ ├── index.vue
|
|
||||||
│ │ └── about/
|
|
||||||
│ │ └── index.vue
|
|
||||||
│ ├── static/ # Static assets
|
|
||||||
│ ├── stores/ # Pinia stores
|
|
||||||
│ ├── styles/ # Global styles
|
|
||||||
│ ├── App.vue
|
|
||||||
│ ├── main.ts
|
|
||||||
│ ├── manifest.config.ts # TypeScript manifest
|
|
||||||
│ └── pages.config.ts # Global pages config
|
|
||||||
├── types/ # Type definitions
|
|
||||||
├── index.html
|
|
||||||
├── package.json
|
|
||||||
├── tsconfig.json
|
|
||||||
├── vite.config.ts
|
|
||||||
├── uno.config.ts
|
|
||||||
└── eslint.config.js
|
|
||||||
```
|
|
||||||
|
|
||||||
## Pre-configured Plugins
|
|
||||||
|
|
||||||
The template includes these pre-configured plugins:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite.config.ts
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [
|
|
||||||
UniComponents({
|
|
||||||
resolvers: [AnoUIResolver()],
|
|
||||||
}),
|
|
||||||
UniPages(),
|
|
||||||
UniLayouts(),
|
|
||||||
UniManifest(),
|
|
||||||
Uni(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Auto Imports
|
|
||||||
|
|
||||||
### Components
|
|
||||||
|
|
||||||
Components in `src/components/` are auto-imported:
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<!-- No import needed -->
|
|
||||||
<MyButton />
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Composables
|
|
||||||
|
|
||||||
Files in `src/composables/` are auto-imported:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// src/composables/useCounter.ts
|
|
||||||
export function useCounter() {
|
|
||||||
const count = ref(0)
|
|
||||||
const inc = () => count.value++
|
|
||||||
return { count, inc }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
// No import needed
|
|
||||||
const { count, inc } = useCounter()
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Vue APIs
|
|
||||||
|
|
||||||
Vue APIs are auto-imported:
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
// No need to import ref, computed, etc.
|
|
||||||
const count = ref(0)
|
|
||||||
const doubled = computed(() => count.value * 2)
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Routing
|
|
||||||
|
|
||||||
File-based routing with pages in `src/pages/`:
|
|
||||||
|
|
||||||
```
|
|
||||||
pages/
|
|
||||||
├── index.vue # /pages/index/index
|
|
||||||
├── about.vue # /pages/about/index
|
|
||||||
└── user/
|
|
||||||
├── index.vue # /pages/user/index
|
|
||||||
└── [id].vue # /pages/user/:id
|
|
||||||
```
|
|
||||||
|
|
||||||
### Page Configuration
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
definePage({
|
|
||||||
style: {
|
|
||||||
navigationBarTitleText: 'About',
|
|
||||||
},
|
|
||||||
layout: 'default',
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Layouts
|
|
||||||
|
|
||||||
Create layouts in `src/layouts/`:
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<!-- src/layouts/default.vue -->
|
|
||||||
<template>
|
|
||||||
<view class="layout">
|
|
||||||
<slot name="header" />
|
|
||||||
<slot />
|
|
||||||
<slot name="footer" />
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
Apply in pages:
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
definePage({
|
|
||||||
layout: 'default',
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<template #header>
|
|
||||||
<NavBar />
|
|
||||||
</template>
|
|
||||||
<div>Content</div>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Styling with UnoCSS
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<view class="p-32 bg-gray-100">
|
|
||||||
<text class="text-32 font-bold text-primary">
|
|
||||||
Styled with UnoCSS
|
|
||||||
</text>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
## State Management with Pinia
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// src/stores/user.ts
|
|
||||||
import { defineStore } from 'pinia'
|
|
||||||
|
|
||||||
export const useUserStore = defineStore('user', () => {
|
|
||||||
const name = ref('')
|
|
||||||
const isLoggedIn = computed(() => !!name.value)
|
|
||||||
|
|
||||||
function login(userName: string) {
|
|
||||||
name.value = userName
|
|
||||||
}
|
|
||||||
|
|
||||||
return { name, isLoggedIn, login }
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
const user = useUserStore()
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Data Fetching
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
const { data: users, pending, error } = useFetch('/api/users')
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<view v-if="pending">Loading...</view>
|
|
||||||
<view v-else-if="error">Error: {{ error.message }}</view>
|
|
||||||
<view v-else>
|
|
||||||
<view v-for="user in users" :key="user.id">
|
|
||||||
{{ user.name }}
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Deployment
|
|
||||||
|
|
||||||
### Build for production
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
### Platform-specific builds
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# H5
|
|
||||||
npm run build:h5
|
|
||||||
|
|
||||||
# WeChat Mini Program
|
|
||||||
npm run build:mp-weixin
|
|
||||||
|
|
||||||
# App
|
|
||||||
npm run build:app
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://github.com/uni-helper/vitesse-uni-app
|
|
||||||
- https://uni-helper.js.org/vitesse-uni-app
|
|
||||||
-->
|
|
||||||
@ -1,155 +0,0 @@
|
|||||||
---
|
|
||||||
name: uni-env
|
|
||||||
description: Environment detection utilities for uni-app - detect platform at runtime
|
|
||||||
---
|
|
||||||
|
|
||||||
# uni-env
|
|
||||||
|
|
||||||
Runtime environment detection utilities for uni-app. Detect the current platform in a type-safe way.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm i @uni-helper/uni-env
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { isH5, isMpWeixin, isApp, isMp } from '@uni-helper/uni-env'
|
|
||||||
|
|
||||||
if (isH5) {
|
|
||||||
console.log('Running in H5')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isMpWeixin) {
|
|
||||||
console.log('Running in WeChat Mini Program')
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Available Detectors
|
|
||||||
|
|
||||||
### Platform Groups
|
|
||||||
|
|
||||||
| Detector | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `isH5` | H5/Web platform |
|
|
||||||
| `isApp` | iOS/Android App |
|
|
||||||
| `isMp` | Any Mini Program |
|
|
||||||
| `isQuickapp` | Quick App |
|
|
||||||
|
|
||||||
### Mini Program Detectors
|
|
||||||
|
|
||||||
| Detector | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `isMpWeixin` | WeChat Mini Program |
|
|
||||||
| `isMpAlipay` | Alipay Mini Program |
|
|
||||||
| `isMpBaidu` | Baidu Smart Program |
|
|
||||||
| `isMpToutiao` | Douyin Mini Program |
|
|
||||||
| `isMpQQ` | QQ Mini Program |
|
|
||||||
| `isMpKuaishou` | Kuaishou Mini Program |
|
|
||||||
| `isMpJd` | JD Mini Program |
|
|
||||||
| `isMpHarmony` | HarmonyOS Meta Service |
|
|
||||||
| `isMpLark` | Feishu/Lark Mini Program |
|
|
||||||
|
|
||||||
### Development Detectors
|
|
||||||
|
|
||||||
| Detector | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `isDev` | Development environment |
|
|
||||||
| `isProd` | Production environment |
|
|
||||||
|
|
||||||
## Use Cases
|
|
||||||
|
|
||||||
### Conditional Logic
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { isH5, isMpWeixin, isApp } from '@uni-helper/uni-env'
|
|
||||||
|
|
||||||
function getStorageAdapter() {
|
|
||||||
if (isH5) {
|
|
||||||
return new LocalStorageAdapter()
|
|
||||||
}
|
|
||||||
if (isMpWeixin) {
|
|
||||||
return new WechatStorageAdapter()
|
|
||||||
}
|
|
||||||
if (isApp) {
|
|
||||||
return new NativeStorageAdapter()
|
|
||||||
}
|
|
||||||
return new UniStorageAdapter()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Platform-Specific Features
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { isH5, isMpWeixin } from '@uni-helper/uni-env'
|
|
||||||
|
|
||||||
function share(content: ShareContent) {
|
|
||||||
if (isMpWeixin) {
|
|
||||||
// Use WeChat share API
|
|
||||||
wx.showShareMenu({ withShareTicket: true })
|
|
||||||
} else if (isH5) {
|
|
||||||
// Use Web Share API
|
|
||||||
navigator.share({
|
|
||||||
title: content.title,
|
|
||||||
url: content.url,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
// Use uni-app generic share
|
|
||||||
uni.share({
|
|
||||||
title: content.title,
|
|
||||||
path: content.path,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### With definePage
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { isH5 } from '@uni-helper/uni-env'
|
|
||||||
|
|
||||||
definePage(() => {
|
|
||||||
const title = isH5 ? 'H5 Version' : 'Mobile Version'
|
|
||||||
|
|
||||||
return {
|
|
||||||
style: {
|
|
||||||
navigationBarTitleText: title,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Important Note
|
|
||||||
|
|
||||||
This package is primarily for **plugin developers**. For runtime conditional compilation, use uni-app's [official conditional compilation](https://uniapp.dcloud.net.cn/tutorial/platform.html#preprocessor) with `/* #ifdef */` comments.
|
|
||||||
|
|
||||||
For better conditional compilation support, consider using [unplugin-preprocessor-directives](https://github.com/KeJunMao/unplugin-preprocessor-directives).
|
|
||||||
|
|
||||||
## Comparison with Conditional Compilation
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Runtime detection (uni-env)
|
|
||||||
import { isH5 } from '@uni-helper/uni-env'
|
|
||||||
if (isH5) {
|
|
||||||
// This code exists in all builds
|
|
||||||
doH5Thing()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build-time conditional compilation
|
|
||||||
// #ifdef H5
|
|
||||||
doH5Thing()
|
|
||||||
// #endif
|
|
||||||
// This code only exists in H5 builds
|
|
||||||
```
|
|
||||||
|
|
||||||
Choose based on your needs:
|
|
||||||
- Use **uni-env** for runtime detection when you need the same bundle for multiple platforms
|
|
||||||
- Use **conditional compilation** for build-time optimization and smaller bundles
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://github.com/uni-helper/uni-env
|
|
||||||
- https://uni-helper.js.org/uni-env
|
|
||||||
-->
|
|
||||||
@ -1,175 +0,0 @@
|
|||||||
---
|
|
||||||
name: unocss-preset-uni
|
|
||||||
description: UnoCSS preset for uni-app with platform-specific utilities and rpx support
|
|
||||||
---
|
|
||||||
|
|
||||||
# unocss-preset-uni
|
|
||||||
|
|
||||||
UnoCSS preset designed for uni-app development. Provides utilities for rpx units and platform-specific styling.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm i -D @uni-helper/unocss-preset-uni
|
|
||||||
```
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// uno.config.ts
|
|
||||||
import { defineConfig } from 'unocss'
|
|
||||||
import { presetUni } from '@uni-helper/unocss-preset-uni'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
presets: [
|
|
||||||
presetUni(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **rpx support**: Automatic conversion to rpx units
|
|
||||||
- **Platform variants**: Style targeting specific platforms
|
|
||||||
- **Safe area utilities**: Handle notches and safe areas
|
|
||||||
- **Compatible with**: Mini programs, H5, and App
|
|
||||||
|
|
||||||
## RPX Utilities
|
|
||||||
|
|
||||||
The preset automatically converts pixel values to rpx:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!-- These generate rpx units -->
|
|
||||||
<view class="w-100"></view> <!-- width: 100rpx -->
|
|
||||||
<view class="h-50"></view> <!-- height: 50rpx -->
|
|
||||||
<view class="m-20"></view> <!-- margin: 20rpx -->
|
|
||||||
<view class="p-16"></view> <!-- padding: 16rpx -->
|
|
||||||
<view class="text-32"></view> <!-- font-size: 32rpx -->
|
|
||||||
```
|
|
||||||
|
|
||||||
## Platform Variants
|
|
||||||
|
|
||||||
Apply styles to specific platforms:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!-- Only apply on H5 -->
|
|
||||||
<view class="h5:bg-red"></view>
|
|
||||||
|
|
||||||
<!-- Only apply on WeChat -->
|
|
||||||
<view class="mp-weixin:bg-blue"></view>
|
|
||||||
|
|
||||||
<!-- Only apply on App -->
|
|
||||||
<view class="app:bg-green"></view>
|
|
||||||
|
|
||||||
<!-- Combine with responsive -->
|
|
||||||
<view class="h5:p-20 mp-weixin:p-10"></view>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Safe Area Utilities
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!-- Safe area padding -->
|
|
||||||
<view class="pt-safe"></view> <!-- padding-top: env(safe-area-inset-top) -->
|
|
||||||
<view class="pb-safe"></view> <!-- padding-bottom: env(safe-area-inset-bottom) -->
|
|
||||||
<view class="px-safe"></view> <!-- padding-x with safe area -->
|
|
||||||
|
|
||||||
<!-- Safe area margin -->
|
|
||||||
<view class="mt-safe"></view>
|
|
||||||
<view class="mb-safe"></view>
|
|
||||||
|
|
||||||
<!-- Full safe area -->
|
|
||||||
<view class="safe-area"></view>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Tab Bar Utilities
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!-- Padding for tab bar -->
|
|
||||||
<view class="pb-tabbar"></view>
|
|
||||||
|
|
||||||
<!-- Margin for tab bar -->
|
|
||||||
<view class="mb-tabbar"></view>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Status Bar Utilities
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!-- Status bar height -->
|
|
||||||
<view class="h-statusbar"></view>
|
|
||||||
<view class="pt-statusbar"></view>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// uno.config.ts
|
|
||||||
import { defineConfig } from 'unocss'
|
|
||||||
import { presetUni } from '@uni-helper/unocss-preset-uni'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
presets: [
|
|
||||||
presetUni({
|
|
||||||
// rpx base width (default: 750)
|
|
||||||
baseWidth: 750,
|
|
||||||
|
|
||||||
// Enable platform variants
|
|
||||||
platform: true,
|
|
||||||
|
|
||||||
// Enable safe area utilities
|
|
||||||
safeArea: true,
|
|
||||||
|
|
||||||
// Custom platforms
|
|
||||||
platforms: ['mp-weixin', 'mp-alipay', 'h5', 'app'],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## With Vite
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite.config.ts
|
|
||||||
import Uni from '@dcloudio/vite-plugin-uni'
|
|
||||||
import UnoCSS from 'unocss/vite'
|
|
||||||
import { defineConfig } from 'vite'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [
|
|
||||||
UnoCSS(),
|
|
||||||
Uni(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<view class="page bg-gray-100">
|
|
||||||
<!-- Status bar spacing -->
|
|
||||||
<view class="h-statusbar"></view>
|
|
||||||
|
|
||||||
<!-- Header with safe area -->
|
|
||||||
<view class="px-32 pt-safe flex items-center justify-between">
|
|
||||||
<text class="text-36 font-bold">Title</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- Content -->
|
|
||||||
<view class="p-32 flex flex-col gap-20">
|
|
||||||
<!-- Platform-specific styling -->
|
|
||||||
<view class="card h5:shadow-lg mp-weixin:border">
|
|
||||||
<text class="text-28 text-gray-800">Content</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- Bottom safe area for devices with notches -->
|
|
||||||
<view class="pb-safe"></view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://github.com/uni-helper/unocss-preset-uni
|
|
||||||
- https://uni-helper.js.org/unocss-preset-uni
|
|
||||||
-->
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
# Generation Info
|
|
||||||
|
|
||||||
- **Source:** `sources/unocss`
|
|
||||||
- **Git SHA:** `2f7f267d0cc0c43d44357208aabb35b049359a08`
|
|
||||||
- **Generated:** 2026-01-28
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2025-PRESENT Anthony Fu <https://github.com/antfu>
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@ -1,64 +0,0 @@
|
|||||||
---
|
|
||||||
name: unocss
|
|
||||||
description: UnoCSS instant atomic CSS engine, superset of Tailwind CSS. Use when configuring UnoCSS, writing utility rules, shortcuts, or working with presets like Wind, Icons, Attributify.
|
|
||||||
metadata:
|
|
||||||
author: Anthony Fu
|
|
||||||
version: "2026.1.28"
|
|
||||||
source: Generated from https://github.com/unocss/unocss, scripts located at https://github.com/antfu/skills
|
|
||||||
---
|
|
||||||
|
|
||||||
UnoCSS is an instant atomic CSS engine designed to be flexible and extensible. The core is un-opinionated - all CSS utilities are provided via presets. It's a superset of Tailwind CSS, so you can reuse your Tailwind knowledge for basic syntax usage.
|
|
||||||
|
|
||||||
**Important:** Before writing UnoCSS code, agents should check for `uno.config.*` or `unocss.config.*` files in the project root to understand what presets, rules, and shortcuts are available. If the project setup is unclear, avoid using attributify mode and other advanced features - stick to basic `class` usage.
|
|
||||||
|
|
||||||
> The skill is based on UnoCSS 66.x, generated at 2026-01-28.
|
|
||||||
|
|
||||||
## Core
|
|
||||||
|
|
||||||
| Topic | Description | Reference |
|
|
||||||
|-------|-------------|-----------|
|
|
||||||
| Configuration | Config file setup and all configuration options | [core-config](references/core-config.md) |
|
|
||||||
| Rules | Static and dynamic rules for generating CSS utilities | [core-rules](references/core-rules.md) |
|
|
||||||
| Shortcuts | Combine multiple rules into single shorthands | [core-shortcuts](references/core-shortcuts.md) |
|
|
||||||
| Theme | Theming system for colors, breakpoints, and design tokens | [core-theme](references/core-theme.md) |
|
|
||||||
| Variants | Apply variations like hover:, dark:, responsive to rules | [core-variants](references/core-variants.md) |
|
|
||||||
| Extracting | How UnoCSS extracts utilities from source code | [core-extracting](references/core-extracting.md) |
|
|
||||||
| Safelist & Blocklist | Force include or exclude specific utilities | [core-safelist](references/core-safelist.md) |
|
|
||||||
| Layers & Preflights | CSS layer ordering and raw CSS injection | [core-layers](references/core-layers.md) |
|
|
||||||
|
|
||||||
## Presets
|
|
||||||
|
|
||||||
### Main Presets
|
|
||||||
|
|
||||||
| Topic | Description | Reference |
|
|
||||||
|-------|-------------|-----------|
|
|
||||||
| Preset Wind3 | Tailwind CSS v3 / Windi CSS compatible preset (most common) | [preset-wind3](references/preset-wind3.md) |
|
|
||||||
| Preset Wind4 | Tailwind CSS v4 compatible preset with modern CSS features | [preset-wind4](references/preset-wind4.md) |
|
|
||||||
| Preset Mini | Minimal preset with essential utilities for custom builds | [preset-mini](references/preset-mini.md) |
|
|
||||||
|
|
||||||
### Feature Presets
|
|
||||||
|
|
||||||
| Topic | Description | Reference |
|
|
||||||
|-------|-------------|-----------|
|
|
||||||
| Preset Icons | Pure CSS icons using Iconify with any icon set | [preset-icons](references/preset-icons.md) |
|
|
||||||
| Preset Attributify | Group utilities in HTML attributes instead of class | [preset-attributify](references/preset-attributify.md) |
|
|
||||||
| Preset Typography | Prose classes for typographic defaults | [preset-typography](references/preset-typography.md) |
|
|
||||||
| Preset Web Fonts | Easy Google Fonts and other web fonts integration | [preset-web-fonts](references/preset-web-fonts.md) |
|
|
||||||
| Preset Tagify | Use utilities as HTML tag names | [preset-tagify](references/preset-tagify.md) |
|
|
||||||
| Preset Rem to Px | Convert rem units to px for utilities | [preset-rem-to-px](references/preset-rem-to-px.md) |
|
|
||||||
|
|
||||||
## Transformers
|
|
||||||
|
|
||||||
| Topic | Description | Reference |
|
|
||||||
|-------|-------------|-----------|
|
|
||||||
| Variant Group | Shorthand for grouping utilities with common prefixes | [transformer-variant-group](references/transformer-variant-group.md) |
|
|
||||||
| Directives | CSS directives: @apply, @screen, theme(), icon() | [transformer-directives](references/transformer-directives.md) |
|
|
||||||
| Compile Class | Compile multiple classes into one hashed class | [transformer-compile-class](references/transformer-compile-class.md) |
|
|
||||||
| Attributify JSX | Support valueless attributify in JSX/TSX | [transformer-attributify-jsx](references/transformer-attributify-jsx.md) |
|
|
||||||
|
|
||||||
## Integrations
|
|
||||||
|
|
||||||
| Topic | Description | Reference |
|
|
||||||
|-------|-------------|-----------|
|
|
||||||
| Vite Integration | Setting up UnoCSS with Vite and framework-specific tips | [integrations-vite](references/integrations-vite.md) |
|
|
||||||
| Nuxt Integration | UnoCSS module for Nuxt applications | [integrations-nuxt](references/integrations-nuxt.md) |
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
# Sync Info
|
|
||||||
|
|
||||||
- **Source:** `vendor/antfu/skills/unocss`
|
|
||||||
- **Git SHA:** `4086f59fa12d308fb05592ca8ce1704fbb940aa3`
|
|
||||||
- **Synced:** 2026-01-30
|
|
||||||
@ -1,187 +0,0 @@
|
|||||||
---
|
|
||||||
name: unocss-configuration
|
|
||||||
description: Config file setup and all configuration options for UnoCSS
|
|
||||||
---
|
|
||||||
|
|
||||||
# UnoCSS Configuration
|
|
||||||
|
|
||||||
UnoCSS is configured via a dedicated config file in your project root.
|
|
||||||
|
|
||||||
## Config File
|
|
||||||
|
|
||||||
**Recommended:** Use a dedicated `uno.config.ts` file for best IDE support and HMR.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// uno.config.ts
|
|
||||||
import {
|
|
||||||
defineConfig,
|
|
||||||
presetAttributify,
|
|
||||||
presetIcons,
|
|
||||||
presetTypography,
|
|
||||||
presetWebFonts,
|
|
||||||
presetWind3,
|
|
||||||
transformerDirectives,
|
|
||||||
transformerVariantGroup
|
|
||||||
} from 'unocss'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
shortcuts: [
|
|
||||||
// ...
|
|
||||||
],
|
|
||||||
theme: {
|
|
||||||
colors: {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
},
|
|
||||||
presets: [
|
|
||||||
presetWind3(),
|
|
||||||
presetAttributify(),
|
|
||||||
presetIcons(),
|
|
||||||
presetTypography(),
|
|
||||||
presetWebFonts({
|
|
||||||
fonts: {
|
|
||||||
// ...
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
transformers: [
|
|
||||||
transformerDirectives(),
|
|
||||||
transformerVariantGroup(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
UnoCSS automatically looks for `uno.config.{js,ts,mjs,mts}` or `unocss.config.{js,ts,mjs,mts}` in the project root.
|
|
||||||
|
|
||||||
## Key Configuration Options
|
|
||||||
|
|
||||||
### rules
|
|
||||||
Define CSS utility rules. Later entries have higher priority.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
rules: [
|
|
||||||
['m-1', { margin: '0.25rem' }],
|
|
||||||
[/^m-(\d+)$/, ([, d]) => ({ margin: `${d / 4}rem` })],
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
### shortcuts
|
|
||||||
Combine multiple rules into a single shorthand.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
shortcuts: {
|
|
||||||
'btn': 'py-2 px-4 font-semibold rounded-lg shadow-md',
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### theme
|
|
||||||
Theme object for design tokens shared between rules.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
theme: {
|
|
||||||
colors: {
|
|
||||||
brand: '#942192',
|
|
||||||
},
|
|
||||||
breakpoints: {
|
|
||||||
sm: '640px',
|
|
||||||
md: '768px',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### presets
|
|
||||||
Predefined configurations bundling rules, variants, and themes.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presets: [
|
|
||||||
presetWind3(),
|
|
||||||
presetIcons(),
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
### transformers
|
|
||||||
Transform source code to support special syntax.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
transformers: [
|
|
||||||
transformerDirectives(),
|
|
||||||
transformerVariantGroup(),
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
### variants
|
|
||||||
Preprocess selectors with ability to rewrite CSS output.
|
|
||||||
|
|
||||||
### extractors
|
|
||||||
Handle source files and extract utility class names.
|
|
||||||
|
|
||||||
### preflights
|
|
||||||
Inject raw CSS globally.
|
|
||||||
|
|
||||||
### layers
|
|
||||||
Control the order of CSS layers. Default is `0`.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
layers: {
|
|
||||||
'components': -1,
|
|
||||||
'default': 1,
|
|
||||||
'utilities': 2,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### safelist
|
|
||||||
Utilities that are always included in output.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
safelist: ['p-1', 'p-2', 'p-3']
|
|
||||||
```
|
|
||||||
|
|
||||||
### blocklist
|
|
||||||
Utilities that are always excluded.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
blocklist: ['p-1', /^p-[2-4]$/]
|
|
||||||
```
|
|
||||||
|
|
||||||
### content
|
|
||||||
Configure where to extract utilities from.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
content: {
|
|
||||||
pipeline: {
|
|
||||||
include: [/\.(vue|svelte|tsx|html)($|\?)/],
|
|
||||||
},
|
|
||||||
filesystem: ['src/**/*.php'],
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### separators
|
|
||||||
Variant separator characters. Default: `[':', '-']`
|
|
||||||
|
|
||||||
### outputToCssLayers
|
|
||||||
Output UnoCSS layers as CSS Cascade Layers.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
outputToCssLayers: true
|
|
||||||
```
|
|
||||||
|
|
||||||
## Specifying Config File Location
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite.config.ts
|
|
||||||
import UnoCSS from 'unocss/vite'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [
|
|
||||||
UnoCSS({
|
|
||||||
configFile: '../my-uno.config.ts',
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://unocss.dev/guide/config-file
|
|
||||||
- https://unocss.dev/config/
|
|
||||||
-->
|
|
||||||
@ -1,137 +0,0 @@
|
|||||||
---
|
|
||||||
name: unocss-extracting
|
|
||||||
description: How UnoCSS extracts utilities from source code
|
|
||||||
---
|
|
||||||
|
|
||||||
# Extracting
|
|
||||||
|
|
||||||
UnoCSS searches for utility usages in your codebase and generates CSS on-demand.
|
|
||||||
|
|
||||||
## Content Sources
|
|
||||||
|
|
||||||
### Pipeline Extraction (Vite/Webpack)
|
|
||||||
|
|
||||||
Most efficient - extracts from build tool pipeline.
|
|
||||||
|
|
||||||
**Default file types:** `.jsx`, `.tsx`, `.vue`, `.md`, `.html`, `.svelte`, `.astro`, `.marko`
|
|
||||||
|
|
||||||
**Not included by default:** `.js`, `.ts`
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
content: {
|
|
||||||
pipeline: {
|
|
||||||
include: [
|
|
||||||
/\.(vue|svelte|[jt]sx|mdx?|astro|html)($|\?)/,
|
|
||||||
'src/**/*.{js,ts}', // Add js/ts
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Filesystem Extraction
|
|
||||||
|
|
||||||
For files not in build pipeline:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
content: {
|
|
||||||
filesystem: [
|
|
||||||
'src/**/*.php',
|
|
||||||
'public/*.html',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Inline Text Extraction
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
content: {
|
|
||||||
inline: [
|
|
||||||
'<div class="p-4 text-red">Some text</div>',
|
|
||||||
async () => (await fetch('https://example.com')).text(),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Magic Comments
|
|
||||||
|
|
||||||
### @unocss-include
|
|
||||||
|
|
||||||
Force scan a file:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// @unocss-include
|
|
||||||
export const classes = {
|
|
||||||
active: 'bg-primary text-white',
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### @unocss-ignore
|
|
||||||
|
|
||||||
Skip entire file:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// @unocss-ignore
|
|
||||||
```
|
|
||||||
|
|
||||||
### @unocss-skip-start / @unocss-skip-end
|
|
||||||
|
|
||||||
Skip specific blocks:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<p class="text-green">Extracted</p>
|
|
||||||
<!-- @unocss-skip-start -->
|
|
||||||
<p class="text-red">NOT extracted</p>
|
|
||||||
<!-- @unocss-skip-end -->
|
|
||||||
```
|
|
||||||
|
|
||||||
## Limitations
|
|
||||||
|
|
||||||
UnoCSS works at **build time** - dynamic classes don't work:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!-- Won't work! -->
|
|
||||||
<div class="p-${size}"></div>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Solutions
|
|
||||||
|
|
||||||
**1. Safelist** - Pre-generate known values:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
safelist: ['p-1', 'p-2', 'p-3', 'p-4']
|
|
||||||
```
|
|
||||||
|
|
||||||
**2. Static mapping** - List combinations statically:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const colors = {
|
|
||||||
red: 'text-red border-red',
|
|
||||||
blue: 'text-blue border-blue',
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**3. Runtime** - Use `@unocss/runtime` for true runtime generation.
|
|
||||||
|
|
||||||
## Custom Extractors
|
|
||||||
|
|
||||||
```ts
|
|
||||||
extractors: [
|
|
||||||
{
|
|
||||||
name: 'my-extractor',
|
|
||||||
extract({ code }) {
|
|
||||||
return code.match(/class:[\w-]+/g) || []
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://unocss.dev/guide/extracting
|
|
||||||
-->
|
|
||||||
@ -1,104 +0,0 @@
|
|||||||
---
|
|
||||||
name: unocss-layers-preflights
|
|
||||||
description: CSS layer ordering and raw CSS injection
|
|
||||||
---
|
|
||||||
|
|
||||||
# Layers and Preflights
|
|
||||||
|
|
||||||
Control CSS output order and inject global CSS.
|
|
||||||
|
|
||||||
## Layers
|
|
||||||
|
|
||||||
Set layer on rules:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
rules: [
|
|
||||||
[/^m-(\d)$/, ([, d]) => ({ margin: `${d / 4}rem` }), { layer: 'utilities' }],
|
|
||||||
['btn', { padding: '4px' }], // default layer
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Layer Ordering
|
|
||||||
|
|
||||||
```ts
|
|
||||||
layers: {
|
|
||||||
'components': -1,
|
|
||||||
'default': 1,
|
|
||||||
'utilities': 2,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Import Layers Separately
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import 'uno:components.css'
|
|
||||||
import 'uno.css'
|
|
||||||
import './my-custom.css'
|
|
||||||
import 'uno:utilities.css'
|
|
||||||
```
|
|
||||||
|
|
||||||
### CSS Cascade Layers
|
|
||||||
|
|
||||||
```ts
|
|
||||||
outputToCssLayers: true
|
|
||||||
|
|
||||||
// Or with custom names
|
|
||||||
outputToCssLayers: {
|
|
||||||
cssLayerName: (layer) => {
|
|
||||||
if (layer === 'default') return 'utilities'
|
|
||||||
if (layer === 'shortcuts') return 'utilities.shortcuts'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Layer Variants
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!-- UnoCSS layer -->
|
|
||||||
<p class="uno-layer-my-layer:text-xl">
|
|
||||||
|
|
||||||
<!-- CSS @layer -->
|
|
||||||
<p class="layer-my-layer:text-xl">
|
|
||||||
```
|
|
||||||
|
|
||||||
## Preflights
|
|
||||||
|
|
||||||
Inject raw CSS globally:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
preflights: [
|
|
||||||
{
|
|
||||||
getCSS: ({ theme }) => `
|
|
||||||
* {
|
|
||||||
color: ${theme.colors.gray?.[700] ?? '#333'};
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
With layer:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
preflights: [
|
|
||||||
{
|
|
||||||
layer: 'base',
|
|
||||||
getCSS: () => `html { font-family: system-ui; }`,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
## preset-wind4 Layers
|
|
||||||
|
|
||||||
| Layer | Description | Order |
|
|
||||||
|-------|-------------|-------|
|
|
||||||
| `properties` | CSS @property rules | -200 |
|
|
||||||
| `theme` | Theme CSS variables | -150 |
|
|
||||||
| `base` | Reset styles | -100 |
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://unocss.dev/config/layers
|
|
||||||
- https://unocss.dev/config/preflights
|
|
||||||
-->
|
|
||||||
@ -1,166 +0,0 @@
|
|||||||
---
|
|
||||||
name: unocss-rules
|
|
||||||
description: Static and dynamic rules for generating CSS utilities in UnoCSS
|
|
||||||
---
|
|
||||||
|
|
||||||
# UnoCSS Rules
|
|
||||||
|
|
||||||
Rules define utility classes and the CSS they generate. UnoCSS has many built-in rules via presets and allows custom rules.
|
|
||||||
|
|
||||||
## Static Rules
|
|
||||||
|
|
||||||
Simple mapping from class name to CSS properties:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
rules: [
|
|
||||||
['m-1', { margin: '0.25rem' }],
|
|
||||||
['font-bold', { 'font-weight': 700 }],
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
Usage: `<div class="m-1">` generates `.m-1 { margin: 0.25rem; }`
|
|
||||||
|
|
||||||
**Note:** Use CSS property syntax with hyphens (e.g., `font-weight` not `fontWeight`). Quote properties with hyphens.
|
|
||||||
|
|
||||||
## Dynamic Rules
|
|
||||||
|
|
||||||
Use RegExp matcher with function body for flexible utilities:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
rules: [
|
|
||||||
// Match m-1, m-2, m-100, etc.
|
|
||||||
[/^m-(\d+)$/, ([, d]) => ({ margin: `${d / 4}rem` })],
|
|
||||||
|
|
||||||
// Access theme and context
|
|
||||||
[/^p-(\d+)$/, (match, ctx) => ({ padding: `${match[1] / 4}rem` })],
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
The function receives:
|
|
||||||
1. RegExp match result (destructure to get captured groups)
|
|
||||||
2. Context object with `theme`, `symbols`, etc.
|
|
||||||
|
|
||||||
## CSS Fallback Values
|
|
||||||
|
|
||||||
Return 2D array for CSS property fallbacks (browser compatibility):
|
|
||||||
|
|
||||||
```ts
|
|
||||||
rules: [
|
|
||||||
[/^h-(\d+)dvh$/, ([_, d]) => [
|
|
||||||
['height', `${d}vh`],
|
|
||||||
['height', `${d}dvh`],
|
|
||||||
]],
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
Generates: `.h-100dvh { height: 100vh; height: 100dvh; }`
|
|
||||||
|
|
||||||
## Special Symbols
|
|
||||||
|
|
||||||
Control CSS output with symbols from `@unocss/core`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { symbols } from '@unocss/core'
|
|
||||||
|
|
||||||
rules: [
|
|
||||||
['grid', {
|
|
||||||
[symbols.parent]: '@supports (display: grid)',
|
|
||||||
display: 'grid',
|
|
||||||
}],
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Available Symbols
|
|
||||||
|
|
||||||
| Symbol | Description |
|
|
||||||
|--------|-------------|
|
|
||||||
| `symbols.parent` | Parent wrapper (e.g., `@supports`, `@media`) |
|
|
||||||
| `symbols.selector` | Function to modify the selector |
|
|
||||||
| `symbols.layer` | Set the UnoCSS layer |
|
|
||||||
| `symbols.variants` | Array of variant handlers |
|
|
||||||
| `symbols.shortcutsNoMerge` | Disable merging in shortcuts |
|
|
||||||
| `symbols.noMerge` | Disable rule merging |
|
|
||||||
| `symbols.sort` | Override sorting order |
|
|
||||||
| `symbols.body` | Full control of CSS body |
|
|
||||||
|
|
||||||
## Multi-Selector Rules
|
|
||||||
|
|
||||||
Use generator functions to yield multiple CSS rules:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
rules: [
|
|
||||||
[/^button-(.*)$/, function* ([, color], { symbols }) {
|
|
||||||
yield { background: color }
|
|
||||||
yield {
|
|
||||||
[symbols.selector]: selector => `${selector}:hover`,
|
|
||||||
background: `color-mix(in srgb, ${color} 90%, black)`
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
Generates both `.button-red { background: red; }` and `.button-red:hover { ... }`
|
|
||||||
|
|
||||||
## Fully Controlled Rules
|
|
||||||
|
|
||||||
Return a string for complete CSS control (advanced):
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineConfig, toEscapedSelector as e } from 'unocss'
|
|
||||||
|
|
||||||
rules: [
|
|
||||||
[/^custom-(.+)$/, ([, name], { rawSelector, theme }) => {
|
|
||||||
const selector = e(rawSelector)
|
|
||||||
return `
|
|
||||||
${selector} { font-size: ${theme.fontSize.sm}; }
|
|
||||||
${selector}::after { content: 'after'; }
|
|
||||||
@media (min-width: ${theme.breakpoints.sm}) {
|
|
||||||
${selector} { font-size: ${theme.fontSize.lg}; }
|
|
||||||
}
|
|
||||||
`
|
|
||||||
}],
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Warning:** Fully controlled rules don't work with variants like `hover:`.
|
|
||||||
|
|
||||||
## Symbols.body for Variant Support
|
|
||||||
|
|
||||||
Use `symbols.body` to keep variant support with custom CSS:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
rules: [
|
|
||||||
['custom-red', {
|
|
||||||
[symbols.body]: `
|
|
||||||
font-size: 1rem;
|
|
||||||
&::after { content: 'after'; }
|
|
||||||
& > .bar { color: red; }
|
|
||||||
`,
|
|
||||||
[symbols.selector]: selector => `:is(${selector})`,
|
|
||||||
}]
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Rule Ordering
|
|
||||||
|
|
||||||
Later rules have higher priority. Dynamic rules output is sorted alphabetically within the group.
|
|
||||||
|
|
||||||
## Rule Merging
|
|
||||||
|
|
||||||
UnoCSS merges rules with identical CSS bodies:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<div class="m-2 hover:m2">
|
|
||||||
```
|
|
||||||
|
|
||||||
Generates:
|
|
||||||
```css
|
|
||||||
.hover\:m2:hover, .m-2 { margin: 0.5rem; }
|
|
||||||
```
|
|
||||||
|
|
||||||
Use `symbols.noMerge` to disable.
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://unocss.dev/config/rules
|
|
||||||
-->
|
|
||||||
@ -1,105 +0,0 @@
|
|||||||
---
|
|
||||||
name: unocss-safelist-blocklist
|
|
||||||
description: Force include or exclude specific utilities
|
|
||||||
---
|
|
||||||
|
|
||||||
# Safelist and Blocklist
|
|
||||||
|
|
||||||
Control which utilities are always included or excluded.
|
|
||||||
|
|
||||||
## Safelist
|
|
||||||
|
|
||||||
Utilities always included, regardless of detection:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
safelist: [
|
|
||||||
'p-1', 'p-2', 'p-3',
|
|
||||||
// Dynamic generation
|
|
||||||
...Array.from({ length: 4 }, (_, i) => `p-${i + 1}`),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Function Form
|
|
||||||
|
|
||||||
```ts
|
|
||||||
safelist: [
|
|
||||||
'p-1',
|
|
||||||
() => ['m-1', 'm-2'],
|
|
||||||
(context) => {
|
|
||||||
const colors = Object.keys(context.theme.colors || {})
|
|
||||||
return colors.map(c => `bg-${c}-500`)
|
|
||||||
},
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Common Use Cases
|
|
||||||
|
|
||||||
```ts
|
|
||||||
safelist: [
|
|
||||||
// Dynamic colors from CMS
|
|
||||||
() => ['primary', 'secondary'].flatMap(c => [
|
|
||||||
`bg-${c}`, `text-${c}`, `border-${c}`,
|
|
||||||
]),
|
|
||||||
|
|
||||||
// Component variants
|
|
||||||
() => {
|
|
||||||
const variants = ['primary', 'danger']
|
|
||||||
const sizes = ['sm', 'md', 'lg']
|
|
||||||
return variants.flatMap(v => sizes.map(s => `btn-${v}-${s}`))
|
|
||||||
},
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Blocklist
|
|
||||||
|
|
||||||
Utilities never generated:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
blocklist: [
|
|
||||||
'p-1', // Exact match
|
|
||||||
/^p-[2-4]$/, // Regex
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Messages
|
|
||||||
|
|
||||||
```ts
|
|
||||||
blocklist: [
|
|
||||||
['bg-red-500', { message: 'Use bg-red-600 instead' }],
|
|
||||||
[/^text-xs$/, { message: 'Use text-sm for accessibility' }],
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Safelist vs Blocklist
|
|
||||||
|
|
||||||
| Feature | Safelist | Blocklist |
|
|
||||||
|---------|----------|-----------|
|
|
||||||
| Purpose | Always include | Always exclude |
|
|
||||||
| Strings | ✅ | ✅ |
|
|
||||||
| Regex | ❌ | ✅ |
|
|
||||||
| Functions | ✅ | ❌ |
|
|
||||||
|
|
||||||
**Note:** Blocklist wins if utility is in both.
|
|
||||||
|
|
||||||
## Best Practice
|
|
||||||
|
|
||||||
Prefer static mappings over safelist:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Better: UnoCSS extracts automatically
|
|
||||||
const sizes = {
|
|
||||||
sm: 'text-sm p-2',
|
|
||||||
md: 'text-base p-4',
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid: Large safelist
|
|
||||||
safelist: ['text-sm', 'text-base', 'p-2', 'p-4']
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://unocss.dev/config/safelist
|
|
||||||
- https://unocss.dev/guide/extracting
|
|
||||||
-->
|
|
||||||
@ -1,89 +0,0 @@
|
|||||||
---
|
|
||||||
name: unocss-shortcuts
|
|
||||||
description: Combine multiple utility rules into single shorthand classes
|
|
||||||
---
|
|
||||||
|
|
||||||
# UnoCSS Shortcuts
|
|
||||||
|
|
||||||
Shortcuts combine multiple rules into a single shorthand, inspired by Windi CSS.
|
|
||||||
|
|
||||||
## Static Shortcuts
|
|
||||||
|
|
||||||
Define as an object mapping shortcut names to utility combinations:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
shortcuts: {
|
|
||||||
// Multiple utilities combined
|
|
||||||
'btn': 'py-2 px-4 font-semibold rounded-lg shadow-md',
|
|
||||||
'btn-green': 'text-white bg-green-500 hover:bg-green-700',
|
|
||||||
// Single utility alias
|
|
||||||
'red': 'text-red-100',
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
```html
|
|
||||||
<button class="btn btn-green">Click me</button>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Dynamic Shortcuts
|
|
||||||
|
|
||||||
Use RegExp matcher with function, similar to dynamic rules:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
shortcuts: [
|
|
||||||
// Static shortcuts can be in array too
|
|
||||||
{
|
|
||||||
btn: 'py-2 px-4 font-semibold rounded-lg shadow-md',
|
|
||||||
},
|
|
||||||
// Dynamic shortcut
|
|
||||||
[/^btn-(.*)$/, ([, c]) => `bg-${c}-400 text-${c}-100 py-2 px-4 rounded-lg`],
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
Now `btn-green` and `btn-red` generate:
|
|
||||||
|
|
||||||
```css
|
|
||||||
.btn-green {
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
--un-bg-opacity: 1;
|
|
||||||
background-color: rgb(74 222 128 / var(--un-bg-opacity));
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
--un-text-opacity: 1;
|
|
||||||
color: rgb(220 252 231 / var(--un-text-opacity));
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Accessing Theme in Shortcuts
|
|
||||||
|
|
||||||
Dynamic shortcuts receive context with theme access:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
shortcuts: [
|
|
||||||
[/^badge-(.*)$/, ([, c], { theme }) => {
|
|
||||||
if (Object.keys(theme.colors).includes(c))
|
|
||||||
return `bg-${c}4:10 text-${c}5 rounded`
|
|
||||||
}],
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Shortcuts Layer
|
|
||||||
|
|
||||||
Shortcuts are output to the `shortcuts` layer by default. Configure with:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
shortcutsLayer: 'my-shortcuts-layer'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Key Points
|
|
||||||
|
|
||||||
- Later shortcuts have higher priority
|
|
||||||
- Shortcuts can reference other shortcuts
|
|
||||||
- Dynamic shortcuts work like dynamic rules
|
|
||||||
- Shortcuts are expanded at build time, not runtime
|
|
||||||
- All variants work with shortcuts (`hover:btn`, `dark:btn`, etc.)
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://unocss.dev/config/shortcuts
|
|
||||||
-->
|
|
||||||
@ -1,172 +0,0 @@
|
|||||||
---
|
|
||||||
name: unocss-theme
|
|
||||||
description: Theming system for colors, breakpoints, and design tokens
|
|
||||||
---
|
|
||||||
|
|
||||||
# UnoCSS Theme
|
|
||||||
|
|
||||||
UnoCSS supports theming similar to Tailwind CSS / Windi CSS. The `theme` property is deep-merged with the default theme.
|
|
||||||
|
|
||||||
## Basic Usage
|
|
||||||
|
|
||||||
```ts
|
|
||||||
theme: {
|
|
||||||
colors: {
|
|
||||||
veryCool: '#0000ff', // class="text-very-cool"
|
|
||||||
brand: {
|
|
||||||
primary: 'hsl(var(--hue, 217) 78% 51%)', // class="bg-brand-primary"
|
|
||||||
DEFAULT: '#942192' // class="bg-brand"
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Using Theme in Rules
|
|
||||||
|
|
||||||
Access theme values in dynamic rules:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
rules: [
|
|
||||||
[/^text-(.*)$/, ([, c], { theme }) => {
|
|
||||||
if (theme.colors[c])
|
|
||||||
return { color: theme.colors[c] }
|
|
||||||
}],
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Using Theme in Variants
|
|
||||||
|
|
||||||
```ts
|
|
||||||
variants: [
|
|
||||||
{
|
|
||||||
name: 'variant-name',
|
|
||||||
match(matcher, { theme }) {
|
|
||||||
// Access theme.breakpoints, theme.colors, etc.
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Using Theme in Shortcuts
|
|
||||||
|
|
||||||
```ts
|
|
||||||
shortcuts: [
|
|
||||||
[/^badge-(.*)$/, ([, c], { theme }) => {
|
|
||||||
if (Object.keys(theme.colors).includes(c))
|
|
||||||
return `bg-${c}4:10 text-${c}5 rounded`
|
|
||||||
}],
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Breakpoints
|
|
||||||
|
|
||||||
**Warning:** Custom `breakpoints` object **overrides** the default, not merges.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
theme: {
|
|
||||||
breakpoints: {
|
|
||||||
sm: '320px',
|
|
||||||
md: '640px',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Only `sm:` and `md:` variants will be available.
|
|
||||||
|
|
||||||
### Inherit Default Breakpoints
|
|
||||||
|
|
||||||
Use `extendTheme` to merge with defaults:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
extendTheme: (theme) => {
|
|
||||||
return {
|
|
||||||
...theme,
|
|
||||||
breakpoints: {
|
|
||||||
...theme.breakpoints,
|
|
||||||
sm: '320px',
|
|
||||||
md: '640px',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** `verticalBreakpoints` works the same for vertical layout.
|
|
||||||
|
|
||||||
### Breakpoint Sorting
|
|
||||||
|
|
||||||
Breakpoints are sorted by size. Use consistent units to avoid errors:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
theme: {
|
|
||||||
breakpoints: {
|
|
||||||
sm: '320px',
|
|
||||||
// Don't mix units - convert rem to px
|
|
||||||
// md: '40rem', // Bad
|
|
||||||
md: `${40 * 16}px`, // Good
|
|
||||||
lg: '960px',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## ExtendTheme
|
|
||||||
|
|
||||||
`extendTheme` lets you modify the merged theme object:
|
|
||||||
|
|
||||||
### Mutate Theme
|
|
||||||
|
|
||||||
```ts
|
|
||||||
extendTheme: (theme) => {
|
|
||||||
theme.colors.veryCool = '#0000ff'
|
|
||||||
theme.colors.brand = {
|
|
||||||
primary: 'hsl(var(--hue, 217) 78% 51%)',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Replace Theme
|
|
||||||
|
|
||||||
Return a new object to completely replace:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
extendTheme: (theme) => {
|
|
||||||
return {
|
|
||||||
...theme,
|
|
||||||
colors: {
|
|
||||||
...theme.colors,
|
|
||||||
veryCool: '#0000ff',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Theme Differences in Presets
|
|
||||||
|
|
||||||
### preset-wind3 vs preset-wind4
|
|
||||||
|
|
||||||
| preset-wind3 | preset-wind4 |
|
|
||||||
|--------------|--------------|
|
|
||||||
| `fontFamily` | `font` |
|
|
||||||
| `fontSize` | `text.fontSize` |
|
|
||||||
| `lineHeight` | `text.lineHeight` or `leading` |
|
|
||||||
| `letterSpacing` | `text.letterSpacing` or `tracking` |
|
|
||||||
| `borderRadius` | `radius` |
|
|
||||||
| `easing` | `ease` |
|
|
||||||
| `breakpoints` | `breakpoint` |
|
|
||||||
| `boxShadow` | `shadow` |
|
|
||||||
| `transitionProperty` | `property` |
|
|
||||||
|
|
||||||
## Common Theme Keys
|
|
||||||
|
|
||||||
- `colors` - Color palette
|
|
||||||
- `breakpoints` - Responsive breakpoints
|
|
||||||
- `fontFamily` - Font stacks
|
|
||||||
- `fontSize` - Text sizes
|
|
||||||
- `spacing` - Spacing scale
|
|
||||||
- `borderRadius` - Border radius values
|
|
||||||
- `boxShadow` - Shadow definitions
|
|
||||||
- `animation` - Animation keyframes and timing
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://unocss.dev/config/theme
|
|
||||||
-->
|
|
||||||
@ -1,175 +0,0 @@
|
|||||||
---
|
|
||||||
name: unocss-variants
|
|
||||||
description: Apply variations like hover:, dark:, responsive to rules
|
|
||||||
---
|
|
||||||
|
|
||||||
# UnoCSS Variants
|
|
||||||
|
|
||||||
Variants apply modifications to utility rules, like `hover:`, `dark:`, or responsive prefixes.
|
|
||||||
|
|
||||||
## How Variants Work
|
|
||||||
|
|
||||||
When matching `hover:m-2`:
|
|
||||||
|
|
||||||
1. `hover:m-2` is extracted from source
|
|
||||||
2. Sent to all variants for matching
|
|
||||||
3. `hover:` variant matches and returns `m-2`
|
|
||||||
4. Result `m-2` continues to next variants
|
|
||||||
5. Finally matches the rule `.m-2 { margin: 0.5rem; }`
|
|
||||||
6. Variant transformation applied: `.hover\:m-2:hover { margin: 0.5rem; }`
|
|
||||||
|
|
||||||
## Creating Custom Variants
|
|
||||||
|
|
||||||
```ts
|
|
||||||
variants: [
|
|
||||||
// hover: variant
|
|
||||||
(matcher) => {
|
|
||||||
if (!matcher.startsWith('hover:'))
|
|
||||||
return matcher
|
|
||||||
return {
|
|
||||||
// Remove prefix, pass to next variants/rules
|
|
||||||
matcher: matcher.slice(6),
|
|
||||||
// Modify the selector
|
|
||||||
selector: s => `${s}:hover`,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
|
||||||
rules: [
|
|
||||||
[/^m-(\d)$/, ([, d]) => ({ margin: `${d / 4}rem` })],
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Variant Return Object
|
|
||||||
|
|
||||||
- `matcher` - The processed class name to pass forward
|
|
||||||
- `selector` - Function to customize the CSS selector
|
|
||||||
- `parent` - Wrapper like `@media`, `@supports`
|
|
||||||
- `layer` - Specify output layer
|
|
||||||
- `sort` - Control ordering
|
|
||||||
|
|
||||||
## Built-in Variants (preset-wind3)
|
|
||||||
|
|
||||||
### Pseudo-classes
|
|
||||||
- `hover:`, `focus:`, `active:`, `visited:`
|
|
||||||
- `first:`, `last:`, `odd:`, `even:`
|
|
||||||
- `disabled:`, `checked:`, `required:`
|
|
||||||
- `focus-within:`, `focus-visible:`
|
|
||||||
|
|
||||||
### Pseudo-elements
|
|
||||||
- `before:`, `after:`
|
|
||||||
- `placeholder:`, `selection:`
|
|
||||||
- `marker:`, `file:`
|
|
||||||
|
|
||||||
### Responsive
|
|
||||||
- `sm:`, `md:`, `lg:`, `xl:`, `2xl:`
|
|
||||||
- `lt-sm:` (less than sm)
|
|
||||||
- `at-lg:` (at lg only)
|
|
||||||
|
|
||||||
### Dark Mode
|
|
||||||
- `dark:` - Class-based dark mode (default)
|
|
||||||
- `@dark:` - Media query dark mode
|
|
||||||
|
|
||||||
### Group/Peer
|
|
||||||
- `group-hover:`, `group-focus:`
|
|
||||||
- `peer-checked:`, `peer-focus:`
|
|
||||||
|
|
||||||
### Container Queries
|
|
||||||
- `@container`, `@sm:`, `@md:`
|
|
||||||
|
|
||||||
### Print
|
|
||||||
- `print:`
|
|
||||||
|
|
||||||
### Supports
|
|
||||||
- `supports-[display:grid]:`
|
|
||||||
|
|
||||||
### Aria
|
|
||||||
- `aria-checked:`, `aria-disabled:`
|
|
||||||
|
|
||||||
### Data Attributes
|
|
||||||
- `data-[state=open]:`
|
|
||||||
|
|
||||||
## Dark Mode Configuration
|
|
||||||
|
|
||||||
### Class-based (default)
|
|
||||||
```ts
|
|
||||||
presetWind3({
|
|
||||||
dark: 'class'
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
```html
|
|
||||||
<div class="dark:bg-gray-800">
|
|
||||||
```
|
|
||||||
|
|
||||||
Generates: `.dark .dark\:bg-gray-800 { ... }`
|
|
||||||
|
|
||||||
### Media Query
|
|
||||||
```ts
|
|
||||||
presetWind3({
|
|
||||||
dark: 'media'
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Generates: `@media (prefers-color-scheme: dark) { ... }`
|
|
||||||
|
|
||||||
### Opt-in Media Query
|
|
||||||
Use `@dark:` regardless of config:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<div class="@dark:bg-gray-800">
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom Selectors
|
|
||||||
```ts
|
|
||||||
presetWind3({
|
|
||||||
dark: {
|
|
||||||
light: '.light-mode',
|
|
||||||
dark: '.dark-mode',
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## CSS @layer Variant
|
|
||||||
|
|
||||||
Native CSS `@layer` support:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<div class="layer-foo:p-4" />
|
|
||||||
```
|
|
||||||
|
|
||||||
Generates:
|
|
||||||
```css
|
|
||||||
@layer foo {
|
|
||||||
.layer-foo\:p-4 { padding: 1rem; }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Breakpoint Differences from Windi CSS
|
|
||||||
|
|
||||||
| Windi CSS | UnoCSS |
|
|
||||||
|-----------|--------|
|
|
||||||
| `<sm:p-1` | `lt-sm:p-1` |
|
|
||||||
| `@lg:p-1` | `at-lg:p-1` |
|
|
||||||
| `>xl:p-1` | `xl:p-1` |
|
|
||||||
|
|
||||||
## Media Hover (Experimental)
|
|
||||||
|
|
||||||
Addresses sticky hover on touch devices:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<div class="@hover-text-red">
|
|
||||||
```
|
|
||||||
|
|
||||||
Generates:
|
|
||||||
```css
|
|
||||||
@media (hover: hover) and (pointer: fine) {
|
|
||||||
.\@hover-text-red:hover { color: rgb(248 113 113); }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://unocss.dev/config/variants
|
|
||||||
- https://unocss.dev/presets/wind3
|
|
||||||
- https://unocss.dev/presets/mini
|
|
||||||
-->
|
|
||||||
@ -1,199 +0,0 @@
|
|||||||
---
|
|
||||||
name: unocss-nuxt-integration
|
|
||||||
description: UnoCSS module for Nuxt applications
|
|
||||||
---
|
|
||||||
|
|
||||||
# UnoCSS Nuxt Integration
|
|
||||||
|
|
||||||
The official Nuxt module for UnoCSS.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm add -D unocss @unocss/nuxt
|
|
||||||
```
|
|
||||||
|
|
||||||
Add to Nuxt config:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// nuxt.config.ts
|
|
||||||
export default defineNuxtConfig({
|
|
||||||
modules: [
|
|
||||||
'@unocss/nuxt',
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Create config file:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// uno.config.ts
|
|
||||||
import { defineConfig, presetWind3 } from 'unocss'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
presets: [
|
|
||||||
presetWind3(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** The `uno.css` entry is automatically injected by the module.
|
|
||||||
|
|
||||||
## Support Status
|
|
||||||
|
|
||||||
| Build Tool | Nuxt 2 | Nuxt Bridge | Nuxt 3 |
|
|
||||||
|------------|--------|-------------|--------|
|
|
||||||
| Webpack Dev | ✅ | ✅ | 🚧 |
|
|
||||||
| Webpack Build | ✅ | ✅ | ✅ |
|
|
||||||
| Vite Dev | - | ✅ | ✅ |
|
|
||||||
| Vite Build | - | ✅ | ✅ |
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
### Using uno.config.ts (Recommended)
|
|
||||||
|
|
||||||
Use a dedicated config file for best IDE support:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// uno.config.ts
|
|
||||||
import { defineConfig, presetWind3, presetIcons } from 'unocss'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
presets: [
|
|
||||||
presetWind3(),
|
|
||||||
presetIcons(),
|
|
||||||
],
|
|
||||||
shortcuts: {
|
|
||||||
'btn': 'py-2 px-4 font-semibold rounded-lg',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Nuxt Layers Support
|
|
||||||
|
|
||||||
Enable automatic config merging from Nuxt layers:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// nuxt.config.ts
|
|
||||||
export default defineNuxtConfig({
|
|
||||||
unocss: {
|
|
||||||
nuxtLayers: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Then in your root config:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// uno.config.ts
|
|
||||||
import config from './.nuxt/uno.config.mjs'
|
|
||||||
|
|
||||||
export default config
|
|
||||||
```
|
|
||||||
|
|
||||||
Or extend the merged config:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// uno.config.ts
|
|
||||||
import { mergeConfigs } from '@unocss/core'
|
|
||||||
import config from './.nuxt/uno.config.mjs'
|
|
||||||
|
|
||||||
export default mergeConfigs([config, {
|
|
||||||
// Your overrides
|
|
||||||
shortcuts: {
|
|
||||||
'custom': 'text-red-500',
|
|
||||||
},
|
|
||||||
}])
|
|
||||||
```
|
|
||||||
|
|
||||||
## Common Setup Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// nuxt.config.ts
|
|
||||||
export default defineNuxtConfig({
|
|
||||||
modules: [
|
|
||||||
'@unocss/nuxt',
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// uno.config.ts
|
|
||||||
import {
|
|
||||||
defineConfig,
|
|
||||||
presetAttributify,
|
|
||||||
presetIcons,
|
|
||||||
presetTypography,
|
|
||||||
presetWebFonts,
|
|
||||||
presetWind3,
|
|
||||||
transformerDirectives,
|
|
||||||
transformerVariantGroup,
|
|
||||||
} from 'unocss'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
presets: [
|
|
||||||
presetWind3(),
|
|
||||||
presetAttributify(),
|
|
||||||
presetIcons({
|
|
||||||
scale: 1.2,
|
|
||||||
}),
|
|
||||||
presetTypography(),
|
|
||||||
presetWebFonts({
|
|
||||||
fonts: {
|
|
||||||
sans: 'DM Sans',
|
|
||||||
mono: 'DM Mono',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
transformers: [
|
|
||||||
transformerDirectives(),
|
|
||||||
transformerVariantGroup(),
|
|
||||||
],
|
|
||||||
shortcuts: [
|
|
||||||
['btn', 'px-4 py-1 rounded inline-block bg-teal-600 text-white cursor-pointer hover:bg-teal-700 disabled:cursor-default disabled:bg-gray-600 disabled:opacity-50'],
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage in Components
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<div class="p-4 text-center">
|
|
||||||
<h1 class="text-3xl font-bold text-blue-600">
|
|
||||||
Hello UnoCSS!
|
|
||||||
</h1>
|
|
||||||
<button class="btn mt-4">
|
|
||||||
Click me
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
With attributify mode:
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<div p="4" text="center">
|
|
||||||
<h1 text="3xl blue-600" font="bold">
|
|
||||||
Hello UnoCSS!
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Inspector
|
|
||||||
|
|
||||||
In development, visit `/_nuxt/__unocss` to access the UnoCSS inspector.
|
|
||||||
|
|
||||||
## Key Differences from Vite
|
|
||||||
|
|
||||||
- No need to import `virtual:uno.css` - automatically injected
|
|
||||||
- Config file discovery works the same
|
|
||||||
- All Vite plugin features available
|
|
||||||
- Nuxt layers config merging available
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://unocss.dev/integrations/nuxt
|
|
||||||
-->
|
|
||||||
@ -1,283 +0,0 @@
|
|||||||
---
|
|
||||||
name: unocss-vite-integration
|
|
||||||
description: Setting up UnoCSS with Vite and framework-specific tips
|
|
||||||
---
|
|
||||||
|
|
||||||
# UnoCSS Vite Integration
|
|
||||||
|
|
||||||
The Vite plugin is the most common way to use UnoCSS.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm add -D unocss
|
|
||||||
```
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite.config.ts
|
|
||||||
import UnoCSS from 'unocss/vite'
|
|
||||||
import { defineConfig } from 'vite'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [
|
|
||||||
UnoCSS(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Create config file:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// uno.config.ts
|
|
||||||
import { defineConfig, presetWind3 } from 'unocss'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
presets: [
|
|
||||||
presetWind3(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Add to entry:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// main.ts
|
|
||||||
import 'virtual:uno.css'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Modes
|
|
||||||
|
|
||||||
### global (default)
|
|
||||||
|
|
||||||
Standard mode - generates global CSS injected via `uno.css` import.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import 'virtual:uno.css'
|
|
||||||
```
|
|
||||||
|
|
||||||
### vue-scoped
|
|
||||||
|
|
||||||
Injects generated CSS into Vue SFC `<style scoped>`.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
UnoCSS({
|
|
||||||
mode: 'vue-scoped',
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### shadow-dom
|
|
||||||
|
|
||||||
For Web Components using Shadow DOM:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
UnoCSS({
|
|
||||||
mode: 'shadow-dom',
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Add placeholder in component styles:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const template = document.createElement('template')
|
|
||||||
template.innerHTML = `
|
|
||||||
<style>
|
|
||||||
:host { ... }
|
|
||||||
@unocss-placeholder
|
|
||||||
</style>
|
|
||||||
<div class="m-1em">...</div>
|
|
||||||
`
|
|
||||||
```
|
|
||||||
|
|
||||||
### per-module (experimental)
|
|
||||||
|
|
||||||
Generates CSS per module with optional scoping.
|
|
||||||
|
|
||||||
### dist-chunk (experimental)
|
|
||||||
|
|
||||||
Generates CSS per chunk on build for MPA.
|
|
||||||
|
|
||||||
## DevTools Support
|
|
||||||
|
|
||||||
Edit classes directly in browser DevTools:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import 'virtual:uno.css'
|
|
||||||
import 'virtual:unocss-devtools'
|
|
||||||
```
|
|
||||||
|
|
||||||
**Warning:** Uses MutationObserver to detect changes. Dynamic classes from scripts will also be included.
|
|
||||||
|
|
||||||
## Framework-Specific Setup
|
|
||||||
|
|
||||||
### React
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite.config.ts
|
|
||||||
import React from '@vitejs/plugin-react'
|
|
||||||
import UnoCSS from 'unocss/vite'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
plugins: [
|
|
||||||
UnoCSS(), // Must be before React when using attributify
|
|
||||||
React(),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** Remove `tsc` from build script if using `@unocss/preset-attributify`.
|
|
||||||
|
|
||||||
### Vue
|
|
||||||
|
|
||||||
Works out of the box with `@vitejs/plugin-vue`.
|
|
||||||
|
|
||||||
### Svelte
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { svelte } from '@sveltejs/vite-plugin-svelte'
|
|
||||||
import extractorSvelte from '@unocss/extractor-svelte'
|
|
||||||
import UnoCSS from 'unocss/vite'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
plugins: [
|
|
||||||
UnoCSS({
|
|
||||||
extractors: [extractorSvelte()],
|
|
||||||
}),
|
|
||||||
svelte(),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Supports `class:foo` and `class:foo={bar}` syntax.
|
|
||||||
|
|
||||||
### SvelteKit
|
|
||||||
|
|
||||||
Same as Svelte, use `sveltekit()` from `@sveltejs/kit/vite`.
|
|
||||||
|
|
||||||
### Solid
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import UnoCSS from 'unocss/vite'
|
|
||||||
import solidPlugin from 'vite-plugin-solid'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
plugins: [
|
|
||||||
UnoCSS(),
|
|
||||||
solidPlugin(),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Preact
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import Preact from '@preact/preset-vite'
|
|
||||||
import UnoCSS from 'unocss/vite'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
plugins: [
|
|
||||||
UnoCSS(),
|
|
||||||
Preact(),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Elm
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import Elm from 'vite-plugin-elm'
|
|
||||||
import UnoCSS from 'unocss/vite'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
plugins: [
|
|
||||||
Elm(),
|
|
||||||
UnoCSS(),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Web Components (Lit)
|
|
||||||
|
|
||||||
```ts
|
|
||||||
UnoCSS({
|
|
||||||
mode: 'shadow-dom',
|
|
||||||
shortcuts: [
|
|
||||||
{ 'cool-blue': 'bg-blue-500 text-white' },
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// my-element.ts
|
|
||||||
@customElement('my-element')
|
|
||||||
export class MyElement extends LitElement {
|
|
||||||
static styles = css`
|
|
||||||
:host { ... }
|
|
||||||
@unocss-placeholder
|
|
||||||
`
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Supports `part-[<part-name>]:<utility>` for `::part` styling.
|
|
||||||
|
|
||||||
## Inspector
|
|
||||||
|
|
||||||
Visit `http://localhost:5173/__unocss` in dev mode to:
|
|
||||||
|
|
||||||
- Inspect generated CSS rules
|
|
||||||
- See applied classes per file
|
|
||||||
- Test utilities in REPL
|
|
||||||
|
|
||||||
## Legacy Browser Support
|
|
||||||
|
|
||||||
With `@vitejs/plugin-legacy`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import legacy from '@vitejs/plugin-legacy'
|
|
||||||
import UnoCSS from 'unocss/vite'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
plugins: [
|
|
||||||
UnoCSS({
|
|
||||||
legacy: {
|
|
||||||
renderModernChunks: false,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
legacy({
|
|
||||||
targets: ['defaults', 'not IE 11'],
|
|
||||||
renderModernChunks: false,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## VanillaJS / TypeScript
|
|
||||||
|
|
||||||
By default, `.js` and `.ts` files are not extracted. Configure to include:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// uno.config.ts
|
|
||||||
export default defineConfig({
|
|
||||||
content: {
|
|
||||||
pipeline: {
|
|
||||||
include: [
|
|
||||||
/\.(vue|svelte|[jt]sx|html)($|\?)/,
|
|
||||||
'src/**/*.{js,ts}',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Or use magic comment in files:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// @unocss-include
|
|
||||||
export const classes = {
|
|
||||||
active: 'bg-primary text-white',
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://unocss.dev/integrations/vite
|
|
||||||
-->
|
|
||||||
@ -1,142 +0,0 @@
|
|||||||
---
|
|
||||||
name: preset-attributify
|
|
||||||
description: Group utilities in HTML attributes instead of class
|
|
||||||
---
|
|
||||||
|
|
||||||
# Preset Attributify
|
|
||||||
|
|
||||||
Group utilities in HTML attributes for better readability.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineConfig, presetAttributify, presetWind3 } from 'unocss'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
presets: [
|
|
||||||
presetWind3(),
|
|
||||||
presetAttributify(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Basic Usage
|
|
||||||
|
|
||||||
Instead of long class strings:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<button class="bg-blue-400 hover:bg-blue-500 text-sm text-white font-mono font-light py-2 px-4 rounded border-2 border-blue-200">
|
|
||||||
Button
|
|
||||||
</button>
|
|
||||||
```
|
|
||||||
|
|
||||||
Group by prefix in attributes:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<button
|
|
||||||
bg="blue-400 hover:blue-500"
|
|
||||||
text="sm white"
|
|
||||||
font="mono light"
|
|
||||||
p="y-2 x-4"
|
|
||||||
border="2 rounded blue-200"
|
|
||||||
>
|
|
||||||
Button
|
|
||||||
</button>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Prefix Self-Referencing
|
|
||||||
|
|
||||||
For utilities matching their prefix (`flex`, `grid`, `border`), use `~`:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!-- Before -->
|
|
||||||
<button class="border border-red">Button</button>
|
|
||||||
|
|
||||||
<!-- After -->
|
|
||||||
<button border="~ red">Button</button>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Valueless Attributify
|
|
||||||
|
|
||||||
Use utilities as boolean attributes:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<div m-2 rounded text-teal-400 />
|
|
||||||
```
|
|
||||||
|
|
||||||
## Handling Property Conflicts
|
|
||||||
|
|
||||||
When attribute names conflict with HTML properties:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!-- Use un- prefix -->
|
|
||||||
<a un-text="red">Text color to red</a>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Enforce Prefix
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presetAttributify({
|
|
||||||
prefix: 'un-',
|
|
||||||
prefixedOnly: true,
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Options
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presetAttributify({
|
|
||||||
strict: false, // Only generate CSS for attributify
|
|
||||||
prefix: 'un-', // Attribute prefix
|
|
||||||
prefixedOnly: false, // Require prefix for all
|
|
||||||
nonValuedAttribute: true, // Support valueless attributes
|
|
||||||
ignoreAttributes: [], // Attributes to ignore
|
|
||||||
trueToNonValued: false, // Treat value="true" as valueless
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## TypeScript Support
|
|
||||||
|
|
||||||
### Vue 3
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// html.d.ts
|
|
||||||
declare module '@vue/runtime-dom' {
|
|
||||||
interface HTMLAttributes { [key: string]: any }
|
|
||||||
}
|
|
||||||
declare module '@vue/runtime-core' {
|
|
||||||
interface AllowedComponentProps { [key: string]: any }
|
|
||||||
}
|
|
||||||
export {}
|
|
||||||
```
|
|
||||||
|
|
||||||
### React
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import type { AttributifyAttributes } from '@unocss/preset-attributify'
|
|
||||||
|
|
||||||
declare module 'react' {
|
|
||||||
interface HTMLAttributes<T> extends AttributifyAttributes {}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## JSX Support
|
|
||||||
|
|
||||||
For JSX where `<div foo>` becomes `<div foo={true}>`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { transformerAttributifyJsx } from 'unocss'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
transformers: [
|
|
||||||
transformerAttributifyJsx(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
**Important:** Only use attributify if `uno.config.*` shows `presetAttributify()` is enabled.
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://unocss.dev/presets/attributify
|
|
||||||
-->
|
|
||||||
@ -1,184 +0,0 @@
|
|||||||
---
|
|
||||||
name: preset-icons
|
|
||||||
description: Pure CSS icons using Iconify with any icon set
|
|
||||||
---
|
|
||||||
|
|
||||||
# Preset Icons
|
|
||||||
|
|
||||||
Use any icon as a pure CSS class, powered by Iconify.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm add -D @unocss/preset-icons @iconify-json/[collection-name]
|
|
||||||
```
|
|
||||||
|
|
||||||
Example: `@iconify-json/mdi` for Material Design Icons, `@iconify-json/carbon` for Carbon icons.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineConfig, presetIcons } from 'unocss'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
presets: [
|
|
||||||
presetIcons(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
Two naming conventions:
|
|
||||||
- `<prefix><collection>-<icon>` → `i-ph-anchor-simple-thin`
|
|
||||||
- `<prefix><collection>:<icon>` → `i-ph:anchor-simple-thin`
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!-- Phosphor anchor icon -->
|
|
||||||
<div class="i-ph-anchor-simple-thin" />
|
|
||||||
|
|
||||||
<!-- Material Design alarm with color -->
|
|
||||||
<div class="i-mdi-alarm text-orange-400" />
|
|
||||||
|
|
||||||
<!-- Large Vue logo -->
|
|
||||||
<div class="i-logos-vue text-3xl" />
|
|
||||||
|
|
||||||
<!-- Dynamic: Sun in light mode, Moon in dark -->
|
|
||||||
<button class="i-carbon-sun dark:i-carbon-moon" />
|
|
||||||
|
|
||||||
<!-- Hover effect -->
|
|
||||||
<div class="i-twemoji-grinning-face-with-smiling-eyes hover:i-twemoji-face-with-tears-of-joy" />
|
|
||||||
```
|
|
||||||
|
|
||||||
Browse icons at [icones.js.org](https://icones.js.org/) or [Iconify](https://icon-sets.iconify.design/).
|
|
||||||
|
|
||||||
## Icon Modes
|
|
||||||
|
|
||||||
Icons automatically choose between `mask` (monochrome) and `background-img` (colorful).
|
|
||||||
|
|
||||||
### Force Specific Mode
|
|
||||||
|
|
||||||
- `?mask` - Render as mask (colorable with `currentColor`)
|
|
||||||
- `?bg` - Render as background image (preserves original colors)
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!-- Original with colors -->
|
|
||||||
<div class="i-vscode-icons:file-type-light-pnpm" />
|
|
||||||
|
|
||||||
<!-- Force mask mode, apply custom color -->
|
|
||||||
<div class="i-vscode-icons:file-type-light-pnpm?mask text-red-300" />
|
|
||||||
```
|
|
||||||
|
|
||||||
## Options
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presetIcons({
|
|
||||||
scale: 1.2, // Scale relative to font size
|
|
||||||
prefix: 'i-', // Class prefix (default)
|
|
||||||
mode: 'auto', // 'auto' | 'mask' | 'bg'
|
|
||||||
extraProperties: {
|
|
||||||
'display': 'inline-block',
|
|
||||||
'vertical-align': 'middle',
|
|
||||||
},
|
|
||||||
warn: true, // Warn on missing icons
|
|
||||||
autoInstall: true, // Auto-install missing icon sets
|
|
||||||
cdn: 'https://esm.sh/', // CDN for browser usage
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Custom Icon Collections
|
|
||||||
|
|
||||||
### Inline SVGs
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presetIcons({
|
|
||||||
collections: {
|
|
||||||
custom: {
|
|
||||||
circle: '<svg viewBox="0 0 120 120"><circle cx="60" cy="60" r="50"></circle></svg>',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Usage: `<span class="i-custom:circle"></span>`
|
|
||||||
|
|
||||||
### File System Loader
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { FileSystemIconLoader } from '@iconify/utils/lib/loader/node-loaders'
|
|
||||||
|
|
||||||
presetIcons({
|
|
||||||
collections: {
|
|
||||||
'my-icons': FileSystemIconLoader(
|
|
||||||
'./assets/icons',
|
|
||||||
svg => svg.replace(/#fff/, 'currentColor')
|
|
||||||
),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Dynamic Import (Browser)
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import presetIcons from '@unocss/preset-icons/browser'
|
|
||||||
|
|
||||||
presetIcons({
|
|
||||||
collections: {
|
|
||||||
carbon: () => import('@iconify-json/carbon/icons.json').then(i => i.default),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Icon Customization
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presetIcons({
|
|
||||||
customizations: {
|
|
||||||
// Transform SVG
|
|
||||||
transform(svg, collection, icon) {
|
|
||||||
return svg.replace(/#fff/, 'currentColor')
|
|
||||||
},
|
|
||||||
// Global sizing
|
|
||||||
customize(props) {
|
|
||||||
props.width = '2em'
|
|
||||||
props.height = '2em'
|
|
||||||
return props
|
|
||||||
},
|
|
||||||
// Per-collection
|
|
||||||
iconCustomizer(collection, icon, props) {
|
|
||||||
if (collection === 'mdi') {
|
|
||||||
props.width = '2em'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## CSS Directive
|
|
||||||
|
|
||||||
Use `icon()` in CSS (requires transformer-directives):
|
|
||||||
|
|
||||||
```css
|
|
||||||
.icon {
|
|
||||||
background-image: icon('i-carbon-sun');
|
|
||||||
}
|
|
||||||
.icon-colored {
|
|
||||||
background-image: icon('i-carbon-moon', '#fff');
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Accessibility
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!-- With label -->
|
|
||||||
<a href="/profile" aria-label="Profile" class="i-ph:user-duotone"></a>
|
|
||||||
|
|
||||||
<!-- Decorative -->
|
|
||||||
<a href="/profile">
|
|
||||||
<span aria-hidden="true" class="i-ph:user-duotone"></span>
|
|
||||||
My Profile
|
|
||||||
</a>
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://unocss.dev/presets/icons
|
|
||||||
-->
|
|
||||||
@ -1,158 +0,0 @@
|
|||||||
---
|
|
||||||
name: preset-mini
|
|
||||||
description: Minimal preset with essential utilities for UnoCSS
|
|
||||||
---
|
|
||||||
|
|
||||||
# Preset Mini
|
|
||||||
|
|
||||||
The minimal preset with only essential rules and variants. Good starting point for custom presets.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineConfig, presetMini } from 'unocss'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
presets: [
|
|
||||||
presetMini(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## What's Included
|
|
||||||
|
|
||||||
Subset of `preset-wind3` with essential utilities aligned to CSS properties:
|
|
||||||
|
|
||||||
- Basic spacing (margin, padding)
|
|
||||||
- Display (flex, grid, block, etc.)
|
|
||||||
- Positioning (absolute, relative, fixed)
|
|
||||||
- Sizing (width, height)
|
|
||||||
- Colors (text, background, border)
|
|
||||||
- Typography basics (font-size, font-weight)
|
|
||||||
- Borders and border-radius
|
|
||||||
- Basic transforms and transitions
|
|
||||||
|
|
||||||
## What's NOT Included
|
|
||||||
|
|
||||||
Opinionated or complex Tailwind utilities:
|
|
||||||
- `container`
|
|
||||||
- Complex animations
|
|
||||||
- Gradients
|
|
||||||
- Advanced typography
|
|
||||||
- Prose classes
|
|
||||||
|
|
||||||
## Use Cases
|
|
||||||
|
|
||||||
1. **Building custom presets** - Start with mini and add only what you need
|
|
||||||
2. **Minimal bundle size** - When you only need basic utilities
|
|
||||||
3. **Learning** - Understand UnoCSS core without Tailwind complexity
|
|
||||||
|
|
||||||
## Dark Mode
|
|
||||||
|
|
||||||
Same as preset-wind3:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presetMini({
|
|
||||||
dark: 'class' // or 'media'
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
```html
|
|
||||||
<div class="dark:bg-red:10" />
|
|
||||||
```
|
|
||||||
|
|
||||||
Class-based:
|
|
||||||
```css
|
|
||||||
.dark .dark\:bg-red\:10 {
|
|
||||||
background-color: rgb(248 113 113 / 0.1);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Media query:
|
|
||||||
```css
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
.dark\:bg-red\:10 {
|
|
||||||
background-color: rgb(248 113 113 / 0.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## CSS @layer Variant
|
|
||||||
|
|
||||||
Native CSS layer support:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<div class="layer-foo:p4" />
|
|
||||||
```
|
|
||||||
|
|
||||||
```css
|
|
||||||
@layer foo {
|
|
||||||
.layer-foo\:p4 {
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Theme Customization
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presetMini({
|
|
||||||
theme: {
|
|
||||||
colors: {
|
|
||||||
veryCool: '#0000ff',
|
|
||||||
brand: {
|
|
||||||
primary: 'hsl(var(--hue, 217) 78% 51%)',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** `breakpoints` property is overridden, not merged.
|
|
||||||
|
|
||||||
## Options
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presetMini({
|
|
||||||
// Dark mode: 'class' | 'media' | { light: string, dark: string }
|
|
||||||
dark: 'class',
|
|
||||||
|
|
||||||
// Generate [group=""] instead of .group for attributify
|
|
||||||
attributifyPseudo: false,
|
|
||||||
|
|
||||||
// CSS variable prefix (default: 'un-')
|
|
||||||
variablePrefix: 'un-',
|
|
||||||
|
|
||||||
// Utility prefix
|
|
||||||
prefix: undefined,
|
|
||||||
|
|
||||||
// Preflight generation: true | false | 'on-demand'
|
|
||||||
preflight: true,
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Building on Mini
|
|
||||||
|
|
||||||
Create custom preset extending mini:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { presetMini } from 'unocss'
|
|
||||||
import type { Preset } from 'unocss'
|
|
||||||
|
|
||||||
export const myPreset: Preset = {
|
|
||||||
name: 'my-preset',
|
|
||||||
presets: [presetMini()],
|
|
||||||
rules: [
|
|
||||||
// Add custom rules
|
|
||||||
['card', { 'border-radius': '8px', 'box-shadow': '0 2px 8px rgba(0,0,0,0.1)' }],
|
|
||||||
],
|
|
||||||
shortcuts: {
|
|
||||||
'btn': 'px-4 py-2 rounded bg-blue-500 text-white',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://unocss.dev/presets/mini
|
|
||||||
-->
|
|
||||||
@ -1,97 +0,0 @@
|
|||||||
---
|
|
||||||
name: preset-rem-to-px
|
|
||||||
description: Convert rem units to px for utilities
|
|
||||||
---
|
|
||||||
|
|
||||||
# Preset Rem to Px
|
|
||||||
|
|
||||||
Converts `rem` units to `px` in generated utilities.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineConfig, presetRemToPx, presetWind3 } from 'unocss'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
presets: [
|
|
||||||
presetWind3(),
|
|
||||||
presetRemToPx(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## What It Does
|
|
||||||
|
|
||||||
Transforms all rem values to px:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<div class="p-4">
|
|
||||||
```
|
|
||||||
|
|
||||||
Without preset:
|
|
||||||
```css
|
|
||||||
.p-4 { padding: 1rem; }
|
|
||||||
```
|
|
||||||
|
|
||||||
With preset:
|
|
||||||
```css
|
|
||||||
.p-4 { padding: 16px; }
|
|
||||||
```
|
|
||||||
|
|
||||||
## Use Cases
|
|
||||||
|
|
||||||
- Projects requiring pixel-perfect designs
|
|
||||||
- Environments where rem doesn't work well
|
|
||||||
- Consistency with pixel-based design systems
|
|
||||||
- Email templates (better compatibility)
|
|
||||||
|
|
||||||
## Options
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presetRemToPx({
|
|
||||||
// Base font size for conversion (default: 16)
|
|
||||||
baseFontSize: 16,
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Custom base:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presetRemToPx({
|
|
||||||
baseFontSize: 14, // 1rem = 14px
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## With Preset Wind4
|
|
||||||
|
|
||||||
**Note:** `presetRemToPx` is not needed with `preset-wind4`. Use the built-in processor instead:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { createRemToPxProcessor } from '@unocss/preset-wind4/utils'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
presets: [
|
|
||||||
presetWind4({
|
|
||||||
preflights: {
|
|
||||||
theme: {
|
|
||||||
process: createRemToPxProcessor(),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
// Also apply to utilities
|
|
||||||
postprocess: [createRemToPxProcessor()],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Important Notes
|
|
||||||
|
|
||||||
- Order matters: place after the preset that generates rem values
|
|
||||||
- Affects all utilities with rem units
|
|
||||||
- Theme values in rem are also converted
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://unocss.dev/presets/rem-to-px
|
|
||||||
- https://unocss.dev/presets/wind4
|
|
||||||
-->
|
|
||||||
@ -1,134 +0,0 @@
|
|||||||
---
|
|
||||||
name: preset-tagify
|
|
||||||
description: Use utilities as HTML tag names
|
|
||||||
---
|
|
||||||
|
|
||||||
# Preset Tagify
|
|
||||||
|
|
||||||
Use CSS utilities directly as HTML tag names.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineConfig, presetTagify } from 'unocss'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
presets: [
|
|
||||||
presetTagify(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Basic Usage
|
|
||||||
|
|
||||||
Instead of:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<span class="text-red">red text</span>
|
|
||||||
<div class="flex">flexbox</div>
|
|
||||||
<span class="i-line-md-emoji-grin"></span>
|
|
||||||
```
|
|
||||||
|
|
||||||
Use tag names directly:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<text-red>red text</text-red>
|
|
||||||
<flex>flexbox</flex>
|
|
||||||
<i-line-md-emoji-grin />
|
|
||||||
```
|
|
||||||
|
|
||||||
Works exactly the same!
|
|
||||||
|
|
||||||
## With Prefix
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presetTagify({
|
|
||||||
prefix: 'un-'
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!-- Matched -->
|
|
||||||
<un-flex></un-flex>
|
|
||||||
|
|
||||||
<!-- Not matched -->
|
|
||||||
<flex></flex>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Extra Properties
|
|
||||||
|
|
||||||
Add CSS properties to matched tags:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presetTagify({
|
|
||||||
// Add display: inline-block to icons
|
|
||||||
extraProperties: matched => matched.startsWith('i-')
|
|
||||||
? { display: 'inline-block' }
|
|
||||||
: {},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Or apply to all:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presetTagify({
|
|
||||||
extraProperties: { display: 'block' }
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Options
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presetTagify({
|
|
||||||
// Tag prefix
|
|
||||||
prefix: '',
|
|
||||||
|
|
||||||
// Excluded tags (won't be processed)
|
|
||||||
excludedTags: ['b', /^h\d+$/, 'table'],
|
|
||||||
|
|
||||||
// Extra CSS properties
|
|
||||||
extraProperties: {},
|
|
||||||
|
|
||||||
// Enable default extractor
|
|
||||||
defaultExtractor: true,
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Excluded Tags
|
|
||||||
|
|
||||||
By default, these tags are excluded:
|
|
||||||
- `b` (bold)
|
|
||||||
- `h1` through `h6` (headings)
|
|
||||||
- `table`
|
|
||||||
|
|
||||||
Add your own:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presetTagify({
|
|
||||||
excludedTags: [
|
|
||||||
'b',
|
|
||||||
/^h\d+$/,
|
|
||||||
'table',
|
|
||||||
'article', // Add custom exclusions
|
|
||||||
/^my-/, // Exclude tags starting with 'my-'
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Use Cases
|
|
||||||
|
|
||||||
- Quick prototyping
|
|
||||||
- Cleaner HTML for simple pages
|
|
||||||
- Icon embedding: `<i-carbon-sun />`
|
|
||||||
- Semantic-like styling: `<flex-center>`, `<text-red>`
|
|
||||||
|
|
||||||
## Limitations
|
|
||||||
|
|
||||||
- Custom element names must contain a hyphen (HTML spec)
|
|
||||||
- Some frameworks may not support all custom elements
|
|
||||||
- Utilities without hyphens need the prefix option
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://unocss.dev/presets/tagify
|
|
||||||
-->
|
|
||||||
@ -1,95 +0,0 @@
|
|||||||
---
|
|
||||||
name: preset-typography
|
|
||||||
description: Prose classes for typographic defaults on vanilla HTML content
|
|
||||||
---
|
|
||||||
|
|
||||||
# Preset Typography
|
|
||||||
|
|
||||||
Prose classes for adding typographic defaults to vanilla HTML content.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineConfig, presetTypography, presetWind3 } from 'unocss'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
presets: [
|
|
||||||
presetWind3(), // Required!
|
|
||||||
presetTypography(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Basic Usage
|
|
||||||
|
|
||||||
```html
|
|
||||||
<article class="prose">
|
|
||||||
<h1>My Article</h1>
|
|
||||||
<p>This is styled with typographic defaults...</p>
|
|
||||||
</article>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Sizes
|
|
||||||
|
|
||||||
```html
|
|
||||||
<article class="prose prose-sm">Small</article>
|
|
||||||
<article class="prose prose-base">Base (default)</article>
|
|
||||||
<article class="prose prose-lg">Large</article>
|
|
||||||
<article class="prose prose-xl">Extra large</article>
|
|
||||||
<article class="prose prose-2xl">2X large</article>
|
|
||||||
```
|
|
||||||
|
|
||||||
Responsive:
|
|
||||||
```html
|
|
||||||
<article class="prose prose-sm md:prose-base lg:prose-lg">
|
|
||||||
Responsive typography
|
|
||||||
</article>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Colors
|
|
||||||
|
|
||||||
```html
|
|
||||||
<article class="prose prose-gray">Gray (default)</article>
|
|
||||||
<article class="prose prose-slate">Slate</article>
|
|
||||||
<article class="prose prose-blue">Blue</article>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Dark Mode
|
|
||||||
|
|
||||||
```html
|
|
||||||
<article class="prose dark:prose-invert">
|
|
||||||
Dark mode typography
|
|
||||||
</article>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Excluding Elements
|
|
||||||
|
|
||||||
```html
|
|
||||||
<article class="prose">
|
|
||||||
<p>Styled</p>
|
|
||||||
<div class="not-prose">
|
|
||||||
<p>NOT styled</p>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** `not-prose` only works as a class.
|
|
||||||
|
|
||||||
## Options
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presetTypography({
|
|
||||||
selectorName: 'prose', // Custom selector
|
|
||||||
cssVarPrefix: '--un-prose', // CSS variable prefix
|
|
||||||
important: false, // Make !important
|
|
||||||
cssExtend: {
|
|
||||||
'code': { color: '#8b5cf6' },
|
|
||||||
'a:hover': { color: '#f43f5e' },
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://unocss.dev/presets/typography
|
|
||||||
-->
|
|
||||||
@ -1,91 +0,0 @@
|
|||||||
---
|
|
||||||
name: preset-web-fonts
|
|
||||||
description: Easy Google Fonts and other web fonts integration
|
|
||||||
---
|
|
||||||
|
|
||||||
# Preset Web Fonts
|
|
||||||
|
|
||||||
Easily use web fonts from Google Fonts and other providers.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineConfig, presetWebFonts, presetWind3 } from 'unocss'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
presets: [
|
|
||||||
presetWind3(),
|
|
||||||
presetWebFonts({
|
|
||||||
provider: 'google',
|
|
||||||
fonts: {
|
|
||||||
sans: 'Roboto',
|
|
||||||
mono: 'Fira Code',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Providers
|
|
||||||
|
|
||||||
- `google` - Google Fonts (default)
|
|
||||||
- `bunny` - Privacy-friendly alternative
|
|
||||||
- `fontshare` - Quality fonts by ITF
|
|
||||||
- `fontsource` - Self-hosted open source fonts
|
|
||||||
- `coollabs` - Privacy-friendly drop-in replacement
|
|
||||||
- `none` - Treat as system font
|
|
||||||
|
|
||||||
## Font Configuration
|
|
||||||
|
|
||||||
```ts
|
|
||||||
fonts: {
|
|
||||||
// Simple
|
|
||||||
sans: 'Roboto',
|
|
||||||
|
|
||||||
// Multiple (fallback)
|
|
||||||
mono: ['Fira Code', 'Fira Mono:400,700'],
|
|
||||||
|
|
||||||
// Detailed
|
|
||||||
lato: [
|
|
||||||
{
|
|
||||||
name: 'Lato',
|
|
||||||
weights: ['400', '700'],
|
|
||||||
italic: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'sans-serif',
|
|
||||||
provider: 'none',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```html
|
|
||||||
<p class="font-sans">Roboto</p>
|
|
||||||
<code class="font-mono">Fira Code</code>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Local Fonts
|
|
||||||
|
|
||||||
Self-host fonts:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { createLocalFontProcessor } from '@unocss/preset-web-fonts/local'
|
|
||||||
|
|
||||||
presetWebFonts({
|
|
||||||
provider: 'none',
|
|
||||||
fonts: { sans: 'Roboto' },
|
|
||||||
processors: createLocalFontProcessor({
|
|
||||||
cacheDir: 'node_modules/.cache/unocss/fonts',
|
|
||||||
fontAssetsDir: 'public/assets/fonts',
|
|
||||||
fontServeBaseUrl: '/assets/fonts',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://unocss.dev/presets/web-fonts
|
|
||||||
-->
|
|
||||||
@ -1,194 +0,0 @@
|
|||||||
---
|
|
||||||
name: preset-wind3
|
|
||||||
description: Tailwind CSS / Windi CSS compatible preset for UnoCSS
|
|
||||||
---
|
|
||||||
|
|
||||||
# Preset Wind3
|
|
||||||
|
|
||||||
The Tailwind CSS / Windi CSS compatible preset. Most commonly used preset for UnoCSS.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineConfig, presetWind3 } from 'unocss'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
presets: [
|
|
||||||
presetWind3(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** `@unocss/preset-uno` and `@unocss/preset-wind` are deprecated and renamed to `@unocss/preset-wind3`.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- Full Tailwind CSS v3 compatibility
|
|
||||||
- Dark mode (`dark:`, `@dark:`)
|
|
||||||
- All responsive variants (`sm:`, `md:`, `lg:`, `xl:`, `2xl:`)
|
|
||||||
- All standard utilities (flex, grid, spacing, colors, typography, etc.)
|
|
||||||
- Animation support (includes Animate.css animations)
|
|
||||||
|
|
||||||
## Dark Mode
|
|
||||||
|
|
||||||
### Class-based (default)
|
|
||||||
|
|
||||||
```html
|
|
||||||
<div class="dark:bg-gray-800">
|
|
||||||
```
|
|
||||||
|
|
||||||
Generates: `.dark .dark\:bg-gray-800 { ... }`
|
|
||||||
|
|
||||||
### Media Query Based
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presetWind3({
|
|
||||||
dark: 'media'
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Generates: `@media (prefers-color-scheme: dark) { ... }`
|
|
||||||
|
|
||||||
### Opt-in Media Query
|
|
||||||
|
|
||||||
Use `@dark:` regardless of config:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<div class="@dark:bg-gray-800">
|
|
||||||
```
|
|
||||||
|
|
||||||
## Options
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presetWind3({
|
|
||||||
// Dark mode strategy
|
|
||||||
dark: 'class', // 'class' | 'media' | { light: '.light', dark: '.dark' }
|
|
||||||
|
|
||||||
// Generate pseudo selector as [group=""] instead of .group
|
|
||||||
attributifyPseudo: false,
|
|
||||||
|
|
||||||
// CSS custom properties prefix
|
|
||||||
variablePrefix: 'un-',
|
|
||||||
|
|
||||||
// Utils prefix
|
|
||||||
prefix: '',
|
|
||||||
|
|
||||||
// Generate preflight CSS
|
|
||||||
preflight: true, // true | false | 'on-demand'
|
|
||||||
|
|
||||||
// Mark all utilities as !important
|
|
||||||
important: false, // boolean | string (selector)
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Important Option
|
|
||||||
|
|
||||||
Make all utilities `!important`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presetWind3({
|
|
||||||
important: true,
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Or scope with selector to increase specificity without `!important`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presetWind3({
|
|
||||||
important: '#app',
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Output: `#app :is(.dark .dark\:bg-blue) { ... }`
|
|
||||||
|
|
||||||
## Differences from Tailwind CSS
|
|
||||||
|
|
||||||
### Quotes Not Supported
|
|
||||||
|
|
||||||
Template quotes don't work due to extractor:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!-- Won't work -->
|
|
||||||
<div class="before:content-['']">
|
|
||||||
|
|
||||||
<!-- Use shortcut instead -->
|
|
||||||
<div class="before:content-empty">
|
|
||||||
```
|
|
||||||
|
|
||||||
### Background Position
|
|
||||||
|
|
||||||
Use `position:` prefix for custom values:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!-- Tailwind -->
|
|
||||||
<div class="bg-[center_top_1rem]">
|
|
||||||
|
|
||||||
<!-- UnoCSS -->
|
|
||||||
<div class="bg-[position:center_top_1rem]">
|
|
||||||
```
|
|
||||||
|
|
||||||
### Animations
|
|
||||||
|
|
||||||
UnoCSS integrates Animate.css. Use `-alt` suffix for Animate.css versions when names conflict:
|
|
||||||
|
|
||||||
- `animate-bounce` - Tailwind version
|
|
||||||
- `animate-bounce-alt` - Animate.css version
|
|
||||||
|
|
||||||
Custom animations:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
theme: {
|
|
||||||
animation: {
|
|
||||||
keyframes: {
|
|
||||||
custom: '{0%, 100% { opacity: 0; } 50% { opacity: 1; }}',
|
|
||||||
},
|
|
||||||
durations: {
|
|
||||||
custom: '1s',
|
|
||||||
},
|
|
||||||
timingFns: {
|
|
||||||
custom: 'ease-in-out',
|
|
||||||
},
|
|
||||||
counts: {
|
|
||||||
custom: 'infinite',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Differences from Windi CSS
|
|
||||||
|
|
||||||
| Windi CSS | UnoCSS |
|
|
||||||
|-----------|--------|
|
|
||||||
| `<sm:p-1` | `lt-sm:p-1` |
|
|
||||||
| `@lg:p-1` | `at-lg:p-1` |
|
|
||||||
| `>xl:p-1` | `xl:p-1` |
|
|
||||||
|
|
||||||
Bracket syntax uses `_` instead of `,`:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!-- Windi CSS -->
|
|
||||||
<div class="grid-cols-[1fr,10px,max-content]">
|
|
||||||
|
|
||||||
<!-- UnoCSS -->
|
|
||||||
<div class="grid-cols-[1fr_10px_max-content]">
|
|
||||||
```
|
|
||||||
|
|
||||||
## Experimental: Media Hover
|
|
||||||
|
|
||||||
Addresses sticky hover on touch devices:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<div class="@hover-text-red">
|
|
||||||
```
|
|
||||||
|
|
||||||
Generates:
|
|
||||||
```css
|
|
||||||
@media (hover: hover) and (pointer: fine) {
|
|
||||||
.\@hover-text-red:hover { ... }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://unocss.dev/presets/wind3
|
|
||||||
-->
|
|
||||||
@ -1,247 +0,0 @@
|
|||||||
---
|
|
||||||
name: preset-wind4
|
|
||||||
description: Tailwind CSS v4 compatible preset with enhanced features
|
|
||||||
---
|
|
||||||
|
|
||||||
# Preset Wind4
|
|
||||||
|
|
||||||
The Tailwind CSS v4 compatible preset. Enhances preset-wind3 with modern CSS features.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineConfig, presetWind4 } from 'unocss'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
presets: [
|
|
||||||
presetWind4(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Key Differences from Wind3
|
|
||||||
|
|
||||||
### Built-in CSS Reset
|
|
||||||
|
|
||||||
No need for `@unocss/reset` - reset is built-in:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Remove these imports
|
|
||||||
import '@unocss/reset/tailwind.css' // ❌ Not needed
|
|
||||||
import '@unocss/reset/tailwind-compat.css' // ❌ Not needed
|
|
||||||
|
|
||||||
// Enable in config
|
|
||||||
presetWind4({
|
|
||||||
preflights: {
|
|
||||||
reset: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### OKLCH Color Model
|
|
||||||
|
|
||||||
Uses `oklch` for better color perception and contrast. Not compatible with `presetLegacyCompat`.
|
|
||||||
|
|
||||||
### Theme CSS Variables
|
|
||||||
|
|
||||||
Automatically generates CSS variables from theme:
|
|
||||||
|
|
||||||
```css
|
|
||||||
:root, :host {
|
|
||||||
--spacing: 0.25rem;
|
|
||||||
--font-sans: ui-sans-serif, system-ui, sans-serif;
|
|
||||||
--colors-black: #000;
|
|
||||||
--colors-white: #fff;
|
|
||||||
/* ... */
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### @property CSS Rules
|
|
||||||
|
|
||||||
Uses `@property` for better browser optimization:
|
|
||||||
|
|
||||||
```css
|
|
||||||
@property --un-text-opacity {
|
|
||||||
syntax: '<percentage>';
|
|
||||||
inherits: false;
|
|
||||||
initial-value: 100%;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Theme Key Changes
|
|
||||||
|
|
||||||
| preset-wind3 | preset-wind4 |
|
|
||||||
|--------------|--------------|
|
|
||||||
| `fontFamily` | `font` |
|
|
||||||
| `fontSize` | `text.fontSize` |
|
|
||||||
| `lineHeight` | `text.lineHeight` or `leading` |
|
|
||||||
| `letterSpacing` | `text.letterSpacing` or `tracking` |
|
|
||||||
| `borderRadius` | `radius` |
|
|
||||||
| `easing` | `ease` |
|
|
||||||
| `breakpoints` | `breakpoint` |
|
|
||||||
| `verticalBreakpoints` | `verticalBreakpoint` |
|
|
||||||
| `boxShadow` | `shadow` |
|
|
||||||
| `transitionProperty` | `property` |
|
|
||||||
| `container.maxWidth` | `containers.maxWidth` |
|
|
||||||
| Size properties (`width`, `height`, etc.) | Unified to `spacing` |
|
|
||||||
|
|
||||||
## Options
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presetWind4({
|
|
||||||
preflights: {
|
|
||||||
// Built-in reset styles
|
|
||||||
reset: true,
|
|
||||||
|
|
||||||
// Theme CSS variables generation
|
|
||||||
theme: 'on-demand', // true | false | 'on-demand'
|
|
||||||
|
|
||||||
// @property CSS rules
|
|
||||||
property: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Theme Variable Processing
|
|
||||||
|
|
||||||
Convert rem to px for theme variables:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { createRemToPxProcessor } from '@unocss/preset-wind4/utils'
|
|
||||||
|
|
||||||
presetWind4({
|
|
||||||
preflights: {
|
|
||||||
theme: {
|
|
||||||
mode: 'on-demand',
|
|
||||||
process: createRemToPxProcessor(),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// Also apply to utilities
|
|
||||||
export default defineConfig({
|
|
||||||
postprocess: [createRemToPxProcessor()],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Property Layer Customization
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presetWind4({
|
|
||||||
preflights: {
|
|
||||||
property: {
|
|
||||||
// Custom parent wrapper
|
|
||||||
parent: '@layer custom-properties',
|
|
||||||
// Custom selector
|
|
||||||
selector: ':where(*, ::before, ::after)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Remove `@supports` wrapper:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presetWind4({
|
|
||||||
preflights: {
|
|
||||||
property: {
|
|
||||||
parent: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Generated Layers
|
|
||||||
|
|
||||||
| Layer | Description | Order |
|
|
||||||
|-------|-------------|-------|
|
|
||||||
| `properties` | CSS `@property` rules | -200 |
|
|
||||||
| `theme` | Theme CSS variables | -150 |
|
|
||||||
| `base` | Reset/preflight styles | -100 |
|
|
||||||
|
|
||||||
## Theme.defaults
|
|
||||||
|
|
||||||
Global default configuration for reset styles:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import type { Theme } from '@unocss/preset-wind4/theme'
|
|
||||||
|
|
||||||
const defaults: Theme['default'] = {
|
|
||||||
transition: {
|
|
||||||
duration: '150ms',
|
|
||||||
timingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)',
|
|
||||||
},
|
|
||||||
font: {
|
|
||||||
family: 'var(--font-sans)',
|
|
||||||
featureSettings: 'var(--font-sans--font-feature-settings)',
|
|
||||||
variationSettings: 'var(--font-sans--font-variation-settings)',
|
|
||||||
},
|
|
||||||
monoFont: {
|
|
||||||
family: 'var(--font-mono)',
|
|
||||||
// ...
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Compatibility Notes
|
|
||||||
|
|
||||||
### presetRemToPx
|
|
||||||
|
|
||||||
Not needed - use built-in processor instead:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
presetWind4({
|
|
||||||
preflights: {
|
|
||||||
theme: {
|
|
||||||
process: createRemToPxProcessor(),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### presetLegacyCompat
|
|
||||||
|
|
||||||
**Not compatible** with preset-wind4 due to `oklch` color model.
|
|
||||||
|
|
||||||
## Migration from Wind3
|
|
||||||
|
|
||||||
1. Update theme keys according to the table above
|
|
||||||
2. Remove `@unocss/reset` imports
|
|
||||||
3. Enable `preflights.reset: true`
|
|
||||||
4. Test color outputs (oklch vs rgb)
|
|
||||||
5. Update any custom theme extensions
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Before (wind3)
|
|
||||||
theme: {
|
|
||||||
fontFamily: { sans: 'Roboto' },
|
|
||||||
fontSize: { lg: '1.125rem' },
|
|
||||||
breakpoints: { sm: '640px' },
|
|
||||||
}
|
|
||||||
|
|
||||||
// After (wind4)
|
|
||||||
theme: {
|
|
||||||
font: { sans: 'Roboto' },
|
|
||||||
text: { lg: { fontSize: '1.125rem' } },
|
|
||||||
breakpoint: { sm: '640px' },
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## When to Use Wind4
|
|
||||||
|
|
||||||
Choose **preset-wind4** when:
|
|
||||||
- Starting a new project
|
|
||||||
- Targeting modern browsers
|
|
||||||
- Want built-in reset and CSS variables
|
|
||||||
- Following Tailwind v4 conventions
|
|
||||||
|
|
||||||
Choose **preset-wind3** when:
|
|
||||||
- Need legacy browser support
|
|
||||||
- Migrating from Tailwind v3
|
|
||||||
- Using presetLegacyCompat
|
|
||||||
- Want stable, proven preset
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://unocss.dev/presets/wind4
|
|
||||||
-->
|
|
||||||
@ -1,156 +0,0 @@
|
|||||||
---
|
|
||||||
name: transformer-attributify-jsx
|
|
||||||
description: Support valueless attributify in JSX/TSX
|
|
||||||
---
|
|
||||||
|
|
||||||
# Transformer Attributify JSX
|
|
||||||
|
|
||||||
Fixes valueless attributify mode in JSX where `<div foo>` becomes `<div foo={true}>`.
|
|
||||||
|
|
||||||
## The Problem
|
|
||||||
|
|
||||||
In JSX, valueless attributes are transformed:
|
|
||||||
|
|
||||||
```jsx
|
|
||||||
// You write
|
|
||||||
<div m-2 rounded text-teal-400 />
|
|
||||||
|
|
||||||
// JSX compiles to
|
|
||||||
<div m-2={true} rounded={true} text-teal-400={true} />
|
|
||||||
```
|
|
||||||
|
|
||||||
The `={true}` breaks UnoCSS attributify detection.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import {
|
|
||||||
defineConfig,
|
|
||||||
presetAttributify,
|
|
||||||
transformerAttributifyJsx
|
|
||||||
} from 'unocss'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
presets: [
|
|
||||||
presetAttributify(),
|
|
||||||
],
|
|
||||||
transformers: [
|
|
||||||
transformerAttributifyJsx(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
The transformer converts JSX boolean attributes back to strings:
|
|
||||||
|
|
||||||
```jsx
|
|
||||||
// Input (after JSX compilation)
|
|
||||||
<div m-2={true} rounded={true} />
|
|
||||||
|
|
||||||
// Output (transformed)
|
|
||||||
<div m-2="" rounded="" />
|
|
||||||
```
|
|
||||||
|
|
||||||
Now UnoCSS can properly extract the attributify classes.
|
|
||||||
|
|
||||||
## Options
|
|
||||||
|
|
||||||
```ts
|
|
||||||
transformerAttributifyJsx({
|
|
||||||
// Only transform specific attributes
|
|
||||||
// Default: transforms all that match attributify patterns
|
|
||||||
blocklist: ['text', 'font'],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## When to Use
|
|
||||||
|
|
||||||
Required when using:
|
|
||||||
- React
|
|
||||||
- Preact
|
|
||||||
- Solid
|
|
||||||
- Any JSX-based framework
|
|
||||||
|
|
||||||
With valueless attributify syntax:
|
|
||||||
|
|
||||||
```jsx
|
|
||||||
// This needs the transformer
|
|
||||||
<div flex items-center gap-4 />
|
|
||||||
|
|
||||||
// This works without transformer (has values)
|
|
||||||
<div flex="~" items="center" gap="4" />
|
|
||||||
```
|
|
||||||
|
|
||||||
## Framework Setup
|
|
||||||
|
|
||||||
### React
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite.config.ts
|
|
||||||
import React from '@vitejs/plugin-react'
|
|
||||||
import UnoCSS from 'unocss/vite'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
plugins: [
|
|
||||||
UnoCSS(), // Must be before React
|
|
||||||
React(),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// uno.config.ts
|
|
||||||
import {
|
|
||||||
defineConfig,
|
|
||||||
presetAttributify,
|
|
||||||
presetWind3,
|
|
||||||
transformerAttributifyJsx
|
|
||||||
} from 'unocss'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
presets: [
|
|
||||||
presetWind3(),
|
|
||||||
presetAttributify(),
|
|
||||||
],
|
|
||||||
transformers: [
|
|
||||||
transformerAttributifyJsx(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Preact
|
|
||||||
|
|
||||||
Same as React, use `@preact/preset-vite` or `@prefresh/vite`.
|
|
||||||
|
|
||||||
### Solid
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import UnoCSS from 'unocss/vite'
|
|
||||||
import solidPlugin from 'vite-plugin-solid'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
plugins: [
|
|
||||||
UnoCSS(),
|
|
||||||
solidPlugin(),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## TypeScript Support
|
|
||||||
|
|
||||||
Add type declarations:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// shims.d.ts
|
|
||||||
import type { AttributifyAttributes } from '@unocss/preset-attributify'
|
|
||||||
|
|
||||||
declare module 'react' {
|
|
||||||
interface HTMLAttributes<T> extends AttributifyAttributes {}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://unocss.dev/transformers/attributify-jsx
|
|
||||||
-->
|
|
||||||
@ -1,128 +0,0 @@
|
|||||||
---
|
|
||||||
name: transformer-compile-class
|
|
||||||
description: Compile multiple classes into one hashed class
|
|
||||||
---
|
|
||||||
|
|
||||||
# Transformer Compile Class
|
|
||||||
|
|
||||||
Compiles multiple utility classes into a single hashed class for smaller HTML.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineConfig, transformerCompileClass } from 'unocss'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
transformers: [
|
|
||||||
transformerCompileClass(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
Add `:uno:` prefix to mark classes for compilation:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!-- Before -->
|
|
||||||
<div class=":uno: text-center sm:text-left">
|
|
||||||
<div class=":uno: text-sm font-bold hover:text-red" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- After -->
|
|
||||||
<div class="uno-qlmcrp">
|
|
||||||
<div class="uno-0qw2gr" />
|
|
||||||
</div>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Generated CSS
|
|
||||||
|
|
||||||
```css
|
|
||||||
.uno-qlmcrp {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.uno-0qw2gr {
|
|
||||||
font-size: 0.875rem;
|
|
||||||
line-height: 1.25rem;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
.uno-0qw2gr:hover {
|
|
||||||
--un-text-opacity: 1;
|
|
||||||
color: rgb(248 113 113 / var(--un-text-opacity));
|
|
||||||
}
|
|
||||||
@media (min-width: 640px) {
|
|
||||||
.uno-qlmcrp {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Options
|
|
||||||
|
|
||||||
```ts
|
|
||||||
transformerCompileClass({
|
|
||||||
// Custom trigger string (default: ':uno:')
|
|
||||||
trigger: ':uno:',
|
|
||||||
|
|
||||||
// Custom class prefix (default: 'uno-')
|
|
||||||
classPrefix: 'uno-',
|
|
||||||
|
|
||||||
// Hash function for class names
|
|
||||||
hashFn: (str) => /* custom hash */,
|
|
||||||
|
|
||||||
// Keep original classes alongside compiled
|
|
||||||
keepOriginal: false,
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Use Cases
|
|
||||||
|
|
||||||
- **Smaller HTML** - Reduce repetitive class strings
|
|
||||||
- **Obfuscation** - Hide utility class names in production
|
|
||||||
- **Performance** - Fewer class attributes to parse
|
|
||||||
|
|
||||||
## ESLint Integration
|
|
||||||
|
|
||||||
Enforce compile class usage across project:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"rules": {
|
|
||||||
"@unocss/enforce-class-compile": "warn"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This rule:
|
|
||||||
- Warns when class attribute doesn't start with `:uno:`
|
|
||||||
- Auto-fixes by adding the prefix
|
|
||||||
|
|
||||||
Options:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"rules": {
|
|
||||||
"@unocss/enforce-class-compile": ["warn", {
|
|
||||||
"prefix": ":uno:",
|
|
||||||
"enableFix": true
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Combining with Other Transformers
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
transformers: [
|
|
||||||
transformerVariantGroup(), // Process variant groups first
|
|
||||||
transformerDirectives(), // Then directives
|
|
||||||
transformerCompileClass(), // Compile last
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://unocss.dev/transformers/compile-class
|
|
||||||
-->
|
|
||||||
@ -1,157 +0,0 @@
|
|||||||
---
|
|
||||||
name: transformer-directives
|
|
||||||
description: CSS directives @apply, @screen, theme(), and icon()
|
|
||||||
---
|
|
||||||
|
|
||||||
# Transformer Directives
|
|
||||||
|
|
||||||
Enables `@apply`, `@screen`, `theme()`, and `icon()` directives in CSS.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineConfig, transformerDirectives } from 'unocss'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
transformers: [
|
|
||||||
transformerDirectives(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## @apply
|
|
||||||
|
|
||||||
Apply utility classes in CSS:
|
|
||||||
|
|
||||||
```css
|
|
||||||
.custom-btn {
|
|
||||||
@apply py-2 px-4 font-semibold rounded-lg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* With variants - use quotes */
|
|
||||||
.custom-btn {
|
|
||||||
@apply 'hover:bg-blue-600 focus:ring-2';
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### CSS Custom Property Alternative
|
|
||||||
|
|
||||||
For vanilla CSS compatibility:
|
|
||||||
|
|
||||||
```css
|
|
||||||
.custom-div {
|
|
||||||
--at-apply: text-center my-0 font-medium;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Supported aliases: `--at-apply`, `--uno-apply`, `--uno`
|
|
||||||
|
|
||||||
Configure aliases:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
transformerDirectives({
|
|
||||||
applyVariable: ['--at-apply', '--uno-apply', '--uno'],
|
|
||||||
// or disable: applyVariable: false
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## @screen
|
|
||||||
|
|
||||||
Create breakpoint media queries:
|
|
||||||
|
|
||||||
```css
|
|
||||||
.grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
}
|
|
||||||
|
|
||||||
@screen sm {
|
|
||||||
.grid {
|
|
||||||
grid-template-columns: repeat(3, 1fr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@screen lg {
|
|
||||||
.grid {
|
|
||||||
grid-template-columns: repeat(4, 1fr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Breakpoint Variants
|
|
||||||
|
|
||||||
```css
|
|
||||||
/* Less than breakpoint */
|
|
||||||
@screen lt-sm {
|
|
||||||
.item { display: none; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* At specific breakpoint only */
|
|
||||||
@screen at-md {
|
|
||||||
.item { width: 50%; }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## theme()
|
|
||||||
|
|
||||||
Access theme values in CSS:
|
|
||||||
|
|
||||||
```css
|
|
||||||
.btn-blue {
|
|
||||||
background-color: theme('colors.blue.500');
|
|
||||||
padding: theme('spacing.4');
|
|
||||||
border-radius: theme('borderRadius.lg');
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Dot notation paths into your theme config.
|
|
||||||
|
|
||||||
## icon()
|
|
||||||
|
|
||||||
Convert icon utility to SVG (requires preset-icons):
|
|
||||||
|
|
||||||
```css
|
|
||||||
.icon-sun {
|
|
||||||
background-image: icon('i-carbon-sun');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* With custom color */
|
|
||||||
.icon-moon {
|
|
||||||
background-image: icon('i-carbon-moon', '#fff');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Using theme color */
|
|
||||||
.icon-alert {
|
|
||||||
background-image: icon('i-carbon-warning', 'theme("colors.red.500")');
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Complete Example
|
|
||||||
|
|
||||||
```css
|
|
||||||
.card {
|
|
||||||
@apply rounded-lg shadow-md p-4;
|
|
||||||
background-color: theme('colors.white');
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
@apply 'font-bold text-lg border-b';
|
|
||||||
padding-bottom: theme('spacing.2');
|
|
||||||
}
|
|
||||||
|
|
||||||
@screen md {
|
|
||||||
.card {
|
|
||||||
@apply flex gap-4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-icon {
|
|
||||||
background-image: icon('i-carbon-document');
|
|
||||||
@apply w-6 h-6;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://unocss.dev/transformers/directives
|
|
||||||
-->
|
|
||||||
@ -1,97 +0,0 @@
|
|||||||
---
|
|
||||||
name: transformer-variant-group
|
|
||||||
description: Shorthand for grouping utilities with common prefixes
|
|
||||||
---
|
|
||||||
|
|
||||||
# Transformer Variant Group
|
|
||||||
|
|
||||||
Enables shorthand syntax for grouping utilities with common prefixes.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineConfig, transformerVariantGroup } from 'unocss'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
transformers: [
|
|
||||||
transformerVariantGroup(),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
Group multiple utilities under one variant prefix using parentheses:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!-- Before transformation -->
|
|
||||||
<div class="hover:(bg-gray-400 font-medium) font-(light mono)" />
|
|
||||||
|
|
||||||
<!-- After transformation -->
|
|
||||||
<div class="hover:bg-gray-400 hover:font-medium font-light font-mono" />
|
|
||||||
```
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Hover States
|
|
||||||
|
|
||||||
```html
|
|
||||||
<button class="hover:(bg-blue-600 text-white scale-105)">
|
|
||||||
Hover me
|
|
||||||
</button>
|
|
||||||
```
|
|
||||||
|
|
||||||
Expands to: `hover:bg-blue-600 hover:text-white hover:scale-105`
|
|
||||||
|
|
||||||
### Dark Mode
|
|
||||||
|
|
||||||
```html
|
|
||||||
<div class="dark:(bg-gray-800 text-white)">
|
|
||||||
Dark content
|
|
||||||
</div>
|
|
||||||
```
|
|
||||||
|
|
||||||
Expands to: `dark:bg-gray-800 dark:text-white`
|
|
||||||
|
|
||||||
### Responsive
|
|
||||||
|
|
||||||
```html
|
|
||||||
<div class="md:(flex items-center gap-4)">
|
|
||||||
Responsive flex
|
|
||||||
</div>
|
|
||||||
```
|
|
||||||
|
|
||||||
Expands to: `md:flex md:items-center md:gap-4`
|
|
||||||
|
|
||||||
### Nested Groups
|
|
||||||
|
|
||||||
```html
|
|
||||||
<div class="lg:hover:(bg-blue-500 text-white)">
|
|
||||||
Large screen hover
|
|
||||||
</div>
|
|
||||||
```
|
|
||||||
|
|
||||||
Expands to: `lg:hover:bg-blue-500 lg:hover:text-white`
|
|
||||||
|
|
||||||
### Multiple Prefixes
|
|
||||||
|
|
||||||
```html
|
|
||||||
<div class="text-(sm gray-600) font-(medium mono)">
|
|
||||||
Styled text
|
|
||||||
</div>
|
|
||||||
```
|
|
||||||
|
|
||||||
Expands to: `text-sm text-gray-600 font-medium font-mono`
|
|
||||||
|
|
||||||
## Key Points
|
|
||||||
|
|
||||||
- Use parentheses `()` to group utilities
|
|
||||||
- The prefix applies to all utilities inside the group
|
|
||||||
- Can be combined with any variant (hover, dark, responsive, etc.)
|
|
||||||
- Nesting is supported
|
|
||||||
- Works in class attributes and other extraction sources
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://unocss.dev/transformers/variant-group
|
|
||||||
-->
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
# Generation Info
|
|
||||||
|
|
||||||
- **Source:** `sources/vite`
|
|
||||||
- **Git SHA:** `b40292ce6a7dbbbbac9c6dae5f126b7f44c3e1b7`
|
|
||||||
- **Generated:** 2026-01-28
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2025-PRESENT Anthony Fu <https://github.com/antfu>
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@ -1,50 +0,0 @@
|
|||||||
---
|
|
||||||
name: vite
|
|
||||||
description: Vite next-generation frontend build tool with fast HMR and optimized builds. Use when configuring Vite, adding plugins, working with dev server, or building for production.
|
|
||||||
metadata:
|
|
||||||
author: Anthony Fu
|
|
||||||
version: "2026.1.28"
|
|
||||||
source: Generated from https://github.com/vitejs/vite, scripts located at https://github.com/antfu/skills
|
|
||||||
---
|
|
||||||
|
|
||||||
Vite is a modern build tool for frontend development featuring instant server start with native ES modules, lightning-fast HMR, and optimized production builds using Rolldown/Rollup. It supports TypeScript, JSX, CSS pre-processors out of the box and has a rich plugin ecosystem.
|
|
||||||
|
|
||||||
> The skill is based on Vite 6.x, generated at 2026-01-28.
|
|
||||||
|
|
||||||
## Core
|
|
||||||
|
|
||||||
| Topic | Description | Reference |
|
|
||||||
|-------|-------------|-----------|
|
|
||||||
| Configuration | Config file setup, defineConfig, conditional and async configs | [core-config](references/core-config.md) |
|
|
||||||
| CLI Commands | Dev server, build, preview commands and options | [core-cli](references/core-cli.md) |
|
|
||||||
| Core Features | TypeScript, JSX, CSS, HTML processing, JSON handling | [core-features](references/core-features.md) |
|
|
||||||
| Using Plugins | Adding, configuring, and ordering plugins | [core-plugins](references/core-plugins.md) |
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
| Topic | Description | Reference |
|
|
||||||
|-------|-------------|-----------|
|
|
||||||
| CSS Handling | CSS modules, pre-processors, PostCSS, Lightning CSS | [features-css](references/features-css.md) |
|
|
||||||
| Static Assets | Asset imports, public directory, URL handling | [features-assets](references/features-assets.md) |
|
|
||||||
| Glob Import | import.meta.glob, dynamic imports, batch loading | [features-glob-import](references/features-glob-import.md) |
|
|
||||||
| Environment Variables | .env files, modes, import.meta.env constants | [features-env](references/features-env.md) |
|
|
||||||
| HMR API | Hot Module Replacement client API | [features-hmr](references/features-hmr.md) |
|
|
||||||
| Web Workers | Worker imports and configuration | [features-workers](references/features-workers.md) |
|
|
||||||
| Dependency Pre-Bundling | optimizeDeps, caching, monorepo setup | [features-dep-bundling](references/features-dep-bundling.md) |
|
|
||||||
|
|
||||||
## Build
|
|
||||||
|
|
||||||
| Topic | Description | Reference |
|
|
||||||
|-------|-------------|-----------|
|
|
||||||
| Production Build | Build options, browser targets, multi-page apps | [build-production](references/build-production.md) |
|
|
||||||
| Library Mode | Building libraries with proper package exports | [build-library](references/build-library.md) |
|
|
||||||
| SSR | Server-side rendering setup and configuration | [build-ssr](references/build-ssr.md) |
|
|
||||||
|
|
||||||
## Advanced
|
|
||||||
|
|
||||||
| Topic | Description | Reference |
|
|
||||||
|-------|-------------|-----------|
|
|
||||||
| JavaScript API | createServer, build, preview programmatic APIs | [advanced-api](references/advanced-api.md) |
|
|
||||||
| Plugin API | Creating Vite plugins, hooks, virtual modules | [advanced-plugin-api](references/advanced-plugin-api.md) |
|
|
||||||
| Performance | Optimization tips for dev server and builds | [advanced-performance](references/advanced-performance.md) |
|
|
||||||
| Backend Integration | Integrating Vite with traditional backends | [advanced-backend](references/advanced-backend.md) |
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
# Sync Info
|
|
||||||
|
|
||||||
- **Source:** `vendor/antfu/skills/vite`
|
|
||||||
- **Git SHA:** `4086f59fa12d308fb05592ca8ce1704fbb940aa3`
|
|
||||||
- **Synced:** 2026-01-30
|
|
||||||
@ -1,218 +0,0 @@
|
|||||||
---
|
|
||||||
name: advanced-api
|
|
||||||
description: Vite's JavaScript API for programmatic usage
|
|
||||||
---
|
|
||||||
|
|
||||||
# JavaScript API
|
|
||||||
|
|
||||||
Vite's APIs are fully typed. Use TypeScript or enable JS type checking for intellisense.
|
|
||||||
|
|
||||||
## `createServer`
|
|
||||||
|
|
||||||
Create a development server programmatically:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { createServer } from 'vite'
|
|
||||||
|
|
||||||
const server = await createServer({
|
|
||||||
configFile: false,
|
|
||||||
root: __dirname,
|
|
||||||
server: {
|
|
||||||
port: 1337
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
await server.listen()
|
|
||||||
server.printUrls()
|
|
||||||
server.bindCLIShortcuts({ print: true })
|
|
||||||
```
|
|
||||||
|
|
||||||
### ViteDevServer Interface
|
|
||||||
|
|
||||||
```ts
|
|
||||||
interface ViteDevServer {
|
|
||||||
config: ResolvedConfig
|
|
||||||
middlewares: Connect.Server // Connect app for custom middleware
|
|
||||||
httpServer: http.Server | null // Node HTTP server
|
|
||||||
watcher: FSWatcher // Chokidar watcher
|
|
||||||
ws: WebSocketServer // WebSocket for HMR
|
|
||||||
moduleGraph: ModuleGraph // Module import relationships
|
|
||||||
|
|
||||||
// Transform without HTTP
|
|
||||||
transformRequest(url: string): Promise<TransformResult | null>
|
|
||||||
|
|
||||||
// Apply HTML transforms
|
|
||||||
transformIndexHtml(url: string, html: string): Promise<string>
|
|
||||||
|
|
||||||
// Load module for SSR
|
|
||||||
ssrLoadModule(url: string): Promise<Record<string, any>>
|
|
||||||
|
|
||||||
// Fix SSR error stack traces
|
|
||||||
ssrFixStacktrace(e: Error): void
|
|
||||||
|
|
||||||
// Control
|
|
||||||
listen(port?: number): Promise<ViteDevServer>
|
|
||||||
restart(): Promise<void>
|
|
||||||
close(): Promise<void>
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## `build`
|
|
||||||
|
|
||||||
Build for production:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { build } from 'vite'
|
|
||||||
|
|
||||||
await build({
|
|
||||||
root: './project',
|
|
||||||
base: '/foo/',
|
|
||||||
build: {
|
|
||||||
rolldownOptions: {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## `preview`
|
|
||||||
|
|
||||||
Preview production build locally:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { preview } from 'vite'
|
|
||||||
|
|
||||||
const previewServer = await preview({
|
|
||||||
preview: {
|
|
||||||
port: 8080,
|
|
||||||
open: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
previewServer.printUrls()
|
|
||||||
```
|
|
||||||
|
|
||||||
## `resolveConfig`
|
|
||||||
|
|
||||||
Resolve config without starting server:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { resolveConfig } from 'vite'
|
|
||||||
|
|
||||||
const config = await resolveConfig(
|
|
||||||
{ root: './project' },
|
|
||||||
'serve', // 'serve' | 'build'
|
|
||||||
'development' // default mode
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## `mergeConfig`
|
|
||||||
|
|
||||||
Deep merge two configs:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { mergeConfig } from 'vite'
|
|
||||||
|
|
||||||
const merged = mergeConfig(baseConfig, overrideConfig)
|
|
||||||
```
|
|
||||||
|
|
||||||
Merge callback config:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineConfig, mergeConfig } from 'vite'
|
|
||||||
|
|
||||||
export default defineConfig((env) =>
|
|
||||||
mergeConfig(configAsCallback(env), configAsObject)
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## `loadEnv`
|
|
||||||
|
|
||||||
Load .env files:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { loadEnv } from 'vite'
|
|
||||||
|
|
||||||
// Load VITE_* vars
|
|
||||||
const env = loadEnv('development', process.cwd())
|
|
||||||
|
|
||||||
// Load all vars (empty prefix)
|
|
||||||
const allEnv = loadEnv('development', process.cwd(), '')
|
|
||||||
```
|
|
||||||
|
|
||||||
## `searchForWorkspaceRoot`
|
|
||||||
|
|
||||||
Find monorepo workspace root:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { searchForWorkspaceRoot } from 'vite'
|
|
||||||
|
|
||||||
const workspaceRoot = searchForWorkspaceRoot(process.cwd())
|
|
||||||
```
|
|
||||||
|
|
||||||
## `normalizePath`
|
|
||||||
|
|
||||||
Normalize paths for cross-platform:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { normalizePath } from 'vite'
|
|
||||||
|
|
||||||
normalizePath('foo\\bar') // 'foo/bar'
|
|
||||||
```
|
|
||||||
|
|
||||||
## `transformWithOxc`
|
|
||||||
|
|
||||||
Transform JS/TS with Oxc Transformer:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { transformWithOxc } from 'vite'
|
|
||||||
|
|
||||||
const result = await transformWithOxc(
|
|
||||||
code,
|
|
||||||
'file.ts',
|
|
||||||
{ target: 'es2020' }
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## `preprocessCSS`
|
|
||||||
|
|
||||||
Pre-process CSS files:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { preprocessCSS, resolveConfig } from 'vite'
|
|
||||||
|
|
||||||
const config = await resolveConfig({}, 'serve')
|
|
||||||
const result = await preprocessCSS(code, 'styles.scss', config)
|
|
||||||
// result.code - plain CSS
|
|
||||||
// result.modules - CSS modules mapping
|
|
||||||
```
|
|
||||||
|
|
||||||
## `loadConfigFromFile`
|
|
||||||
|
|
||||||
Load config file manually:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { loadConfigFromFile } from 'vite'
|
|
||||||
|
|
||||||
const result = await loadConfigFromFile(
|
|
||||||
{ command: 'serve', mode: 'development' },
|
|
||||||
'vite.config.ts'
|
|
||||||
)
|
|
||||||
// result.config, result.path, result.dependencies
|
|
||||||
```
|
|
||||||
|
|
||||||
## InlineConfig
|
|
||||||
|
|
||||||
Extends UserConfig with:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
interface InlineConfig extends UserConfig {
|
|
||||||
configFile?: string | false // Config file path or false to skip
|
|
||||||
mode?: string
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://vite.dev/guide/api-javascript.html
|
|
||||||
-->
|
|
||||||
@ -1,164 +0,0 @@
|
|||||||
---
|
|
||||||
name: advanced-backend
|
|
||||||
description: Integrating Vite with traditional backend frameworks
|
|
||||||
---
|
|
||||||
|
|
||||||
# Backend Integration
|
|
||||||
|
|
||||||
Integrate Vite with traditional backends (Rails, Laravel, etc.) for asset serving.
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite.config.ts
|
|
||||||
export default defineConfig({
|
|
||||||
server: {
|
|
||||||
cors: {
|
|
||||||
origin: 'http://my-backend.example.com'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
build: {
|
|
||||||
manifest: true, // Generate .vite/manifest.json
|
|
||||||
rolldownOptions: {
|
|
||||||
input: '/path/to/main.js' // Override HTML entry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Import polyfill in entry:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// main.js
|
|
||||||
import 'vite/modulepreload-polyfill'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
Inject Vite client and entry in your backend template:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!-- Development only -->
|
|
||||||
<script type="module" src="http://localhost:5173/@vite/client"></script>
|
|
||||||
<script type="module" src="http://localhost:5173/main.js"></script>
|
|
||||||
```
|
|
||||||
|
|
||||||
### React Setup
|
|
||||||
|
|
||||||
Add before other scripts:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<script type="module">
|
|
||||||
import RefreshRuntime from 'http://localhost:5173/@react-refresh'
|
|
||||||
RefreshRuntime.injectIntoGlobalHook(window)
|
|
||||||
window.$RefreshReg$ = () => {}
|
|
||||||
window.$RefreshSig$ = () => (type) => type
|
|
||||||
window.__vite_plugin_react_preamble_installed__ = true
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Asset Proxying
|
|
||||||
|
|
||||||
Either:
|
|
||||||
1. Proxy static asset requests to Vite
|
|
||||||
2. Set `server.origin`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
server: {
|
|
||||||
origin: 'http://localhost:5173'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Production
|
|
||||||
|
|
||||||
Build generates `.vite/manifest.json`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"views/foo.js": {
|
|
||||||
"file": "assets/foo-BRBmoGS9.js",
|
|
||||||
"src": "views/foo.js",
|
|
||||||
"isEntry": true,
|
|
||||||
"imports": ["_shared-B7PI925R.js"],
|
|
||||||
"css": ["assets/foo-5UjPuW-k.css"]
|
|
||||||
},
|
|
||||||
"_shared-B7PI925R.js": {
|
|
||||||
"file": "assets/shared-B7PI925R.js",
|
|
||||||
"css": ["assets/shared-ChJ_j-JJ.css"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Rendering Tags
|
|
||||||
|
|
||||||
For entry `views/foo.js`, render in this order:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!-- 1. Entry CSS -->
|
|
||||||
<link rel="stylesheet" href="/assets/foo-5UjPuW-k.css" />
|
|
||||||
|
|
||||||
<!-- 2. Imported chunks' CSS (recursive) -->
|
|
||||||
<link rel="stylesheet" href="/assets/shared-ChJ_j-JJ.css" />
|
|
||||||
|
|
||||||
<!-- 3. Entry script -->
|
|
||||||
<script type="module" src="/assets/foo-BRBmoGS9.js"></script>
|
|
||||||
|
|
||||||
<!-- 4. Optional: preload imports -->
|
|
||||||
<link rel="modulepreload" href="/assets/shared-B7PI925R.js" />
|
|
||||||
```
|
|
||||||
|
|
||||||
## Manifest Structure
|
|
||||||
|
|
||||||
```ts
|
|
||||||
interface ManifestChunk {
|
|
||||||
src?: string // Input file name
|
|
||||||
file: string // Output file name
|
|
||||||
css?: string[] // CSS files (JS chunks only)
|
|
||||||
assets?: string[] // Non-CSS assets (JS chunks only)
|
|
||||||
isEntry?: boolean // Is entry point
|
|
||||||
isDynamicEntry?: boolean // Is dynamic import
|
|
||||||
imports?: string[] // Static imports (manifest keys)
|
|
||||||
dynamicImports?: string[] // Dynamic imports (manifest keys)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Processing Imports
|
|
||||||
|
|
||||||
Recursively collect all imported chunks:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
function getImportedChunks(manifest, name) {
|
|
||||||
const seen = new Set()
|
|
||||||
const chunks = []
|
|
||||||
|
|
||||||
function collect(chunk) {
|
|
||||||
for (const file of chunk.imports ?? []) {
|
|
||||||
if (seen.has(file)) continue
|
|
||||||
seen.add(file)
|
|
||||||
|
|
||||||
const importee = manifest[file]
|
|
||||||
collect(importee)
|
|
||||||
chunks.push(importee)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
collect(manifest[name])
|
|
||||||
return chunks
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Existing Integrations
|
|
||||||
|
|
||||||
Check [Awesome Vite](https://github.com/vitejs/awesome-vite#integrations-with-backends) for:
|
|
||||||
- Laravel (laravel-vite)
|
|
||||||
- Rails
|
|
||||||
- Django
|
|
||||||
- Flask
|
|
||||||
- And more
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://vite.dev/guide/backend-integration.html
|
|
||||||
-->
|
|
||||||
@ -1,168 +0,0 @@
|
|||||||
---
|
|
||||||
name: advanced-performance
|
|
||||||
description: Performance optimization tips for Vite dev server and builds
|
|
||||||
---
|
|
||||||
|
|
||||||
# Performance Optimization
|
|
||||||
|
|
||||||
## Browser Setup
|
|
||||||
|
|
||||||
- Use dev-only browser profile without extensions
|
|
||||||
- Disable "Disable Cache" in DevTools when using Vite
|
|
||||||
- Extensions can interfere with requests and slow startup
|
|
||||||
|
|
||||||
## Audit Plugin Performance
|
|
||||||
|
|
||||||
1. **Lazy load large dependencies** in plugins
|
|
||||||
2. **Avoid long operations** in `buildStart`, `config`, `configResolved`
|
|
||||||
3. **Optimize transform hooks** - check `id` extension before processing
|
|
||||||
|
|
||||||
Debug transform times:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
vite --debug plugin-transform
|
|
||||||
```
|
|
||||||
|
|
||||||
Use [vite-plugin-inspect](https://github.com/antfu/vite-plugin-inspect) to inspect transforms.
|
|
||||||
|
|
||||||
## Profiling
|
|
||||||
|
|
||||||
```bash
|
|
||||||
vite --profile
|
|
||||||
# Visit site, press 'p + enter' to record .cpuprofile
|
|
||||||
# Open in https://www.speedscope.app
|
|
||||||
```
|
|
||||||
|
|
||||||
## Reduce Resolve Operations
|
|
||||||
|
|
||||||
Be explicit with extensions to avoid filesystem checks:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Slow: checks .mjs, .js, .mts, .ts, .jsx, .tsx, .json
|
|
||||||
import Component from './Component'
|
|
||||||
|
|
||||||
// Fast: direct hit
|
|
||||||
import Component from './Component.tsx'
|
|
||||||
```
|
|
||||||
|
|
||||||
Enable TypeScript path resolution for explicit imports:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"allowImportingTsExtensions": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Avoid Barrel Files
|
|
||||||
|
|
||||||
Barrel files (`index.js` re-exporting everything) cause all files to load:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Slow: loads all utils
|
|
||||||
import { slash } from './utils'
|
|
||||||
|
|
||||||
// Fast: loads only slash.js
|
|
||||||
import { slash } from './utils/slash.js'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Warm Up Frequently Used Files
|
|
||||||
|
|
||||||
Pre-transform files that are always loaded:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
server: {
|
|
||||||
warmup: {
|
|
||||||
clientFiles: [
|
|
||||||
'./src/components/BigComponent.vue',
|
|
||||||
'./src/utils/big-utils.js'
|
|
||||||
],
|
|
||||||
ssrFiles: ['./src/server/modules/*.js']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Find files to warm up:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
vite --debug transform
|
|
||||||
```
|
|
||||||
|
|
||||||
## Use Native/Less Tooling
|
|
||||||
|
|
||||||
**Do less work:**
|
|
||||||
- CSS instead of Sass/Less (PostCSS has nesting)
|
|
||||||
- Don't transform SVGs to components - import as strings/URLs
|
|
||||||
- Skip Babel in `@vitejs/plugin-react` if not needed
|
|
||||||
|
|
||||||
**Use native tools:**
|
|
||||||
- Try Lightning CSS for faster CSS processing
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
css: {
|
|
||||||
transformer: 'lightningcss'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Server Options
|
|
||||||
|
|
||||||
### Open Browser Automatically
|
|
||||||
|
|
||||||
Triggers warmup of entry point:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
server: {
|
|
||||||
open: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Limit File Watching
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
server: {
|
|
||||||
watch: {
|
|
||||||
ignored: ['**/large-folder/**']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Build Performance
|
|
||||||
|
|
||||||
### Disable Reporting
|
|
||||||
|
|
||||||
Skip gzip size calculation for large projects:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
build: {
|
|
||||||
reportCompressedSize: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Sourcemaps
|
|
||||||
|
|
||||||
Disable if not needed:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
build: {
|
|
||||||
sourcemap: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://vite.dev/guide/performance.html
|
|
||||||
-->
|
|
||||||
@ -1,258 +0,0 @@
|
|||||||
---
|
|
||||||
name: advanced-plugin-api
|
|
||||||
description: Creating Vite plugins, hooks, virtual modules, and client-server communication
|
|
||||||
---
|
|
||||||
|
|
||||||
# Plugin API
|
|
||||||
|
|
||||||
Vite plugins extend Rolldown's plugin interface with Vite-specific hooks.
|
|
||||||
|
|
||||||
## Basic Plugin Structure
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default function myPlugin(options = {}) {
|
|
||||||
return {
|
|
||||||
name: 'vite-plugin-my-plugin',
|
|
||||||
|
|
||||||
// Hooks...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Naming Conventions
|
|
||||||
|
|
||||||
- Vite-only plugins: `vite-plugin-*`
|
|
||||||
- Rollup-compatible: `rollup-plugin-*`
|
|
||||||
- Framework-specific: `vite-plugin-vue-*`, `vite-plugin-react-*`
|
|
||||||
|
|
||||||
## Universal Hooks (from Rolldown)
|
|
||||||
|
|
||||||
Called on server start:
|
|
||||||
- `options` - Modify Rolldown options
|
|
||||||
- `buildStart` - Build starting
|
|
||||||
|
|
||||||
Called per module request:
|
|
||||||
- `resolveId` - Resolve import paths
|
|
||||||
- `load` - Load module content
|
|
||||||
- `transform` - Transform module code
|
|
||||||
|
|
||||||
Called on server close:
|
|
||||||
- `buildEnd`
|
|
||||||
- `closeBundle`
|
|
||||||
|
|
||||||
## Vite-Specific Hooks
|
|
||||||
|
|
||||||
### `config`
|
|
||||||
|
|
||||||
Modify config before resolution:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
{
|
|
||||||
name: 'modify-config',
|
|
||||||
config(config, { command, mode }) {
|
|
||||||
if (command === 'build') {
|
|
||||||
return {
|
|
||||||
resolve: {
|
|
||||||
alias: { foo: 'bar' }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### `configResolved`
|
|
||||||
|
|
||||||
Access final resolved config:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
{
|
|
||||||
name: 'read-config',
|
|
||||||
configResolved(config) {
|
|
||||||
this.config = config
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### `configureServer`
|
|
||||||
|
|
||||||
Add dev server middleware:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
{
|
|
||||||
name: 'configure-server',
|
|
||||||
configureServer(server) {
|
|
||||||
// Before Vite's middlewares
|
|
||||||
server.middlewares.use((req, res, next) => {
|
|
||||||
// Handle request
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
|
|
||||||
// Return function to run after Vite's middlewares
|
|
||||||
return () => {
|
|
||||||
server.middlewares.use((req, res, next) => {
|
|
||||||
// Post middleware
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### `transformIndexHtml`
|
|
||||||
|
|
||||||
Transform HTML files:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
{
|
|
||||||
name: 'html-transform',
|
|
||||||
transformIndexHtml(html) {
|
|
||||||
return html.replace(/<title>(.*?)<\/title>/, '<title>New Title</title>')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Inject tags:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
{
|
|
||||||
name: 'html-inject',
|
|
||||||
transformIndexHtml() {
|
|
||||||
return {
|
|
||||||
tags: [
|
|
||||||
{
|
|
||||||
tag: 'script',
|
|
||||||
attrs: { src: '/inject.js' },
|
|
||||||
injectTo: 'body' // 'head' | 'body' | 'head-prepend' | 'body-prepend'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### `handleHotUpdate`
|
|
||||||
|
|
||||||
Custom HMR handling:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
{
|
|
||||||
name: 'custom-hmr',
|
|
||||||
handleHotUpdate({ file, server, modules }) {
|
|
||||||
if (file.endsWith('.custom')) {
|
|
||||||
server.ws.send({
|
|
||||||
type: 'custom',
|
|
||||||
event: 'custom-update',
|
|
||||||
data: { file }
|
|
||||||
})
|
|
||||||
return [] // Prevent default HMR
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Virtual Modules
|
|
||||||
|
|
||||||
Provide build-time information to source code:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default function myPlugin() {
|
|
||||||
const virtualModuleId = 'virtual:my-module'
|
|
||||||
const resolvedId = '\0' + virtualModuleId
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: 'virtual-module',
|
|
||||||
|
|
||||||
resolveId(id) {
|
|
||||||
if (id === virtualModuleId) {
|
|
||||||
return resolvedId
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
load(id) {
|
|
||||||
if (id === resolvedId) {
|
|
||||||
return `export const msg = "from virtual module"`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { msg } from 'virtual:my-module'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Client-Server Communication
|
|
||||||
|
|
||||||
### Server to Client
|
|
||||||
|
|
||||||
```ts
|
|
||||||
{
|
|
||||||
configureServer(server) {
|
|
||||||
server.ws.on('connection', () => {
|
|
||||||
server.ws.send('my:greetings', { msg: 'hello' })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Client receives:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
if (import.meta.hot) {
|
|
||||||
import.meta.hot.on('my:greetings', (data) => {
|
|
||||||
console.log(data.msg)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Client to Server
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Client
|
|
||||||
if (import.meta.hot) {
|
|
||||||
import.meta.hot.send('my:from-client', { msg: 'Hey!' })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Server (in plugin)
|
|
||||||
{
|
|
||||||
configureServer(server) {
|
|
||||||
server.ws.on('my:from-client', (data, client) => {
|
|
||||||
console.log(data.msg)
|
|
||||||
client.send('my:reply', { msg: 'Got it!' })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Transform with Filtering
|
|
||||||
|
|
||||||
```ts
|
|
||||||
{
|
|
||||||
name: 'transform-js',
|
|
||||||
transform: {
|
|
||||||
filter: {
|
|
||||||
id: /\.js$/ // Only .js files
|
|
||||||
},
|
|
||||||
handler(code, id) {
|
|
||||||
return transformCode(code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Path Normalization
|
|
||||||
|
|
||||||
Use POSIX separators for cross-platform compatibility:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { normalizePath } from 'vite'
|
|
||||||
|
|
||||||
normalizePath('foo\\bar') // 'foo/bar'
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://vite.dev/guide/api-plugin.html
|
|
||||||
-->
|
|
||||||
@ -1,172 +0,0 @@
|
|||||||
---
|
|
||||||
name: build-library
|
|
||||||
description: Building libraries with Vite including proper package.json exports
|
|
||||||
---
|
|
||||||
|
|
||||||
# Library Mode
|
|
||||||
|
|
||||||
Build browser-oriented libraries for distribution.
|
|
||||||
|
|
||||||
## Basic Configuration
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { resolve } from 'path'
|
|
||||||
import { defineConfig } from 'vite'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
build: {
|
|
||||||
lib: {
|
|
||||||
entry: resolve(__dirname, 'lib/main.js'),
|
|
||||||
name: 'MyLib', // Global variable name for UMD
|
|
||||||
fileName: 'my-lib' // Output filename (without extension)
|
|
||||||
},
|
|
||||||
rolldownOptions: {
|
|
||||||
external: ['vue'], // Don't bundle these
|
|
||||||
output: {
|
|
||||||
globals: {
|
|
||||||
vue: 'Vue' // Global var for externals in UMD
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Multiple Entry Points
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
build: {
|
|
||||||
lib: {
|
|
||||||
entry: {
|
|
||||||
'my-lib': resolve(__dirname, 'lib/main.js'),
|
|
||||||
'secondary': resolve(__dirname, 'lib/secondary.js')
|
|
||||||
},
|
|
||||||
name: 'MyLib'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Output Formats
|
|
||||||
|
|
||||||
Single entry defaults: `['es', 'umd']`
|
|
||||||
Multiple entries defaults: `['es', 'cjs']`
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
build: {
|
|
||||||
lib: {
|
|
||||||
entry: resolve(__dirname, 'lib/main.js'),
|
|
||||||
formats: ['es', 'cjs', 'umd', 'iife']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Custom File Names
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
build: {
|
|
||||||
lib: {
|
|
||||||
entry: resolve(__dirname, 'lib/main.js'),
|
|
||||||
fileName: (format, entryName) => `${entryName}.${format}.js`,
|
|
||||||
cssFileName: 'styles' // For bundled CSS
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Package.json Configuration
|
|
||||||
|
|
||||||
### Single Entry
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "my-lib",
|
|
||||||
"type": "module",
|
|
||||||
"files": ["dist"],
|
|
||||||
"main": "./dist/my-lib.umd.cjs",
|
|
||||||
"module": "./dist/my-lib.js",
|
|
||||||
"exports": {
|
|
||||||
".": {
|
|
||||||
"import": "./dist/my-lib.js",
|
|
||||||
"require": "./dist/my-lib.umd.cjs"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Multiple Entries
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "my-lib",
|
|
||||||
"type": "module",
|
|
||||||
"files": ["dist"],
|
|
||||||
"main": "./dist/my-lib.cjs",
|
|
||||||
"module": "./dist/my-lib.js",
|
|
||||||
"exports": {
|
|
||||||
".": {
|
|
||||||
"import": "./dist/my-lib.js",
|
|
||||||
"require": "./dist/my-lib.cjs"
|
|
||||||
},
|
|
||||||
"./secondary": {
|
|
||||||
"import": "./dist/secondary.js",
|
|
||||||
"require": "./dist/secondary.cjs"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### With CSS
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"exports": {
|
|
||||||
".": {
|
|
||||||
"import": "./dist/my-lib.js",
|
|
||||||
"require": "./dist/my-lib.umd.cjs"
|
|
||||||
},
|
|
||||||
"./style.css": "./dist/my-lib.css"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Library Entry File
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// lib/main.js
|
|
||||||
import Foo from './Foo.vue'
|
|
||||||
import Bar from './Bar.vue'
|
|
||||||
|
|
||||||
export { Foo, Bar }
|
|
||||||
```
|
|
||||||
|
|
||||||
## Environment Variables
|
|
||||||
|
|
||||||
In library mode:
|
|
||||||
- `import.meta.env.*` is statically replaced
|
|
||||||
- `process.env.*` is NOT replaced (consumers can change it)
|
|
||||||
|
|
||||||
To replace `process.env`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
define: {
|
|
||||||
'process.env.NODE_ENV': '"production"'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- `assetsInlineLimit` is ignored - assets always inlined
|
|
||||||
- `cssCodeSplit` defaults to `false`
|
|
||||||
- For non-browser libraries, consider using [tsdown](https://tsdown.dev/) or Rolldown directly
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://vite.dev/guide/build.html#library-mode
|
|
||||||
-->
|
|
||||||
@ -1,220 +0,0 @@
|
|||||||
---
|
|
||||||
name: build-production
|
|
||||||
description: Building for production including targets, multi-page apps, and optimizations
|
|
||||||
---
|
|
||||||
|
|
||||||
# Building for Production
|
|
||||||
|
|
||||||
## Basic Build
|
|
||||||
|
|
||||||
```bash
|
|
||||||
vite build
|
|
||||||
```
|
|
||||||
|
|
||||||
Uses `<root>/index.html` as entry point, outputs to `dist/`.
|
|
||||||
|
|
||||||
## Browser Compatibility
|
|
||||||
|
|
||||||
Default target: Baseline Widely Available browsers (Chrome 111+, Edge 111+, Firefox 114+, Safari 16.4+).
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
build: {
|
|
||||||
target: 'es2020', // Or specific browsers
|
|
||||||
// target: ['chrome64', 'firefox78', 'safari12']
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
For legacy browsers:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm add -D @vitejs/plugin-legacy
|
|
||||||
```
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import legacy from '@vitejs/plugin-legacy'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [
|
|
||||||
legacy({
|
|
||||||
targets: ['defaults', 'not IE 11']
|
|
||||||
})
|
|
||||||
]
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Output Configuration
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
build: {
|
|
||||||
outDir: 'dist', // Output directory
|
|
||||||
assetsDir: 'assets', // Assets subdirectory
|
|
||||||
emptyOutDir: true, // Clear outDir before build
|
|
||||||
sourcemap: true, // Generate sourcemaps
|
|
||||||
// sourcemap: 'inline' | 'hidden'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Public Base Path
|
|
||||||
|
|
||||||
For deploying under a subpath:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
base: '/my-app/'
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Relative base (works anywhere):
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
base: './'
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Access in code:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const base = import.meta.env.BASE_URL
|
|
||||||
```
|
|
||||||
|
|
||||||
## Multi-Page App
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { resolve } from 'path'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
build: {
|
|
||||||
rolldownOptions: {
|
|
||||||
input: {
|
|
||||||
main: resolve(__dirname, 'index.html'),
|
|
||||||
nested: resolve(__dirname, 'nested/index.html')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Minification
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
build: {
|
|
||||||
minify: 'oxc', // Default, fastest
|
|
||||||
// minify: 'terser', // More options, slower
|
|
||||||
// minify: false, // Disable
|
|
||||||
|
|
||||||
terserOptions: { // If using terser
|
|
||||||
compress: {
|
|
||||||
drop_console: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Chunk Strategy
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
build: {
|
|
||||||
rolldownOptions: {
|
|
||||||
output: {
|
|
||||||
codeSplitting: {
|
|
||||||
// Manual chunks configuration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
chunkSizeWarningLimit: 500 // KB
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## CSS Options
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
build: {
|
|
||||||
cssCodeSplit: true, // CSS per async chunk
|
|
||||||
cssMinify: 'lightningcss', // or 'esbuild'
|
|
||||||
cssTarget: 'chrome61' // Different from JS target
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Asset Handling
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
build: {
|
|
||||||
assetsInlineLimit: 4096, // Inline assets < 4KB as base64
|
|
||||||
copyPublicDir: true // Copy public/ to outDir
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Manifest
|
|
||||||
|
|
||||||
Generate manifest for backend integration:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
build: {
|
|
||||||
manifest: true // .vite/manifest.json
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Watch Mode
|
|
||||||
|
|
||||||
Rebuild on file changes:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
vite build --watch
|
|
||||||
```
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
build: {
|
|
||||||
watch: {} // Enable programmatically
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Load Error Handling
|
|
||||||
|
|
||||||
Handle dynamic import failures (e.g., after deployment):
|
|
||||||
|
|
||||||
```ts
|
|
||||||
window.addEventListener('vite:preloadError', (event) => {
|
|
||||||
window.location.reload()
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Build Optimizations (Automatic)
|
|
||||||
|
|
||||||
- **CSS code splitting** - CSS per async chunk
|
|
||||||
- **Preload directives** - `<link rel="modulepreload">`
|
|
||||||
- **Async chunk optimization** - Parallel fetching of dependencies
|
|
||||||
|
|
||||||
## License Generation
|
|
||||||
|
|
||||||
Generate license file for dependencies:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
build: {
|
|
||||||
license: true // .vite/license.md
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://vite.dev/guide/build.html
|
|
||||||
- https://vite.dev/config/build-options.html
|
|
||||||
-->
|
|
||||||
@ -1,194 +0,0 @@
|
|||||||
---
|
|
||||||
name: build-ssr
|
|
||||||
description: Server-side rendering setup and configuration with Vite
|
|
||||||
---
|
|
||||||
|
|
||||||
# Server-Side Rendering (SSR)
|
|
||||||
|
|
||||||
Low-level API for framework authors. For applications, use higher-level tools from [Awesome Vite SSR](https://github.com/vitejs/awesome-vite#ssr).
|
|
||||||
|
|
||||||
## Project Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
├── index.html
|
|
||||||
├── server.js # Express/Node server
|
|
||||||
└── src/
|
|
||||||
├── main.js # Universal app code
|
|
||||||
├── entry-client.js # Mounts app to DOM
|
|
||||||
└── entry-server.js # Renders app with SSR API
|
|
||||||
```
|
|
||||||
|
|
||||||
## index.html
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<body>
|
|
||||||
<div id="app"><!--ssr-outlet--></div>
|
|
||||||
<script type="module" src="/src/entry-client.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Development Server
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// server.js
|
|
||||||
import express from 'express'
|
|
||||||
import { createServer as createViteServer } from 'vite'
|
|
||||||
|
|
||||||
async function createServer() {
|
|
||||||
const app = express()
|
|
||||||
|
|
||||||
const vite = await createViteServer({
|
|
||||||
server: { middlewareMode: true },
|
|
||||||
appType: 'custom'
|
|
||||||
})
|
|
||||||
|
|
||||||
app.use(vite.middlewares)
|
|
||||||
|
|
||||||
app.use('*all', async (req, res, next) => {
|
|
||||||
const url = req.originalUrl
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 1. Read index.html
|
|
||||||
let template = fs.readFileSync(
|
|
||||||
path.resolve(__dirname, 'index.html'),
|
|
||||||
'utf-8'
|
|
||||||
)
|
|
||||||
|
|
||||||
// 2. Apply Vite transforms
|
|
||||||
template = await vite.transformIndexHtml(url, template)
|
|
||||||
|
|
||||||
// 3. Load server entry
|
|
||||||
const { render } = await vite.ssrLoadModule('/src/entry-server.js')
|
|
||||||
|
|
||||||
// 4. Render app HTML
|
|
||||||
const appHtml = await render(url)
|
|
||||||
|
|
||||||
// 5. Inject into template
|
|
||||||
const html = template.replace('<!--ssr-outlet-->', appHtml)
|
|
||||||
|
|
||||||
res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
|
|
||||||
} catch (e) {
|
|
||||||
vite.ssrFixStacktrace(e)
|
|
||||||
next(e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
app.listen(5173)
|
|
||||||
}
|
|
||||||
|
|
||||||
createServer()
|
|
||||||
```
|
|
||||||
|
|
||||||
## Conditional Logic
|
|
||||||
|
|
||||||
```ts
|
|
||||||
if (import.meta.env.SSR) {
|
|
||||||
// Server-only code (tree-shaken on client)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Production Build
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"scripts": {
|
|
||||||
"build:client": "vite build --outDir dist/client",
|
|
||||||
"build:server": "vite build --outDir dist/server --ssr src/entry-server.js"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Production Server
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Differences from dev:
|
|
||||||
// 1. Use dist/client/index.html as template
|
|
||||||
// 2. Use import('./dist/server/entry-server.js') instead of ssrLoadModule
|
|
||||||
// 3. Serve static files from dist/client
|
|
||||||
```
|
|
||||||
|
|
||||||
## SSR Manifest
|
|
||||||
|
|
||||||
For preload directives:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
vite build --outDir dist/client --ssrManifest
|
|
||||||
```
|
|
||||||
|
|
||||||
Generates `dist/client/.vite/ssr-manifest.json` with module-to-chunk mappings.
|
|
||||||
|
|
||||||
## SSR Externals
|
|
||||||
|
|
||||||
Dependencies are externalized by default. To transform with Vite:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
ssr: {
|
|
||||||
noExternal: ['package-that-needs-transform'],
|
|
||||||
external: ['package-to-externalize']
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## SSR-specific Plugin Logic
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export function mySSRPlugin() {
|
|
||||||
return {
|
|
||||||
name: 'my-ssr',
|
|
||||||
transform(code, id, options) {
|
|
||||||
if (options?.ssr) {
|
|
||||||
// SSR-specific transform
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## SSR Target
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
ssr: {
|
|
||||||
target: 'node', // Default
|
|
||||||
// target: 'webworker' // For edge runtimes
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## SSR Bundle
|
|
||||||
|
|
||||||
Bundle all dependencies (for workers):
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
ssr: {
|
|
||||||
noExternal: true // Bundle everything
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Resolve Conditions
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
ssr: {
|
|
||||||
resolve: {
|
|
||||||
conditions: ['node'],
|
|
||||||
externalConditions: ['node']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Pre-Rendering / SSG
|
|
||||||
|
|
||||||
Pre-render routes with known data into static HTML at build time.
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://vite.dev/guide/ssr.html
|
|
||||||
-->
|
|
||||||
@ -1,137 +0,0 @@
|
|||||||
---
|
|
||||||
name: core-cli
|
|
||||||
description: Vite CLI commands for development, building, and previewing
|
|
||||||
---
|
|
||||||
|
|
||||||
# Vite CLI
|
|
||||||
|
|
||||||
## Dev Server
|
|
||||||
|
|
||||||
Start the development server:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
vite [root]
|
|
||||||
vite dev [root] # alias
|
|
||||||
vite serve [root] # alias
|
|
||||||
```
|
|
||||||
|
|
||||||
### Dev Server Options
|
|
||||||
|
|
||||||
| Option | Description |
|
|
||||||
|--------|-------------|
|
|
||||||
| `--host [host]` | Specify hostname (use `0.0.0.0` for LAN access) |
|
|
||||||
| `--port <port>` | Specify port (default: 5173) |
|
|
||||||
| `--open [path]` | Open browser on startup |
|
|
||||||
| `--cors` | Enable CORS |
|
|
||||||
| `--strictPort` | Exit if port is in use |
|
|
||||||
| `--force` | Force optimizer to re-bundle dependencies |
|
|
||||||
| `-c, --config <file>` | Use specified config file |
|
|
||||||
| `--base <path>` | Public base path |
|
|
||||||
| `-m, --mode <mode>` | Set env mode |
|
|
||||||
| `-l, --logLevel <level>` | info \| warn \| error \| silent |
|
|
||||||
| `--clearScreen` | Allow/disable clear screen when logging |
|
|
||||||
|
|
||||||
## Build
|
|
||||||
|
|
||||||
Build for production:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
vite build [root]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Build Options
|
|
||||||
|
|
||||||
| Option | Description |
|
|
||||||
|--------|-------------|
|
|
||||||
| `--target <target>` | Transpile target (default: `"modules"`) |
|
|
||||||
| `--outDir <dir>` | Output directory (default: `dist`) |
|
|
||||||
| `--assetsDir <dir>` | Assets directory under outDir (default: `"assets"`) |
|
|
||||||
| `--assetsInlineLimit <number>` | Inline threshold in bytes (default: 4096) |
|
|
||||||
| `--ssr [entry]` | Build for SSR |
|
|
||||||
| `--sourcemap [output]` | Generate source maps (`boolean \| "inline" \| "hidden"`) |
|
|
||||||
| `--minify [minifier]` | Minifier (`boolean \| "oxc" \| "terser" \| "esbuild"`) |
|
|
||||||
| `--manifest [name]` | Generate build manifest JSON |
|
|
||||||
| `--ssrManifest [name]` | Generate SSR manifest JSON |
|
|
||||||
| `--emptyOutDir` | Force empty outDir |
|
|
||||||
| `-w, --watch` | Watch mode for rebuilding |
|
|
||||||
|
|
||||||
## Preview
|
|
||||||
|
|
||||||
Locally preview the production build:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
vite preview [root]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Preview Options
|
|
||||||
|
|
||||||
| Option | Description |
|
|
||||||
|--------|-------------|
|
|
||||||
| `--host [host]` | Specify hostname |
|
|
||||||
| `--port <port>` | Specify port |
|
|
||||||
| `--strictPort` | Exit if port is in use |
|
|
||||||
| `--open [path]` | Open browser on startup |
|
|
||||||
| `--outDir <dir>` | Output directory (default: `dist`) |
|
|
||||||
|
|
||||||
## Package Scripts
|
|
||||||
|
|
||||||
Typical `package.json` scripts:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"scripts": {
|
|
||||||
"dev": "vite",
|
|
||||||
"build": "vite build",
|
|
||||||
"preview": "vite preview"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Running Vite
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# With npm
|
|
||||||
npx vite
|
|
||||||
|
|
||||||
# With pnpm
|
|
||||||
pnpm vite
|
|
||||||
|
|
||||||
# With yarn
|
|
||||||
yarn vite
|
|
||||||
|
|
||||||
# With bun
|
|
||||||
bunx vite
|
|
||||||
```
|
|
||||||
|
|
||||||
## Scaffolding New Project
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Interactive prompts
|
|
||||||
npm create vite@latest
|
|
||||||
|
|
||||||
# With project name and template
|
|
||||||
npm create vite@latest my-app -- --template vue-ts
|
|
||||||
|
|
||||||
# Available templates: vanilla, vanilla-ts, vue, vue-ts, react, react-ts,
|
|
||||||
# react-swc, react-swc-ts, preact, preact-ts, lit, lit-ts, svelte, svelte-ts,
|
|
||||||
# solid, solid-ts, qwik, qwik-ts
|
|
||||||
```
|
|
||||||
|
|
||||||
## Debugging
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Debug plugin transforms
|
|
||||||
vite --debug plugin-transform
|
|
||||||
|
|
||||||
# Debug with profiling
|
|
||||||
vite --profile
|
|
||||||
# Then press 'p + enter' to record .cpuprofile
|
|
||||||
|
|
||||||
# Filter debug logs
|
|
||||||
vite --debug -f plugin-transform
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://vite.dev/guide/cli.html
|
|
||||||
-->
|
|
||||||
@ -1,176 +0,0 @@
|
|||||||
---
|
|
||||||
name: core-config
|
|
||||||
description: Vite configuration file setup, defineConfig helper, conditional and async configs
|
|
||||||
---
|
|
||||||
|
|
||||||
# Vite Configuration
|
|
||||||
|
|
||||||
Vite automatically resolves a config file named `vite.config.*` in the project root.
|
|
||||||
|
|
||||||
## Basic Configuration
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite.config.ts
|
|
||||||
import { defineConfig } from 'vite'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
// config options
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Use `defineConfig` for TypeScript intellisense. Alternatively, use JSDoc annotations:
|
|
||||||
|
|
||||||
```js
|
|
||||||
/** @type {import('vite').UserConfig} */
|
|
||||||
export default {
|
|
||||||
// config options
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Conditional Config
|
|
||||||
|
|
||||||
Export a function to conditionally determine options based on command, mode, or build type:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineConfig } from 'vite'
|
|
||||||
|
|
||||||
export default defineConfig(({ command, mode, isSsrBuild, isPreview }) => {
|
|
||||||
if (command === 'serve') {
|
|
||||||
// dev specific config
|
|
||||||
return {
|
|
||||||
define: {
|
|
||||||
__DEV__: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// build specific config
|
|
||||||
return {
|
|
||||||
define: {
|
|
||||||
__DEV__: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
- `command` is `'serve'` during dev (`vite`, `vite dev`, `vite serve`) and `'build'` for production
|
|
||||||
- `mode` defaults to `'development'` for serve, `'production'` for build
|
|
||||||
|
|
||||||
## Async Config
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineConfig } from 'vite'
|
|
||||||
|
|
||||||
export default defineConfig(async ({ command, mode }) => {
|
|
||||||
const data = await fetchRemoteConfig()
|
|
||||||
return {
|
|
||||||
// config using fetched data
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Key Configuration Options
|
|
||||||
|
|
||||||
### Root and Base
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
root: './src', // Project root directory (where index.html is)
|
|
||||||
base: '/my-app/', // Public base path for assets
|
|
||||||
publicDir: 'public', // Static assets directory
|
|
||||||
cacheDir: 'node_modules/.vite' // Cache directory
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Resolve Aliases
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { resolve } from 'path'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
'@': resolve(__dirname, 'src'),
|
|
||||||
'~': resolve(__dirname, 'src/components')
|
|
||||||
},
|
|
||||||
// File extensions to try for imports without extension
|
|
||||||
extensions: ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json']
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Define Global Constants
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
define: {
|
|
||||||
__APP_VERSION__: JSON.stringify('1.0.0'),
|
|
||||||
__API_URL__: JSON.stringify('https://api.example.com')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Values must be JSON-serializable or a single identifier. Add TypeScript declarations:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite-env.d.ts
|
|
||||||
declare const __APP_VERSION__: string
|
|
||||||
declare const __API_URL__: string
|
|
||||||
```
|
|
||||||
|
|
||||||
### JSON Handling
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
json: {
|
|
||||||
namedExports: true, // Support named imports from JSON
|
|
||||||
stringify: 'auto' // Stringify large JSON for performance
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Using Environment Variables in Config
|
|
||||||
|
|
||||||
Variables from `.env` files are NOT automatically available in config. Use `loadEnv`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineConfig, loadEnv } from 'vite'
|
|
||||||
|
|
||||||
export default defineConfig(({ mode }) => {
|
|
||||||
// Load env vars from .env files
|
|
||||||
const env = loadEnv(mode, process.cwd(), '')
|
|
||||||
|
|
||||||
return {
|
|
||||||
define: {
|
|
||||||
__APP_ENV__: JSON.stringify(env.APP_ENV)
|
|
||||||
},
|
|
||||||
server: {
|
|
||||||
port: env.APP_PORT ? Number(env.APP_PORT) : 5173
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Specifying Config File
|
|
||||||
|
|
||||||
```bash
|
|
||||||
vite --config my-config.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
## Config Loading Methods
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Default: bundle with Rolldown (may have issues in monorepos)
|
|
||||||
vite
|
|
||||||
|
|
||||||
# Use module runner (no temp file, transforms on the fly)
|
|
||||||
vite --configLoader runner
|
|
||||||
|
|
||||||
# Use native runtime (requires Node.js with TypeScript support)
|
|
||||||
vite --configLoader native
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://vite.dev/config/
|
|
||||||
-->
|
|
||||||
@ -1,170 +0,0 @@
|
|||||||
---
|
|
||||||
name: core-features
|
|
||||||
description: Core Vite features including TypeScript, JSX, CSS, and HTML processing
|
|
||||||
---
|
|
||||||
|
|
||||||
# Core Features
|
|
||||||
|
|
||||||
## TypeScript
|
|
||||||
|
|
||||||
Vite supports `.ts` files out of the box with transpilation via Oxc Transformer (20-30x faster than tsc).
|
|
||||||
|
|
||||||
### Important: Transpile Only
|
|
||||||
|
|
||||||
Vite does NOT perform type checking. Run type checking separately:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Production build
|
|
||||||
tsc --noEmit && vite build
|
|
||||||
|
|
||||||
# During development (separate process)
|
|
||||||
tsc --noEmit --watch
|
|
||||||
|
|
||||||
# Or use vite-plugin-checker for browser error reporting
|
|
||||||
```
|
|
||||||
|
|
||||||
### TypeScript Configuration
|
|
||||||
|
|
||||||
Required `tsconfig.json` settings:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"isolatedModules": true,
|
|
||||||
"useDefineForClassFields": true,
|
|
||||||
"skipLibCheck": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Client Types
|
|
||||||
|
|
||||||
Add Vite's client types for `import.meta.env` and asset imports:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"types": ["vite/client"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This provides types for:
|
|
||||||
- Asset imports (`.svg`, `.png`, etc.)
|
|
||||||
- `import.meta.env` constants
|
|
||||||
- `import.meta.hot` HMR API
|
|
||||||
|
|
||||||
### Custom Type Overrides
|
|
||||||
|
|
||||||
Override default asset import types:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite-env-override.d.ts
|
|
||||||
declare module '*.svg' {
|
|
||||||
const content: React.FC<React.SVGProps<SVGElement>>
|
|
||||||
export default content
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Path Aliases with tsconfig
|
|
||||||
|
|
||||||
Enable tsconfig paths resolution:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite.config.ts
|
|
||||||
export default defineConfig({
|
|
||||||
resolve: {
|
|
||||||
tsconfigPaths: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## JSX
|
|
||||||
|
|
||||||
`.jsx` and `.tsx` files are supported out of the box. Custom JSX configuration:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
oxc: {
|
|
||||||
jsx: {
|
|
||||||
runtime: 'classic', // or 'automatic'
|
|
||||||
pragma: 'h',
|
|
||||||
pragmaFrag: 'Fragment'
|
|
||||||
},
|
|
||||||
// Auto-inject JSX helpers
|
|
||||||
jsxInject: `import React from 'react'`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## HTML
|
|
||||||
|
|
||||||
`index.html` is the entry point, not tucked away in `public/`. Vite processes it as part of the module graph.
|
|
||||||
|
|
||||||
### Supported Elements
|
|
||||||
|
|
||||||
Vite processes these HTML element attributes:
|
|
||||||
|
|
||||||
- `<script type="module" src>`
|
|
||||||
- `<link href>` (stylesheets)
|
|
||||||
- `<img src>`, `<img srcset>`
|
|
||||||
- `<video src>`, `<video poster>`
|
|
||||||
- `<audio src>`
|
|
||||||
- `<source src>`, `<source srcset>`
|
|
||||||
- `<meta content>` (for og:image, twitter:image, etc.)
|
|
||||||
|
|
||||||
### Opt-out of Processing
|
|
||||||
|
|
||||||
```html
|
|
||||||
<script vite-ignore type="module" src="https://cdn.example.com/lib.js"></script>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Multi-Page Apps
|
|
||||||
|
|
||||||
Access any HTML file by its path:
|
|
||||||
|
|
||||||
- `<root>/index.html` → `http://localhost:5173/`
|
|
||||||
- `<root>/about.html` → `http://localhost:5173/about.html`
|
|
||||||
- `<root>/blog/index.html` → `http://localhost:5173/blog/index.html`
|
|
||||||
|
|
||||||
## JSON
|
|
||||||
|
|
||||||
Direct import with named exports support:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Import entire object
|
|
||||||
import json from './data.json'
|
|
||||||
|
|
||||||
// Named imports (tree-shakeable)
|
|
||||||
import { field } from './data.json'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Framework Support
|
|
||||||
|
|
||||||
Official framework plugins:
|
|
||||||
|
|
||||||
| Framework | Plugin |
|
|
||||||
|-----------|--------|
|
|
||||||
| Vue 3 | `@vitejs/plugin-vue` |
|
|
||||||
| Vue 3 JSX | `@vitejs/plugin-vue-jsx` |
|
|
||||||
| React | `@vitejs/plugin-react` |
|
|
||||||
| React (SWC) | `@vitejs/plugin-react-swc` |
|
|
||||||
| React Server Components | `@vitejs/plugin-rsc` |
|
|
||||||
| Legacy browsers | `@vitejs/plugin-legacy` |
|
|
||||||
|
|
||||||
## Content Security Policy
|
|
||||||
|
|
||||||
Configure nonce for CSP:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
html: {
|
|
||||||
cspNonce: 'PLACEHOLDER' // Replace per-request
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://vite.dev/guide/features.html
|
|
||||||
-->
|
|
||||||
@ -1,154 +0,0 @@
|
|||||||
---
|
|
||||||
name: core-plugins
|
|
||||||
description: Adding, configuring, and ordering Vite plugins
|
|
||||||
---
|
|
||||||
|
|
||||||
# Using Plugins
|
|
||||||
|
|
||||||
Vite extends Rolldown's plugin interface with extra Vite-specific options.
|
|
||||||
|
|
||||||
## Adding Plugins
|
|
||||||
|
|
||||||
Install and add to config:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm add -D @vitejs/plugin-vue
|
|
||||||
```
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite.config.ts
|
|
||||||
import vue from '@vitejs/plugin-vue'
|
|
||||||
import { defineConfig } from 'vite'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [vue()]
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Plugin Arrays
|
|
||||||
|
|
||||||
Plugins can return arrays (for complex features):
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Framework plugin returning multiple plugins
|
|
||||||
export default function framework(config) {
|
|
||||||
return [
|
|
||||||
frameworkRefresh(config),
|
|
||||||
frameworkDevtools(config)
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Conditional Plugins
|
|
||||||
|
|
||||||
Falsy values are ignored:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [
|
|
||||||
vue(),
|
|
||||||
process.env.ANALYZE && visualizer() // Only if ANALYZE is set
|
|
||||||
]
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Enforcing Plugin Order
|
|
||||||
|
|
||||||
Control when plugin runs relative to Vite core:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [
|
|
||||||
{
|
|
||||||
...somePlugin(),
|
|
||||||
enforce: 'pre' // Before Vite core plugins
|
|
||||||
},
|
|
||||||
{
|
|
||||||
...anotherPlugin(),
|
|
||||||
enforce: 'post' // After Vite build plugins
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
**Order:**
|
|
||||||
1. Alias
|
|
||||||
2. Plugins with `enforce: 'pre'`
|
|
||||||
3. Vite core plugins
|
|
||||||
4. Plugins without enforce
|
|
||||||
5. Vite build plugins
|
|
||||||
6. Plugins with `enforce: 'post'`
|
|
||||||
7. Vite post-build plugins (minify, manifest)
|
|
||||||
|
|
||||||
## Conditional Application
|
|
||||||
|
|
||||||
Apply only during serve or build:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [
|
|
||||||
{
|
|
||||||
...typescript2(),
|
|
||||||
apply: 'build' // Only during build
|
|
||||||
},
|
|
||||||
{
|
|
||||||
...devOnlyPlugin(),
|
|
||||||
apply: 'serve' // Only during dev
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Function form for more control:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
{
|
|
||||||
...myPlugin(),
|
|
||||||
apply(config, { command }) {
|
|
||||||
// Apply only on build but not for SSR
|
|
||||||
return command === 'build' && !config.build.ssr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Finding Plugins
|
|
||||||
|
|
||||||
1. Check [Vite Features Guide](https://vite.dev/guide/features.html) - many use cases are built-in
|
|
||||||
2. Official plugins in [Vite Plugins](https://vite.dev/plugins/)
|
|
||||||
3. Community plugins in [awesome-vite](https://github.com/vitejs/awesome-vite#plugins)
|
|
||||||
4. Search npm for `vite-plugin-*` or `rollup-plugin-*`
|
|
||||||
|
|
||||||
## Official Plugins
|
|
||||||
|
|
||||||
| Plugin | Purpose |
|
|
||||||
|--------|---------|
|
|
||||||
| `@vitejs/plugin-vue` | Vue 3 SFC support |
|
|
||||||
| `@vitejs/plugin-vue-jsx` | Vue 3 JSX support |
|
|
||||||
| `@vitejs/plugin-react` | React with Babel/Oxc |
|
|
||||||
| `@vitejs/plugin-react-swc` | React with SWC |
|
|
||||||
| `@vitejs/plugin-rsc` | React Server Components |
|
|
||||||
| `@vitejs/plugin-legacy` | Legacy browser support |
|
|
||||||
|
|
||||||
## Rollup/Rolldown Plugin Compatibility
|
|
||||||
|
|
||||||
Many Rollup plugins work directly with Vite if they:
|
|
||||||
- Don't use `moduleParsed` hook
|
|
||||||
- Don't rely on Rolldown-specific options
|
|
||||||
- Don't have strong coupling between bundle and output phases
|
|
||||||
|
|
||||||
For build-only Rollup plugins:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
build: {
|
|
||||||
rolldownOptions: {
|
|
||||||
plugins: [rollupPluginForBuildOnly()]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://vite.dev/guide/using-plugins.html
|
|
||||||
-->
|
|
||||||
@ -1,138 +0,0 @@
|
|||||||
---
|
|
||||||
name: features-assets
|
|
||||||
description: Static asset handling in Vite including imports, public directory, and URL handling
|
|
||||||
---
|
|
||||||
|
|
||||||
# Static Asset Handling
|
|
||||||
|
|
||||||
## Importing Assets as URL
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import imgUrl from './img.png'
|
|
||||||
document.getElementById('hero-img').src = imgUrl
|
|
||||||
// Dev: /src/img.png
|
|
||||||
// Build: /assets/img.2d8efhg.png
|
|
||||||
```
|
|
||||||
|
|
||||||
Common image, media, and font types are detected automatically.
|
|
||||||
|
|
||||||
## Import Queries
|
|
||||||
|
|
||||||
### Explicit URL Import
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import workletURL from './worklet.js?url'
|
|
||||||
CSS.paintWorklet.addModule(workletURL)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Import as String (Raw)
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import shaderString from './shader.glsl?raw'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Control Inlining
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import imgUrl1 from './img.svg?no-inline' // Never inline
|
|
||||||
import imgUrl2 from './img.png?inline' // Always inline as base64
|
|
||||||
```
|
|
||||||
|
|
||||||
## Asset Inlining
|
|
||||||
|
|
||||||
Assets smaller than `assetsInlineLimit` (default 4KB) are inlined as base64:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
build: {
|
|
||||||
assetsInlineLimit: 4096, // 4KB
|
|
||||||
// Or use callback for fine control
|
|
||||||
assetsInlineLimit: (filePath) => {
|
|
||||||
return !filePath.endsWith('.svg')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## The `public` Directory
|
|
||||||
|
|
||||||
Files in `public/` are:
|
|
||||||
- Served at root path `/` during dev
|
|
||||||
- Copied as-is to `dist/` root during build
|
|
||||||
- Not processed or hashed
|
|
||||||
|
|
||||||
```
|
|
||||||
public/
|
|
||||||
favicon.ico → /favicon.ico
|
|
||||||
robots.txt → /robots.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
Reference with absolute paths in source:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<img src="/icon.png" />
|
|
||||||
```
|
|
||||||
|
|
||||||
Configure directory:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
publicDir: 'static' // or false to disable
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Extending Asset Types
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
assetsInclude: ['**/*.gltf', '**/*.hdr']
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Dynamic URLs with import.meta.url
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Works natively in modern browsers
|
|
||||||
const imgUrl = new URL('./img.png', import.meta.url).href
|
|
||||||
|
|
||||||
// Dynamic pattern (limited)
|
|
||||||
function getImageUrl(name) {
|
|
||||||
return new URL(`./dir/${name}.png`, import.meta.url).href
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Limitations:**
|
|
||||||
- URL string must be static for build analysis
|
|
||||||
- Does not work with SSR (different semantics in Node.js vs browser)
|
|
||||||
|
|
||||||
## TypeScript Support
|
|
||||||
|
|
||||||
Add `vite/client` to types for asset import recognition:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"types": ["vite/client"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## URL Handling in CSS
|
|
||||||
|
|
||||||
```css
|
|
||||||
.hero {
|
|
||||||
background: url('./img.png'); /* Processed and rebased */
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
For dynamically constructed SVG URLs:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import imgUrl from './img.svg'
|
|
||||||
element.style.background = `url("${imgUrl}")` // Note double quotes
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://vite.dev/guide/assets.html
|
|
||||||
-->
|
|
||||||
@ -1,215 +0,0 @@
|
|||||||
---
|
|
||||||
name: features-css
|
|
||||||
description: CSS handling in Vite including modules, pre-processors, PostCSS, and Lightning CSS
|
|
||||||
---
|
|
||||||
|
|
||||||
# CSS Handling
|
|
||||||
|
|
||||||
Vite provides rich CSS support with HMR, `@import` inlining, and automatic URL rebasing.
|
|
||||||
|
|
||||||
## Basic CSS Import
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import './styles.css' // Injected into page with HMR support
|
|
||||||
```
|
|
||||||
|
|
||||||
## CSS Modules
|
|
||||||
|
|
||||||
Files ending with `.module.css` are treated as CSS modules:
|
|
||||||
|
|
||||||
```css
|
|
||||||
/* example.module.css */
|
|
||||||
.red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import classes from './example.module.css'
|
|
||||||
element.className = classes.red
|
|
||||||
```
|
|
||||||
|
|
||||||
### Named Imports with camelCase
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite.config.ts
|
|
||||||
export default defineConfig({
|
|
||||||
css: {
|
|
||||||
modules: {
|
|
||||||
localsConvention: 'camelCaseOnly'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// .apply-color -> applyColor
|
|
||||||
import { applyColor } from './example.module.css'
|
|
||||||
```
|
|
||||||
|
|
||||||
## CSS Pre-processors
|
|
||||||
|
|
||||||
Install the pre-processor, no Vite plugin needed:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Sass (sass-embedded recommended for performance)
|
|
||||||
npm add -D sass-embedded
|
|
||||||
|
|
||||||
# Less
|
|
||||||
npm add -D less
|
|
||||||
|
|
||||||
# Stylus
|
|
||||||
npm add -D stylus
|
|
||||||
```
|
|
||||||
|
|
||||||
Use by file extension:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import './styles.scss'
|
|
||||||
import './styles.less'
|
|
||||||
import './styles.styl'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Pre-processor Options
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
css: {
|
|
||||||
preprocessorOptions: {
|
|
||||||
scss: {
|
|
||||||
additionalData: `$injectedColor: orange;`,
|
|
||||||
importers: [/* ... */]
|
|
||||||
},
|
|
||||||
less: {
|
|
||||||
math: 'parens-division'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
preprocessorMaxWorkers: true // Use multiple threads
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Combined with CSS Modules
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import styles from './component.module.scss'
|
|
||||||
```
|
|
||||||
|
|
||||||
## PostCSS
|
|
||||||
|
|
||||||
Automatically applied if `postcss.config.js` exists:
|
|
||||||
|
|
||||||
```js
|
|
||||||
// postcss.config.js
|
|
||||||
export default {
|
|
||||||
plugins: [
|
|
||||||
require('postcss-nesting'),
|
|
||||||
require('autoprefixer')
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Or configure inline:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
css: {
|
|
||||||
postcss: {
|
|
||||||
plugins: [
|
|
||||||
postcssNesting(),
|
|
||||||
autoprefixer()
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Lightning CSS
|
|
||||||
|
|
||||||
Experimental faster CSS processing:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm add -D lightningcss
|
|
||||||
```
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
css: {
|
|
||||||
transformer: 'lightningcss',
|
|
||||||
lightningcss: {
|
|
||||||
targets: {
|
|
||||||
chrome: 111
|
|
||||||
},
|
|
||||||
cssModules: {
|
|
||||||
// Lightning CSS modules config
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Use Lightning CSS for minification only:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
build: {
|
|
||||||
cssMinify: 'lightningcss'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Disable CSS Injection
|
|
||||||
|
|
||||||
Import CSS as string without injecting:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import styles from './styles.css?inline' // Returns CSS string, not injected
|
|
||||||
```
|
|
||||||
|
|
||||||
## Source Maps
|
|
||||||
|
|
||||||
Enable CSS source maps in development:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
css: {
|
|
||||||
devSourcemap: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## CSS Code Splitting
|
|
||||||
|
|
||||||
By default, CSS is extracted per async chunk. Disable to get single CSS file:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
build: {
|
|
||||||
cssCodeSplit: false // Single CSS file for entire app
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## CSS Target
|
|
||||||
|
|
||||||
Set different browser target for CSS:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
build: {
|
|
||||||
cssTarget: 'chrome61' // For Android WeChat WebView
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## @import and URL Handling
|
|
||||||
|
|
||||||
- `@import` statements are inlined automatically
|
|
||||||
- Vite aliases work in `@import`
|
|
||||||
- `url()` references are rebased for correctness
|
|
||||||
- Works across Sass/Less files in different directories
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://vite.dev/guide/features.html#css
|
|
||||||
-->
|
|
||||||
@ -1,148 +0,0 @@
|
|||||||
---
|
|
||||||
name: features-dep-bundling
|
|
||||||
description: Dependency pre-bundling configuration and caching
|
|
||||||
---
|
|
||||||
|
|
||||||
# Dependency Pre-Bundling
|
|
||||||
|
|
||||||
Vite pre-bundles dependencies on first run for faster dev server startup.
|
|
||||||
|
|
||||||
## Why Pre-Bundling
|
|
||||||
|
|
||||||
1. **CommonJS/UMD to ESM** - Convert non-ESM dependencies
|
|
||||||
2. **Performance** - Bundle many internal modules into single file (e.g., lodash-es has 600+ modules)
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Works thanks to smart import analysis
|
|
||||||
import React, { useState } from 'react'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Automatic Discovery
|
|
||||||
|
|
||||||
Vite crawls source code to find bare imports and pre-bundles them with Rolldown.
|
|
||||||
|
|
||||||
New dependencies discovered after server start trigger re-bundling.
|
|
||||||
|
|
||||||
## Including Dependencies
|
|
||||||
|
|
||||||
Force pre-bundling for dependencies not auto-discovered:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
optimizeDeps: {
|
|
||||||
include: [
|
|
||||||
'some-package',
|
|
||||||
'another-package/nested' // Deep imports
|
|
||||||
]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
**When to include:**
|
|
||||||
- Dynamically imported (via plugin transform)
|
|
||||||
- Large dependencies with many internal modules
|
|
||||||
- CommonJS dependencies
|
|
||||||
|
|
||||||
## Excluding Dependencies
|
|
||||||
|
|
||||||
Skip pre-bundling for small ESM-only dependencies:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
optimizeDeps: {
|
|
||||||
exclude: ['small-esm-dep']
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Monorepo Linked Dependencies
|
|
||||||
|
|
||||||
Linked packages are treated as source code by default. If not ESM:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
optimizeDeps: {
|
|
||||||
include: ['linked-dep']
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Restart with `--force` after making changes to linked deps.
|
|
||||||
|
|
||||||
## Custom Rolldown Options
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
optimizeDeps: {
|
|
||||||
rolldownOptions: {
|
|
||||||
plugins: [/* Rolldown plugins */],
|
|
||||||
// Other Rolldown options
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Caching
|
|
||||||
|
|
||||||
### File System Cache
|
|
||||||
|
|
||||||
Located in `node_modules/.vite`. Re-runs when:
|
|
||||||
|
|
||||||
- Package lockfile changes (`package-lock.json`, `pnpm-lock.yaml`, etc.)
|
|
||||||
- Patches folder modified
|
|
||||||
- `vite.config.js` changes
|
|
||||||
- `NODE_ENV` changes
|
|
||||||
|
|
||||||
Force re-bundle:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
vite --force
|
|
||||||
# Or delete node_modules/.vite
|
|
||||||
```
|
|
||||||
|
|
||||||
### Browser Cache
|
|
||||||
|
|
||||||
Pre-bundled deps are cached with `max-age=31536000,immutable`.
|
|
||||||
|
|
||||||
To debug dependencies with local edits:
|
|
||||||
|
|
||||||
1. Disable cache in browser DevTools Network tab
|
|
||||||
2. Restart Vite with `--force`
|
|
||||||
3. Reload page
|
|
||||||
|
|
||||||
## Entries
|
|
||||||
|
|
||||||
Specify custom entry points for discovery:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
optimizeDeps: {
|
|
||||||
entries: [
|
|
||||||
'src/main.ts',
|
|
||||||
'src/other-entry.ts'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
By default, all HTML files are used as entries.
|
|
||||||
|
|
||||||
## esbuildOptions (Deprecated)
|
|
||||||
|
|
||||||
Use `rolldownOptions` instead:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
optimizeDeps: {
|
|
||||||
// Deprecated
|
|
||||||
esbuildOptions: {},
|
|
||||||
// Use instead
|
|
||||||
rolldownOptions: {}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://vite.dev/guide/dep-pre-bundling.html
|
|
||||||
-->
|
|
||||||
@ -1,161 +0,0 @@
|
|||||||
---
|
|
||||||
name: features-env
|
|
||||||
description: Environment variables, .env files, and modes in Vite
|
|
||||||
---
|
|
||||||
|
|
||||||
# Environment Variables and Modes
|
|
||||||
|
|
||||||
## Built-in Constants
|
|
||||||
|
|
||||||
Available via `import.meta.env`:
|
|
||||||
|
|
||||||
| Constant | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `import.meta.env.MODE` | App mode (`'development'` or `'production'`) |
|
|
||||||
| `import.meta.env.BASE_URL` | Base URL from `base` config |
|
|
||||||
| `import.meta.env.PROD` | `true` in production |
|
|
||||||
| `import.meta.env.DEV` | `true` in development |
|
|
||||||
| `import.meta.env.SSR` | `true` in server-side rendering |
|
|
||||||
|
|
||||||
```ts
|
|
||||||
if (import.meta.env.DEV) {
|
|
||||||
console.log('Development mode')
|
|
||||||
// Tree-shaken in production
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Custom Environment Variables
|
|
||||||
|
|
||||||
Only variables prefixed with `VITE_` are exposed to client code:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# .env
|
|
||||||
VITE_API_URL=https://api.example.com
|
|
||||||
DB_PASSWORD=secret # NOT exposed to client
|
|
||||||
```
|
|
||||||
|
|
||||||
```ts
|
|
||||||
console.log(import.meta.env.VITE_API_URL) // "https://api.example.com"
|
|
||||||
console.log(import.meta.env.DB_PASSWORD) // undefined
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom Prefix
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default defineConfig({
|
|
||||||
envPrefix: ['VITE_', 'APP_'] // Expose VITE_* and APP_*
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## .env Files
|
|
||||||
|
|
||||||
Load order (later has higher priority):
|
|
||||||
|
|
||||||
```
|
|
||||||
.env # Always loaded
|
|
||||||
.env.local # Always loaded, gitignored
|
|
||||||
.env.[mode] # Only in specified mode
|
|
||||||
.env.[mode].local # Only in specified mode, gitignored
|
|
||||||
```
|
|
||||||
|
|
||||||
### Variable Expansion
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# .env
|
|
||||||
KEY=123
|
|
||||||
EXPANDED=test$KEY # test123
|
|
||||||
ESCAPED=test\$foo # test$foo (escaped)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Modes
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Development mode (default for dev)
|
|
||||||
vite
|
|
||||||
|
|
||||||
# Production mode (default for build)
|
|
||||||
vite build
|
|
||||||
|
|
||||||
# Custom mode
|
|
||||||
vite build --mode staging
|
|
||||||
```
|
|
||||||
|
|
||||||
Create mode-specific env file:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# .env.staging
|
|
||||||
VITE_APP_TITLE=My App (staging)
|
|
||||||
NODE_ENV=production # Still production build
|
|
||||||
```
|
|
||||||
|
|
||||||
### NODE_ENV vs Mode
|
|
||||||
|
|
||||||
| Command | NODE_ENV | Mode |
|
|
||||||
|---------|----------|------|
|
|
||||||
| `vite build` | `production` | `production` |
|
|
||||||
| `vite build --mode development` | `production` | `development` |
|
|
||||||
| `NODE_ENV=development vite build` | `development` | `production` |
|
|
||||||
|
|
||||||
## TypeScript IntelliSense
|
|
||||||
|
|
||||||
Create type declarations for custom env variables:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite-env.d.ts
|
|
||||||
interface ImportMetaEnv {
|
|
||||||
readonly VITE_APP_TITLE: string
|
|
||||||
readonly VITE_API_URL: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ImportMeta {
|
|
||||||
readonly env: ImportMetaEnv
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
For strict typing (disallow unknown keys):
|
|
||||||
|
|
||||||
```ts
|
|
||||||
interface ViteTypeOptions {
|
|
||||||
strictImportMetaEnv: unknown
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## HTML Replacement
|
|
||||||
|
|
||||||
Use `%VARIABLE%` syntax in HTML:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<title>%VITE_APP_TITLE%</title>
|
|
||||||
<p>Mode: %MODE%</p>
|
|
||||||
```
|
|
||||||
|
|
||||||
Non-existent variables are left as-is (not replaced with `undefined`).
|
|
||||||
|
|
||||||
## Loading Env in Config
|
|
||||||
|
|
||||||
Env vars are NOT available in `vite.config.ts` automatically:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineConfig, loadEnv } from 'vite'
|
|
||||||
|
|
||||||
export default defineConfig(({ mode }) => {
|
|
||||||
const env = loadEnv(mode, process.cwd(), '') // '' loads all vars
|
|
||||||
|
|
||||||
return {
|
|
||||||
define: {
|
|
||||||
__APP_ENV__: JSON.stringify(env.APP_ENV)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Security Notes
|
|
||||||
|
|
||||||
- Add `*.local` to `.gitignore`
|
|
||||||
- `VITE_*` variables end up in client bundle - no secrets
|
|
||||||
- Never set `envPrefix` to `''` (exposes everything)
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://vite.dev/guide/env-and-mode.html
|
|
||||||
-->
|
|
||||||
@ -1,161 +0,0 @@
|
|||||||
---
|
|
||||||
name: features-glob-import
|
|
||||||
description: Vite's import.meta.glob for batch importing modules and dynamic imports
|
|
||||||
---
|
|
||||||
|
|
||||||
# Glob Import
|
|
||||||
|
|
||||||
## Basic Usage
|
|
||||||
|
|
||||||
Import multiple modules using glob patterns:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const modules = import.meta.glob('./dir/*.js')
|
|
||||||
// Transformed to:
|
|
||||||
// {
|
|
||||||
// './dir/foo.js': () => import('./dir/foo.js'),
|
|
||||||
// './dir/bar.js': () => import('./dir/bar.js'),
|
|
||||||
// }
|
|
||||||
```
|
|
||||||
|
|
||||||
Iterate and load:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
for (const path in modules) {
|
|
||||||
modules[path]().then((mod) => {
|
|
||||||
console.log(path, mod)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Eager Loading
|
|
||||||
|
|
||||||
Load all modules immediately (no dynamic import):
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const modules = import.meta.glob('./dir/*.js', { eager: true })
|
|
||||||
// Transformed to:
|
|
||||||
// import * as __glob_0 from './dir/foo.js'
|
|
||||||
// import * as __glob_1 from './dir/bar.js'
|
|
||||||
// const modules = {
|
|
||||||
// './dir/foo.js': __glob_0,
|
|
||||||
// './dir/bar.js': __glob_1,
|
|
||||||
// }
|
|
||||||
```
|
|
||||||
|
|
||||||
## Multiple Patterns
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const modules = import.meta.glob([
|
|
||||||
'./dir/*.js',
|
|
||||||
'./another/*.js'
|
|
||||||
])
|
|
||||||
```
|
|
||||||
|
|
||||||
## Negative Patterns
|
|
||||||
|
|
||||||
Exclude files with `!` prefix:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const modules = import.meta.glob([
|
|
||||||
'./dir/*.js',
|
|
||||||
'!**/bar.js' // Exclude bar.js
|
|
||||||
])
|
|
||||||
```
|
|
||||||
|
|
||||||
## Named Imports
|
|
||||||
|
|
||||||
Import specific exports for tree-shaking:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const modules = import.meta.glob('./dir/*.js', {
|
|
||||||
import: 'setup'
|
|
||||||
})
|
|
||||||
// './dir/foo.js': () => import('./dir/foo.js').then(m => m.setup)
|
|
||||||
```
|
|
||||||
|
|
||||||
Import default export:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const modules = import.meta.glob('./dir/*.js', {
|
|
||||||
import: 'default',
|
|
||||||
eager: true
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Custom Queries
|
|
||||||
|
|
||||||
Import as raw strings or URLs:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const moduleStrings = import.meta.glob('./dir/*.svg', {
|
|
||||||
query: '?raw',
|
|
||||||
import: 'default'
|
|
||||||
})
|
|
||||||
|
|
||||||
const moduleUrls = import.meta.glob('./dir/*.svg', {
|
|
||||||
query: '?url',
|
|
||||||
import: 'default'
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Custom queries for plugins:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const modules = import.meta.glob('./dir/*.js', {
|
|
||||||
query: { foo: 'bar', bar: true }
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Base Path
|
|
||||||
|
|
||||||
Change the base path for imports:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const modules = import.meta.glob('./**/*.js', {
|
|
||||||
base: './base'
|
|
||||||
})
|
|
||||||
// Keys: './dir/foo.js'
|
|
||||||
// Imports: './base/dir/foo.js'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Important Caveats
|
|
||||||
|
|
||||||
1. **Vite-only feature** - Not a web standard
|
|
||||||
2. **Patterns must be literals** - Cannot use variables
|
|
||||||
3. **Relative or absolute** - Must start with `./`, `/`, or use an alias
|
|
||||||
4. **Glob matching** - Uses [tinyglobby](https://github.com/SuperchupuDev/tinyglobby)
|
|
||||||
|
|
||||||
## Dynamic Import with Variables
|
|
||||||
|
|
||||||
Limited dynamic import support:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const module = await import(`./dir/${file}.js`)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Rules:**
|
|
||||||
- Must start with `./` or `../`
|
|
||||||
- Must end with file extension
|
|
||||||
- Variable represents only one level (no `foo/bar`)
|
|
||||||
- Own directory needs filename pattern: `./prefix-${foo}.js` not `./${foo}.js`
|
|
||||||
|
|
||||||
## Practical Example: Loading Route Components
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Lazy load all page components
|
|
||||||
const pages = import.meta.glob('./pages/*.vue')
|
|
||||||
|
|
||||||
const routes = Object.keys(pages).map((path) => {
|
|
||||||
const name = path.match(/\.\/pages\/(.*)\.vue$/)[1]
|
|
||||||
return {
|
|
||||||
path: `/${name.toLowerCase()}`,
|
|
||||||
component: pages[path] // Lazy loaded
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://vite.dev/guide/features.html#glob-import
|
|
||||||
-->
|
|
||||||
@ -1,200 +0,0 @@
|
|||||||
---
|
|
||||||
name: features-hmr
|
|
||||||
description: Vite's Hot Module Replacement (HMR) client API
|
|
||||||
---
|
|
||||||
|
|
||||||
# HMR API
|
|
||||||
|
|
||||||
The HMR API is exposed via `import.meta.hot`. Primarily for framework and tooling authors.
|
|
||||||
|
|
||||||
## Conditional Guard
|
|
||||||
|
|
||||||
Always guard HMR code for tree-shaking in production:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
if (import.meta.hot) {
|
|
||||||
// HMR code
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## TypeScript Support
|
|
||||||
|
|
||||||
Add to `tsconfig.json`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"types": ["vite/client"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Self-Accepting Module
|
|
||||||
|
|
||||||
Module handles its own updates:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export const count = 1
|
|
||||||
|
|
||||||
if (import.meta.hot) {
|
|
||||||
import.meta.hot.accept((newModule) => {
|
|
||||||
if (newModule) {
|
|
||||||
console.log('updated: count is now', newModule.count)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Accept Dependency Updates
|
|
||||||
|
|
||||||
React to changes in dependencies without self-reload:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { foo } from './foo.js'
|
|
||||||
|
|
||||||
foo()
|
|
||||||
|
|
||||||
if (import.meta.hot) {
|
|
||||||
// Single dependency
|
|
||||||
import.meta.hot.accept('./foo.js', (newFoo) => {
|
|
||||||
newFoo?.foo()
|
|
||||||
})
|
|
||||||
|
|
||||||
// Multiple dependencies
|
|
||||||
import.meta.hot.accept(
|
|
||||||
['./foo.js', './bar.js'],
|
|
||||||
([newFooModule, newBarModule]) => {
|
|
||||||
// Handle updates
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Cleanup on Update
|
|
||||||
|
|
||||||
Clean up side effects before module is replaced:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
function setupSideEffect() {
|
|
||||||
const interval = setInterval(() => {}, 1000)
|
|
||||||
return interval
|
|
||||||
}
|
|
||||||
|
|
||||||
const interval = setupSideEffect()
|
|
||||||
|
|
||||||
if (import.meta.hot) {
|
|
||||||
import.meta.hot.dispose((data) => {
|
|
||||||
clearInterval(interval)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Prune Callback
|
|
||||||
|
|
||||||
Called when module is no longer imported:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
if (import.meta.hot) {
|
|
||||||
import.meta.hot.prune((data) => {
|
|
||||||
// Cleanup when module is removed from page
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Persistent Data
|
|
||||||
|
|
||||||
Pass data between module instances:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
if (import.meta.hot) {
|
|
||||||
// Mutate properties, don't reassign data itself
|
|
||||||
import.meta.hot.data.count = (import.meta.hot.data.count || 0) + 1
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Invalidate
|
|
||||||
|
|
||||||
Force propagation to importers:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
if (import.meta.hot) {
|
|
||||||
import.meta.hot.accept((module) => {
|
|
||||||
if (cannotHandleUpdate(module)) {
|
|
||||||
import.meta.hot.invalidate() // Propagate to importers
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## HMR Events
|
|
||||||
|
|
||||||
Listen to built-in events:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
if (import.meta.hot) {
|
|
||||||
import.meta.hot.on('vite:beforeUpdate', (payload) => {
|
|
||||||
console.log('Update incoming')
|
|
||||||
})
|
|
||||||
|
|
||||||
import.meta.hot.on('vite:afterUpdate', (payload) => {
|
|
||||||
console.log('Update applied')
|
|
||||||
})
|
|
||||||
|
|
||||||
import.meta.hot.on('vite:beforeFullReload', () => {
|
|
||||||
console.log('Full reload')
|
|
||||||
})
|
|
||||||
|
|
||||||
import.meta.hot.on('vite:error', (error) => {
|
|
||||||
console.error('HMR error', error)
|
|
||||||
})
|
|
||||||
|
|
||||||
import.meta.hot.on('vite:ws:connect', () => {
|
|
||||||
console.log('WebSocket connected')
|
|
||||||
})
|
|
||||||
|
|
||||||
import.meta.hot.on('vite:ws:disconnect', () => {
|
|
||||||
console.log('WebSocket disconnected')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Custom Events
|
|
||||||
|
|
||||||
Send events to server:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Client
|
|
||||||
if (import.meta.hot) {
|
|
||||||
import.meta.hot.send('my:event', { msg: 'Hello from client' })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Receive from server:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Client
|
|
||||||
if (import.meta.hot) {
|
|
||||||
import.meta.hot.on('my:response', (data) => {
|
|
||||||
console.log(data.msg)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## TypeScript for Custom Events
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// events.d.ts
|
|
||||||
import 'vite/types/customEvent.d.ts'
|
|
||||||
|
|
||||||
declare module 'vite/types/customEvent.d.ts' {
|
|
||||||
interface CustomEventMap {
|
|
||||||
'my:event': { msg: string }
|
|
||||||
'my:response': { msg: string }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://vite.dev/guide/api-hmr.html
|
|
||||||
-->
|
|
||||||
@ -1,115 +0,0 @@
|
|||||||
---
|
|
||||||
name: features-workers
|
|
||||||
description: Web Worker support in Vite
|
|
||||||
---
|
|
||||||
|
|
||||||
# Web Workers
|
|
||||||
|
|
||||||
## Constructor Syntax (Recommended)
|
|
||||||
|
|
||||||
Standard Web Worker creation:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const worker = new Worker(new URL('./worker.js', import.meta.url))
|
|
||||||
```
|
|
||||||
|
|
||||||
Module worker:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const worker = new Worker(new URL('./worker.js', import.meta.url), {
|
|
||||||
type: 'module'
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Shared Worker:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const sharedWorker = new SharedWorker(
|
|
||||||
new URL('./shared-worker.js', import.meta.url)
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** The `new URL()` must be used directly inside `new Worker()` for detection.
|
|
||||||
|
|
||||||
## Query Suffix Syntax
|
|
||||||
|
|
||||||
Import with `?worker` suffix:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import MyWorker from './worker?worker'
|
|
||||||
|
|
||||||
const worker = new MyWorker()
|
|
||||||
```
|
|
||||||
|
|
||||||
Shared worker:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import MySharedWorker from './worker?sharedworker'
|
|
||||||
|
|
||||||
const worker = new MySharedWorker()
|
|
||||||
```
|
|
||||||
|
|
||||||
### Inline Worker
|
|
||||||
|
|
||||||
Inline as base64 string (no separate chunk):
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import MyWorker from './worker?worker&inline'
|
|
||||||
|
|
||||||
const worker = new MyWorker()
|
|
||||||
```
|
|
||||||
|
|
||||||
### Worker URL Only
|
|
||||||
|
|
||||||
Get URL instead of constructor:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import workerUrl from './worker?worker&url'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Worker Script
|
|
||||||
|
|
||||||
Workers can use ESM `import` statements:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// worker.js
|
|
||||||
import { heavyComputation } from './utils'
|
|
||||||
|
|
||||||
self.onmessage = (e) => {
|
|
||||||
const result = heavyComputation(e.data)
|
|
||||||
self.postMessage(result)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Worker Options
|
|
||||||
|
|
||||||
Configure worker bundling:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// vite.config.ts
|
|
||||||
export default defineConfig({
|
|
||||||
worker: {
|
|
||||||
format: 'es', // or 'iife'
|
|
||||||
plugins: () => [/* worker-specific plugins */],
|
|
||||||
rollupOptions: {
|
|
||||||
// Rollup options for worker bundle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## WebAssembly in Workers
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// worker.js
|
|
||||||
import init from './module.wasm?init'
|
|
||||||
|
|
||||||
init().then((instance) => {
|
|
||||||
instance.exports.compute()
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Source references:
|
|
||||||
- https://vite.dev/guide/features.html#web-workers
|
|
||||||
-->
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2025 hyf0, SerKo <https://github.com/serkodev>
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@ -1,313 +0,0 @@
|
|||||||
---
|
|
||||||
name: vue-best-practices
|
|
||||||
description: MUST be used for Vue.js tasks. Strongly recommends Composition API with `<script setup>` and TypeScript as the standard approach. Covers Vue 3, SSR, Volar, vue-tsc. Load for any Vue, .vue files, Vue Router, Pinia, or Vite with Vue work. ALWAYS use Composition API unless the project explicitly requires Options API.
|
|
||||||
license: MIT
|
|
||||||
metadata:
|
|
||||||
author: github.com/vuejs-ai
|
|
||||||
version: "17.0.0"
|
|
||||||
---
|
|
||||||
|
|
||||||
Vue 3 best practices, common gotchas, and performance optimization.
|
|
||||||
|
|
||||||
### Reactivity
|
|
||||||
- Accessing ref() values without .value in scripts → See [ref-value-access](reference/ref-value-access.md)
|
|
||||||
- Destructuring reactive() objects, losing reactivity → See [reactive-destructuring](reference/reactive-destructuring.md)
|
|
||||||
- Choosing between ref() and reactive() for state → See [prefer-ref-over-reactive](reference/prefer-ref-over-reactive.md)
|
|
||||||
- Accessing refs inside arrays and collections → See [refs-in-collections-need-value](reference/refs-in-collections-need-value.md)
|
|
||||||
- Large objects or external library data overhead → See [shallow-ref-for-performance](reference/shallow-ref-for-performance.md)
|
|
||||||
- Using nested refs in template expressions → See [template-ref-unwrapping-top-level](reference/template-ref-unwrapping-top-level.md)
|
|
||||||
- Comparing reactive objects with === operator → See [reactivity-proxy-identity-hazard](reference/reactivity-proxy-identity-hazard.md)
|
|
||||||
- Library instances breaking in reactive state → See [reactivity-markraw-for-non-reactive](reference/reactivity-markraw-for-non-reactive.md)
|
|
||||||
- Expecting watchers to fire for each state change → See [reactivity-same-tick-batching](reference/reactivity-same-tick-batching.md)
|
|
||||||
- Integrating external state management libraries → See [reactivity-external-state-integration](reference/reactivity-external-state-integration.md)
|
|
||||||
- Tracing unexpected re-renders and state updates → See [reactivity-debugging-hooks](reference/reactivity-debugging-hooks.md)
|
|
||||||
- Deriving state with watchEffect instead of computed → See [reactivity-computed-over-watcheffect-mutations](reference/reactivity-computed-over-watcheffect-mutations.md)
|
|
||||||
|
|
||||||
### Computed
|
|
||||||
- Computed getter is making API calls or mutations → See [computed-no-side-effects](reference/computed-no-side-effects.md)
|
|
||||||
- Mutating computed values causes lost changes unexpectedly → See [computed-return-value-readonly](reference/computed-return-value-readonly.md)
|
|
||||||
- Computed property doesn't update when expected → See [computed-conditional-dependencies](reference/computed-conditional-dependencies.md)
|
|
||||||
- Sorting or reversing arrays destroys original data → See [computed-array-mutation](reference/computed-array-mutation.md)
|
|
||||||
- Expensive operations running too frequently every render → See [computed-vs-methods-caching](reference/computed-vs-methods-caching.md)
|
|
||||||
- Trying to pass arguments to computed properties → See [computed-no-parameters](reference/computed-no-parameters.md)
|
|
||||||
- Complex conditions bloating inline class bindings → See [computed-properties-for-class-logic](reference/computed-properties-for-class-logic.md)
|
|
||||||
|
|
||||||
### Watchers
|
|
||||||
- Need to watch a reactive object property → See [watch-reactive-property-getter](reference/watch-reactive-property-getter.md)
|
|
||||||
- Large nested data structures causing performance issues → See [watch-deep-performance](reference/watch-deep-performance.md)
|
|
||||||
- Async operations overwriting with stale data → See [watch-async-cleanup](reference/watch-async-cleanup.md)
|
|
||||||
- Creating watchers inside async callbacks → See [watch-async-creation-memory-leak](reference/watch-async-creation-memory-leak.md)
|
|
||||||
- Dependencies accessed after await not tracking → See [watcheffect-async-dependency-tracking](reference/watcheffect-async-dependency-tracking.md)
|
|
||||||
- Need to access updated DOM in watchers → See [watch-flush-timing](reference/watch-flush-timing.md)
|
|
||||||
- Uncertain whether to use watch or watchEffect → See [watch-vs-watcheffect](reference/watch-vs-watcheffect.md)
|
|
||||||
- Duplicating initial calls and watch callbacks → See [watch-immediate-option](reference/watch-immediate-option.md)
|
|
||||||
- Can't compare old and new values correctly → See [watch-deep-same-object-reference](reference/watch-deep-same-object-reference.md)
|
|
||||||
- Template refs appearing null or stale → See [watcheffect-flush-post-for-refs](reference/watcheffect-flush-post-for-refs.md)
|
|
||||||
|
|
||||||
### Components
|
|
||||||
- Prop values being changed from a child component → See [props-are-read-only](reference/props-are-read-only.md)
|
|
||||||
- Parent can't access child ref data in script setup → See [component-ref-requires-defineexpose](reference/component-ref-requires-defineexpose.md)
|
|
||||||
- Child component throws "component not found" error → See [local-components-not-in-descendants](reference/local-components-not-in-descendants.md)
|
|
||||||
- Click listener doesn't fire on custom component → See [click-events-on-components](reference/click-events-on-components.md)
|
|
||||||
- HTML template parsing breaks Vue component syntax → See [in-dom-template-parsing-caveats](reference/in-dom-template-parsing-caveats.md)
|
|
||||||
- Grandparent can't listen to grandchild emitted events → See [component-events-dont-bubble](reference/component-events-dont-bubble.md)
|
|
||||||
- Wrong component renders due to naming collisions → See [component-naming-conflicts](reference/component-naming-conflicts.md)
|
|
||||||
- Distinguishing Vue components from native elements → See [component-naming-pascalcase](reference/component-naming-pascalcase.md)
|
|
||||||
- Parent styles don't apply to multi-root component → See [multi-root-component-class-attrs](reference/multi-root-component-class-attrs.md)
|
|
||||||
- Recursive component needs to reference itself → See [self-referencing-component-name](reference/self-referencing-component-name.md)
|
|
||||||
- Bundle includes components that aren't used → See [prefer-local-component-registration](reference/prefer-local-component-registration.md)
|
|
||||||
- Tight coupling through component ref access → See [prefer-props-emit-over-component-refs](reference/prefer-props-emit-over-component-refs.md)
|
|
||||||
|
|
||||||
### Props & Emits
|
|
||||||
- Boolean prop not parsing as expected → See [prop-boolean-casting-order](reference/prop-boolean-casting-order.md)
|
|
||||||
- Composable doesn't update when props change → See [prop-composable-reactivity-loss](reference/prop-composable-reactivity-loss.md)
|
|
||||||
- Variables referenced in defineProps cause errors → See [prop-defineprops-scope-limitation](reference/prop-defineprops-scope-limitation.md)
|
|
||||||
- Destructured props not updating watchers → See [prop-destructured-watch-getter](reference/prop-destructured-watch-getter.md)
|
|
||||||
- Prop validation needs component instance data → See [prop-validation-before-instance](reference/prop-validation-before-instance.md)
|
|
||||||
- Component emits undeclared event causing warnings → See [declare-emits-for-documentation](reference/declare-emits-for-documentation.md)
|
|
||||||
- defineEmits used inside function or conditional → See [defineEmits-must-be-top-level](reference/defineEmits-must-be-top-level.md)
|
|
||||||
- defineEmits has both type and runtime arguments → See [defineEmits-no-runtime-and-type-mixed](reference/defineEmits-no-runtime-and-type-mixed.md)
|
|
||||||
- Event name inconsistency in templates and scripts → See [emit-kebab-case-in-templates](reference/emit-kebab-case-in-templates.md)
|
|
||||||
- Event payloads need validation during development → See [emit-validation-for-complex-payloads](reference/emit-validation-for-complex-payloads.md)
|
|
||||||
- Native event listeners not responding to clicks → See [native-event-collision-with-emits](reference/native-event-collision-with-emits.md)
|
|
||||||
- Component event fires twice when clicking → See [undeclared-emits-double-firing](reference/undeclared-emits-double-firing.md)
|
|
||||||
|
|
||||||
### Templates
|
|
||||||
- Rendering untrusted user content as HTML → See [v-html-xss-security](reference/v-html-xss-security.md)
|
|
||||||
- Filtering or conditionally hiding list items → See [no-v-if-with-v-for](reference/no-v-if-with-v-for.md)
|
|
||||||
- List items disappearing or swapping state unexpectedly → See [v-for-key-attribute](reference/v-for-key-attribute.md)
|
|
||||||
- Getting template compilation errors with statements → See [template-expressions-restrictions](reference/template-expressions-restrictions.md)
|
|
||||||
- Dynamic directive arguments not working properly → See [dynamic-argument-constraints](reference/dynamic-argument-constraints.md)
|
|
||||||
- Functions in templates modifying data unexpectedly → See [template-functions-no-side-effects](reference/template-functions-no-side-effects.md)
|
|
||||||
- v-else elements rendering unconditionally always → See [v-else-must-follow-v-if](reference/v-else-must-follow-v-if.md)
|
|
||||||
- Child components in loops showing undefined data → See [v-for-component-props](reference/v-for-component-props.md)
|
|
||||||
- Array order changing after sorting or reversing → See [v-for-computed-reverse-sort](reference/v-for-computed-reverse-sort.md)
|
|
||||||
- Getting off-by-one errors with range iteration → See [v-for-range-starts-at-one](reference/v-for-range-starts-at-one.md)
|
|
||||||
- Performance issues with filtered or sorted lists → See [v-for-use-computed-for-filtering](reference/v-for-use-computed-for-filtering.md)
|
|
||||||
- "Cannot read property of undefined" runtime errors → See [v-if-null-check-order](reference/v-if-null-check-order.md)
|
|
||||||
- Deciding between v-if and v-show for conditionals → See [v-if-vs-v-show-performance](reference/v-if-vs-v-show-performance.md)
|
|
||||||
- v-show or v-else not working on template elements → See [v-show-template-limitation](reference/v-show-template-limitation.md)
|
|
||||||
|
|
||||||
### Template Refs
|
|
||||||
- Ref becomes null when element is conditionally hidden → See [template-ref-null-with-v-if](reference/template-ref-null-with-v-if.md)
|
|
||||||
- Ref array indices don't match data array in loops → See [template-ref-v-for-order](reference/template-ref-v-for-order.md)
|
|
||||||
- Refactoring template ref names breaks silently in code → See [use-template-ref-vue35](reference/use-template-ref-vue35.md)
|
|
||||||
|
|
||||||
### Forms & v-model
|
|
||||||
- Initial form values not showing when using v-model → See [v-model-ignores-html-attributes](reference/v-model-ignores-html-attributes.md)
|
|
||||||
- Textarea content changes not updating the ref → See [textarea-no-interpolation](reference/textarea-no-interpolation.md)
|
|
||||||
- iOS users cannot select dropdown first option → See [select-initial-value-ios-bug](reference/select-initial-value-ios-bug.md)
|
|
||||||
- Parent and child components have different values → See [define-model-default-value-sync](reference/define-model-default-value-sync.md)
|
|
||||||
- Need to handle v-model modifiers in child → See [definemodel-hidden-modifier-props](reference/definemodel-hidden-modifier-props.md)
|
|
||||||
- Object property changes not syncing to parent → See [definemodel-object-mutation-no-emit](reference/definemodel-object-mutation-no-emit.md)
|
|
||||||
- Need to use updated value immediately after change → See [definemodel-value-next-tick](reference/definemodel-value-next-tick.md)
|
|
||||||
- Real-time search/validation broken for Chinese/Japanese input → See [v-model-ime-composition](reference/v-model-ime-composition.md)
|
|
||||||
- Number input returns empty string instead of zero → See [v-model-number-modifier-behavior](reference/v-model-number-modifier-behavior.md)
|
|
||||||
- Migrating Vue 2 components to Vue 3 → See [v-model-vue3-breaking-changes](reference/v-model-vue3-breaking-changes.md)
|
|
||||||
- Custom checkbox values not submitted in forms → See [checkbox-true-false-value-form-submission](reference/checkbox-true-false-value-form-submission.md)
|
|
||||||
|
|
||||||
### Events & Modifiers
|
|
||||||
- Chaining multiple event modifiers produces unexpected results → See [event-modifier-order-matters](reference/event-modifier-order-matters.md)
|
|
||||||
- Need to handle same event only one time → See [event-once-modifier-for-single-use](reference/event-once-modifier-for-single-use.md)
|
|
||||||
- Keyboard shortcuts fire with unintended modifier combinations → See [exact-modifier-for-precise-shortcuts](reference/exact-modifier-for-precise-shortcuts.md)
|
|
||||||
- Keyboard shortcuts don't fire with system modifier keys → See [keyup-modifier-timing](reference/keyup-modifier-timing.md)
|
|
||||||
- Using left-handed mouse or non-standard input devices → See [mouse-button-modifiers-intent](reference/mouse-button-modifiers-intent.md)
|
|
||||||
- Preventing default browser action and scroll performance together → See [no-passive-with-prevent](reference/no-passive-with-prevent.md)
|
|
||||||
|
|
||||||
### Lifecycle
|
|
||||||
- Lifecycle hooks don't execute asynchronously → See [lifecycle-hooks-synchronous-registration](reference/lifecycle-hooks-synchronous-registration.md)
|
|
||||||
- DOM access fails before component mounts → See [lifecycle-dom-access-timing](reference/lifecycle-dom-access-timing.md)
|
|
||||||
- Memory leaks from unremoved event listeners → See [cleanup-side-effects](reference/cleanup-side-effects.md)
|
|
||||||
- SSR rendering differs from client hydration → See [lifecycle-ssr-awareness](reference/lifecycle-ssr-awareness.md)
|
|
||||||
- Expensive operations slow performance drastically → See [updated-hook-performance](reference/updated-hook-performance.md)
|
|
||||||
- DOM reads return stale values after state changes → See [dom-update-timing-nexttick](reference/dom-update-timing-nexttick.md)
|
|
||||||
|
|
||||||
### Slots
|
|
||||||
- Accessing child component data in slot content → See [slot-render-scope-parent-only](reference/slot-render-scope-parent-only.md)
|
|
||||||
- Mixing named and scoped slots together → See [slot-named-scoped-explicit-default](reference/slot-named-scoped-explicit-default.md)
|
|
||||||
- Using v-slot on native HTML elements → See [slot-v-slot-on-components-or-templates-only](reference/slot-v-slot-on-components-or-templates-only.md)
|
|
||||||
- Empty wrapper elements rendering unnecessarily → See [slot-conditional-rendering-with-slots](reference/slot-conditional-rendering-with-slots.md)
|
|
||||||
- Scoped slot props lack TypeScript type safety → See [slot-define-slots-for-typescript](reference/slot-define-slots-for-typescript.md)
|
|
||||||
- Rendering empty component slots without defaults → See [slot-fallback-content-default-values](reference/slot-fallback-content-default-values.md)
|
|
||||||
- Wrapper components breaking child slot functionality → See [slot-forwarding-to-child-components](reference/slot-forwarding-to-child-components.md)
|
|
||||||
- Confused about which slot content goes where → See [slot-implicit-default-content](reference/slot-implicit-default-content.md)
|
|
||||||
- Expecting name property in scoped slot props → See [slot-name-reserved-prop](reference/slot-name-reserved-prop.md)
|
|
||||||
- Choosing between renderless components and composables → See [slot-renderless-components-vs-composables](reference/slot-renderless-components-vs-composables.md)
|
|
||||||
|
|
||||||
### Provide/Inject
|
|
||||||
- Injected values not updating when provider changes → See [provide-inject-reactivity-not-automatic](reference/provide-inject-reactivity-not-automatic.md)
|
|
||||||
- Calling provide after async operations fails silently → See [provide-inject-synchronous-setup](reference/provide-inject-synchronous-setup.md)
|
|
||||||
- String keys collide in large applications → See [provide-inject-symbol-keys](reference/provide-inject-symbol-keys.md)
|
|
||||||
- Tracing where provided values come from → See [provide-inject-debugging-challenges](reference/provide-inject-debugging-challenges.md)
|
|
||||||
- Multiple components share same default object → See [provide-inject-default-value-factory](reference/provide-inject-default-value-factory.md)
|
|
||||||
- State mutations scattered across components → See [provide-inject-mutations-in-provider](reference/provide-inject-mutations-in-provider.md)
|
|
||||||
- Passing props through many component layers → See [avoid-prop-drilling-use-provide-inject](reference/avoid-prop-drilling-use-provide-inject.md)
|
|
||||||
|
|
||||||
### Attrs
|
|
||||||
- Both internal and fallthrough event handlers execute → See [attrs-event-listener-merging](reference/attrs-event-listener-merging.md)
|
|
||||||
- Accessing hyphenated attributes in JavaScript code → See [attrs-hyphenated-property-access](reference/attrs-hyphenated-property-access.md)
|
|
||||||
- Watching fallthrough attributes for changes with watch() → See [attrs-not-reactive](reference/attrs-not-reactive.md)
|
|
||||||
- Explicit attributes overwritten by fallthrough values → See [fallthrough-attrs-overwrite-vue3](reference/fallthrough-attrs-overwrite-vue3.md)
|
|
||||||
- Attributes applying to wrong element in wrappers → See [inheritattrs-false-for-wrapper-components](reference/inheritattrs-false-for-wrapper-components.md)
|
|
||||||
|
|
||||||
### Composables
|
|
||||||
- Composable has unexpected side effects affecting external state → See [composable-avoid-hidden-side-effects](reference/composable-avoid-hidden-side-effects.md)
|
|
||||||
- Composable called outside setup context or asynchronously → See [composable-call-location-restrictions](reference/composable-call-location-restrictions.md)
|
|
||||||
- Building complex logic from smaller focused composables → See [composable-composition-pattern](reference/composable-composition-pattern.md)
|
|
||||||
- Inconsistent composable names or destructuring loses reactivity → See [composable-naming-return-pattern](reference/composable-naming-return-pattern.md)
|
|
||||||
- Composable has many optional parameters or confusing argument order → See [composable-options-object-pattern](reference/composable-options-object-pattern.md)
|
|
||||||
- Need to prevent uncontrolled mutations of composable state → See [composable-readonly-state](reference/composable-readonly-state.md)
|
|
||||||
- Composable reactive dependency not updating when input changes → See [composable-tovalue-inside-watcheffect](reference/composable-tovalue-inside-watcheffect.md)
|
|
||||||
- Unsure whether logic belongs in composable or utility function → See [composable-vs-utility-functions](reference/composable-vs-utility-functions.md)
|
|
||||||
|
|
||||||
### Composition API
|
|
||||||
- Optimizing production bundle size and performance → See [composition-api-bundle-size-minification](reference/composition-api-bundle-size-minification.md)
|
|
||||||
- Composition API code becoming scattered and hard to maintain → See [composition-api-code-organization](reference/composition-api-code-organization.md)
|
|
||||||
- Fixing naming conflicts and unclear data origins in mixins → See [composition-api-mixins-replacement](reference/composition-api-mixins-replacement.md)
|
|
||||||
- Applying functional patterns incorrectly to Vue state → See [composition-api-not-functional-programming](reference/composition-api-not-functional-programming.md)
|
|
||||||
- Gradually migrating large Options API codebase → See [composition-api-options-api-coexistence](reference/composition-api-options-api-coexistence.md)
|
|
||||||
- Lifecycle hooks failing silently after async operations → See [composition-api-script-setup-async-context](reference/composition-api-script-setup-async-context.md)
|
|
||||||
- Coming from React, over-engineering Vue patterns unnecessarily → See [composition-api-vs-react-hooks-differences](reference/composition-api-vs-react-hooks-differences.md)
|
|
||||||
- Parent component refs unable to access exposed properties → See [define-expose-before-await](reference/define-expose-before-await.md)
|
|
||||||
|
|
||||||
### Directives
|
|
||||||
- Storing state across directive hooks → See [directive-arguments-read-only](reference/directive-arguments-read-only.md)
|
|
||||||
- Applying custom directives to Vue components → See [directive-avoid-on-components](reference/directive-avoid-on-components.md)
|
|
||||||
- Creating intervals or event listeners in directives → See [directive-cleanup-in-unmounted](reference/directive-cleanup-in-unmounted.md)
|
|
||||||
- Simplifying directives with identical behavior → See [directive-function-shorthand](reference/directive-function-shorthand.md)
|
|
||||||
- Using custom directives in script setup → See [directive-naming-v-prefix](reference/directive-naming-v-prefix.md)
|
|
||||||
- Choosing between custom and built-in directives → See [directive-prefer-declarative-templating](reference/directive-prefer-declarative-templating.md)
|
|
||||||
- Deciding between directives and components → See [directive-vs-component-decision](reference/directive-vs-component-decision.md)
|
|
||||||
- Migrating Vue 2 directives to Vue 3 → See [directive-vue2-migration-hooks](reference/directive-vue2-migration-hooks.md)
|
|
||||||
|
|
||||||
### Transitions
|
|
||||||
- Wrapping multiple elements or components in transitions → See [transition-single-element-slot](reference/transition-single-element-slot.md)
|
|
||||||
- Transitioning between same element types without animation → See [transition-key-for-same-element](reference/transition-key-for-same-element.md)
|
|
||||||
- Using JavaScript animations without calling done callback → See [transition-js-hooks-done-callback](reference/transition-js-hooks-done-callback.md)
|
|
||||||
- Animating lists with TransitionGroup without unique keys → See [transition-group-key-requirement](reference/transition-group-key-requirement.md)
|
|
||||||
- Performance problems with janky list animations → See [transition-animate-transform-opacity](reference/transition-animate-transform-opacity.md)
|
|
||||||
- Move animations failing on inline list elements → See [transition-group-flip-inline-elements](reference/transition-group-flip-inline-elements.md)
|
|
||||||
- List items jumping instead of smoothly animating → See [transition-group-move-animation-position-absolute](reference/transition-group-move-animation-position-absolute.md)
|
|
||||||
- Vue 2 to Vue 3 transition layout breaks unexpectedly → See [transition-group-no-default-wrapper-vue3](reference/transition-group-no-default-wrapper-vue3.md)
|
|
||||||
- Trying to sequence list animations with mode prop → See [transition-group-no-mode-prop](reference/transition-group-no-mode-prop.md)
|
|
||||||
- Creating cascading delays for list item animations → See [transition-group-staggered-animations](reference/transition-group-staggered-animations.md)
|
|
||||||
- Overlapping elements or layout jumping during transitions → See [transition-mode-out-in](reference/transition-mode-out-in.md)
|
|
||||||
- Nested transition animations cutting off prematurely → See [transition-nested-duration](reference/transition-nested-duration.md)
|
|
||||||
- Reusable transition components with scoped styles breaking → See [transition-reusable-scoped-style](reference/transition-reusable-scoped-style.md)
|
|
||||||
- RouterView transitions unexpectedly animating on page load → See [transition-router-view-appear](reference/transition-router-view-appear.md)
|
|
||||||
- Mixing CSS transitions and animations causing timing issues → See [transition-type-when-mixed](reference/transition-type-when-mixed.md)
|
|
||||||
- Component cleanup not firing during fast transition replacements → See [transition-unmount-hook-timing](reference/transition-unmount-hook-timing.md)
|
|
||||||
|
|
||||||
### Animation
|
|
||||||
- Need to animate elements staying in DOM → See [animation-class-based-technique](reference/animation-class-based-technique.md)
|
|
||||||
- Animations not triggering on content changes → See [animation-key-for-rerender](reference/animation-key-for-rerender.md)
|
|
||||||
- Building interactive animations with user input → See [animation-state-driven-technique](reference/animation-state-driven-technique.md)
|
|
||||||
- Animating list changes causing noticeable lag → See [animation-transitiongroup-performance](reference/animation-transitiongroup-performance.md)
|
|
||||||
|
|
||||||
### KeepAlive
|
|
||||||
- Using KeepAlive without proper cache limits or cleanup → See [keepalive-memory-management](reference/keepalive-memory-management.md)
|
|
||||||
- KeepAlive include/exclude props not matching cached components → See [keepalive-component-name-requirement](reference/keepalive-component-name-requirement.md)
|
|
||||||
- Need to programmatically remove component from KeepAlive cache → See [keepalive-no-cache-removal-vue3](reference/keepalive-no-cache-removal-vue3.md)
|
|
||||||
- Users see stale cached content when expecting fresh page data → See [keepalive-router-fresh-vs-cached](reference/keepalive-router-fresh-vs-cached.md)
|
|
||||||
- Child components mount twice with nested Vue Router routes → See [keepalive-router-nested-double-mount](reference/keepalive-router-nested-double-mount.md)
|
|
||||||
- Memory grows when combining KeepAlive with Transition animations → See [keepalive-transition-memory-leak](reference/keepalive-transition-memory-leak.md)
|
|
||||||
- Dynamic component state resets when switching between them → See [dynamic-components-with-keepalive](reference/dynamic-components-with-keepalive.md)
|
|
||||||
|
|
||||||
### Async Components
|
|
||||||
- Setting up Vue Router route component loading → See [async-component-vue-router](reference/async-component-vue-router.md)
|
|
||||||
- Async component options ignored by parent Suspense → See [async-component-suspense-control](reference/async-component-suspense-control.md)
|
|
||||||
- Network failures or timeouts loading components → See [async-component-error-handling](reference/async-component-error-handling.md)
|
|
||||||
- Improving Time to Interactive with SSR apps → See [async-component-hydration-strategies](reference/async-component-hydration-strategies.md)
|
|
||||||
- Template refs undefined after component reactivation → See [async-component-keepalive-ref-issue](reference/async-component-keepalive-ref-issue.md)
|
|
||||||
- Loading spinner flashing on fast networks → See [async-component-loading-delay](reference/async-component-loading-delay.md)
|
|
||||||
|
|
||||||
### Render Functions
|
|
||||||
- Render function from setup doesn't update reactively → See [rendering-render-function-return-from-setup](reference/rendering-render-function-return-from-setup.md)
|
|
||||||
- Same vnode appearing multiple times in tree → See [render-function-vnodes-must-be-unique](reference/render-function-vnodes-must-be-unique.md)
|
|
||||||
- Rendering lists in render functions without keys → See [render-function-v-for-keys-required](reference/render-function-v-for-keys-required.md)
|
|
||||||
- Implementing .stop, .prevent in render functions → See [render-function-event-modifiers](reference/render-function-event-modifiers.md)
|
|
||||||
- Two-way binding on components in render functions → See [render-function-v-model-implementation](reference/render-function-v-model-implementation.md)
|
|
||||||
- Using string names for components in render functions → See [rendering-resolve-component-for-string-names](reference/rendering-resolve-component-for-string-names.md)
|
|
||||||
- Accessing vnode internals like el or shapeFlag → See [render-function-avoid-internal-vnode-properties](reference/render-function-avoid-internal-vnode-properties.md)
|
|
||||||
- Creating simple stateless presentational components → See [render-function-functional-components](reference/render-function-functional-components.md)
|
|
||||||
- Applying custom directives in render functions → See [render-function-custom-directives](reference/render-function-custom-directives.md)
|
|
||||||
- Excessive rerenders from watchers or deep watchers → See [rendering-excessive-rerenders-watch-vs-computed](reference/rendering-excessive-rerenders-watch-vs-computed.md)
|
|
||||||
- Choosing render functions over templates → See [rendering-prefer-templates-over-render-functions](reference/rendering-prefer-templates-over-render-functions.md)
|
|
||||||
- Migrating Vue 2 render functions to Vue 3 → See [rendering-render-function-h-import-vue3](reference/rendering-render-function-h-import-vue3.md)
|
|
||||||
- Passing slot content to h() incorrectly → See [rendering-render-function-slots-as-functions](reference/rendering-render-function-slots-as-functions.md)
|
|
||||||
- Understanding Vue's vdom optimization blocks → See [rendering-understand-vdom-block-structure](reference/rendering-understand-vdom-block-structure.md)
|
|
||||||
|
|
||||||
### Teleport
|
|
||||||
- Teleport target element not found in DOM → See [teleport-target-must-exist](reference/teleport-target-must-exist.md)
|
|
||||||
- Teleported content breaks SSR hydration → See [teleport-ssr-hydration](reference/teleport-ssr-hydration.md)
|
|
||||||
- Modal breaks with parent CSS transforms → See [teleport-css-positioning-issues](reference/teleport-css-positioning-issues.md)
|
|
||||||
- Content needs different layout on mobile → See [teleport-disabled-for-responsive](reference/teleport-disabled-for-responsive.md)
|
|
||||||
- Unsure if props/events work through teleport → See [teleport-logical-hierarchy-preserved](reference/teleport-logical-hierarchy-preserved.md)
|
|
||||||
- Multiple modals targeting same container → See [teleport-multiple-to-same-target](reference/teleport-multiple-to-same-target.md)
|
|
||||||
- Scoped styles not applying to teleported content → See [teleport-scoped-styles-limitation](reference/teleport-scoped-styles-limitation.md)
|
|
||||||
|
|
||||||
### Suspense
|
|
||||||
- Need to handle async errors from Suspense components → See [suspense-no-builtin-error-handling](reference/suspense-no-builtin-error-handling.md)
|
|
||||||
- Want to track Suspense loading states programmatically → See [suspense-events-for-state-tracking](reference/suspense-events-for-state-tracking.md)
|
|
||||||
- Planning Suspense usage in production applications → See [suspense-experimental-api-stability](reference/suspense-experimental-api-stability.md)
|
|
||||||
- Fallback not showing when content changes → See [suspense-fallback-not-immediate-on-revert](reference/suspense-fallback-not-immediate-on-revert.md)
|
|
||||||
- Nesting Suspense components together → See [suspense-nested-suspensible-prop](reference/suspense-nested-suspensible-prop.md)
|
|
||||||
- Combining Suspense with Router, Transition, KeepAlive → See [suspense-nesting-order-with-router](reference/suspense-nesting-order-with-router.md)
|
|
||||||
- Nested async component not showing loading indicator → See [suspense-revert-only-on-root-change](reference/suspense-revert-only-on-root-change.md)
|
|
||||||
- Multiple async components in single Suspense → See [suspense-single-child-requirement](reference/suspense-single-child-requirement.md)
|
|
||||||
- Using Suspense with server-side rendering → See [suspense-ssr-hydration-issues](reference/suspense-ssr-hydration-issues.md)
|
|
||||||
|
|
||||||
### TypeScript
|
|
||||||
- Declaring props with TypeScript in composition API components → See [ts-defineprops-type-based-declaration](reference/ts-defineprops-type-based-declaration.md)
|
|
||||||
- Providing default values to mutable prop types → See [ts-withdefaults-mutable-factory-function](reference/ts-withdefaults-mutable-factory-function.md)
|
|
||||||
- Typing reactive state with ref unwrapping concerns → See [ts-reactive-no-generic-argument](reference/ts-reactive-no-generic-argument.md)
|
|
||||||
- Accessing DOM elements after component mounts → See [ts-template-ref-null-handling](reference/ts-template-ref-null-handling.md)
|
|
||||||
- Typing refs to child Vue components → See [ts-component-ref-typeof-instancetype](reference/ts-component-ref-typeof-instancetype.md)
|
|
||||||
- Using custom directives with TypeScript support → See [ts-custom-directive-type-augmentation](reference/ts-custom-directive-type-augmentation.md)
|
|
||||||
- Declaring component events with full type safety → See [ts-defineemits-type-based-syntax](reference/ts-defineemits-type-based-syntax.md)
|
|
||||||
- Handling optional boolean props in TypeScript → See [ts-defineprops-boolean-default-false](reference/ts-defineprops-boolean-default-false.md)
|
|
||||||
- Using imported types safely in defineProps → See [ts-defineprops-imported-types-limitations](reference/ts-defineprops-imported-types-limitations.md)
|
|
||||||
- Handling DOM events with strict TypeScript checking → See [ts-event-handler-explicit-typing](reference/ts-event-handler-explicit-typing.md)
|
|
||||||
- Sharing data between components with type safety → See [ts-provide-inject-injection-key](reference/ts-provide-inject-injection-key.md)
|
|
||||||
- Storing Vue components in reactive state → See [ts-shallowref-for-dynamic-components](reference/ts-shallowref-for-dynamic-components.md)
|
|
||||||
- Working with union types in Vue templates → See [ts-template-type-casting](reference/ts-template-type-casting.md)
|
|
||||||
|
|
||||||
### SSR
|
|
||||||
- User data leaking between server requests → See [state-ssr-cross-request-pollution](reference/state-ssr-cross-request-pollution.md)
|
|
||||||
- HTML differs between server and client renders → See [ssr-hydration-mismatch-causes](reference/ssr-hydration-mismatch-causes.md)
|
|
||||||
- Code runs on both server and browser environments → See [ssr-platform-specific-apis](reference/ssr-platform-specific-apis.md)
|
|
||||||
- Custom directives not displaying on server-rendered HTML → See [ssr-custom-directive-getssrprops](reference/ssr-custom-directive-getssrprops.md)
|
|
||||||
|
|
||||||
### Performance
|
|
||||||
- Many list items re-rendering unnecessarily during state changes → See [perf-props-stability-update-optimization](reference/perf-props-stability-update-optimization.md)
|
|
||||||
- Rendering hundreds or thousands of items causing DOM performance issues → See [perf-virtualize-large-lists](reference/perf-virtualize-large-lists.md)
|
|
||||||
- Static content re-evaluated on every parent component update → See [perf-v-once-v-memo-directives](reference/perf-v-once-v-memo-directives.md)
|
|
||||||
- List performance degrading from deeply nested component structure → See [perf-avoid-component-abstraction-in-lists](reference/perf-avoid-component-abstraction-in-lists.md)
|
|
||||||
- Computed properties returning objects triggering effects unexpectedly → See [perf-computed-object-stability](reference/perf-computed-object-stability.md)
|
|
||||||
- Page load metrics suffering from client-side JavaScript execution delay → See [perf-ssr-ssg-for-page-load](reference/perf-ssr-ssg-for-page-load.md)
|
|
||||||
|
|
||||||
### SFC (Single File Components)
|
|
||||||
- Trying to use named exports from component script blocks → See [sfc-named-exports-forbidden](reference/sfc-named-exports-forbidden.md)
|
|
||||||
- Starting a Vue project with a build setup → See [sfc-recommended-for-build-projects](reference/sfc-recommended-for-build-projects.md)
|
|
||||||
- Styling child component elements with scoped CSS → See [sfc-scoped-css-child-component-styling](reference/sfc-scoped-css-child-component-styling.md)
|
|
||||||
- Styling content added dynamically with v-html → See [sfc-scoped-css-dynamic-content](reference/sfc-scoped-css-dynamic-content.md)
|
|
||||||
- Optimizing scoped CSS selector performance → See [sfc-scoped-css-performance](reference/sfc-scoped-css-performance.md)
|
|
||||||
- Styling content passed through component slots → See [sfc-scoped-css-slot-content](reference/sfc-scoped-css-slot-content.md)
|
|
||||||
- Variables not updating in template after changes → See [sfc-script-setup-reactivity](reference/sfc-script-setup-reactivity.md)
|
|
||||||
- Organizing component template, logic, and styles → See [sfc-separation-of-concerns-colocate](reference/sfc-separation-of-concerns-colocate.md)
|
|
||||||
- Binding inline styles with property names → See [style-binding-camelcase](reference/style-binding-camelcase.md)
|
|
||||||
- Building Tailwind classes with string concatenation → See [tailwind-dynamic-class-generation](reference/tailwind-dynamic-class-generation.md)
|
|
||||||
|
|
||||||
### Plugins
|
|
||||||
- Debugging why global properties cause naming conflicts → See [plugin-global-properties-sparingly](reference/plugin-global-properties-sparingly.md)
|
|
||||||
- Plugin not working or inject returns undefined → See [plugin-install-before-mount](reference/plugin-install-before-mount.md)
|
|
||||||
- Global properties not available in setup function → See [plugin-prefer-provide-inject-over-global-properties](reference/plugin-prefer-provide-inject-over-global-properties.md)
|
|
||||||
- Creating a new Vue plugin from scratch → See [plugin-structure-install-method](reference/plugin-structure-install-method.md)
|
|
||||||
- Preventing collisions between multiple plugins → See [plugin-symbol-injection-keys](reference/plugin-symbol-injection-keys.md)
|
|
||||||
- Global properties missing TypeScript autocomplete support → See [plugin-typescript-type-augmentation](reference/plugin-typescript-type-augmentation.md)
|
|
||||||
|
|
||||||
### App Configuration
|
|
||||||
- App configuration methods not working after mount call → See [configure-app-before-mount](reference/configure-app-before-mount.md)
|
|
||||||
- Need to chain app configuration methods after mount → See [mount-return-value](reference/mount-return-value.md)
|
|
||||||
- Vue only controlling specific page sections → See [multiple-app-instances](reference/multiple-app-instances.md)
|
|
||||||
- Migrating dynamic component registration to Vite → See [dynamic-component-registration-vite](reference/dynamic-component-registration-vite.md)
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
# Sync Info
|
|
||||||
|
|
||||||
- **Source:** `vendor/vue-best-practices/skills/vue-best-practices`
|
|
||||||
- **Git SHA:** `12f43f8925fe802c7e67c472096d0e9bcff1e6bf`
|
|
||||||
- **Synced:** 2026-01-30
|
|
||||||
@ -1,258 +0,0 @@
|
|||||||
---
|
|
||||||
title: Use Class-based Animations for Non-Enter/Leave Effects
|
|
||||||
impact: LOW
|
|
||||||
impactDescription: Class-based animations are simpler and more performant for elements that remain in the DOM
|
|
||||||
type: best-practice
|
|
||||||
tags: [vue3, animation, css, class-binding, state]
|
|
||||||
---
|
|
||||||
|
|
||||||
# Use Class-based Animations for Non-Enter/Leave Effects
|
|
||||||
|
|
||||||
**Impact: LOW** - For animations on elements that are not entering or leaving the DOM, use CSS class-based animations triggered by Vue's reactive state. This is simpler than `<Transition>` and more appropriate for feedback animations like shake, pulse, or highlight effects.
|
|
||||||
|
|
||||||
## Task Checklist
|
|
||||||
|
|
||||||
- [ ] Use class-based animations for elements staying in the DOM
|
|
||||||
- [ ] Use `<Transition>` only for enter/leave animations
|
|
||||||
- [ ] Combine CSS animations with Vue's class bindings (`:class`)
|
|
||||||
- [ ] Consider using `setTimeout` to auto-remove animation classes
|
|
||||||
|
|
||||||
**When to Use Class-based Animations:**
|
|
||||||
- User feedback (shake on error, pulse on success)
|
|
||||||
- Attention-grabbing effects (highlight changes)
|
|
||||||
- Hover/focus states that need more than CSS transitions
|
|
||||||
- Any animation where the element stays mounted
|
|
||||||
|
|
||||||
**When to Use Transition Component:**
|
|
||||||
- Elements entering/leaving the DOM (v-if/v-show)
|
|
||||||
- Route transitions
|
|
||||||
- List item additions/removals
|
|
||||||
|
|
||||||
## Basic Pattern
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<div :class="{ shake: showError }">
|
|
||||||
<button @click="submitForm">Submit</button>
|
|
||||||
<span v-if="showError">This feature is disabled!</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
const showError = ref(false)
|
|
||||||
|
|
||||||
function submitForm() {
|
|
||||||
if (!isValid()) {
|
|
||||||
// Trigger shake animation
|
|
||||||
showError.value = true
|
|
||||||
|
|
||||||
// Auto-remove class after animation completes
|
|
||||||
setTimeout(() => {
|
|
||||||
showError.value = false
|
|
||||||
}, 820) // Match animation duration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.shake {
|
|
||||||
animation: shake 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
|
|
||||||
transform: translate3d(0, 0, 0); /* Enable GPU acceleration */
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes shake {
|
|
||||||
10%, 90% { transform: translate3d(-1px, 0, 0); }
|
|
||||||
20%, 80% { transform: translate3d(2px, 0, 0); }
|
|
||||||
30%, 50%, 70% { transform: translate3d(-4px, 0, 0); }
|
|
||||||
40%, 60% { transform: translate3d(4px, 0, 0); }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Common Animation Patterns
|
|
||||||
|
|
||||||
### Pulse on Success
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<button
|
|
||||||
@click="save"
|
|
||||||
:class="{ pulse: saved }"
|
|
||||||
>
|
|
||||||
{{ saved ? 'Saved!' : 'Save' }}
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
const saved = ref(false)
|
|
||||||
|
|
||||||
async function save() {
|
|
||||||
await saveData()
|
|
||||||
saved.value = true
|
|
||||||
setTimeout(() => saved.value = false, 1000)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.pulse {
|
|
||||||
animation: pulse 0.5s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes pulse {
|
|
||||||
0%, 100% { transform: scale(1); }
|
|
||||||
50% { transform: scale(1.05); }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Highlight on Change
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<div
|
|
||||||
:class="{ highlight: justUpdated }"
|
|
||||||
>
|
|
||||||
Value: {{ value }}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, watch } from 'vue'
|
|
||||||
|
|
||||||
const value = ref(0)
|
|
||||||
const justUpdated = ref(false)
|
|
||||||
|
|
||||||
watch(value, () => {
|
|
||||||
justUpdated.value = true
|
|
||||||
setTimeout(() => justUpdated.value = false, 1000)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.highlight {
|
|
||||||
animation: highlight 1s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes highlight {
|
|
||||||
0% { background-color: yellow; }
|
|
||||||
100% { background-color: transparent; }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Bounce Attention
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<div
|
|
||||||
:class="{ bounce: needsAttention }"
|
|
||||||
@animationend="needsAttention = false"
|
|
||||||
>
|
|
||||||
<BellIcon />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
const needsAttention = ref(false)
|
|
||||||
|
|
||||||
function notifyUser() {
|
|
||||||
needsAttention.value = true
|
|
||||||
// No setTimeout needed - using animationend event
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.bounce {
|
|
||||||
animation: bounce 0.5s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes bounce {
|
|
||||||
0%, 100% { transform: translateY(0); }
|
|
||||||
50% { transform: translateY(-10px); }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Using animationend Event
|
|
||||||
|
|
||||||
Instead of `setTimeout`, use the `animationend` event for cleaner code:
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<div
|
|
||||||
:class="{ animate: isAnimating }"
|
|
||||||
@animationend="isAnimating = false"
|
|
||||||
>
|
|
||||||
Content
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
const isAnimating = ref(false)
|
|
||||||
|
|
||||||
function triggerAnimation() {
|
|
||||||
isAnimating.value = true
|
|
||||||
// Class is automatically removed when animation ends
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Composable for Reusable Animations
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// composables/useAnimation.js
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
export function useAnimation(duration = 500) {
|
|
||||||
const isAnimating = ref(false)
|
|
||||||
|
|
||||||
function trigger() {
|
|
||||||
isAnimating.value = true
|
|
||||||
setTimeout(() => {
|
|
||||||
isAnimating.value = false
|
|
||||||
}, duration)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
isAnimating,
|
|
||||||
trigger
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
import { useAnimation } from '@/composables/useAnimation'
|
|
||||||
|
|
||||||
const shake = useAnimation(820)
|
|
||||||
const pulse = useAnimation(500)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<button
|
|
||||||
:class="{ shake: shake.isAnimating.value }"
|
|
||||||
@click="shake.trigger()"
|
|
||||||
>
|
|
||||||
Shake me
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
:class="{ pulse: pulse.isAnimating.value }"
|
|
||||||
@click="pulse.trigger()"
|
|
||||||
>
|
|
||||||
Pulse me
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Reference
|
|
||||||
- [Vue.js Animation Techniques - Class-based Animations](https://vuejs.org/guide/extras/animation.html#class-based-animations)
|
|
||||||
- [CSS Animations MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations)
|
|
||||||
@ -1,160 +0,0 @@
|
|||||||
---
|
|
||||||
title: Use Key Attribute to Force Re-render Animations
|
|
||||||
impact: MEDIUM
|
|
||||||
impactDescription: Without key attributes, Vue reuses DOM elements and animation libraries like AutoAnimate cannot detect changes to animate
|
|
||||||
type: gotcha
|
|
||||||
tags: [vue3, animation, key, autoanimate, rerender, dom]
|
|
||||||
---
|
|
||||||
|
|
||||||
# Use Key Attribute to Force Re-render Animations
|
|
||||||
|
|
||||||
**Impact: MEDIUM** - Vue optimizes performance by reusing DOM elements when possible. However, this optimization can prevent animation libraries (like AutoAnimate) from detecting changes, because the element is updated in place rather than re-created. Adding a `:key` attribute forces Vue to treat changed elements as new, triggering proper animations.
|
|
||||||
|
|
||||||
## Task Checklist
|
|
||||||
|
|
||||||
- [ ] Add `:key` to elements that should animate when their content changes
|
|
||||||
- [ ] Use unique, changing values for keys (not indices)
|
|
||||||
- [ ] For route transitions, add `:key="$route.fullPath"` to `<router-view>`
|
|
||||||
- [ ] Apply `v-auto-animate` to the parent element of keyed children
|
|
||||||
|
|
||||||
**Problematic Code:**
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<!-- BAD: Text changes but no animation occurs -->
|
|
||||||
<div v-auto-animate>
|
|
||||||
<p>{{ message }}</p> <!-- No key - element is reused -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- BAD: Image source changes but no animation -->
|
|
||||||
<div v-auto-animate>
|
|
||||||
<img :src="imageUrl" /> <!-- No key - element is reused -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- BAD: Route changes don't animate -->
|
|
||||||
<router-view v-auto-animate /> <!-- No key -->
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
const message = ref('Hello')
|
|
||||||
const imageUrl = ref('/images/photo1.jpg')
|
|
||||||
|
|
||||||
// Changing these won't trigger animations because
|
|
||||||
// Vue updates the existing elements rather than replacing them
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
**Correct Code:**
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<!-- GOOD: Key forces re-render, triggering animation -->
|
|
||||||
<div v-auto-animate>
|
|
||||||
<p :key="message">{{ message }}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- GOOD: Image animates when source changes -->
|
|
||||||
<div v-auto-animate>
|
|
||||||
<img :key="imageUrl" :src="imageUrl" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- GOOD: Route changes animate properly -->
|
|
||||||
<router-view :key="$route.fullPath" v-auto-animate />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
const message = ref('Hello')
|
|
||||||
const imageUrl = ref('/images/photo1.jpg')
|
|
||||||
|
|
||||||
// Now changing these will trigger animations
|
|
||||||
function updateMessage() {
|
|
||||||
message.value = 'World' // Triggers enter animation for new <p>
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Why This Works
|
|
||||||
|
|
||||||
When Vue sees a `:key` change:
|
|
||||||
1. It considers the old element and new element as different
|
|
||||||
2. The old element is removed (triggering leave animation)
|
|
||||||
3. A new element is created (triggering enter animation)
|
|
||||||
|
|
||||||
Without `:key`:
|
|
||||||
1. Vue sees the same element type in the same position
|
|
||||||
2. It updates the element's properties in place
|
|
||||||
3. No DOM addition/removal occurs, so no animation triggers
|
|
||||||
|
|
||||||
## Common Use Cases
|
|
||||||
|
|
||||||
### Animating Text Content Changes
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<div v-auto-animate>
|
|
||||||
<h1 :key="title">{{ title }}</h1>
|
|
||||||
<p :key="description">{{ description }}</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Animating Dynamic Components
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<div v-auto-animate>
|
|
||||||
<component :is="currentComponent" :key="currentComponent" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Animating Route Transitions
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<router-view v-slot="{ Component, route }">
|
|
||||||
<div v-auto-animate>
|
|
||||||
<component :is="Component" :key="route.fullPath" />
|
|
||||||
</div>
|
|
||||||
</router-view>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
## With Vue's Built-in Transition
|
|
||||||
|
|
||||||
The same principle applies to Vue's `<Transition>` component:
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<!-- GOOD: Key triggers transition on content change -->
|
|
||||||
<Transition name="fade" mode="out-in">
|
|
||||||
<p :key="message">{{ message }}</p>
|
|
||||||
</Transition>
|
|
||||||
|
|
||||||
<!-- GOOD: Different keys for conditional content -->
|
|
||||||
<Transition name="fade" mode="out-in">
|
|
||||||
<div v-if="isLoading" key="loading">Loading...</div>
|
|
||||||
<div v-else key="content">{{ content }}</div>
|
|
||||||
</Transition>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Caution: Performance Implications
|
|
||||||
|
|
||||||
Using `:key` forces full component re-creation. For frequently changing data:
|
|
||||||
- The entire component tree under the keyed element is destroyed and recreated
|
|
||||||
- Any component state is lost
|
|
||||||
- Consider whether the animation is worth the performance cost
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<!-- Be cautious with complex components -->
|
|
||||||
<ComplexDashboard :key="refreshTrigger" />
|
|
||||||
<!-- This destroys and recreates the entire dashboard! -->
|
|
||||||
```
|
|
||||||
|
|
||||||
## Reference
|
|
||||||
- [Vue.js Animation Techniques](https://vuejs.org/guide/extras/animation.html)
|
|
||||||
- [AutoAnimate with Vue](https://auto-animate.formkit.com/#usage-vue)
|
|
||||||
- [Vue.js v-for with key](https://vuejs.org/guide/essentials/list.html#maintaining-state-with-key)
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user