create-vue 与 Pinia 上手试玩

1,528 阅读6分钟

导言

不得不说前端是真的卷,Vue-cli 的核心开发者这又出了一个新的 cli,create-vue,基于 Vite2 和 Vue3 的脚手架工具。目前虽然还只是在开发中,很多功能都不太完善,但可以想象到未来 create-vue 大概率会逐渐替代掉 vue-cli 的...

一、create-vue

1. 安装

安装很简单,只需要一行 npm 语句,不过需要注意下,create-vue 只能安装在非中文路径、中文名称的文件夹下。

npm init vue@next

一些配置:

  • 项目名称
  • 是否安装 TS
  • 是否安装 JSX
  • 是否安装 router
  • 是否安装 vuex
  • 是否安装 Cypress 测试

image.png
文件目录配置也没有太大区别
image.png
进入文件夹后首先安装配置文件

npm install

运行

npm run dev

首页:
image.png

2. 简单体验

其实使用上与 Vue-cli+Vue3 没区别,只是这个脚手架组件页面的 template、script、style 三者标签顺序有改变。
App.vue
image.png
嵌套路由
image.png
页面展示
image.png

二、Pinia

官网:

pinia.esm.dev/

Pinia 是 Vuex 团队成员新开发的一个 Vue 的状态管理方案。
在 Pinia 中减去了 mutations 以及 module 两个概念,转而使用 actions 代替 mutations 完成同异步方法。
同时 Pinia 中的每个 store 都拥有自己的 id,是互相独立的状态库,且在 store 中可以通过 id 直接导入其它的 store 使用。
其优点:

  • 完整的 TypeScript 支持。
  • 及其轻巧(体积大约 1kb)
  • Store 中的 Actions 配置项既可以直行同步方法也可以支持异步方法。
  • 模块不需要嵌套,可以声明多个 Store。
  • 支持 Vue DevTools、SSR 和 Webpack 代码拆分。

**Tips:**Pinia 不是用来代替 Vuex 的,它们的应用场景不同。

Pinia 该什么时候使用?

  1. Pinia 是轻量级的,体积小,更加适合中小型项目,如果是大型项目还是更加适合 Vuex。
  2. Pinia 中无论同步还是异步改变 state,都用 Actions,如果在 Vuex 中分辨不清 mutations 和 Actions 的概念可以使用 Pinia。
  3. Pinia 中完整支持 TypeScript,不会给 Vuex 添加 TypeScript,就用 Pinia。

1. 安装和配置 Pinia

  1. 安装:

npm i pinia@next

  1. 在入口文件中挂载 Pinia
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
// 引入 Pinia
import { createPinia } from 'Pinia'

const app = createApp(App)

// 使用 Pinia 
app.use(createPinia())
app.use(router)

app.mount('#app')
  1. 在 src/stores/index.js 文件夹中声明 store。

Pinia 通过 defineStore 定义 store,这个 API 拥有两个参数,第一个参数是它的唯一性 id,通过这个唯一性 id,我们可以在其它仓库去引用它。
第二个参数就是常规的 store、getters、actions 可选项。

defineStore('id', {options})

// 引入 Pinia 定义
import { defineStore } from 'pinia'
// 定义 
export const useUserStore = defineStore('user', {
    state: () => {
        return {
            name: 'achens'
        }
    }
})
  1. 在组件页面引入并使用
<script setup>
// 引入 useUserStore
import { useUserStore } from './stores';
// 使用 useUserStore
const store = useUserStore()
// 输出:achens
console.log(store.name)
</script>

2. state 与 getters 的使用

1.使用 storeToRefs 引入 state

使用 Pinia 中的 storeToRefs 去解构引入的 store。
storeToRefs 会将 store 中的 state、getters、actions 转换为 ref 响应式对象。

// 引入 storeToRefs
import { storeToRefs } from 'pinia'
// 引入 useUserStore
import { useUserStore } from './stores';
// 使用 storeToRefs 解构式拿出 useUserStore 中的 state
const { name } = storeToRefs(useUserStore())
// 输出: ref 对象
console.log(name)
// 输出:张三
console.log(name.value)

将其放到页面中也是直接使用

<template>
  <h1>Hello achens!</h1>
  <!-- <p>直接使用 store:{{ store.name }}</p> -->
  <p>使用 storeToRefs:{{ name }}</p>
  <router-link to="/home">首页</router-link>
  <router-link to="/about">关于页</router-link>
  <router-view></router-view>
</template>

页面呈现
image.png

2. Pinia 中的 Getters

1. 简单使用 Getters

定义 getters 与 Vuex 没有什么太大的区别。

import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
    state: () => {
        return {
            name: '张三',
            count: 5
        }
    },
    getters:{
        // 以箭头函数的方式声明一个 getters,使 state.count 的值永远乘以2。
        // getters 的第一个参数永远是 state,这点与 Vuex 相比没有区别。
        doubleCount: state => state.count * 2
    }
})
<template>
  <h1>Hello achens!</h1>
  <!-- <p>直接使用 store:{{ store.name }}</p> -->
  <p>使用 storeToRefs:{{ name }}</p>
	// 使用 getters
  <p>{{ count }} -- {{ doubleCount }}</p>
  <router-link to="/home">首页</router-link>
  <router-link to="/about">关于页</router-link>
  <router-view></router-view>
