【前端Vue】Vue3+Pinia小兔鲜电商项目第2篇:什么是pinia,1. 创建空Vue项目【附代码文档】

  • 【前端Vue】Vue3+Pinia小兔鲜电商项目第2篇:什么是pinia,1. 创建空Vue项目【附代码文档】已关闭评论
  • 115 次浏览
  • A+
所属分类:Web前端
摘要

Pinia 是 Vue 的专属状态管理库,可以实现跨组件或页面共享状态,是 vuex 状态管理工具的替代品,和 Vuex相比,具备以下优势

【前端Vue】Vue3+Pinia小兔鲜电商项目第2篇:什么是pinia,1. 创建空Vue项目【附代码文档】

全套笔记资料代码移步: 前往gitee仓库查看

感兴趣的小伙伴可以自取哦,欢迎大家点赞转发~


全套教程部分目录:

【前端Vue】Vue3+Pinia小兔鲜电商项目第2篇:什么是pinia,1. 创建空Vue项目【附代码文档】

【前端Vue】Vue3+Pinia小兔鲜电商项目第2篇:什么是pinia,1. 创建空Vue项目【附代码文档】


部分文件图片:

【前端Vue】Vue3+Pinia小兔鲜电商项目第2篇:什么是pinia,1. 创建空Vue项目【附代码文档】

什么是pinia

Pinia 是 Vue 的专属状态管理库,可以实现跨组件或页面共享状态,是 vuex 状态管理工具的替代品,和 Vuex相比,具备以下优势

  1. 提供更加简单的API (去掉了 mutation )
  2. 提供符合组合式API风格的API (和 Vue3 新语法统一)
  3. 去掉了modules的概念,每一个store都是一个独立的模块
  4. 搭配 TypeScript 一起使用提供可靠的类型推断

创建空Vue项目并安装Pinia

1. 创建空Vue项目

npm init vue@latest 

2. 安装Pinia并注册

npm i pinia 
 import { createPinia } from 'pinia'  const app = createApp(App) // 以插件的形式注册 app.use(createPinia()) app.use(router) app.mount('#app') 

实现counter

核心步骤:

  1. 定义store
  2. 组件使用store

1- 定义store

