Initial commit

This commit is contained in:
wfz
2026-05-13 16:24:00 +08:00
commit 5728d3cbda
55 changed files with 37267 additions and 0 deletions

240
docs/vue/basic.md Normal file
View 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
View 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
View 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
View 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
View 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)
```