Vue3.3新特性------defineOptions
背景说明
- 有
<script setup>
之前,如果要定义 props, emits 可以轻而易举地添加一个与setup平级的属性 - 但是用了
script setup
后,就没法这么干了,setup属性已经没有了,自然无法添加与其平级的属性
为了解决这一问题,引入了defineProps
与 defineEmits
这两个宏。但这只解决了props
与emits
这两个属性。如果我们要定义组件的name
或其他自定义的属性,还是得回到最原始的用法 -- 再添加一个普通的<script>
标签。
这样就会存在两个<script>
标签。让人无法接受。
引入 defineOptions
-
所以在Vue3.3中新引入了
defineOptions
宏.顾名思义,主要是用来定义Option API
的选项. 可以用defineOptions
定义任意的选项, -
props
,emits
,expose
,slots
除外(因为这些是可以使用defineXXX
来做到)
js
<script setup>
defineOptions({
name: 'Foo',
inheritAttrs: false,
// ...更多自定义属性
})
<script>
Vue3.3新特性------defineModel
用defineModel的前提 是需要在vite.config.js中加配置
js
plugins: [
vue({
script:{
defineModel:true
}
}),
],
在Vue3中,自定义组件 上使用v-model
,相当于传递一个modelValue属性, 同时触发 updata:modelValue
事件
js
<Child v-model="isVisible">
// 相当于
<Child :modelValue="isVisible" @updata:modelValue="isVisible=$event">
我们需要先定义 props, 再定义 emits. 其中有许多重复的代码. 如果需要修改此值, 还需要手动调用 emit 函数
如何用defineModel优化👇
封装一个输入框组件,并且v-model跟现在的数据做一个绑定,实现同步更新
父组件一点也没动,跟原来一样;只需要改子组件处即可实现父传子
app.vue
js
<script setup>
import MyInput from '@/components/my-input.vue'
import { ref } from 'vue'
const txt = ref('123456')
</script>
<template>
<div>
<MyInput v-model="txt"></MyInput>
{{ txt }}
</div>
</template>
my-input.vue
js
//<script setup>
//defineProps({
// modelValue: String
//})
//const emit = //defineEmits(['updata:modelValue'])
//</script>
<script setup>
// 1. 不再需要props来接收了,而是直接model接收
//2.你需要修改这个数据也不再需要emit,而是直接修改
import { defineModel } from 'vue'
const modelValue = defineModel()
</script>
<template>
<div>
<input
type="text"
:value="modelValue"
@input="e => modelValue = e.target.value"
>
</div>
</template>
Pinia
是什么
Pinia 是 Vue 的最新状态管理工具, 是Vuex的 替代品
- 提供了更加简单的API(去掉了mutation,也就是action既可以异步又可以修改数据了)
- pinia如何实现getter?------computed计算属性函数
- 提供符合组合式风格的API(和Vue3新语法统一)
- 弃掉了modules的概念, 每一个store都是一个独立的模块
- 配合 TypeScript 更加友好,提供可靠的类型推断
手动添加Pinia到Vue项目
在实际开发项目的时候,关于Pinia的配置,可以在创建项目时自动添加
- 使用Vite创建一个空的项目Vue3项目:npm create vue@latest
- 按照官方文档 安装 pinia 到项目中(官网:cn.vuejs.org)
- vue3官网中有个生态系统,其中有pinia
Pinia | The intuitive store for Vue.js (vuejs.org)
- npm install pinia
- 在main.js里改
js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia=createPinia() // 创建pinia实例
const app=createApp(App) // 创建根实例
app.use(pinia) // pinia插件的安装配置
app.mount('#app') // 视图的挂载
- npm run dev启动
pinia的基本语法
pinia的基础使用------计数器案例
- 定义store
- 组件使用store
定义store👇
我们一般用第二种方法,这个更像setup组合式api写法
使用👇
action同步实现------按钮同步加减
counter.js
- 创建好仓库之后,如果别人要用的话,我们就可以在前面做一个接收const,它返回值是一个函数,基于这个函数我们将来就可以使用到里面的数据了
- 函数一旦调用,就可以使用里面的数据了,所以我们export把这个函数导出,之后在页面调就可以了
js
import { defineStore } from 'pinia'
import { ref,computed } from 'vue'
// 定义store
// 你可以对 `defineStore()` 的返回值进行任意命名,但最好使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾。(比如 `useUserStore`,`useCartStore`,`useProductStore`)
// 第一个参数是你的应用中 Store 的唯一 ID。
// defineStore(仓库唯一的标识, () => { ... })
export const useCounterStore = defineStore('counter', () => {
// 声明数据 state(就是ref) - count
const count = ref(100)
// 同步
// 声明操作数据的方法 action
// 普通函数就是action
const addCount = () => count.value++
const subCount = () => count.value--
// 声明基于数据派生的computed计算属性 getters
const double = computed(() => count.value * 2)
// 声明数据 state - msg
const msg = ref('hello pinia')
return {
count,
double,
addCount,
subCount,
msg
}
})
.vue
js
//app.vue------------------------------------------------------------------
<script setup>
import Son1Com from '@/components/Son1Com.vue'
import Son2Com from '@/components/Son2Com.vue'
// 哪个vue需要,就放哪个里面
import { useCounterStore } from '@/store/counter'
//这里不要对counterStore解构,会丢失数据的响应式,需要里面的直接下面打点调用
const counterStore =useCounterStore()
</script>
<template>
<div>
<h3>
APP.vue根组件 -0 -
{{ counterStore.count }}-
{{ counterStore.msg }}
</h3>
<Son1Com></Son1Com>
<Son2Com></Son2Com>
</div>
</template>
<style scoped>
</style>
// Son1------------------------------------------------------------------------------------------------------------
<script setup>
// 哪个vue需要,就放哪个里面
import { useCounterStore } from '@/store/counter'
//这里不要对counterStore解构,会丢失数据的响应式,需要里面的直接下面打点调用
const counterStore =useCounterStore()
</script>
<template>
<div>
我是Son1 - 0 - {{ counterStore.count }}-{{ counterStore.double }}
<button @click="counterStore.addCount">+</button>
</div>
</template>
<style scoped>
</style>
//Son2------------------------------------------------------------------------------------------------
<script setup>
// 哪个vue需要,就放哪个里面
import { useCounterStore } from '@/store/counter'
//这里不要对counterStore解构,会丢失数据的响应式,需要里面的直接下面打点调用
const counterStore =useCounterStore()
</script>
<template>
<div>
我是Son2 - 0 - {{ counterStore.count }}
<button @click="counterStore.subCount">-</button>
</div>
</template>
<style scoped>
</style>
action异步实现------点击获取频道数据
在action里面不需要考虑同步还是异步,它都可以写,函数里面既可以直接修改数据,也可以先发个请求,拿到数据回来之后再去修改上面的数据
- 编写方式: 异步action函数的写法和 组件中 获取异步数据的写法完全一致
- 接口地址: geek.itheima.net/v1_0/channe...
- 需求: 在Pinia中获取频道列表数据并把数据渲染App组件的模板中
- yarn add axios
- 重启项目
- 代码
channel.js
js
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
}
})
app.vue
js
import { useChannelStore } from '@/store/channel'
const channelStore=useChannelStore()
<button @click="channelStore.getList">获取频道数据</button>
<ul>
<li v-for="item in channelStore.channelList" :key="item.id">{{ item.name }}</li>
</ul>
pinia------storeToRefs方法
上面提到一个bug,关于 解构数据 之后响应化丢失
js
//这里不要对counterStore解构,会丢失数据的响应式,需要里面的直接下面打点调用
const counterStore =useCounterStore()
怎么解决呢????
js
import {storeToRefs} from 'pinia'
const { count,msg} =storeToRefs(counterStore)
但是!!作为action的的方法可以直接从store中解构(因为方法不需要响应式,方法就是一个固定的函数)
即👇
js
<script setup>
import { storeToRefs } from 'pinia'
const store = useCounterStore()
// `name` 和 `doubleCount` 是响应式的 ref
// 同时通过插件添加的属性也会被提取为 ref
// 并且会跳过所有的 action 或非响应式 (不是 ref 或 reactive) 的属性
const { name, doubleCount } = storeToRefs(store)
// 作为 action 的 increment 可以直接解构
const { increment } = store
</script>
pinia------持久化(存储在本地)
实现
pinia的调试
官方文档:prazdevs.github.io/pinia-plugi...
- 安装插件
npm i pinia-plugin-persistedstate
- main.js
js
// 导入持久化插件
import persist from 'pinia-plugin-persistedstate'
// 所以需要挂载在pinia上
app.use(pinia.use(persist))
- store仓库中,
persist:true
开启
在你需要的模块仓库的第三个参数处写
js
export const useCounterStore = defineStore('counter', () => {
...
},
{
persist:true
}
)
关于pinia的配置
配置 | pinia-plugin-persistedstate (prazdevs.github.io)