Pinia 是近年比较热门的 Vue 相关状态管理库,我对其的学习步骤如下:
- 使用 pinia.js 实现一个简单的 TodoList 功能;
- 观察 pinia.js 的 api 和数据结构,自己实现一个伪 pinia,并将 TodoList 的依赖转为伪 pinia。
(一)使用 pinia.js 实现一个简单的 TodoList 功能
首先在 main.js 中引入:
// src/main.js
import { createPinia } from 'pinia'
createApp(App).use(createPinia()).mount('#app')
在 src/store 目录下新建 todoList.js
// src/store/todoList.js
import { defineStore } from 'pinia'
export default defineStore('todoList', {
state: () => ({
todos: [], // id: number, text: string, isFinished: boolean
filter: 'all', // 'all' 'finished' 'unfinished'
nextId: 0
}),
actions: {
addTodo(text) {
this.todos.push({
id: this.nextId++,
text,
isFinished: false
})
},
toggleTodo(id) {
this.todos = this.todos.map((todo) => {
if (todo.id === id) {
todo.isFinished = !todo.isFinished
}
return todo
})
},
removeTodo(id) {
this.todos = this.todos.filter((todo) => todo.id !== id)
}
},
getters: {
finishedTodos(state) {
return state.todos.filter((todo) => todo.isFinished)
},
unFinishedTodos(state) {
return state.todos.filter((todo) => !todo.isFinished)
},
filteredTodos(state) {
switch (this.filter) {
case 'finished':
return this.finishedTodos
case 'unfinished':
return this.unFinishedTodos
default:
return this.todos
}
}
}
})
在 src/store 目录下新建 index.js
import useTodoListStore from './todoList'
export { useTodoListStore }
如此,就能在 src/App.vue 中引入:
// src/App.vue
import { useTodoListStore } from './store'
const todoListStore = useTodoListStore()
console.log(todoListStore)
将 todoListStore 打印出来观察:
todoListStore
- 这是一个
Proxy对象; - 有
$dispose、$id、$onAction、$patch、$reset、$subscribe等一些由 pinia 定义的属性以及一些继承的属性; - 在
state中定义的filter、nextId、todos都是ObjectRefImpl类型的对象,都成为了todoListStore的属性; - 在
actions中定义的addTodo、removeTodo、toggleTodo等方法都成为了todoListStore的属性; - 在
getters中定义的filteredTodos、finishedTodos、unfinishedTodos都是ComputedRefImpl类型的对象,都成为了todoListStore的属性。
观察完后将 src/App.vue 的代码改成:
<template>
<todo-list></todo-list>
</template>
<script setup>
import TodoList from '@/components/TodoList/index.vue'
</script>
下面来具体写 TodoList 组件:
<!-- src/components/TodoList/index.vue -->
<template>
<div>
<todo-tab></todo-tab>
<todo-form></todo-form>
<todos></todos>
</div>
</template>
<script setup>
import TodoTab from './TodoTab.vue'
import TodoForm from './TodoForm.vue'
import Todos from './Todos.vue'
</script>
<!-- src/components/TodoList/TodoTab.vue -->
<template>
<div>
<a
href="javascript:;"
:class="{ active: todoListStore.filter === 'all' }"
@click="setFilter('all')"
>All</a
>
<a
href="javascript:;"
:class="{ active: todoListStore.filter === 'finished' }"
@click="setFilter('finished')"
>Finished</a
>
<a
href="javascript:;"
:class="{ active: todoListStore.filter === 'unfinished' }"
@click="setFilter('unfinished')"
>Unfinished</a
>
</div>
</template>
<script setup>
import { useTodoListStore } from '@/store'
const todoListStore = useTodoListStore()
const setFilter = (filter) => {
todoListStore.$patch({ filter })
}
</script>
<style scoped>
a {
margin-right: 15px;
}
.active {
text-decoration: none;
color: #000;
}
</style>
<!-- src/components/TodoList/TodoForm.vue -->
<template>
<div>
<input
placeholder="Input something..."
type="text"
v-model="inputRef"
/>
<button @click="addTodo">Add Todo</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useTodoListStore } from '@/store'
const todoListStore = useTodoListStore()
const inputRef = ref('')
const addTodo = () => {
todoListStore.addTodo(inputRef.value)
inputRef.value = ''
}
</script>
<!-- src/components/TodoList/Todos.vue -->
<template>
<div>
<div v-for="item of todoListStore.filteredTodos" :key="item.id">
<input
type="checkbox"
:checked="item.isFinished"
@click="todoListStore.toggleTodo(item.id)"
/>
<span :class="{ finished: item.isFinished }"
>{{item.text}}</span
>
<button @click="todoListStore.removeTodo(item.id)">
Delete
</button>
</div>
</div>
</template>
<script setup>
import { useTodoListStore } from '@/store'
const todoListStore = useTodoListStore()
</script>
<style scoped>
.finished {
text-decoration: line-through;
}
</style>
本小节完,下一小节开始自己实现 pinia 相关的功能。



![[译] 终于,JavaScript 有了安全的数组方法](https://img-1252058122.cos.ap-guangzhou.myqcloud.com/blog/article-cover/cmfkkq9a00001uhcg5k63cmt3.jpg)
