背景说明
有之前,如果要定义 props, emits 可以轻而易举地添加一个与setup平级的属性,但是用了
script setup
后,就没法这么干了,setup属性已经没有了,自然无法添加与其平级的属性
为了解决这一问题,引入了defineProps 与 defineEmits这两个宏。但这只解决了props与emits这两个属性。如果我们要定义组件的name或其他自定义的属性,还是得回到最原始的用法 – 再添加一个普通的标签。这样就会存在两个
标签。让人无法接受。
一定要另建一个script给组件起一个名字
所以在Vue3.3中新引入了
defineOptions
宏.顾名思义,主要是用来定义Option API
的选项. 可以用defineOptions
定义任意的选项,props
,emits
,expose
,slots
除外(应为这些可以使用defineXXX
来做到)
{{ txt }}
my-input.vue
emit('updata:modelValue', e.target.value)"
>
新写法
modelValue = e.target.value
>
什么是Pinia
Pinia 是 Vue 的最新状态管理工具, 是Vuex的 替代品
- 提供了更加简单的API(去掉了mutation)
- 提供符合组合式风格的API(和Vue3新语法统一)
- 弃掉了modules的概念, 每一个store都是一个独立的模块
- 配合 TypeScript 更加友好,提供可靠的类型推断
在实际开发项目的时候,关于Pinia的配置,可以在创建项目时自动添加
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia() // 创建Pinia实例
const app = createApp(App) // 创建根实例
app.use(pinia).mount('#app') //这个可以拆分成下面的这两个
app.use(pinia)// pinia插件的安装配置
app.mount('#app')// 视图的挂载
定义Store
在深入研究核心概念之前,我们得知道 Store 是用 defineStore() 定义的,它的第一个参数要求是一个独一无二的名字:
import { defineStore } from 'pinia'
// 你可以对 `defineStore()` 的返回值进行任意命名,但最好使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾。(比如 `useUserStore`,`useCartStore`,`useProductStore`)
// 第一个参数是你的应用中 Store 的唯一 ID。
export const useAlertsStore = defineStore('alerts', {
// 其他配置...
})
这个名字 ,也被用作 id ,是必须传入的, Pinia 将用它来连接 store 和 devtools。为了养成习惯性的用法,将返回的函数命名为 use… 是一个符合组合式函数风格的约定。
defineStore() 的第二个参数可接受两类值:Setup 函数或 Option 对象。
Option Store
与 Vue 的选项式 API 类似,我们也可以传入一个带有 state、actions 与 getters 属性的 Option 对象
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
},
},
})
你可以认为 state 是 store 的数据 (data),getters 是 store 的计算属性 (computed),而 actions 则是方法 (methods)。
为方便上手使用,Option Store 应尽可能直观简单。
Setup Store
也存在另一种定义 store 的可用语法。与 Vue 组合式 API 的 setup 函数 相似,我们可以传入一个函数,该函数定义了一些响应式属性和方法,并且返回一个带有我们想暴露出去的属性和方法的对象。
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})
在 Setup Store 中:
Setup store 比 Option Store 带来了更多的灵活性,因为你可以在一个 store 内创建侦听器,并自由地使用任何组合式函数。不过,请记住,使用组合式函数会让 SSR 变得更加复杂。
新建store/counter.js
import { defineStore } from 'pinia'
import { ref } from 'vue'
// 定义store
// defineStore(仓库唯一的标识, () => { ... })
export const useCounterStore = defineStore('counter', () => {
// 声明数据 state - count
const count = ref(0)
// 声明操作数据的方法 action
const addCount = () => count.value++
const subCount = () => count.value--
// 声明基于数据派生的计算属性 getters
const double = computed(() => count.value * 2)
// 声明数据 state - msg
const msg = ref('hello pinia')
return {
count,
addCount,
subCount,
msg
}
})
Son1Count.vue
<script setup>
import { useCounterStore } from '@/store/counte
const counterStore = useCounterStore()
script>
<template>
<div>
我是Son1.vue - {{ counterStore.count }} - {{ counterStore.double }}
<button @click="counterStore.addCount">+button>
div>
template>
<style scoped>
style>
Son2Count.vue
<script setup>
import { useCounterStore } from '@/store/counte
const counterStore = useCounterStore()
script>
<template>
<div>
我是Son1.vue - {{ counterStore.count }}
<button @click="counterStore.subCount">-button>
div>
template>
<style scoped>
style>
在action里面不需要考虑同步还是异步,它都可以写,函数里面既可以直接修改数据,也可以先发个请求,拿到数据回来之后再去修改上面的数据
编写方式: 异步action函数的写法和组件中获取异步数据的写法完全一致
接口地址: http://geek.itheima.net/v1_0/channels
需求: 在Pinia中获取频道列表数据并把数据渲染App组件的模板中
import { defineStore } from 'pinia'
import { ref } from 'vue'
import axios from 'axios'
export const useChannelStore = defineStore('channel', () => {
// 声明数据
const channelList = ref([])
//声明操作数据的方法
const getList = async () => {
//支持异步
const { data: { data }} = await axios.get('http://geek.itheima.net/v1_0/channels')
channelList.value = data.channels
console.log(data.channels)
}
// 声明getters相关
return {
channelList,
getList
}
})
<script setup>
import Son1Com from '@/components/Son1Com.vue'
import Son2Com from '@/components/Son2Com.vue'
import { useCounterStore } from '@/store/counter'
import { useChannelStore } from './store/channel'
const counterStore = useCounterStore ()
const channelStore = useChannelStore ()
console.log(counterStore)
script>
<template>
<div>
<h3>
App.vue根组件
- {{ counterStore.count }}
- {{ counterStore.msg }}
h3>
<Son1Com>Son1Com>
<Son2Com>Son2Com>
<hr>
<button @click="channelStore.getList">获取频道数据button>
<ul>
<li v-for="item in channelStore.channelList" :key="item.id">{{ item.name }}li>
ul>
div>
template>
<style scoped>
style>
第六点中,如果直接进行解构,不进行处理,数据会丢失响应式,所以可以用到storeToRefs
官方文档:https://prazdevs.github.io/pinia-plugin-persistedstate/zh/
1.安装插件 pinia-plugin-persistedstate
npm i pinia-plugin-persistedstate
2.main.js 使用
import persist from 'pinia-plugin-persistedstate'
app.use(createPinia().use(persist))
3.store仓库中,persist:true
开启
npm
npm i pinia-plugin-persistedstate
将插件添加到 pinia 实例上
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
创建 Store 时,将 persist 选项设置为 true。
import { defineStore } from 'pinia'
export const useStore = defineStore('main', {
state: () => {
return {
someState: '你好 pinia',
}
},
persist: true,
})
import { defineStore } from 'pinia'
export const useStore = defineStore(
'main',
() => {
const someState = ref('你好 pinia')
return { someState }
},
{
persist: true,
},
)