vue实战项目1

vue实战项目学习第一天,尚硅谷实战课程尚品汇项目。

项目开始,主要做的是配置环境和开始开发。今天配置了项目的脚手架,代理(跨域问题),vuex,vue-router,axios二次封装,nprogress进度条的安装,防抖和节流(Lodash.js)

以下是个人理解,如有错误请指正。

创建项目

首先是配置脚手架创建项目

这里用到的是vue-cli脚手架,直接进入cmd使用命令即可创建项目

1
vue create [project-name]

创建成功之后可以进入项目目录,运行npm run serve查看项目是否创建完成。

项目目录结构:

project

– node_modules 项目的依赖包安装目录

– public 项目的静态文件目录

– src 项目代码目录

—— assets 代码中用到的一些静态文件

—— components 组件目录(静态组件或者全局组件)

—— App.vue 所有组件的父组件

—— main.js 项目的入口文件

– .gitignore git仓库的一些忽略文件配置

– babel.config.js babel的配置文件

– jsconfig.json 配置文件

– package-lock.json 依赖包的缓存文件

– package.json 项目信息和依赖包配置

– README.md 项目文档

– vue.config.js vue和webpack配置

上面是通过脚手架创建的项目初始目录结构,主要开发是在src目录下进行的,今天的学习中src创建了其他的一些目录。

– api 对项目api统一管理的目录

– pages 路由组件放置的目录

– router vue-router的配置目录

– store vuex的配置目录

开始

对于尚品汇项目,首先是对项目的页面进行了大致的分析,结构分离出了Header组件和Footer组件,由于两个组件在多个页面中使用同时也没有变化,所以将两个创建为非路由组件放在components目录下。

拆分组件

拆分组件要注意的是将组件的结构和样式要一起拿走,同时还有组件用到的静态文件比如图片之类的,创建Header和Footer目录在components目录下,将组件写在index.vue文件中,将图片信息放在images目录下,这里将结构拿过来的时候要注意图片的路径可能会改变,所以要注意检查修改。

image-20230723204004036

在两个组件拆分完之后在App.vue里面去注册组件并将组件使用到页面上,检查一下组件的结构和样式是否有bug。

Header组件和Footer组件拆分完之后剩下的中间的即为Home组件,这里在Header中测试暂时发现了四个路由组件,也就是Home,Search这两个用到了Header和Footer组件,还有Login,Register这两个组件只用到了Header组件。

故路由组件中先创建了四个,今天做了Home组件的拆分,在拆分Home之前先将四个路由组件的跳转配置了一下。

路由配置

这里使用的是vue-router来进行路由管理,下面是配置中的routes项,这里配置的即是网站上的路由,首页上暂时配置了四个,即首页home,搜索search,登录login,注册register,在这四个下面配置了一个重定向,就是在刚进入是默认展示首页home。可以看到meta路由元信息中配置了一个属性,这个属性是决定Footer组件·是否展示的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
routes:[
{
path:"/home",
component:Home,
meta:{show:true}
},
{
name:'search',
path:"/search/",
component:Search,
meta:{show:true},
// 布尔值,只能传递params
// props:true
// 对象:额外的给路由传递props参数
// props:{a:1,b:2}
// 函数写法:可以params,query,传递给props
// props($route){
// return {keyword:$route.params.keyword, k:$route.query.k}
// }
},
{
path:"/login",
component:Login,
meta:{show:false}
},
{
path:"/register",
component:Register,
meta:{show:false}
},
// 重定向,在项目跑起来的时候,访问/,立马定向到首页
{
path:'*',
redirect:'/home'
}
]

路由配置完之后需要将原先的a标签跳转修改为router-link来完成声明式路由导航的功能,由于搜索是通过按钮来进行跳转的,只能使用函数式路由导航。

路由传参

在路由跳转时就引出了路由传参,路由传参可以通过query和params,还有一个是上面代码配置中注释的一部分,传递props参数,有三种方法来传递

  • 布尔值,只能传递params参数,将params传递的参数通过props形式传递给组件,组件中要用props接收
  • 对象,额外给组件传递固定的值到props
  • 函数写法,可以传递query和params,函数接收一个$route,然后可以通过$route.query或者$route.params取到参数,在返回一个对象传递到props中

注意: 如果传递params参数在代码中调用时用到对象形式时要用name属性,所以在这里配置时一般会给配置好name属性。

Home组件内容

上面配置好可以使用了,接下来是home组件的内容拆分,很容易可以将组件拆分出来,这里有一个三级菜单,是不随着路由跳转的,也就是非路由组件,所以将三级菜单放到components中,其他的路由组件放到pages的Home目录下。

image-20230723211402268

