- A+
所属分类:Web前端
全套笔记资料代码移步: 前往gitee仓库查看
感兴趣的小伙伴可以自取哦,欢迎大家点赞转发~
全套教程部分目录:
部分文件图片:
什么是pinia
Pinia 是 Vue 的专属状态管理库,可以实现跨组件或页面共享状态,是 vuex 状态管理工具的替代品,和 Vuex相比,具备以下优势
- 提供更加简单的API (去掉了 mutation )
- 提供符合组合式API风格的API (和 Vue3 新语法统一)
- 去掉了modules的概念,每一个store都是一个独立的模块
- 搭配 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
核心步骤:
- 定义store
- 组件使用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函数既支持同步也支持异步,和在组件中发送网络请求写法保持一致
步骤:
- store中定义action
- 组件中触发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的组件中,按照官方的配置文档来
- 自动导入定制化样式文件进行样式覆盖
- 按需定制主题配置 (需要安装 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. 基础配置
官方文档地址:[
基础配置通常包括:
- 实例化 - baseURL + timeout
- 拦截器 - 携带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. 静态资源引入
- 图片资源 - 把 images 文件夹放到 assets 目录下
- 样式资源 - 把 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>