首先,先记录两个知识点,稍后再写关于pinia
的部分。
new URL
引入本地图片如果在js
中引入本地图片,如果直接使用相对路径获取图片地址,则打包后会出现找不到图片的错误问题,此时可以通过new URL(
./assets/xxx.png,import.meta.url)
的方式来处理
new URL
的第一个参数,只能是模板字符串的变量或者纯静态图片,不能直接是一个变量,否则是无法取出来参数的
defineProps
的用法defineProps({
imgs:{
type:Array,
default:()=>[]
}
})
//用法就是:props.imgs
defineEmits
的用法const emit = defineEmits(['change'])
emit('change',参数)
//相当于之前的this.$emit('change',参数)
defineExpose
的两种用法setup
中的写法<script>
//父组件调用子组件中的方法时,如果调用失败,则需要查看是否需要通过expose进行方法的暴漏。
setup(props,{emit,expose}){
function switchTo(i){xxxx}//定义切换方法
expose({swtichTo})//子组件给父组件暴漏
return {swtichTo}//当前组件暴漏
}
</script>
setup
在script
标签中的写法//如果是setup的直接写法:
<script setup>
defineExpose({switchTo})//直接这样写就可以了
</script>
pinia
的基本概念pinia
基本概念,相比于vuex
有什么样的优点,为什么官方推荐使用pinia
?pinia,是一个vue阵营的新的状态管理库,现在vue官方已经推荐使用Pinia来代替vuex,或者你可以把pinia看作vuex的最新的版本。
pinia是一个西班牙语,表示菠萝的意思。因为菠萝是一小块一小块组成的,这个和pinia的思想就非常的吻合,在pinia中,每个store仓库都是单独的扁平化存在的。
pinia是由vue官方团队中的一个成员开发的,最早是在2019年11月作为一项实验性工作所提出的,当时的目的是将组合API融入到vuex中,探索新版本的vuex应有的形态,随着探索的进行,最终发现pinia已经实现了vuex5大部分的提案,因此pinia就作为了最新版本的vuex,但是为了尊重作者本人,名字保持不变,仍然叫做pinia.
相比vuex,pinia的API更好而且简单,还支持组合式API,还可以和typescript一起推断类型使用
pinia
优势1.在pinia
不存在mutations
,只有state
,getters
,actions
在创建仓库中,提供了三个选项,分别是state
,getters
,actions
2.actions
支持同步和异步来修改store
,相当于将之前的mutations
和actions
合并
import {defineStore} from 'pinia'
export const useCounterStore = defineStore({
actions:{
//同步的修改仓库状态
increment(){this.count++},
decrement(){this.count--},
//异步的修改
async incrementAsync(){
await new Promise(resolve=>setTimeout(resolve,1000))
this.increment();
},
async decrementAsyn(){
await new Promise(resolve=>setTimeout(resolve,1000))
this.decrement();
}
}
})
3.可以和typescript
一起使用,以此来获得类型推断的支持
import {defineStore} from 'pinia'
//这里定义了一个名为ToDo的接口
interface Todo{
id:number,
text:string,
done:boolean
}
export const useTodoStore = defineStore({
id:'todo',
state:()=>({
todos:[] as Todo[]
}),
getters:{
completedTodos:state=>state.todos.filter(todo=>todo.done)
},
actions:{
addTodoItem(text:string){
const id = state.todos.length+1
const newTodo = {id,text,done:false}
state.todos.push(newTodo)
},
toggleTodoItem(todo:Todo){
todo.done = !todo.done
},
async fetchTodos(){
const respose = await fetch('https://jsonplaceholder.typicode.com/todos')
const todos = await response.json() as Todo[]
state.todos = todos
}
}
})
4.关于store
仓库,每一个store
仓库都是独立的扁平化的存在的,不再像vuex
里面是通过modules
嵌套
5.支持插件扩展,可以通过插件(函数)来扩展仓库的功能,为仓库添加全局属性或者全局方法
const localStoragePlugin = (context:PiniaPluginContext)=>{
const key = 'my-app-state'
//从loclStorage中恢复状态
context.state = localStorage.getItem(key)||context.state
//监听state变化,将变化保存到localStorage
context.subscribe(mutation=>{
localStorage.setItem(key,context.state)
})
}
//创建pinia实例,并注册localStoragePlugin插件
const pinia = createPinia()
pinia.use(localStoragePlugin)
6.更加轻量,压缩之后体积只有1kb左右,基本上可以忽略这个文件的存在
main.js
中使用pinia
import {createPinia} from 'pinia';
//创建pinia实例
const pinia = createPinia();
createApp(App).use(router).use(pinia).mount('#app');
import {defineStore} from 'pinia';
//第二个参数支持两种风格:options api以及composition api
export const useCounterStore = defineStore('counter',{
state:()=>{
return{
num:0
}
}
})
import {useCounterStore} from '@/store/useCounterStore.js'
const store = useCounterStore();//拿到仓库
//接下来我们可以从仓库中解构数据出来
import {storeToRefs} from 'pinia';//为了保证从store中获取的数据具有响应式,则需要通过storeToRefs包裹参数后进行取值,才能保证不破坏响应式数据
const {num} = storeToRefs(store);
pinia
异步的写法async asyncIncrement(){
await new Promise(resolve=>setTimeout(resolve,1000));
this.increment();
},
increment(){
this.num++;
}
store.$reset()
重置方法——可以进行store数据的重置store.$patch()
变更方法除了用store.count++
直接改变store,你还可以调用$patch
方法,它允许用一个state
的补丁对象在同一个时间更改多个属性:
store.$patch({
count:store.count+1,
age:20,
name:'D10'
})
不过,用这种语法的话,有些变更真的很难实现或者很耗时,任何集合的修改(例如,向数组中添加/移除一个元素或是做splice操作)都需要你创建一个新的集合,因此,$patch方法也接受一个函数来组合这种难以用补丁对象实现的变更。
store.$patch(state=>{
state.items.push({name:'shoes',quantity:1});
state.hasChanged = true;
})
两种变更store
方法的主要区别在于,$patch
允许你将多个变更归入devtools
的同一个条目中,同时请注意,直接修改state.$patch()
也会出现在devtools
中,而且可以进行timetravel
pinia
选项式风格该风格基本上跟之前的vuex
是非常相似的,只不过没有mutation
,无论是同步的方法还是异步的方法,都写在actions
里面。
在组件中使用仓库数据时,如果是要获取数据,为了保持数据的响应式,应该使用storeToRefs
方法。
import {storeToRefs} from 'pinia';
const {num,doubleCount} = storeToRefs(store);
在组件中使用仓库数据时,首先引入仓库方法,并执行该方法:
import {useCounterStore} from '@/store/useCounterStore.js'
const store = useCounterStore();//拿到仓库
如果是获取方法,则直接从store
里面解构出来即可。
const {increment,asyncIncrement,asyncDecrement} = store;
另外,仓库还提供了两个好用的api
store.$reset:重置state
store.$patch:变更state
pinia
组合式风格import {defineStore} from 'pinia';
import {reactive,computed} from 'vue';
export const useListStore = defineStore('list',()=>{
//组合式api风格
//创建仓库数据,类似于state
const list = reactive({
items:[
{
text:'学习 Pinia',
isCompleted:true,
},{
text:'打羽毛球',
isCompleted:false
}
],
counter:100
})
const doubleCounter = computed(()=>list.counter*2);
function addItem(item){
list.items.push({
text:item,
});
}
function completeHandle(index){
list.items[index].isCompleted == !list.items[index].isCompleted;
}
function delItem(index){
list.items.splice(index,1);
}
return {list,addItem,completeHandle,delItem};
})
使用方法:
import {useListStore} from '@/store/useListStore.js';
import {storeToRefs} from 'pinia';
//获取到仓库
const store = useListStore();
//从仓库解构数据出来
const {list,doubleCounter} = storeToRefs(store);
const {addItem,completeHandle,delItem} = store;
const newItem = ref("");
//添加新的待办事项
function addHandle(){
//首先拿到用户的输入,添加到状态仓库里面
if(newItem.value){
//list.items.push(newItem.value);//通过newItem.value可以拿到用户输入的值
addItem(newItem.value);
newItem.value = '';
}
}
function delHandle(index){
if(window.confirm(`是否要删除当前项目:\n
${list.value.items[index].text}\n
完成状态:${list.value.items[index].isCompleted?'已完成':'未完成'}
`)){
delItem(index);
}
}
引入其他仓库
import {useCounterStore} from './useCounterStore.js'
const useCounter = useCounterStore();
const otherCounter = computed(()=>useCounter.counter*3);
组合式风格和vue3
中的使用方法是一样的,通过ref
或者reactive
来定义仓库数据。
通过普通的方法来操作仓库数据,无论是数据还是方法最终需要导出出去。