Initial commit
This commit is contained in:
240
docs/vue/basic.md
Normal file
240
docs/vue/basic.md
Normal file
@@ -0,0 +1,240 @@
|
||||
# Vue3基础
|
||||
|
||||
## 创建应用
|
||||
|
||||
### 使用Vite创建项目
|
||||
|
||||
```bash
|
||||
npm create vite@latest my-vue-app -- --template vue
|
||||
cd my-vue-app
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### 应用实例
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const message = ref('Hello Vue 3!')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1>{{ message }}</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
color: #42b883;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
## 模板语法
|
||||
|
||||
### 文本插值
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<p>{{ message }}</p>
|
||||
<p>{{ number + 1 }}</p>
|
||||
<p>{{ ok ? 'YES' : 'NO' }}</p>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const message = ref('Hello')
|
||||
const number = ref(10)
|
||||
const ok = ref(true)
|
||||
</script>
|
||||
```
|
||||
|
||||
### 属性绑定
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div v-bind:id="dynamicId"></div>
|
||||
<div :class="activeClass"></div>
|
||||
<button :disabled="isDisabled">按钮</button>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const dynamicId = ref('my-id')
|
||||
const activeClass = ref('active')
|
||||
const isDisabled = ref(false)
|
||||
</script>
|
||||
```
|
||||
|
||||
### 条件渲染
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div v-if="type === 'A'">类型A</div>
|
||||
<div v-else-if="type === 'B'">类型B</div>
|
||||
<div v-else>其他类型</div>
|
||||
|
||||
<div v-show="isVisible">显示/隐藏</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const type = ref('A')
|
||||
const isVisible = ref(true)
|
||||
</script>
|
||||
```
|
||||
|
||||
### 列表渲染
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<ul>
|
||||
<li v-for="item in items" :key="item.id">
|
||||
{{ item.name }}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul>
|
||||
<li v-for="(item, index) in items" :key="item.id">
|
||||
{{ index }}: {{ item.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const items = ref([
|
||||
{ id: 1, name: 'Vue' },
|
||||
{ id: 2, name: 'React' },
|
||||
{ id: 3, name: 'Angular' }
|
||||
])
|
||||
</script>
|
||||
```
|
||||
|
||||
## 事件处理
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<button v-on:click="count++">点击次数: {{ count }}</button>
|
||||
<button @click="handleClick">点击</button>
|
||||
<button @click="handleClick($event, '参数')">传参</button>
|
||||
|
||||
<form @submit.prevent="onSubmit">
|
||||
<button type="submit">提交</button>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const count = ref(0)
|
||||
|
||||
function handleClick(event) {
|
||||
console.log('点击事件', event)
|
||||
}
|
||||
|
||||
function onSubmit() {
|
||||
console.log('表单提交')
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## 双向绑定
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<input v-model="text" placeholder="输入文本">
|
||||
<p>输入的内容: {{ text }}</p>
|
||||
|
||||
<textarea v-model="content"></textarea>
|
||||
|
||||
<select v-model="selected">
|
||||
<option value="">请选择</option>
|
||||
<option value="a">选项A</option>
|
||||
<option value="b">选项B</option>
|
||||
</select>
|
||||
|
||||
<input type="checkbox" v-model="checked">
|
||||
<input type="radio" v-model="picked" value="one">
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const text = ref('')
|
||||
const content = ref('')
|
||||
const selected = ref('')
|
||||
const checked = ref(false)
|
||||
const picked = ref('')
|
||||
</script>
|
||||
```
|
||||
|
||||
## 计算属性
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<p>原始: {{ message }}</p>
|
||||
<p>反转: {{ reversedMessage }}</p>
|
||||
<p>全名: {{ fullName }}</p>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const message = ref('Hello Vue')
|
||||
const firstName = ref('张')
|
||||
const lastName = ref('三')
|
||||
|
||||
const reversedMessage = computed(() => {
|
||||
return message.value.split('').reverse().join('')
|
||||
})
|
||||
|
||||
const fullName = computed({
|
||||
get() {
|
||||
return firstName.value + lastName.value
|
||||
},
|
||||
set(newValue) {
|
||||
firstName.value = newValue[0]
|
||||
lastName.value = newValue.slice(1)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
## 侦听器
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<input v-model="question">
|
||||
<p>{{ answer }}</p>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue'
|
||||
|
||||
const question = ref('')
|
||||
const answer = ref('请输入问题')
|
||||
|
||||
watch(question, async (newQuestion) => {
|
||||
if (newQuestion.includes('?')) {
|
||||
answer.value = '思考中...'
|
||||
// 模拟API调用
|
||||
setTimeout(() => {
|
||||
answer.value = '这是一个好问题!'
|
||||
}, 1000)
|
||||
}
|
||||
})
|
||||
|
||||
// 监听多个源
|
||||
watch([question, answer], ([newQuestion, newAnswer], [oldQuestion, oldAnswer]) => {
|
||||
console.log('问题或答案改变了')
|
||||
})
|
||||
</script>
|
||||
```
|
||||
251
docs/vue/component.md
Normal file
251
docs/vue/component.md
Normal file
@@ -0,0 +1,251 @@
|
||||
# 组件开发
|
||||
|
||||
## 组件基础
|
||||
|
||||
### 定义组件
|
||||
|
||||
```vue
|
||||
<!-- MyButton.vue -->
|
||||
<template>
|
||||
<button :class="['btn', `btn-${type}`]" @click="handleClick">
|
||||
<slot></slot>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: String,
|
||||
default: 'default',
|
||||
validator: (value) => ['default', 'primary', 'danger'].includes(value)
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['click'])
|
||||
|
||||
function handleClick(event) {
|
||||
emit('click', event)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-default {
|
||||
background: #f0f0f0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #42b883;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: #ff4d4f;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
### 使用组件
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<MyButton type="primary" @click="handleButtonClick">
|
||||
点击我
|
||||
</MyButton>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import MyButton from './MyButton.vue'
|
||||
|
||||
function handleButtonClick() {
|
||||
console.log('按钮被点击')
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
### Props声明
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
// 简单声明
|
||||
const props = defineProps(['title', 'content'])
|
||||
|
||||
// 带类型的声明
|
||||
const props = defineProps({
|
||||
title: String,
|
||||
content: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
count: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
items: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
### Props验证
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
status: {
|
||||
type: String,
|
||||
required: true,
|
||||
validator: (value) => ['active', 'inactive', 'pending'].includes(value)
|
||||
},
|
||||
callback: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
## 事件
|
||||
|
||||
### 定义事件
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
const emit = defineEmits(['update', 'delete', 'change'])
|
||||
|
||||
function handleUpdate() {
|
||||
emit('update', { id: 1, name: '更新数据' })
|
||||
}
|
||||
|
||||
function handleDelete(id) {
|
||||
emit('delete', id)
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### v-model
|
||||
|
||||
```vue
|
||||
<!-- CustomInput.vue -->
|
||||
<template>
|
||||
<input
|
||||
:value="modelValue"
|
||||
@input="$emit('update:modelValue', $event.target.value)"
|
||||
>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps(['modelValue'])
|
||||
defineEmits(['update:modelValue'])
|
||||
</script>
|
||||
|
||||
<!-- 使用 -->
|
||||
<CustomInput v-model="text" />
|
||||
```
|
||||
|
||||
## 插槽
|
||||
|
||||
### 默认插槽
|
||||
|
||||
```vue
|
||||
<!-- Card.vue -->
|
||||
<template>
|
||||
<div class="card">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 使用 -->
|
||||
<Card>
|
||||
<h2>标题</h2>
|
||||
<p>内容</p>
|
||||
</Card>
|
||||
```
|
||||
|
||||
### 具名插槽
|
||||
|
||||
```vue
|
||||
<!-- Layout.vue -->
|
||||
<template>
|
||||
<div class="layout">
|
||||
<header>
|
||||
<slot name="header"></slot>
|
||||
</header>
|
||||
<main>
|
||||
<slot></slot>
|
||||
</main>
|
||||
<footer>
|
||||
<slot name="footer"></slot>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 使用 -->
|
||||
<Layout>
|
||||
<template #header>
|
||||
<h1>页面标题</h1>
|
||||
</template>
|
||||
|
||||
<p>主要内容</p>
|
||||
|
||||
<template #footer>
|
||||
<p>页脚信息</p>
|
||||
</template>
|
||||
</Layout>
|
||||
```
|
||||
|
||||
### 作用域插槽
|
||||
|
||||
```vue
|
||||
<!-- List.vue -->
|
||||
<template>
|
||||
<ul>
|
||||
<li v-for="item in items" :key="item.id">
|
||||
<slot :item="item" :index="index"></slot>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps(['items'])
|
||||
</script>
|
||||
|
||||
<!-- 使用 -->
|
||||
<List :items="users">
|
||||
<template #default="{ item, index }">
|
||||
<span>{{ index }}. {{ item.name }}</span>
|
||||
</template>
|
||||
</List>
|
||||
```
|
||||
|
||||
## 生命周期
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { onMounted, onUpdated, onUnmounted } from 'vue'
|
||||
|
||||
onMounted(() => {
|
||||
console.log('组件已挂载')
|
||||
})
|
||||
|
||||
onUpdated(() => {
|
||||
console.log('组件已更新')
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
console.log('组件已卸载')
|
||||
})
|
||||
</script>
|
||||
```
|
||||
242
docs/vue/composition.md
Normal file
242
docs/vue/composition.md
Normal file
@@ -0,0 +1,242 @@
|
||||
# 组合式API
|
||||
|
||||
## setup函数
|
||||
|
||||
### 基本用法
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { ref, reactive, computed, watch, onMounted } from 'vue'
|
||||
|
||||
// 响应式数据
|
||||
const count = ref(0)
|
||||
const state = reactive({
|
||||
name: 'Vue',
|
||||
version: 3
|
||||
})
|
||||
|
||||
// 计算属性
|
||||
const doubleCount = computed(() => count.value * 2)
|
||||
|
||||
// 方法
|
||||
function increment() {
|
||||
count.value++
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
console.log('组件已挂载')
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<p>Count: {{ count }}</p>
|
||||
<p>Double: {{ doubleCount }}</p>
|
||||
<button @click="increment">+1</button>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
## ref与reactive
|
||||
|
||||
### ref
|
||||
|
||||
用于基本类型和需要替换整个对象的场景。
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const count = ref(0)
|
||||
const user = ref({ name: '张三' })
|
||||
|
||||
// 访问需要 .value
|
||||
console.log(count.value)
|
||||
console.log(user.value.name)
|
||||
|
||||
// 重新赋值
|
||||
user.value = { name: '李四' }
|
||||
</script>
|
||||
```
|
||||
|
||||
### reactive
|
||||
|
||||
用于对象类型,返回原始对象的代理。
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { reactive } from 'vue'
|
||||
|
||||
const state = reactive({
|
||||
count: 0,
|
||||
user: {
|
||||
name: '张三',
|
||||
age: 25
|
||||
}
|
||||
})
|
||||
|
||||
// 直接访问,不需要 .value
|
||||
console.log(state.count)
|
||||
console.log(state.user.name)
|
||||
|
||||
// 修改属性
|
||||
state.count++
|
||||
state.user.age = 26
|
||||
</script>
|
||||
```
|
||||
|
||||
## 组合式函数
|
||||
|
||||
### 创建组合式函数
|
||||
|
||||
```javascript
|
||||
// useCounter.js
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
export function useCounter(initialValue = 0) {
|
||||
const count = ref(initialValue)
|
||||
|
||||
const doubleCount = computed(() => count.value * 2)
|
||||
|
||||
function increment() {
|
||||
count.value++
|
||||
}
|
||||
|
||||
function decrement() {
|
||||
count.value--
|
||||
}
|
||||
|
||||
function reset() {
|
||||
count.value = initialValue
|
||||
}
|
||||
|
||||
return {
|
||||
count,
|
||||
doubleCount,
|
||||
increment,
|
||||
decrement,
|
||||
reset
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 使用组合式函数
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { useCounter } from './useCounter'
|
||||
|
||||
const { count, doubleCount, increment, decrement, reset } = useCounter(10)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<p>Count: {{ count }}</p>
|
||||
<p>Double: {{ doubleCount }}</p>
|
||||
<button @click="increment">+1</button>
|
||||
<button @click="decrement">-1</button>
|
||||
<button @click="reset">Reset</button>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 鼠标位置追踪
|
||||
|
||||
```javascript
|
||||
// useMouse.js
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
|
||||
export function useMouse() {
|
||||
const x = ref(0)
|
||||
const y = ref(0)
|
||||
|
||||
function update(event) {
|
||||
x.value = event.pageX
|
||||
y.value = event.pageY
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('mousemove', update)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('mousemove', update)
|
||||
})
|
||||
|
||||
return { x, y }
|
||||
}
|
||||
```
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { useMouse } from './useMouse'
|
||||
|
||||
const { x, y } = useMouse()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<p>鼠标位置: {{ x }}, {{ y }}</p>
|
||||
</template>
|
||||
```
|
||||
|
||||
## provide/inject
|
||||
|
||||
### 提供数据
|
||||
|
||||
```vue
|
||||
<!-- 祖先组件 -->
|
||||
<script setup>
|
||||
import { provide, ref } from 'vue'
|
||||
|
||||
const theme = ref('dark')
|
||||
const user = ref({ name: '张三' })
|
||||
|
||||
provide('theme', theme)
|
||||
provide('user', user)
|
||||
provide('updateTheme', (newTheme) => {
|
||||
theme.value = newTheme
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
### 注入数据
|
||||
|
||||
```vue
|
||||
<!-- 后代组件 -->
|
||||
<script setup>
|
||||
import { inject } from 'vue'
|
||||
|
||||
const theme = inject('theme', 'light') // 默认值
|
||||
const user = inject('user')
|
||||
const updateTheme = inject('updateTheme')
|
||||
|
||||
function toggleTheme() {
|
||||
updateTheme(theme.value === 'dark' ? 'light' : 'dark')
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## toRef与toRefs
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { reactive, toRef, toRefs } from 'vue'
|
||||
|
||||
const state = reactive({
|
||||
name: '张三',
|
||||
age: 25,
|
||||
city: '北京'
|
||||
})
|
||||
|
||||
// toRef - 单个属性
|
||||
const nameRef = toRef(state, 'name')
|
||||
|
||||
// toRefs - 所有属性
|
||||
const { name, age, city } = toRefs(state)
|
||||
|
||||
// 解构后的ref保持响应性
|
||||
name.value = '李四'
|
||||
console.log(state.name) // 李四
|
||||
</script>
|
||||
```
|
||||
10
docs/vue/index.md
Normal file
10
docs/vue/index.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Vue 笔记
|
||||
|
||||
Vue.js框架学习笔记,涵盖Vue3基础、组件开发、状态管理等内容。
|
||||
|
||||
## 目录
|
||||
|
||||
- [Vue3基础](/vue/basic.html)
|
||||
- [组件开发](/vue/component.html)
|
||||
- [组合式API](/vue/composition.html)
|
||||
- [状态管理](/vue/pinia.html)
|
||||
224
docs/vue/pinia.md
Normal file
224
docs/vue/pinia.md
Normal file
@@ -0,0 +1,224 @@
|
||||
# 状态管理
|
||||
|
||||
## Pinia基础
|
||||
|
||||
### 安装Pinia
|
||||
|
||||
```bash
|
||||
npm install pinia
|
||||
```
|
||||
|
||||
### 创建Store
|
||||
|
||||
```javascript
|
||||
// stores/counter.js
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
export const useCounterStore = defineStore('counter', () => {
|
||||
// state
|
||||
const count = ref(0)
|
||||
|
||||
// getters
|
||||
const doubleCount = computed(() => count.value * 2)
|
||||
|
||||
// actions
|
||||
function increment() {
|
||||
count.value++
|
||||
}
|
||||
|
||||
function decrement() {
|
||||
count.value--
|
||||
}
|
||||
|
||||
async function fetchCount() {
|
||||
// 异步操作
|
||||
const response = await fetch('/api/count')
|
||||
const data = await response.json()
|
||||
count.value = data.count
|
||||
}
|
||||
|
||||
return { count, doubleCount, increment, decrement, fetchCount }
|
||||
})
|
||||
```
|
||||
|
||||
### 使用Store
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { useCounterStore } from '@/stores/counter'
|
||||
|
||||
const counter = useCounterStore()
|
||||
|
||||
// 读取state
|
||||
console.log(counter.count)
|
||||
|
||||
// 读取getters
|
||||
console.log(counter.doubleCount)
|
||||
|
||||
// 调用actions
|
||||
counter.increment()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<p>Count: {{ counter.count }}</p>
|
||||
<p>Double: {{ counter.doubleCount }}</p>
|
||||
<button @click="counter.increment">+1</button>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
## 解构Store
|
||||
|
||||
### storeToRefs
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useCounterStore } from '@/stores/counter'
|
||||
|
||||
const counter = useCounterStore()
|
||||
|
||||
// 解构state和getters(保持响应性)
|
||||
const { count, doubleCount } = storeToRefs(counter)
|
||||
|
||||
// 解构actions(直接解构)
|
||||
const { increment, decrement } = counter
|
||||
</script>
|
||||
```
|
||||
|
||||
## 持久化
|
||||
|
||||
### 安装插件
|
||||
|
||||
```bash
|
||||
npm install pinia-plugin-persistedstate
|
||||
```
|
||||
|
||||
### 配置持久化
|
||||
|
||||
```javascript
|
||||
// stores/index.js
|
||||
import { createPinia } from 'pinia'
|
||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||||
|
||||
const pinia = createPinia()
|
||||
pinia.use(piniaPluginPersistedstate)
|
||||
|
||||
export default pinia
|
||||
```
|
||||
|
||||
### 使用持久化
|
||||
|
||||
```javascript
|
||||
// stores/user.js
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export const useUserStore = defineStore('user', () => {
|
||||
const token = ref('')
|
||||
const userInfo = ref(null)
|
||||
|
||||
function setToken(newToken) {
|
||||
token.value = newToken
|
||||
}
|
||||
|
||||
function setUserInfo(info) {
|
||||
userInfo.value = info
|
||||
}
|
||||
|
||||
function logout() {
|
||||
token.value = ''
|
||||
userInfo.value = null
|
||||
}
|
||||
|
||||
return { token, userInfo, setToken, setUserInfo, logout }
|
||||
}, {
|
||||
persist: {
|
||||
key: 'my-user-store',
|
||||
storage: localStorage,
|
||||
paths: ['token', 'userInfo']
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## 组合多个Store
|
||||
|
||||
```javascript
|
||||
// stores/index.js
|
||||
import { useCounterStore } from './counter'
|
||||
import { useUserStore } from './user'
|
||||
|
||||
export function useRootStore() {
|
||||
const counter = useCounterStore()
|
||||
const user = useUserStore()
|
||||
|
||||
function resetAll() {
|
||||
counter.$reset()
|
||||
user.$reset()
|
||||
}
|
||||
|
||||
return {
|
||||
counter,
|
||||
user,
|
||||
resetAll
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 订阅状态变化
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { useCounterStore } from '@/stores/counter'
|
||||
|
||||
const counter = useCounterStore()
|
||||
|
||||
// 订阅state变化
|
||||
counter.$subscribe((mutation, state) => {
|
||||
console.log('类型:', mutation.type)
|
||||
console.log('storeId:', mutation.storeId)
|
||||
console.log('新状态:', state)
|
||||
|
||||
// 持久化到本地存储
|
||||
localStorage.setItem('counter', JSON.stringify(state))
|
||||
})
|
||||
|
||||
// 订阅actions
|
||||
counter.$onAction(({ name, args, after, onError }) => {
|
||||
console.log(`Action ${name} 被调用,参数:`, args)
|
||||
|
||||
after((result) => {
|
||||
console.log(`Action ${name} 完成,结果:`, result)
|
||||
})
|
||||
|
||||
onError((error) => {
|
||||
console.error(`Action ${name} 出错:`, error)
|
||||
})
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
## 插件开发
|
||||
|
||||
```javascript
|
||||
// plugins/piniaLogger.js
|
||||
export function piniaLogger({ store }) {
|
||||
store.$onAction(({ name, args, after, onError }) => {
|
||||
console.log(`[${store.$id}] ${name} 开始`, args)
|
||||
|
||||
after((result) => {
|
||||
console.log(`[${store.$id}] ${name} 完成`, result)
|
||||
})
|
||||
|
||||
onError((error) => {
|
||||
console.error(`[${store.$id}] ${name} 失败`, error)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 使用插件
|
||||
const pinia = createPinia()
|
||||
pinia.use(piniaLogger)
|
||||
```
|
||||
Reference in New Issue
Block a user