vue3 pinia

197 阅读2分钟

Pinia --笔记

作用:VueX的升级版,对接TS使用

一、安装依赖

yarn add pinia@2.0.23

二、main.ts

import { createApp } from 'vue'
import './styles/base.css'
import './styles/index.css'
import App from './App.vue'
import { createPinia } from 'pinia'

createApp(App).use(createPinia()).mount('#app')

三、data.json & request.ts & 接口类型

模拟数据data.json

{
  "todos": [
    {
      "id": 0,
      "name": "吃饭",
      "done": false
    },
    {
      "id": 1,
      "name": "睡觉",
      "done": true
    },
    {
      "id": 2,
      "name": "写代码",
      "done": false
    }
  ]
}

封装axios请求

  • 先安装依赖yarn add axios
import axios from 'axios'
const request = axios.create({
  baseURL: 'http://localhost:8888/todos',
})

export default request
  • 注:里面的baseURL可以根据开发环境 和 生产环境更换,看情况来编写

.env.development

NODE_ENV = "development"
VUE_APP_BASE_URL = "http://192.168.3.161:8009"
VUE_APP_CDNURL = "http://192.168.3.230:7080"
VUE_APP_GEOURL = "http://192.168.3.161:8010"

.env.production

NODE_ENV = "production"
VUE_APP_BASE_URL = "http://124.70.78.39:8009"
VUE_APP_CDNURL = "http://124.70.78.39"
VUE_APP_GEOURL = "http://124.70.78.39:8010"

api封装

export default class AxiosInterceptors {
	localhost = process.env.VUE_APP_BASE_URL
    constructor (url, params) {
        this.url = url
        this.params = params
  	}
    post () {
        return axios({
          method: 'post',
          baseURL: this.localhost,
          url: this.url,
          data: qs.stringify(this.params),
          withCredentials: true,
          timeout: 5000
        })
          .then(res => {
            if (res.code === 200) {
              return this.successfun(res)
            } else {
              that.$message('出错啦!')
              throw new Error(res.msg)
            }
          }, err => {
            that.$message('出错啦!')
            throw new Error(err.msg)
          })
  	}
}

接口类型

export interface ITodoItem {
  id: number
  name: string
  done: boolean
}

四、store

根store文件

//根据不同的需求 写 不同的模块导入
import useMainStore from './modules/main'
import useFooterStore from './modules/footer'
/*
* o = {}是一个对象类型,然后通过:{} 来 指定该对象的内部键值对类型
* @[key: string] 表示索引键可有多个,每个索引键都是string类型
* @any 表示每个索引键对应的值类型可以是任何类型
*/
const o: { [key: string]: any } = {}
export default function useStore() {
  if (!o.main) o.main = useMainStore()
  if (!o.footer) o.footer = useFooterStore()
  return o
}

a模块

import { defineStore } from 'pinia'
import { ITodoItem } from '../../types/data'   //引用接口类型
import request from '../../utils/request'
import footerStore from './footer'

const useMainStore = defineStore('main', {
  state: () => {
    return {
      list: [] as ITodoItem[],
    }
  },
  actions: {
    async getList() {
      const { data } = await request.get<ITodoItem[]>('/')
      this.list = data
    },
    async deleteTodo(id: number) {
      await request.delete(`/${id}`)
      this.getList()
    },
    async updateTodo(id: number, key: string, value: string | boolean) {
      await request.patch(`/${id}`, {
        [key]: value,
      })
      this.getList()
    },
    async addTodo(name: string) {
      await request.post('/', {
        name,
        done: false,
      })
      this.getList()
    },
    async updateAllDone(done: boolean) {
      //map 映射新数组
      const arrPromise = this.list.map((item) => {
        return request.patch(`/${item.id}`, {
          done,
        })
      })
      await Promise.all(arrPromise)
      this.getList()
    },
    // #2
    async clearCompleted() {
      const arrPromise = this.completed.map((item) => {
        return request.delete(`/${item.id}`)
      })
      await Promise.all(arrPromise)
      this.getList()
    },
  },
  getters: {
    mainRadioStatus(): boolean {
      return this.list.every((item) => item.done)
    },
    // #1
    completed(): ITodoItem[] {
      return this.list.filter((item) => item.done)
    },
    unCompleted(): ITodoItem[] {
      return this.list.filter((item) => !item.done)
    },
    renderList(): ITodoItem[] {
      const footer = footerStore()
      if (footer.active === 'Active') {
        return this.list.filter((item) => !item.done)
      } else if (footer.active === 'Completed') {
        return this.list.filter((item) => item.done)
      }
      return this.list
    },
  },
})

export default useMainStore

b模块

import { defineStore } from 'pinia'

