长路漫漫,极少成多,静下心来,好好做自己,希望这篇文章会帮助到你。
pnpm介绍&安装
pnpm 本质是一个包管理工具,和npm yarn 没有用法上的区别,主要优势在于 安装包的速度极快·磁盘空间利用效率高.
安装:npm i pnpm -g
| npm命令 | pnpm等效 |
|---|---|
| npm install | pnpm install |
| npm i axios | pnpm add axios |
| npm i webpack -D | pnpm add webpack -D |
| npm run dev | pnpm dev |
项目创建
使用 create-vue 脚手架创建项目
create-vue参考地址:github.com/vuejs/creat…
步骤:
- 执行创建命令
`pnpm create vue
#or
npm init vue@latest
#or
yarn create vue`
- 选择项目依赖内容
✔ Project name: … patients-h5-100
✔ Add TypeScript? … No / `Yes`
✔ Add JSX Support? … `No` / Yes
✔ Add Vue Router for Single Page Application development? … No / `Yes` ✔ Add Pinia for state management? … No / `Yes`
✔ Add Vitest for Unit Testing? … `No` / Yes
✔ Add Cypress for both Unit and End-to-End testing? … `No` / Yes
✔ Add ESLint for code quality? … No / `Yes`
✔ Add Prettier for code formatting? … No / `Yes`
Scaffolding project in /Users/zhousg/Desktop/patient-h5-100...
Done. Now run:
cd patient-h5-100
pnpm install pnpm
lint pnpm dev
vscode插件安装#
安装:项目开发需要的一些插件
必装:
Vue Language Features (Volar)vue3语法支持TypeScript Vue Plugin (Volar)vue3中更好的ts提示Eslint代码风格校验
注意
- vscode 安装了
Prettier插件的可以先禁用,或者关闭保存自动格式化功能,避免和项目的Eslint风格冲突。
可选:
gitLens代码git提交记录提示json2tsjson自动转ts类型Error Lens行内错误提示
代码检查工作流
husky 配置
- 初始化与安装
pnpm dlx husky-init && pnpm install
- 修改 .husky/pre-commit 文件
pnpm lint
lint-staged 配置
- 安装
pnpm i lint-staged -D
- 配置
package.json
{
// ... 省略 ...
"lint-staged": {
"*.{js,ts,vue}": [
"eslint --fix"
]
}
}
{
"scripts": {
// ... 省略 ...
"lint-staged": "lint-staged"
}
}
- 修改 .husky/pre-commit 文件
pnpm lint-staged
项目结构调整
每一个目录结构的作用
./src
├── assets `静态资源,图片...`
├── components `通用组件`
├── composable `组合功能通用函数`
├── icons `svg图标`
├── router `路由`
│ └── index.ts
├── services `接口服务API`
├── stores `状态仓库`
├── styles `样式`
│ └── main.scss
├── types `TS类型`
├── utils `工具函数`
├── views `页面`
├── main.ts `入口文件`
└──App.vue `根组件`
项目使用sass预处理器,安装sass,即可支持scss语法:
pnpm add sass -D
路由代码解析
mport { createRouter, createWebHistory } from 'vue-router'
// createRouter 创建路由实例,===> new VueRouter()
// history 是路由模式,hash模式,history模式
// createWebHistory() 是开启history模块 http://xxx/user
// createWebHashHistory() 是开启hash模式 http://xxx/#/user
// vite 的配置 import.meta.env.BASE_URL 是路由的基准地址,默认是 ’/‘
// https://vitejs.dev/guide/build.html#public-base-path
// 如果将来你部署的域名路径是:http://xxx/my-path/user
// vite.config.ts 添加配置 base: my-path,路由这就会加上 my-path 前缀了
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: []
})
export default router
vant组件库#
实现:完整使用vant组件库
安装:
# Vue 3 项目,安装最新版 Vant
npm i vant
# 通过 yarn 安装
yarn add vant
# 通过 pnpm 安装
pnpm add vant
样式:main.ts
import { createApp } from 'vue'
import App from './App.vue'
import pinia from './stores'
import router from './router'
// 样式全局使用
import 'vant/lib/index.css'
import './styles/main.scss'
const app = createApp(App)
app.use(pinia)
app.use(router)
app.mount('#app')
组件按需使用:App.vue
<script setup lang="ts">
import { Button as VanButton } from 'vant'
</script>
<template>
<van-button>按钮</van-button>
</template>
<style scoped></style>
移动端适配#
实现:使用 vw 完成移动端适配
VantUI文档
npm install postcss-px-to-viewport -D
# or
yarn add -D postcss-px-to-viewport
# or
pnpm add -D postcss-px-to-viewport
配置: postcss.config.js
// eslint-disable-next-line no-undef
module.exports = {
plugins: {
'postcss-px-to-viewport': {
// 设备宽度375计算vw的值
viewportWidth: 375,
},
},
};
css变量主题定制#
实现:使用css变量定制项目主题,和修改vant主题
- 如何定义 css 变量使用 css 变量
:root {
--main: #999;
}
a {
color: var(--main)
}
- 定义项目的颜色风格,覆盖vant的主题色 官方文档
styles/main.scss
:root {
色板
--cp-primary: #16C2A3;
--cp-plain: #EAF8F6;
--cp-orange: #FCA21C;
--cp-text1: #121826;
--cp-text2: #3C3E42;
--cp-text3: #6F6F6F;
--cp-tag: #848484;
--cp-dark: #979797;
--cp-tip: #C3C3C5;
--cp-disable: #D9DBDE;
--cp-line: #EDEDED;
--cp-bg: #F6F7F9;
--cp-price: #EB5757;
// 覆盖vant主体色
--van-primary-color: var(--cp-primary);
}
App.vue
<script setup lang="ts"></script>
<template>
<!-- 验证vant颜色被覆盖 -->
<van-button type="primary">按钮</van-button>
<a href="#">123</a>
</template>
<style scoped lang="scss">
// 使用 css 变量
a {
color: var(--cp-primary);
}
</style>
数据持久化#
使用
pinia-plugin-persistedstate实现pinia仓库状态持久化,且完成测试
- 安装
pnpm i pinia-plugin-persistedstate
# or
npm i pinia-plugin-persistedstate
# or
yarn add pinia-plugin-persistedstate
- 使用
main.ts
import persist from 'pinia-plugin-persistedstate'
const app = createApp(App)
app.use(createPinia().use(persist))
- 配置
stores/user.ts
import type { User } from '@/types/user'
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useUserStore = defineStore(
'cp-user',
() => {
// 用户信息
const user = ref<User>()
// 设置用户,登录后使用
const setUser = (u: User) => {
user.value = u
}
// 清空用户,退出后使用
const delUser = () => {
user.value = undefined
}
return { user, setUser, delUser }
},
{
persist: true
}
)
stores统一导出#
仓库的导出统一从
./stores代码简洁,职能单一,入口唯一
- 抽取pinia实例代码,职能单一
stores/index
import { createPinia } from 'pinia'
import persist from 'pinia-plugin-persistedstate'
// 创建pinia实例
const pinia = createPinia()
// 使用pinia插件
pinia.use(persist)
// 导出pinia实例,给main使用
export default pinia
main.ts
import { createApp } from 'vue'
import App from './App.vue'
import pinia from './stores'
import router from './router'
import './styles/main.scss'
const app = createApp(App)
app.use(pinia)
app.use(router)
app.mount('#app')
- 统一导出,代码简洁,入口唯一
stores/index
export * from './modules/user'
App.vue
-import { useUserStore } from './stores/user'
+import { useUserStore } from './stores'
请求工具函数
拦截器逻辑
实现:token请求头携带,错误响应处理,401错误处理
utils/request.ts
模板代码:
import axios from 'axios'
const instance = axios.create({
// TODO 1. 基础地址,超时时间
})
instance.interceptors.request.use(
(config) => {
// TODO 2. 携带token
return config
},
(err) => Promise.reject(err)
)
instance.interceptors.response.use(
(res) => {
// TODO 3. 处理业务失败
// TODO 4. 摘取核心响应数据
return res
},
(err) => {
// TODO 5. 处理401错误
return Promise.reject(err)
}
)
export default instance
代码实现:
import { useUserStore } from '@/stores'
import router from '@/router'
import axios from 'axios'
import { showToast } from 'vant'
// 1. 新axios实例,基础配置
const instance = axios.create({
baseURL: 'https://consult-api.itheima.net/',
timeout: 10000
})
// 2. 请求拦截器,携带token
instance.interceptors.request.use(
(config) => {
const store = useUserStore()
if (store.user?.token && config.headers) {
config.headers['Authorization'] = `Bearer ${store.user?.token}`
}
return config
},
(err) => Promise.reject(err)
)
// 3. 响应拦截器,剥离无效数据,401拦截
instance.interceptors.response.use(
(res) => {
// 后台约定,响应成功,但是code不是10000,是业务逻辑失败
if (res.data?.code !== 10000) {
showToast(res.data?.message || '业务失败')
return Promise.reject(res.data)
}
// 业务逻辑成功,返回响应数据,作为axios成功的结果
return res.data
},
(err) => {
if (err.response.status === 401) {
// 删除用户信息
const store = useUserStore()
store.delUser()
// 跳转登录,带上接口失效所在页面的地址,登录完成后回跳使用
router.push({
path: '/login',
query: { returnUrl: router.currentRoute.value.fullPath }
})
}
return Promise.reject(err)
}
)
export { baseURL, instance }
工具函数封装
导出一个通用的请求工具函数,支持设置响应数据类型
- 导出一个通用的请求工具函数
import axios, { AxiosError, type Method } from 'axios'
// 4. 请求工具函数
const request = (url: string, method: Method = 'GET', submitData?: object) => {
return instance.request({
url,
method,
[method.toUpperCase() === 'GET' ? 'params' : 'data']: submitData
})
}
- 支持不同接口设不同的响应数据的类型
加上泛型
// 这个需要替换axsio.request默认的响应成功后的结果类型
// 之前是:传 { name: string } 然后res是 res = { data: { name: string } }
// 但现在:在响应拦截器中返回了 res.data 也就是将来响应成功后的结果,和上面的类型一致吗?
// 所以要:request<数据类型,数据类型>() 这样才指定了 res.data 的类型
// 但是呢:后台返回的数据结构相同,所以可以抽取相同的类型
type Data<T> = {
code: number
message: string
data: T
}
// 4. 请求工具函数
const request = <T>(url: string, method: Method = 'get', submitData?: object) => {
return instance.request<T, Data<T>>({
url,
method,
[method.toLowerCase() === 'get' ? 'params' : 'data']: submitData
})
}
组件代码片段
配置:一个vue3页面的基础代码片段
- 打开代码片段设置界面:
- windows:ctrl + shift + p
- mac:cmmmand + shift + p
- 新建全局代码片段文件
- 拷贝一下代码,保存即可,输入vueps
{
"Vue 页面 TS 版": {
"scope": "vue,markdown",
"prefix": "vpt",
"body": [
"<script setup lang="ts"></script>",
"",
"<template>",
" <div class="$1-page">$1</div>",
"</template>",
"",
"<style lang="scss" scoped></style>",
""
],
"description": "Vue 页面 TS 版"
}
}
或者安装:vue-vscode-snippets 插件,快捷键可以看插件文档。
自动按需加载
:实现自动按需加载,和自动导入
手动按需使用组件比较麻烦,需要先导入。配置函数自动按需导入后直接使用即可。
- 安装:
# 通过 npm 安装
npm i unplugin-vue-components -D
# 通过 yarn 安装
yarn add unplugin-vue-components -D
# 通过 pnpm 安装
pnpm add unplugin-vue-components -D
- 配置:
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Components from 'unplugin-vue-components/vite'
import { VantResolver } from 'unplugin-vue-components/resolvers'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
// 解析单文件组件的插件
vue(),
// 自动导入的插件,解析器可以是 vant element and-vue
Components({
dts: false,
// 原因:Toast Confirm 这类组件的样式还是需要单独引入,样式全局引入了,关闭自动引入
resolvers: [VantResolver({ importStyle: false })]
})
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})