</template>

页面呈现
image.png

2. 访问自己 store 中的其它 Getters

如果想要在 getters 中互相访问,就必须得将箭头函数改为普通函数的形式,在通过 this 调用相同 store 中的其它 getters。

import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
    state: () => {
        return {
            name: '张三',
            count: 5
        }
    },
    getters:{
      doubleCount(state){
          return state.count * 2
      },
      // 在 getters 中使用其它 getters
      doubleCountPlus(state){
          return this.doubleCount + 1
		}
})
<template>
  <h1>Hello achens!</h1>
  <!-- <p>直接使用 store:{{ store.name }}</p> -->
  <p>使用 storeToRefs:{{ name }}</p>
  <p>{{ count }} -- {{ doubleCount }} -- {{ doubleCountPlus }}</p>
  <router-link to="/home">首页</router-link>
  <router-link to="/about">关于页</router-link>
  <router-view></router-view>
</template>

页面呈现
image.png

3. 给 Getters 传递参数

想要给 getters 传参,稍微有那么点麻烦,如果传了参数,则必须在其内部用箭头函数接收并使用它,最后将其返回出去。

// ...多余代码不粘贴了
doubleCountPlus(state, a){
  	// 使用箭头函数拿到这个参数并在使用完后返回
    return a => {
        return this.doubleCount + a
    }
}
<!-- 多余代码不粘贴了 -->
<p>{{ count }} -- {{ doubleCount }} -- {{ doubleCountPlus(5) }}</p>

页面呈现
image.png

4. 访问其它 store 中的 Getters

在 src/store 下新建 default.js 文件夹声明一个新的 store。

import { defineStore } from 'pinia'
export const useDefaultStore = defineStore('default', {
    state: () => {
        return {
            time: 2
        }
    },
    getters:{
        calcTime(state){
            return state.time + 2
        }
    },

})

在 index.js 中引用 default.js 中的 useDefaultStore。

import { defineStore } from 'pinia'
// 引入同文件夹下的 useDefaultStore
import { useDefaultStore } from './default'

export const useUserStore = defineStore('user', {
    state: () => {
        return {
            name: '张三',
            count: 5
        }
    },
    getters:{
        doubleCount(state){
            return state.count * 2
        },
        doubleCountPlus(state, a){
            return a => {
                return this.doubleCount + a
            }
        },
        addDefault(state){
            // 拿到 useDefaultStore
            const defaultStore = useDefaultStore()
            return state.count + defaultStore.calcTime
        }
    }
})
<p>{{ count }} -- {{ doubleCount }} -- {{ doubleCountPlus(5) }}</p>
<p>{{ addDefault }}</p>

页面呈现:
image.png

3. 使用 actions 处理同异步任务

1. 处理同步任务

在 actions 中如果要修改 state 中的值的话,得使用 this.xxx 来获取 state 中的数据并进行修改。

// ... 其它代码省略
actions:{
    addCount(num){
        this.count += num
    }
}

而且不能使用 storeToRefs 引用 actions 方法。

// 在 useUserStore 中拿出方法
const { addCount } = useUserStore()

页面中也如正常方式一样使用就可以。

<p>{{ count }}</p>
<button @click="addCount(2)">点击</button>

每次点击 count 加 2.
image.png

2. 处理异步任务

添加定时器设置为异步任务也没有任何问题。

actions:{
    addCount(num){
        setTimeout(() => {
            this.count += num
        }, 1000)
    }
}

现在页面会在延迟一秒再去更新数据。
image.png

4. 品牌管理案例

这里只提供思路。

1. 渲染数据

v-for 渲染使用 storeToRefs 结构出来的 state(brandList)。

2. 添加数据

v-model 双向绑定 state 中的数据对象(brand)。 v-on 绑定 actions 点击事件 addBrand(),将 brand 值加入(使用 push) brandList 中。并清空 brand 的值。

3. 删除数据

v-on 绑定 actions 点击事件 delBrand(id),并在点击时将本行 brand 的 id 传入。然后通过 id 删除(filter)指定 brand。 this.brandList = this.brandList.filter(item => itme.id ! == id)

  • id 不等于就返回
  • id 等于就不返回

4. 搜索数据

在 state 中添加一个 keywords 搜索字段,并使用 v-model 与其对应的 input 进行双向绑定。 在之后定义一个 searchResult 计算属性,并对 brandList 中的每一项品牌名称都进行一遍过滤,返回包好关键字的每一项。 state.brandList.filter(item => item.name.includes(state.keywords)) 最后将 v-for 中的 brandLIst 替换成 searchResult。

三、结语

总的来说 create-vue 与 Vue-cli 使用并无大异,只是说一个是用 Webpack5 作为打包工具,一个则是使用 Vite2 打包工具。且 Vue-cli 可选 Vue2、3 的版本,而 Create-vue 则是默认 Vite2 + Vue3 +

四、坑

1. 坑一,create-vue 安装路径不能有中文。

2. 坑二,项目配置名称中不能带加号

如图必须是:create-vue-pinia。
而不能是:create-vue+pinia。
image.png
否则就会报错:
image.png


原文地址:www.yuque.com/docs/share/…