const useFooterStore = defineStore('footer', {
  state: () => {
    return {
      tabs: ['All', 'Active', 'Completed'],
      active: 'All',
    }
  },
  actions: {
    updateActive(active: string) {
      this.active = active
    },
  },
})

export default useFooterStore

五(1)、组件使用

todoItem 是 todoMain的子组件

<script setup lang="ts">
import { ref } from 'vue'
import useStore from '../store'
import { ITodoItem } from '../types/data'
const isEditing = ref(false)
const { main } = useStore()
const { deleteTodo, updateTodo } = main
const handleDelete = (id: number) => deleteTodo(id)
const handleChange = (item: ITodoItem) => updateTodo(item.id, 'done', !item.done)
// #2
const handleDblClick = () => {
  isEditing.value = true
}
defineProps<{
  item: ITodoItem
}>()
</script>
<template>
  <li
    :class="{
      completed: item.done,
      editing: isEditing,
    }"
  >
    <div class="view">
      <input class="toggle" type="checkbox" :checked="item.done" @change="handleChange(item)" />
      <label @dblclick="handleDblClick">{{ item.name }}</label>
      <button class="destroy" @click="handleDelete(item.id)"></button>
    </div>
    <input class="edit" value="Create a TodoMVC template" />
  </li>
</template>

todoMian

<script setup lang="ts">
import TodoItem from './TodoItem.vue'
//pinia部分
import { storeToRefs } from 'pinia'
import useStore from '../store'
const { main } = useStore()
const { getList, updateAllDone } = main
const { mainRadioStatus, renderList } = storeToRefs(main)   //响应式
// #4
const handleChangeAll = (done: boolean) => updateAllDone(done)
getList()
</script>
<template>
  <section class="main">
    <!-- #2 -->
    <input id="toggle-all" class="toggle-all" type="checkbox" :checked="mainRadioStatus" @change="handleChangeAll(!mainRadioStatus)" />
    <label for="toggle-all">Mark all as complete</label>
    <ul class="todo-list">
      <!-- These are here just to show the structure of the list items -->
      <!-- List items should get the class `editing` when editing and `completed` when marked as completed -->
      <TodoItem v-for="item in renderList" :item="item" />
    </ul>
  </section>
</template>

todoFooter

<script setup lang="ts">
import { storeToRefs } from 'pinia'
import useStore from '../store'
const { main, footer } = useStore()
const { clearCompleted } = main
const { updateActive } = footer
const { tabs, active } = storeToRefs(footer)
const { completed, unCompleted, list } = storeToRefs(main)
const initActive = () => {
  const hs = window.location.hash
  // active.value = hs === '#/Active' || hs === '#/Completed' ? hs.slice(2) : 'All'
  const hsRes = hs === '#/Active' || hs === '#/Completed' ? hs.slice(2) : 'All'
  updateActive(hsRes)
}
initActive()
</script>
<template>
  <footer class="footer" v-if="list.length > 0">
    <!-- This should be `0 items left` by default -->
    <span class="todo-count"
      ><strong>{{ unCompleted.length }}</strong> item left</span
    >
    <!-- Remove this if you don't implement routing -->
    <ul class="filters">
      <li v-for="item in tabs" :key="item" @click="updateActive(item)">
        <a
          :class="{
            selected: item === active,
          }"
          :href="`#/${item}`"
          >{{ item }}</a
        >
      </li>
    </ul>
    <!-- Hidden if no completed items are left ↓ -->
    <button v-if="completed.length > 0" class="clear-completed" @click="clearCompleted">Clear completed</button>
  </footer>
</template>

五(2)、组件setup使用Demo2

<template>
  <div class="all">
    <SvgIcon name="github" style="width: 20px;height: 20px;"></SvgIcon>
    <SvgIcon name="twitter" style="width: 20px;height: 20px; margin-left: 15px;"></SvgIcon>
    <SvgIcon name="youtube" style="width: 20px;height: 20px; margin-left: 15px;"></SvgIcon>
    <el-icon :size="20">
      <Edit />
    </el-icon>
    111
    <el-button @click="showList">测试按钮</el-button>
    <div>{{data}}</div>
  </div>
</template>

<script>
import SvgIcon from "./svgIcon/SvgIcon.vue";
import useStore from "../store/index.ts";
import {reactive} from "vue";

export default {
  name: "Home",
  components:{
    SvgIcon
  },
  setup(){
    const data = reactive([])
    async function showList(){
      const { moduleOne } = useStore()
      await moduleOne.getlist().then(function () {
        for(let i in moduleOne.list) {
          data.push(moduleOne.list[i])
        }
      })
    }
    return {
      showList,
      data
    }
  }
}
</script>

<style scoped lang="scss">
.all{
  width: 100%;
  height: 100%;
  background-color: pink;
}
</style>