import { defineStore } from 'pinia' import { ref } from 'vue'  export const useCounterStore = defineStore('counter', ()=>{   // 数据 (state)   const count = ref(0)    // 修改数据的方法 (action)   const increment = ()=>{     count.value++   }    // 以对象形式返回   return {     count,     increment   } })  

2- 组件使用store

<script setup>   // 1. 导入use方法 	import { useCounterStore } from '@/stores/counter'   // 2. 执行方法得到store store里有数据和方法   const counterStore = useCounterStore() </script>  <template> 	<button @click="counterStore.increment">     {{ counterStore.count }}   </button> </template> 

实现getters

getters直接使用计算属性即可实现

// 数据(state) const count = ref(0) // getter (computed) const doubleCount = computed(() => count.value * 2) 

异步action

思想:action函数既支持同步也支持异步,和在组件中发送网络请求写法保持一致
步骤:

  1. store中定义action
  2. 组件中触发action

1- store中定义action

const API_URL = '  export const useCounterStore = defineStore('counter', ()=>{   // 数据   const list = ref([])   // 异步action   const loadList = async ()=>{     const res = await axios.get(API_URL)     list.value = res.data.data.channels   }      return {     list,     loadList   } }) 

2- 组件中调用action

<script setup> 	import { useCounterStore } from '@/stores/counter'   const counterStore = useCounterStore()   // 调用异步action   counterStore.loadList() </script>  <template> 	<ul>     <li v-for="item in counterStore.list" :key="item.id">{{ item.name }}</li>   </ul> </template> 

storeToRefs保持响应式解构

直接基于store进行解构赋值,响应式数据(state和getter)会丢失响应式特性,使用storeToRefs辅助保持响应式

<script setup>   import { storeToRefs } from 'pinia' 	import { useCounterStore } from '@/stores/counter'   const counterStore = useCounterStore()   // 使用它storeToRefs包裹之后解构保持响应式   const { count } = storeToRefs(counterStore)    const { increment } = counterStore    </script>  <template> 	<button @click="increment">     {{ count }}   </button> </template> 

创建项目并整理目录

npm init vue@latest 

![image.png](

jsconfig.json配置别名路径

配置别名路径可以在写代码时联想提示路径

{   "compilerOptions" : {     "baseUrl" : "./",     "paths" : {       "@/*":["src/*"]     }   } } 

elementPlus引入

1. 安装elementPlus和自动导入插件

npm i elementPlus npm install -D unplugin-vue-components unplugin-auto-import 

2. 配置自动按需导入

// 引入插件 import AutoImport from 'unplugin-auto-import/vite' import Components from 'unplugin-vue-components/vite' import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'   export default defineConfig({   plugins: [     // 配置插件     AutoImport({       resolvers: [ElementPlusResolver()],     }),     Components({       resolvers: [ElementPlusResolver()],     }),   ] }) 

3. 测试组件

<template>   <el-button type="primary">i am button</el-button> </template> 

定制elementPlus主题

1. 安装sass

基于vite的项目默认不支持css预处理器,需要开发者单独安装

npm i sass -D 

2. 准备定制化的样式文件

/* 只需要重写你需要的即可 */ @forward 'element-plus/theme-chalk/src/common/var.scss' with (   $colors: (     'primary': (       // 主色       'base': #27ba9b,     ),     'success': (       // 成功色       'base': #1dc779,     ),     'warning': (       // 警告色       'base': #ffb302,     ),     'danger': (       // 危险色       'base': #e26237,     ),     'error': (       // 错误色       'base': #cf4444,     ),   ) ) 

3. 自动导入配置

这里自动导入需要深入到elementPlus的组件中,按照官方的配置文档来

  1. 自动导入定制化样式文件进行样式覆盖
  2. 按需定制主题配置 (需要安装 unplugin-element-plus)
import { fileURLToPath, URL } from 'node:url' import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import AutoImport from 'unplugin-auto-import/vite' import Components from 'unplugin-vue-components/vite' import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' // 导入对应包 import ElementPlus from 'unplugin-element-plus/vite' export default defineConfig({   plugins: [     vue(),     AutoImport({       resolvers: [ElementPlusResolver()],     }),     Components({       resolvers: [ElementPlusResolver()],     }),     // 按需定制主题配置     ElementPlus({       useSource: true,     }),   ],   resolve: {     alias: {       '@': fileURLToPath(new URL('./src', import.meta.url))     }   },   css: {     preprocessorOptions: {       scss: {         // 自动导入定制化样式文件进行样式覆盖         additionalData: `           @use "@/styles/element/index.scss" as *;         `,       }     }   } }) 

axios安装并简单封装

1. 安装axios

npm i axios 

2. 基础配置

官方文档地址:[
基础配置通常包括:

  1. 实例化 - baseURL + timeout
  2. 拦截器 - 携带token 401拦截等
import axios from 'axios'  // 创建axios实例 const http = axios.create({   baseURL: '   timeout: 5000 })  // axios请求拦截器 instance.interceptors.request.use(config => {   return config }, e => Promise.reject(e))  // axios响应式拦截器 instance.interceptors.response.use(res => res.data, e => {   return Promise.reject(e) })   export default http 

3. 封装请求函数并测试

import http from '@/utils/http'  export function getCategoryAPI () {   return http({     url: 'home/category/head'   }) } 

路由整体设计

路由设计原则:找页面的切换方式,如果是整体切换,则为一级路由,如果是在一级路由的内部进行的内容切换,则为二级路由

<template>   我是登录页 </template> 
<template>   我是首页 </template> 
<template>   我是home </template> 
<template>   我是分类 </template> 
// createRouter:创建router实例对象 // createWebHistory:创建history模式的路由  import { createRouter, createWebHistory } from 'vue-router' import Login from '@/views/Login/index.vue' import Layout from '@/views/Layout/index.vue' import Home from '@/views/Home/index.vue' import Category from '@/views/Category/index.vue'  const router = createRouter({   history: createWebHistory(import.meta.env.BASE_URL),   // path和component对应关系的位置   routes: [     {       path: '/',       component: Layout,       children: [         {           path: '',           component: Home         },         {           path: 'category',           component: Category         }       ]     },     {       path: '/login',       component: Login     }   ] })  export default router 

静态资源引入和Error Lens安装

1. 静态资源引入

  1. 图片资源 - 把 images 文件夹放到 assets 目录下
  2. 样式资源 - 把 common.scss 文件放到 styles 目录下

2. Error Lens插件安装

![image.png](

scss变量自动导入

$xtxColor: #27ba9b; $helpColor: #e26237; $sucColor: #1dc779; $warnColor: #ffb302; $priceColor: #cf4444; 
css: {     preprocessorOptions: {       scss: {         // 自动导入scss文件         additionalData: `           @use "@/styles/element/index.scss" as *;           @use "@/styles/var.scss" as *;         `,       }     } } 

组件结构快速搭建

<script setup>  </script>  <template>   <nav class="app-topnav">     <div class="container">       <ul>         <template v-if="true">           <li><a href="javascript:;""><i class="iconfont icon-user"></i>周杰伦</a></li>           <li>             <el-popconfirm title="确认退出吗?" confirm-button-text="确认" cancel-button-text="取消">               <template #reference>                 <a href="javascript:;">退出登录</a>               </template>             </el-popconfirm>           </li>           <li><a href="javascript:;">我的订单</a></li>           <li><a href="javascript:;">会员中心</a></li>         </template>         <template v-else>           <li><a href="javascript:;">请先登录</a></li>           <li><a href="javascript:;">帮助中心</a></li>           <li><a href="javascript:;">关于我们</a></li>         </template>       </ul>     </div>   </nav> </template>   <style scoped lang="scss"> .app-topnav {   background: #333;   ul {     display: flex;     height: 53px;     justify-content: flex-end;     align-items: center;     li {       a {         padding: 0 15px;         color: #cdcdcd;         line-height: 1;         display: inline-block;          i {           font-size: 14px;           margin-right: 2px;         }          &:hover {           color: $xtxColor;         }       }        ~li {         a {           border-left: 2px solid #666;         }       }     }   } } </style> 
<script setup>  </script>  <template>   <header class='app-header'>     <div class="container">       <h1 class="logo">         <RouterLink to="/">小兔鲜</RouterLink>       </h1>       <ul class="app-header-nav">         <li class="home">           <RouterLink to="/">首页</RouterLink>         </li>         <li> <RouterLink to="/">居家</RouterLink> </li>         <li> <RouterLink to="/">美食</RouterLink> </li>         <li> <RouterLink to="/">服饰</RouterLink> </li>       </ul>       <div class="search">         <i class="iconfont icon-search"></i>         <input type="text" placeholder="搜一搜">       </div>       <!-- 头部购物车 -->            </div>   </header> </template>   <style scoped lang='scss'> .app-header {   background: #fff;    .container {     display: flex;     align-items: center;   }    .logo {     width: 200px;      a {       display: block;       height: 132px;       width: 100%;       text-indent: -9999px;       background: url('@/assets/images/logo.png') no-repeat center 18px / contain;     }   }    .app-header-nav {     width: 820px;     display: flex;     padding-left: 40px;     position: relative;     z-index: 998;        li {       margin-right: 40px;       width: 38px;       text-align: center;          a {         font-size: 16px;         line-height: 32px;         height: 32px;         display: inline-block;            &:hover {           color: $xtxColor;           border-bottom: 1px solid $xtxColor;         }       }          .active {         color: $xtxColor;         border-bottom: 1px solid $xtxColor;       }     }   }    .search {     width: 170px;     height: 32px;     position: relative;     border-bottom: 1px solid #e7e7e7;     line-height: 32px;      .icon-search {       font-size: 18px;       margin-left: 5px;     }      input {       width: 140px;       padding-left: 5px;       color: #666;     }   }    .cart {     width: 50px;      .curr {       height: 32px;       line-height: 32px;       text-align: center;       position: relative;       display: block;        .icon-cart {         font-size: 22px;       }        em {         font-style: normal;         position: absolute;         right: 0;         top: 0;         padding: 1px 6px;         line-height: 1;         background: $helpColor;         color: #fff;         font-size: 12px;         border-radius: 10px;         font-family: Arial;       }     }   } } </style> 
<template>   <footer class="app_footer">     <!-- 联系我们 -->     <div class="contact">       <div class="container">         <dl>           <dt>客户服务</dt>           <dd><i class="iconfont icon-kefu"></i> 在线客服</dd>           <dd><i class="iconfont icon-question"></i> 问题反馈</dd>         </dl>         <dl>           <dt>关注我们</dt>           <dd><i class="iconfont icon-weixin"></i>