Vue3.3新特性与Pinia

Vue3.3新特性------defineOptions

背景说明

  • <script setup>之前,如果要定义 props, emits 可以轻而易举地添加一个与setup平级的属性
  • 但是用了script setup后,就没法这么干了,setup属性已经没有了,自然无法添加与其平级的属性

为了解决这一问题,引入了defineProps defineEmits这两个宏。但这只解决了propsemits这两个属性。如果我们要定义组件的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的 替代品

  1. 提供了更加简单的API(去掉了mutation,也就是action既可以异步又可以修改数据了)
  2. pinia如何实现getter?------computed计算属性函数
  3. 提供符合组合式风格的API(和Vue3新语法统一)
  4. 弃掉了modules的概念, 每一个store都是一个独立的模块
  5. 配合 TypeScript 更加友好,提供可靠的类型推断

手动添加Pinia到Vue项目

在实际开发项目的时候,关于Pinia的配置,可以在创建项目时自动添加

  1. 使用Vite创建一个空的项目Vue3项目:npm create vue@latest
  2. 按照官方文档 安装 pinia 到项目中(官网:cn.vuejs.org)
  3. vue3官网中有个生态系统,其中有pinia

Pinia | The intuitive store for Vue.js (vuejs.org)

  1. npm install pinia
  2. 在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') // 视图的挂载
  1. npm run dev启动

pinia的基本语法

pinia的基础使用------计数器案例

  1. 定义store
  2. 组件使用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组件的模板中
  1. yarn add axios
  2. 重启项目
  3. 代码

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...

  1. 安装插件

npm i pinia-plugin-persistedstate

  1. main.js
js 复制代码
// 导入持久化插件
import persist from 'pinia-plugin-persistedstate'
// 所以需要挂载在pinia上
app.use(pinia.use(persist))
  1. store仓库中,persist:true开启

在你需要的模块仓库的第三个参数处写

js 复制代码
export const useCounterStore = defineStore('counter', () => {
...
},
{
    persist:true
}
)

关于pinia的配置

配置 | pinia-plugin-persistedstate (prazdevs.github.io)

相关推荐
To_OC8 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
vipbic8 小时前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy9 小时前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志9 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble10 小时前
c端系统,其实更像一个信息展示平台
前端
李姆斯11 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜12 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司12 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能
kaiking_g12 小时前
vue项目中,静态导入 vs 动态导入 详解
前端·javascript·vue.js
江华森12 小时前
Web 开发基础:HTTP 与 REST
前端·网络协议·http