手动添加Pinia到Vue项目: 在实际开发中,Pinia配置可在项目创建时自动添加。初次学习从零开始:
1. 用Vite创建空的Vue3项目,命令为npm create vue@latest。
2. 按官方文档将pinia安装到项目中。
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') // 视图的挂载
Pinia的基本语法
counter.js:
import { defineStore } from 'pinia'
import { computed, ref } from 'vue'
// 定义store
// defineStore(仓库的唯一标识, () => { ... })
// 导出函数 以函数的形式调用这个仓库
export const useCounterStore = defineStore('counter', () => {
// 声明数据 原本用 state 改为了 count
const count = ref(100)
// 声明操作数据的方法 action (普通函数)
const addCount = () => count.value++
const subCount = () => count.value--
// 声明基于数据派生的计算属性 getters (computed)
const double = computed(() => count.value * 2)
// 声明数据 state -- msg
const msg = ref('hello pinia')
return {
// 必须暴露出去,才能使用
count,
double,
addCount,
subCount,
msg
}
})
Son1Com.vue:
<script setup>
import { useCounterStore } from '@/store/counter'
const counterStore = useCounterStore()
</script>
<template>
<div>
我是Son1.vue - {{ counterStore.count }} - {{ counterStore.double }}
<button @click="counterStore.addCount">+</button>
</div>
</template>
<style scoped>
</style>
Pinia_action异步写法
App.vue:
<script setup>
import Son1Com from '@/components/Son1Com.vue'
import Son2Com from '@/components/Son2Com.vue'
// 导入函数
import { useCounterStore } from '@/store/counter'
import { useChannelStore } from '@/store/channel'
const counterStore = useCounterStore()
const channelStore = useChannelStore()
</script>
<template>
<div>
<h3>
App.vue根组件
- {{ counterStore.count }}
- {{ counterStore.msg }}
</h3>
<Son1Com></Son1Com>
<Son2Com></Son2Com>
<hr>
<button @click="channelStore.getList">获取频道数据</button>
<ul>
<li v-for="item in channelStore.channelList" :key="item.id">{{ item.name }}</li>
</ul>
</div>
</template>
<style scoped>
</style>
channel.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
}
})
storeToRefs解构数据不会丢失响应式
<script setup>
import { storeToRefs } from 'pinia'
import Son1Com from '@/components/Son1Com.vue'
import Son2Com from '@/components/Son2Com.vue'
// 导入函数
import { useCounterStore } from '@/store/counter'
import { useChannelStore } from '@/store/channel'
const counterStore = useCounterStore()
const channelStore = useChannelStore()
// 直接解构,不处理,数据会丢失响应式
// const { count, msg } = (counterStore)
// 使用storeToRefs数据不会丢失响应式
const { count, msg } = storeToRefs(counterStore)
// 方法可以直接被结构
const { getList } = channelStore
</script>
<template>
<div>
<h3>
App.vue根组件
- {{ count }}
- {{ msg }}
</h3>
<Son1Com></Son1Com>
<Son2Com></Son2Com>
<hr>
<button @click="getList">获取频道数据</button>
<ul>
<li v-for="item in channelStore.channelList" :key="item.id">{{ item.name }}</li>
</ul>
</div>
</template>
<style scoped>
</style>
Pinia持久化(本地化存储)
main.js:
import { createApp } from 'vue'
import { createPinia } from 'pinia'
// 1.装包:
// npm i pinia-plugin-persistedstate
// 2.导入持久化插件
import Persist from 'pinia-plugin-persistedstate'
import App from './App.vue'
const pinia = createPinia() // 创建Pinia实例
const app = createApp(App) // 创建根实例
// 2.
app.use(pinia.use(Persist)) // pinia插件的安装配置
app.mount('#app') // 视图的挂载
counter.js:
import { defineStore } from 'pinia'
import { computed, ref } from 'vue'
// 定义store
// defineStore(仓库的唯一标识, () => { ... })
// 导出函数 以函数的形式调用这个仓库
export const useCounterStore = defineStore('counter', () => {
// 声明数据 原本用 state 改为了 count
const count = ref(100)
// 声明操作数据的方法 action (普通函数)
const addCount = () => count.value++
const subCount = () => count.value--
// 声明基于数据派生的计算属性 getters (computed)
const double = computed(() => count.value * 2)
// 声明数据 state -- msg
const msg = ref('hello pinia')
return {
// 必须暴露出去,才能使用
count,
double,
addCount,
subCount,
msg
}
}, {
// 3.
// store.$id 是本地存储的默认 key ,这里是counter
// persist: true // 开启当前模块的持久化
// 可修改key(本地存储的唯一标识)
// pick可指定需要本地化存储的数据
persist: {
key: 'hm-counter',
pick: ['count']
}
})
pnpm 包管理器 - 创建项目:
优势:比同类工具快 2 倍左右、节省磁盘空间等(网址:pnpm - 速度快、节省磁盘空间的软件包管理器 | pnpm中文文档 | pnpm中文网)
安装方式:npm install -g pnpm
创建项目:pnpm create vue
装包:pnpm install
运行:pnpm dev
eslint.config.js:
import js from '@eslint/js'
import pluginVue from 'eslint-plugin-vue'
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'
import eslintPluginPrettier from 'eslint-plugin-prettier/recommended'
export default [
{
name: 'app/files-to-lint',
files: ['**/*.{js,mjs,jsx,vue}'],
},
{
name: 'app/files-to-ignore',
ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**'],
},
js.configs.recommended,
...pluginVue.configs['flat/essential'],
skipFormatting,
eslintPluginPrettier,
{
rules: {
// prettier专注于代码的美观度(格式化工具)
// 前置:
// 1. 禁用格式化插件 prettier format on save 关闭
// 2. 安装Eslint插件,并配置保存时自动修复
'prettier/prettier': [
'warn',
{
singleQuote: true, // 单引号
semi: false, // 无分号
printWidth: 80, // 每行宽度至多80字符
trailingComma: 'none', // 不加对象|数组最后逗号
endOfLine: 'auto' // 换行符号不限制(win mac 不一致)
}
],
// EsLint关注于规范,如果不符合规范,报错
'vue/multi-word-component-names': [
'warn',
{
ignores: ['index'] // vue组件名称多单词组成(忽略index.vue)
}
],
'vue/no-setup-props-destructure': ['off'], // 关闭props解构的校验(props解构丢失响应式)
// 添加未定义变量错误提示,create-vue@3.6.3 关闭,这里加上是为了支持下一个章节演示。
'no-undef': 'error'
}
}
]
提交前做代码检查:
1. 初始化git仓库,执行git init即可。
2. 初始化husky工具配置,执行pnpm dlx husky-init && pnpm install即可。 https://typicode.github.io/husky/
3. 修改.husky/pre-commit文件。
暂存区eslint校验:
1. 安装lint - staged包:pnpm i lint - staged -D。
2. 在package.json配置lint - staged命令。
3. 修改.husky/pre - commit文件。
目录调整: 默认生成目录结构不满足开发需求,需做自定义改动,主要工作如下:
1. 删除一些初始化的默认文件。
2. 修改剩余代码内容。
3. 新增调整所需的目录结构。
4. 拷贝全局样式和图片,安装预处理 器支持。
路由初始化:
1. 创建路由实例由createRouter实现。
2. 路由模式:
- history模式使用createWebHistory()。
- hash模式使用createWebHashHistory()。
- 参数是基础路径,默认/。
按需引入Element Plus:
1. 安装:pnpm add element - plus。
pnpm add -D unplugin-vue-components unplugin-auto-import
2. 配置按需导入,官方文档:https://element - plus.org/zh - CN/guide/quickstart.html。
3. 直接使用组件。
Pinia构建用户仓库和持久化流程:
状态管理(Pinia)→用户仓库(User)→持久化(pinia - plugin - persistedstate)→统一管理
数据交互 - 请求工具设计
axios配置:
- 创建axios实例:设置基准地址、超时时间。
- 请求拦截器:携带token。
- 响应拦截器:业务失败处理,摘取核心响应数据,401处理。
import axios from 'axios'
import { useUserStore } from '@/stores'
import { ElMessage } from 'element - plus'
import router from '@/router'
const baseURL = 'http://big - event - vue - api.itheima.net'
const instance = axios.create({
// TODO 1. 基础地址,超时时间
baseURL,
timeout: 10000
})
// 请求拦截器
instance.interceptors.request.use(
(config) => {
// TODO 2. 携带token
const useStore = useUserStore()
if (useStore.token) {
config.headers.Authorization = useStore.token
}
return config
},
(err) => Promise.reject(err)
)
// 响应拦截器
instance.interceptors.response.use(
(res) => {
// TODO 4. 摘取核心响应数据
if (res.data.code === 0) {
return res
}
// TODO 3. 处理业务失败
// 处理业务失败,给错误提示,抛出错误
ElMessage.error(res.data.message || '服务异常')
return Promise.reject(res.data)
},
(err) => {
// TODO 5. 处理401错误
// 错误的特殊情况 => 401 权限不足 或 token 过期 => 拦截到登录
if (err.response?.status === 401) {
router.push('/login')
}
ElMessage.error(err.response.data.message || '服务异常')
return Promise.reject(err)
}
)
export default instance
export { baseURL }
路由的设计和配置
import { createRouter, createWebHistory } from 'vue-router'
// createRouter 创建路由实例
// 配置 history 模式
// 1. history模式:createWebHistory 地址栏不带 #
// 2. hash模式:createWebHashHistory 地址栏带 #
const router = createRouter({
// vite 中的环境变量 import.meta.env.BASE_URL
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/login', component: () => import('@/views/login/LoginPage.vue') // 登录页
},
{
path: '/',
component: () => import('@/views/layout/LayoutContainer.vue'),
redirect: '/article/manage',
children: [
{
path: '/article/manage',
component: () => import('views/article/ArticleManage.vue')
},
{
path: '/article/channel',
component: () => import('@/views/article/ArticleChannel.vue')
},
{
path: '/user/profile',
component: () => import('@/views/user/UserProfile.vue')
},
{
path: '/user/avatar',
component: () => import('@/views/user/UserAvatar.vue')
},
{
path: '/user/password',
component: () => import('@/views/user/UserPassword.vue')
}
]
}
]
})
export default router
登录注册页面:使用element - plus表单&表单校验 功能需求说明:
1. 注册登录静态结构&基本切换
2. 注册功能(校验+注册)
3. 登录功能(校验+登录+存token)
首页layout架子:使用element - plus菜单组件 功能需求说明:
1. 基本架子拆解(菜单组件的使用)
2. 登录访问拦截
3. 用户基本信息获取&渲染
4. 退出功能(element - plus确认框)