axios二次封装

要用到axios发送请求,获取数据,这里将axios进行二次封装,方便开发,首先是安装axios,直接npm i axios就可,创建api目录来将项目中需要用到的与后端联系的api进行统一管理,方便后期修改。

requests中用axios的create方法创建实例,并创建了两个拦截器,可以在拦截器中写一些逻辑,这里给项目添加了进度条的效果,用到了nprogress库。

requests.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 对于axios进行二次封装
import axios from "axios";
// 引入进度条
import nprogress from "nprogress";
// 引入进度条样式
import 'nprogress/nprogress.css';
// start 进度条开始,done进度条结束

// 1:利用axios对象的方法create,去创建一个axios实例
const requests = axios.create({
// 配置对象
// 基础路径
baseURL: "/api",
// 请求超时的时间
timeout: 5000,
})
// 请求拦截器
requests.interceptors.request.use((config) => {
// config:配置对象,对象里有一个属性很重要,header请求头
nprogress.start();
return config;
})

// 响应拦截器
requests.interceptors.response.use((res) => {
// 服务器成功回调
nprogress.done();
return res.data;
}, (error) => {
// 响应失败回调
return Promise.reject(new Error('faile'));
})

export default requests;

index文件将项目用到的api接口封装为函数,调用函数就可以拿到返回结果。

index.js

1
2
3
4
5
6
7
8
9
// API进行统一管理
import requests from "./requests";

// 三级联动接口
// http://gmall-h5-api.atguigu.cn/api/product/getBaseCategoryList

// 发请求 返回结果为Promise对象
export const reqCategoryList = () => requests({ url: '/product/getBaseCategoryList', method: 'get' });

vuex配置

配置vuex集中管理状态,这里用到了模块化管理,将各组件的数据分模块储存,方便管理条理清晰。

image-20230723212511360

store中有每个模块的仓库,每个模块中又都有独立的actions,mutations,state,这里需要注意模块化要在每个模块中打开namespaced命名空间,设定为true。

index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import Vue from "vue";
import Vuex from 'vuex';

Vue.use(Vuex);
// 引入小仓库
import home from './home';
import search from "./search";

export default new Vuex.Store({
// 实现vuex仓库模块化开发存储数据
modules:{
home,
search
}
})
TypeNav数据获取

调用封装的api函数接口,拿到数据,这里将数据用vuex来管理储存,将后端返回的数据储存到vuex中,组件使用mapState将数据读取出来放到计算属性中。这里因为store是用模块化管理的,所以应该在mapState获取数据的参数加上第一个参数是模块的命名,或者直接使用回调函数,将state下的命名空间内的数据返回,除state之外,其他的actions,mutations,getters都要加上命名空间,而这些的命名空间与state不同,这些是在原先的命名前加上命名空间,例如原本是categoryList,添加命名空间之后为home/categoryList。

TypeNav数据动态展示

数据获取到之后就是展示数据了,菜单是一个三级菜单,也就是说获取到的数组也是三级的,通过v-for来进行循环,展示每一层的数据即可。

数据动态展示之后,有一个鼠标放上背景变色的特效这里用鼠标事件@mouseenter事件来触发,当鼠标放到h3上时将index记录下来,给父元素盒子添加一个动态class属性,当记录的currentIndex值与一级菜单的index相等时给盒子添加一个cur属性,而cur在style中写的即为将背景设置为高亮。这里需要在组件中创建一个data就是currentIndex来储存当前鼠标的index值。

鼠标进入高亮做完了,发现移出时会取消高亮,这里用到@mouseleave事件,鼠标移出时将高亮取消,也就是将组件数据中记录的currentIndex设置为-1,表示没有鼠标移入。细节就是当鼠标在全部商品分类的标题上时还是会有高亮效果,而此时鼠标已经不在一级标题上,这里实现用到了事件委派,将取消高亮的鼠标离开事件放在父元素身上,当鼠标离开父元素之后再将currentIndex值置为-1,这样就实现了高亮取消的效果。

TypeNav点击导航路由传值

这里如果直接将三级导航的每一个a标签替换为router-link也可以实现效果,但是这里三级导航循环之后可能有很多的标签,所以用router-link会导致效率问题,这里只能用编程式导航并将触发回调放在父元素上,这样就只有一个事件渲染,可是还需要实现点击标签然后将name和id传入,通过event点击事件可以获得标签是什么,在a标签上添加data-categoryName属性,在函数中通过event.target.dataset可以获得标签中的data传值,这样就可以通过categoryname来确定是否是要跳转的a标签,在a标签中也传入不同级标题的id这样就可以拿到标题的id经过判断就可以进行向search路由跳转,同时携带query参数。