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)

相关推荐
萧大侠jdeps2 小时前
Vue 3 与 Tauri 集成开发跨端APP
前端·javascript·vue.js·tauri
JYeontu2 小时前
实现一个动态脱敏指令,输入时候显示真实数据,展示的时候进行脱敏
前端·javascript·vue.js
发呆的薇薇°2 小时前
react里使用Day.js显示时间
前端·javascript·react.js
嘤嘤嘤2 小时前
基于大模型技术构建的 GitHub Assistant
前端·github
KeepCatch2 小时前
CSS 动画与过渡效果
前端
跑跑快跑3 小时前
React vite + less
前端·react.js·less
web136885658713 小时前
ctfshow_web入门_命令执行_web29-web39
前端
GISer_Jing3 小时前
前端面试题合集(一)——HTML/CSS/Javascript/ES6
前端·javascript·html
清岚_lxn3 小时前
es6 字符串每隔几个中间插入一个逗号
前端·javascript·算法
胡西风_foxww3 小时前
【ES6复习笔记】Map(14)
前端·笔记·es6·map