# vue3-options-pet
**Repository Path**: zhao-jingtao-l/vue3-options-haigou
## Basic Information
- **Project Name**: vue3-options-pet
- **Description**: 基于Vue3的选项式api c端 精致宠物商城项目
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2022-12-23
- **Last Updated**: 2025-06-18
## Categories & Tags
**Categories**: Uncategorized
**Tags**: vue3, Vue-pinia, vue-router, Vant, Axios
## README
# Vue3-options-精致宠物 项目细节
## 创建项目
- 打开命令行
- 输入指令
```shell
$ npm init vue@latest
```
## 开启 `defineProps` 设置默认值的语法糖
- 在 `vite.config.ts` 文件中进行修改
```js
export default defineConfig({
plugins: [vue({
reactivityTransform: true
})],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
```
## 下载 `sass` 包
- 如果你需要在项目中使用 `sass` 语法
- 下载 `sass` 包
```shell
$ npm install sass -D
```
## 准备一个全局 `scss` 文件
- 在 `src` 目录下新建 `styles` 文件夹
- 创建 `index.scss` 文件, 用于书写全局 样式
- **在 `main.ts` 内引入**
```js
// 导入全局样式
import '@/styles/index.scss'
```
## 配置 `git` 环境
- 打开命令行, 切换目录到项目根目录
- 输入指令: `$ git init`
- 进行第一次提交
## 配置一个 `UI` 组件库
- 一个移动端 `UI` 组件库 `vant`
- 下载 `vant` 组件库
```shell
$ npm install vant
```
- 挂载到自己的项目内
- `main.ts`
```js
// 导入全局样式
import '@/styles/index.scss'
// 导入 vant 组件库
import vant from 'vant'
// 把 vant 挂载到 app 实例身上
app.use(vant)
```
- 适配一下移动端事件
```shell
$ npm i @vant/touch-emulator -S
```
- `main.ts`
```js
import '@vant/touch-emulator'
```
- 配置 `iOs` 无法识别事件
- 在 `body` 标签上添加 `ontouchstart=""`
- 配置全局主题
- `App.vue`
```html
```
- `config/theme.ts`
```ts
import type { ConfigProviderProps } from 'vant'
export const themeVars: ConfigProviderProps['themeVars'] = {
rateIconFullColor: '#07c160',
sliderBarHeight: '4px',
sliderButtonWidth: '20px',
sliderButtonHeight: '20px',
sliderActiveBackgroundColor: '#07c160',
buttonPrimaryBorderColor: '#07c160',
buttonPrimaryBackgroundColor: 'skyblue',
buttonSuccessBorderColor: 'orange',
buttonSuccessBackgroundColor: 'orange',
}
```
## 开始书写布局结构
- 按照移动端基础布局开始书写
- 顶部固定
- 底部固定
- 中间自适应(溢出滚动)
## 开始书写底部导航条
- 使用 `vant` 组件库内的结构书写
- 配置好路由就可以了
```js
{
// 找不到路由
path: '/not-found',
name: 'not-found',
component: NotFound
},
{
// 匹配任意内容
path: '/:pathMatch(.*)*',
redirect: '/not-found'
}
```
## `not-found` 的时候, 不显示底部导航条
- 只有我配置好的路由才会显示 `底部导航`
- 如果是意外路由, 不需要显示 `底部导航`
+ 方式: 给 `SdyFooter` 加一个 `v-if` 指令
+ 通过该指令控制 `底部导航` 加载不加载
- 问题: `v-if` 的值从哪里来
+ 我们在配置路由的时候, 可以配置一个 `路由元信息`
+ 在配置路由的时候给出的初始信息
```js
{
path: '/xxx',
name: 'xxx',
component: xxx,
meta: { } // 直接在切换到当前路由的时候, 给到当前路由的信息
}
```
- 需求: 有多个路由需要 `底部导航`, 只有一个路由不需要 `底部导航`, 你准备如何书写这个信息
+ 方式1:
- 谁有, 设置一个变量为 true
- 谁没有, 设置一个变量为 false
+ 方式2:(更好)
- 谁有, 设置一个变量为 false
- 谁没有, 设置一个变量为 true
- 如何在当前路由内拿到路由元信息 (meta)
+ `Vue2` : `this.$route.meta`
+ `Vue3` : 我们没有 `this`
- 如何拿到 `$route`
- 可以直接在 `vue-router` 第三方内解构出来一个 `useRoute`
```js
import { useRoute } from 'vue-router'
const $route = useRoute()
```
+ `App.vue`
```html
```
## 顶部导航条的二次封装
- 为了各个组件内导航条不一样
- 我们把 导航条放在了 `container` 组件内部
+ 我们需要在每一个组件内 `导入` / `使用`
+ 想法: 把 `header` 注册为全局组件
```js
// 注册全局组件
app.component('SdyHeader', SdyHeader)
```
- 换一种高级的方式注册组件
```js
app.use(xxxx)
```
+ `app.use` 的原理
- 需要传递的参数是一个 `对象`
- 该 `对象` 内必须要有一个 `install` 方法
- `app.use` 其实就是在执行该 `install` 方法
- 并且把 `app` 实例传递给 `install` 方法
- 自己封装组件插件
- `src/utils/plugins.ts`
```ts
import SdyHeader from '@/views/SdyHeader/index.vue'
import type { App } from 'vue'
export default {
install (app: App) {
app.component('SdyHeader', SdyHeader)
}
}
```
- `main.ts`
```js
// 导入自己的插件库
import Plugin from '@/utils/Plugin'
// 挂载自己的插件
app.use(Plugin)
```
## Teleport
- 作用: 把组件内的一部分 `DOM` 结构拿到外边一个指定位置
- 使用:
```html
```
```html
a
b
```
## 修改 `SdyHeader` 组件的插槽设置
- 改成三个插槽限制
```html
{{ title }}
```
## 书写首页的 顶部部分
- 样式穿透
+ 样式穿透, 让该样式离开当前组件
+ 写了一个 scoped 是为了让样式在自己组件内生效
+ 但是有些内容不在当前组件内, 我想让他离开当前组件, 在全局生效
+ 方式1:
- 取消 `style` 标签身上的 `scoped` 属性
- 所有的样式都公开了
+ 方式2:
- 让该样式穿透出去
- 使用 `::v-deep` 伪类作为父级
```css
::v-deep .van-nav-bar__content {
background-color: $globalColor;
}
```
- 该样式会穿透出去
- 最新版的 `vue` 要求你使用 `:deep(你要穿透的选择器)`
```css
:deep(.van-nav-bar__content) {
background-color: $globalColor;
}
```
## 请求数据
- 需要用到的第三方 `axios`
+ 对 `axios` 进行实例封装, 为了可以配置基准地址
- 准备一个文件 `urils/request.ts`
- 封装 `api` 接口
## 首页 轮播图 组件
## 首页 nav 导航组件
## 首页 秒杀/热门 组件
## 服务器挂了
- 当服务器出现问题了, 导致数据不能回来的时候
- 来到我们封装的 `request.ts` 文件内的响应拦截器内做一些事情
```js
// 响应拦截器
// 需要在本次请求失败的回调函数内做一些事情
instance.intercaptors.response.use(成功的回调函数, 失败的回调函数)
```
- 利用响应拦截器, 对失败的请求做出一些处理
```js
// request.ts
instance.interceptors.response.use(
response => {
return response.data
},
err => {
console.log('本次请求失败了', err)
Dialog.alert({ title: '网络错误', message: '网络不给力, 稍后再来' })
}
)
```
## 点击跳转到 `search 路由`
- 给 `van-search` 组件添加一个 `focus` 事件
+ 当该文本框聚焦的时候触发
+ 使用编程式导航进行路由跳转
- 问题: 我需要拿到 `Vue2` 的时候那个 `$router` 的东西才可以
+ 从 `vue-router` 内拿到 `useRouter` 的函数
+ 通过执行这个 `useRouter` 函数得到原先的 `$router`
```js
import { useRouter } from 'vue-router'
const $router = useRouter()
```
## `Search` 组件书写
- 我们准备了两个小组件
+ `SdySearchHistory`
+ `SdySearchList`
- 目前想到的方案
+ 使用 `v-if` 指令
+ 通过判断 `search` 搜索框是否被写入了文本
+ 决定哪一个组件显示哪一个组件隐藏
```html
```
- 换一个高级的解决方式
+ 动态组件
+ `Vue` 里面给我们提供了一个 `component` 的组件
- 是一个组件, 但是不知道是什么
+ 通过该组件身上的一个 `is` 属性, 来决定这个位置渲染什么内容
## 首页的 商品列表
- 问题1: 数据
+ 接口提供
- 问题2: 布局
+ `scss` 自己书写
- 问题3: 瀑布流
+ `vant` 组件库
+ 使用 `List` 组件
+ 包含 下拉刷新 和 上划加载更多(瀑布流)
## 商品分类页面
- 左边侧边栏的滑动
- 方式1:
+ 父级盒子定高 `100%`
- `overflow: auto`
+ 侧变量超出去
+ 问题:
- 没有回弹效果
- 方式2:
+ 需要自己来绑定事件来完成
+ `touchstart` 事件
- 记录下光标的初始位置
- 记录元素初始位置
+ `touchmove` 事件
- 随着光标的移动, 实时拿到光标的坐标位置
- 用实时拿到的坐标位置 减去 初始位置
- 得到的是本次光标移动的距离
- 给 `inner` 元素进行赋值
- 问题: 直接拿到移动距离赋值给 `inner` ?
- 赋值: 元素本身的位置 + 本次的移动距离
+ `touchend` 事件
- 光标抬起来的时候, 让数值回归到 0
+ 上方回弹
- 可以移动的最大值就是 `100`
- 在移动的过程中, 判断, 如果该值超过 100, 不能再继续增加了
- 当光标离开元素的时候
- 只要该值 >= 0, 我们就把值设置为 0
+ 下方回弹
- 可以移动的最大值就是 `inner高度 - slide高度 - 100`
## 登录
- 按照接口文档逐步完成
## Pinia
- `vuex` 的进阶封装版
- 定义一个全局共享的数据
+ 导入 `pinia`, 在 `pinia` 内拿到 `defineStore` 方法
+ `defineStore` 是专门创建仓库使用的
+ 语法:
```js
const 变量 = defineStore('名字', () => {
// 按照 vue3 的语法去定义各种 响应式数据 计算属性 方法 ...
// 别忘了最后导出即可
})
```
- 在组件内使用共享的数据
+ 导入指定文件内导出的仓库即可
```js
// a.vue
import { listStore } from '@/stores/xxxx.ts'
// 直接使用 listStore
const store = listStore()
```