一、 Home组件
开发流程
- 拆分静态组件 | 页面
- 配置 api 请求
- vuex 配置
- 组件获取数据,动态展示数据
1. 全局组件:三级联动组件的实现
- 三级联动组件在多个位置(home、search、detail)使用到了,所以将其注册为
全局组件
(只需要注册一次,就可以在任意需要的地方使用)
- 新建
src / components / TypeNav / index.vue
- 在
main.js
中进行全局注册
全局注册时接收两个参数:第一个参数为全局组件的名字
,后续使用用到的就是这个名字;第二个参数为要注册的组价
// main.js
// 导入三级联动组件
import TypeNav from '@/components/TypeNav'
createApp(App)
.component('type-nav', TypeNav) // 全局注册三级联动组件
.mount('#app')
- 使用
src / view / Home / index.vue
<!-- 三级联动组件 -->
<type-nav></type-nav>
2. 首页局部组件
- 创建需要的局部组件
- 在 views / Home /indec.vue 中引入和使用
<template>
<div>
<!-- 三级联动组件 -->
<type-nav></type-nav>
<!-- 列表组件 -->
<list-con></list-con>
<!-- 今日推荐组件 -->
<today-recommend></today-recommend>
<!-- 商品排行组件 -->
<rank></rank>
<!-- 猜你喜欢组件 -->
<like></like>
<!-- 楼层 -->
<floor></floor>
<!-- 楼层 -->
<floor></floor>
<!-- 商标组件 -->
<brand></brand>
</div>
</template>
<script setup>
import ListCon from './ListContainer'
import todayRecommend from './TodayRecommend'
import rank from './Rank'
import like from './Like'
import floor from './Floor'
import brand from './Brand'
import {
} from 'vue'
</script>
<style lang="scss" scoped></style>
3. 接口工具:postman
- 本项目中:服务器返回code为200,代表请求成功
- 整个项目的接口前缀为 /api
4. axios 的二次封装
目的:基于axios封装一个请求工具,调用接口时使用。封装请求拦截器、响应拦截器,进行一些业务处理
在后续需要调用接口时,要引入该工具
- 安装 axios
cnpm install --save axios
- 新建
src / api / request.js
api 文件夹,用于存放axios相关的
request.js 用于二次封装axios
/** *****************axios的二次封装,主要是封装请求拦截器和响应拦截器 ************/
/** 实现步骤
* 1. 创建一个新的axios实例
* 2. 请求拦截器,如果有token进行头部携带
* 3. 响应拦截器
**/
// 导入axios
import axios from 'axios'
/**
* TODO-1: 利用axios的create方法,创建一个新的axios实例
*/
const instance = axios.create({
// axios的配置
// baseUrl:基础路径,基于哪个路径
// 如本项目中接口前缀为 /api,设置baseURL的作用就是后续见到访问地址时,会自动加上 /api ,避免每次都去书写
baseURL: '/api',
// 请求超时的时间,在5秒内无响应则请求失效
timeout: 5000
})
/**
* TODO-2:请求拦截器
* 在发请求之前,请求拦截器可以进行监测,以便在请求发出前进行一些处理
*/
instance.interceptors.request.use(config => {
// config:配置对象,其中含请求头header属性
return config
})
/**
* TODO-3:响应拦截器
*/
instance.interceptors.response.use(response => {
// 响应成功的回调
// 请求成功返回data,后续可直接使用data
return response.data
}, err => {
// 响应失败的回调
// 终止Promise
return Promise.reject(err)
})
// 对外暴露,外部才可以使用
export default instance
5. API 接口统一管理
- 项目很少,比如只有几个接口,可以直接在组件生命周期中发送请求
- 大项目(比如组件上百,接口几十的情况下),可以对接口统一进行管理,这样方便修改
- 新建
src / api / index.js
用于接口的统一管理
/** *************** 接口的统一管理 *******************/
// 导入封装好的axios
import requests from './request'
/**
* 三级联动接口
*/
export const reqCategoryList = () => requests({
url: '/product/getBaseCategoryList',
method: 'get'
})
5.1 配置代理,解决跨域问题
module.exports = {
// 代理跨域
devServer: {
proxy: {
'/api': {
target: 'http://39.98.123.211' // 服务器地址
// pathRewrite: { '^/api': '' },
}
}
}
}
6. vuex 模块式开发
- vuex 是vue官方提供的插件,用于集中式状态管理,管理项目中公用的数据
- 新建 src / store / index.js
如果安装的时候选择了vuex,则该文件会自动创建并在main.js中注册
import {
createStore } from 'vuex'
// state:仓库存储数据的地方
const state = {
}
// mutations:修改state的唯一手段
const mutations = {
}
// actions:处理action,可以处理异步和书写业务逻辑
const actions = {
}
// getters:仓库的计算属性,用于简化仓库数据
const getters = {
}
export default createStore({
state,
mutations,
actions,
getters
})
- 当项目过大,模块多,接口多,可以使用vuex的模块化管理
- 让代码更好维护,让多种数据分类更加明确
- 每个组件的state、actions、mutations、getters单独管理,最后引入
- 在store目录下建立需要使用vuex的组件文件夹,如:home/index.js、search/index.js,分别管理需要的vuex内容
const state = {
...
}
const mutations = {
...
}
// getters:计算属性,项目中主要用于简化仓库中的数据
const getters = {
...
}
const actions = {
...
}
export default {
state,
mutations,
getters,
actions
}
- store/index.js中引入
import {
createStore } from 'vuex'
import home from './home'
import search from './search'
export default createStore({
// 实现vuex仓库模块式开发存储数据
modules: {
home,
search
}
})
- 开发者工具中查看
7. TypeNav 三级联动组件
7. 1 三级联动组件的接口请求
Home / index.vue
中,组件挂载完毕就向服务发请求获取三级联动列表
import {
onMounted } from 'vue'
import {
useStore } from 'vuex'
// 引入vuex的实例store
const store = useStore()
onMounted(() => {
// 组件挂载完毕后,向服务发请求获取三级联动列表
store.dispatch('getCategoryList')
})
store / home / index.js
配置
// 导入三级联动的接口函数
import {
reqCategoryList } from '@/api/index'
const state = {
// state中的初始值要和服务器返回的类型保持一致
categoryList: []
}
const mutations = {
CATEGORYLIST(state, categoryList) {
state.categoryList = categoryList
}
}
const getters = {
}
const actions = {
// 首页派发的‘getCategoryList’事件
async getCategoryList({
commit }) {
// 发送请求
const result = await reqCategoryList()
if (result.code === 200) {
// 请求成功,派发一个mutation,并将返回的数据传递过去
commit('CATEGORYLIST', result.data)
}
}
}
export default {
state,
mutations,
getters,
actions
}
- 在
Home / index.vue
中使用
// 获取请求到的三级联动列表
const categoryList = store.state.home.categoryList
7.2 通过JS给三级联动设置背景色(事件委托)
- 方式1: 直接修改css,添加一个hover时的背景颜色
&:hover {
background-color: skyblue;
}
- 通过JS设置 ,在 components / TypeNav / index.vue 中实现
<template>
...
<div class="item" :class="{ active: currentIndex === index }">
<h3 @mouseenter="changeIndex(index)" @mouseleave="leaveIndex">
...
</h3>
</div>
...
</template>
<script>
import {
ref } from 'vue'
// 背景色的处理
// 存储鼠标移入的那一个分类的index
const currentIndex = ref(-1)
// 鼠标移入修改 currentIndex
const changeIndex = (index) => {
currentIndex.value = index
}
// 鼠标移除,重置currentIndex
const leaveIndex = () => {
currentIndex.value = -1
}
</script>
<style lang="scss" scoped>
.item {
&.active {
background-color: skyblue;
}
}
</style>
- 希望实现的效果:鼠标先在 “图书、音像、电子书” 分类然后移到 “全部商品分类” 时背景色仍然存在,可以通过
事件委托
实现
事件委托 || 事件委派
:将本来是子元素的事情委派给父元素来处理- 此处将三级联动列表和全部商品分类外层包裹一个 div ,将鼠标移除事件交给这个 div
<!-- 事件委托/事件委派 -->
<div @mouseleave="leaveIndex">
<h2 class="all">全部商品分类</h2>
<div class="sort">
<div class="all-sort-list2">
<div
class="item"
v-for="(categoryItem, index) in categoryList"
:key="categoryItem.categoryId"
:class="{ active: currentIndex === index }"
>
<h3 @mouseenter="changeIndex(index)">
<a href="">{
{ categoryItem.categoryName }}</a>
</h3>
<div class="item-list clearfix">
<div
class="subitem"
v-for="subItem of categoryItem.categoryChild"
:key="subItem.categoryId"
>
<dl class="fore">
<dt>
<a href="">{
{ subItem.categoryName }}</a>
</dt>
<dd>
<em
v-for="childItem of subItem.categoryChild"
:key="childItem.categoryId"
>
<a href="">{
{ childItem.categoryName }}</a>
</em>
</dd>
</dl>
</div>
</div>
</div>
</div>
</div>
</div>
7.3 性能优化:函数防抖与函数节流
- 当短时间内频繁调用函数时(多次触发事件),浏览器会出现卡顿现象
- 函数节流和函数防抖,两者都是优化高频率执行js代码的一种手段
函数节流
:在规定事件间隔范围内不会触发触发回调,只有大于该时间才会触发,这样把频繁触发变成了少量触发。即:用户操作很频繁,但会把频繁的操作变为少量的操作函数防抖
:取消掉之前所有的触发,最后一次执行在规定时间后才会触发,即连续触发多次只会执行一次,即: 用户操作很频繁,但只执行一次函数防抖的应用场景
:最常见的就是用户注册时候的手机号码验证和邮箱验证了。只有等用户输入完毕后,前端才需要检查格式是否正确,如果不正确,再弹出提示语。- 函数防抖在搜索表单也常见,在用户输入完毕后才发送请求
函数节流应用场景
,多数在监听页面元素滚动事件的时候会用到。因为滚动事件,是一个高频触发的事件
7.3.1 lodash 插件
- lodash 插件内部已经封装好了函数节流与防抖的业务
- 安装
注:先搜索项目node_modules中是否含lodash,如果已经自带则不需要安装
cnpm i --save lodash
- 使用
三级联动在频繁切换目录的时候会用到节流,将频繁的操作变为少量的操作
// 按需引入节流
import throttle from 'lodash/throttle'
// 函数节流
const changeIndex = throttle((index) => {
currentIndex.value = index
}, 300)
7.4 三级联动组件的路由跳转和传参
- 通过编程式导航实现,避免声明式导航出现卡顿的现象;
- 利用事件委托