一、项目开发的步骤
1、书写静态页面(HTML,CSS)
2、拆分组件
3、获取服务器的数据动态展示
4、完成相应的动态业务逻辑
经过分析之后,Home首页可以拆分为7个组件:TypeNav三级联动导航,ListContainer,Recommend,Rank,Like,Floor,Brand。主要得看你静态页面把谁谁写在一个结构里面了,组件就得在一个里面。
二、Home首页拆分静态组件
1.TypeNav三级联动的完成
如果有哪个组件在项目中频繁使用(三级联动在Home、Search、Detail都用到了),就把它注册成全局组件。
好处:只需要注册一次可以在项目的任意地方使用。
首先在home下新建一个三级联动组件的文件夹TypeNav,底下有一个文件叫index.vue,然后把它注册为全局组件,回到入口文件main.js,需要使用到Vue.component
//三级联动的组件——全局组件
import TypeNav from '@/pages/Home/TypeNav'
//第一个参数是全局组件的名字,第二个参数是:哪一个组件
Vue.component(TypeNav.name,TypeNav)
现在回到home组件里就可以使用TypeNav了:
<!-- 三级联动全局组件 ,它已经注册为全局组件了,不需要再引入-->
<TypeNav/>
2.完成其余静态组件
完成轮播图和右边快报的部分
拆分组件就三步:结构、样式、图片资源
3.POSTMAN测试接口
刚刚经过postman工具测试,接口是没有问题的
如果服务器返回的数据code字段200,代表服务器返回数据成功
整个项目,接口前缀都有/api字样
三、请求服务器数据的准备工作
1.axios二次封装
向服务器发请求:XMLHttpRequest、fetch、JQ、axios
为什么需要进行二次封装axios?
请求拦截器、响应拦截器:请求拦截器,可以在发请求之前可以处理一些业务、响应拦截器,当服务器数据返回以后,可以处理一些事情
首先安装axios :npm install axios
项目当中通常放API文件夹用于放axios,src/api/request.js:
//对于axios进行二次封装
import axios from 'axios'
//利用axios对象的方法create,去创建一个axios实例
//request就是axios,只不过配置一下
const requests = axios.create({
//配置对象
//基础路径
//baseURL: '/api',
//代表请求时间超时,超过五秒还没发回来说明请求失败
timeout: 5000,
})
//create方法里面可以写对象
//请求拦截器
requests.interceptors.request.use((config) => {
//config是一个配置对象,里面有一个属性很重要:header请求头
return config
})
//响应拦截器,有成功回调和失败回调
requests.interceptors.response.use((res)=>{
//成功的回调函数:服务器相应数据回来以后,响应拦截器可以检测到
return res.data
},(err)=>{
//响应失败
return Promise.reject(new Error('false'))
})
export default requests
2.api接口统一管理
项目很小:完全可以在组件的生命周期函数中发请求,在mounted或者created里发请求,存储在data当中。
项目大的话,专门建立一个index.js进行统一管理
//当前这个模块,所有API接口进行统一的管理
//发请求用到axios,引入进来
import requests from "./request";
//三级联动的接口
export const reqcategoryList=()=>{
//axios发请求返回promise对象
return requests({url:'http://gmall-h5-api.atguigu.cn/api/product/getBaseCategoryList',method:'get'})
}
main.js调用
import {reqcategoryList} from '@/api'
reqcategoryList()
但是请求错误,因为出现了跨域问题,我们所在的是本地服务器,请求的接口不在
解决跨域问题:JSONP、CROS、代理
这里我们选择代理,在vue.config.js:
//代理跨域,第三方
devServer: {
proxy: {
'/api': {
target: 'http://gmall-h5-api.atguigu.cn/api/product/getBaseCategoryList',
//pathRewrite: { '^/api': '' },
},
},
}
注意:数据请求需要服务器+接口 不要只是访问服务器这样是拿不到数据的,
比如 http://gmall-h5-api.atguigu.cn(这是服务器) /api/product/getBaseCategoryList(这是接口)把接口放在服务器后面就可以
例如:http://gmall-h5-api.atguigu.cn/api/product/getBaseCategoryList 不要只是访问服务器
3.nprogress进度条的使用
安装插件:npm install nprogress
start:进度条开始
done:进度条结束
//引入进度条
import nprogress from 'nprogress'
//引入进度条的样式
import 'nprogress/nprogress.css'
//请求拦截器
requests.interceptors.request.use((config) => {
//进度条开始动
nprogress.start()
//config是一个配置对象,里面有一个属性很重要:header请求头
return config
})
//响应拦截器,有成功回调和失败回调
requests.interceptors.response.use((res) => {
//进度条结束
nprogress.done()
//成功的回调函数:服务器相应数据回来以后,响应拦截器可以检测到
return res.data
}, (err) => {
//响应失败
//return Promise.reject(new Error('false'))
return err.message
})
export default requests
四、Vuex模块化开发
vuex是官方提供一个插件,状态管理库,集中式管理项目中组件共用的数据。
切记,并不是全部项目都需要Vuex,如果项目很小,完全不需要Vuex,如果项目很大,组件很多、数据很多,数据维护很费劲,Vuex几个核心概念:
state:仓库存储数据的地方
mutations:唯一修改state手段
actions:处理action,可以书写自己的业务逻辑,也可以处理异步
getters:计算属性,用于简化仓库数据,让组件获取仓库的数据更加方便
modules:模块式开发
安装vuex:npm i vuex@3
注意vue2的一定不要下错!!!!!
vuex是一个对象,store是它的一个方法,而这个方法是一个构造函数,可以初始化vue仓库
新建一个src下的store文件/index.js:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
//对外暴露store类的一个实例
export default new Vuex.Store({
state:{},
mutations:{},
actions:{},
getters:{}
})
然后到入口文件去注册一下:
//引入仓库
import store from './store'
new Vue({
render: h => h(App),
router,
//注册仓库:组件实例的身上会多一个$store的属性
store
//KV一致省略V
//注册路由信息:当这里书写router的时候,组件身上都拥有$route,$router
}).$mount('#app')
如果项目过大,数据过多,可以让vuex实现模块式开发,大仓库拆分成小仓库,每一个小仓库存储相应模块的数据
比如我们现在创建home、search的小仓库,分别在store下创建两个文件夹为home、search
然后他俩下面再创建index.js
//search模块下的小仓库
const state={}
const mutations={}
const actions={}
const getters={}
export default {
state,
mutations,
actions,
getters
}
然后把两个小仓库合并到大仓库中去
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
//引入小仓库
import home from './home'
import search from './search'
//对外暴露store类的一个实例
export default new Vuex.Store({
//实现Vuex仓库模块化开发存储数据
modules:{
home,
search
}
})
历尽千辛万苦终于知道为啥我没有vuex的数据了,下载的时候下成了最新版的vuex,得先卸载了最新的再下,卸载:npm uninstall vuex
五、TypeNav导航三级联动
1.三级联动展示数据
一个小更改,之前的三级联动我们写在了home下,以后像这种不会更改的组件都写在components里
(1)组件挂载完毕后dispatch给Vuex
//组件挂载完毕,可以向服务器发请求
mounted(){
this.$store.dispatch('categoryList')
},
(2)去home仓库请求数据
code号为200时,就把数据给过去
import { reqcategoryList } from "@/api"
//home模块下的小仓库
const state = {
//state中数据默认初始值别瞎写,服务器返回的是对象,服务器返回数组【根据接口返回初始化】
categoryList:[]
}
const mutations = {
CATEGORYLIST(state,categoryList){
state.categoryList=categoryList
}
}
const actions = {
//通过api里面的接口函数调用,向服务器发请求,获取服务器的数据
async categoryList({ commit }) {
let result = await reqcategoryList();
if (result.code == 200) {
commit("CATEGORYLIST", result.data)
}
}
};
(3)TypeNav接收数据
import{mapState} from 'vuex'
、、、
computed:{
...mapState({
//右侧需要的是一个函数,当使用这个计算属性的时候,右侧函数会立即执行一次
//注入一个参数state,其实即为大仓库中的数据
categoryList:(state)=>{
return state.home.categoryList
}
})
}
(4)v-for去掉多余a标签
一级分类叫item,二级分类叫subitem,是一级分类中的categoryChild,还有三级分类,用em做的
<div class="item" v-for="c1 in categoryList" :key="c1.categoryId">
<h3>
<a href="">{{c1.categoryName}}</a>
</h3>
<div class="item-list clearfix">
<div class="subitem" v-for="c2 in c1.categoryChild" :key="c2.categoryId">
<dl class="fore">
<dt>
<a href="">{{c2.categoryName}}</a>
</dt>
<dd>
<em v-for="c3 in c2.categoryChild" :key="c3.categoryId">
<a href="">{{c3.categoryName}}</a>
</em>
</dd>
</dl>
</div>
</div>
</div>
2.一级分类动态展示背景颜色
(1)采用样式完成(hover)
.item:hover{
background-color: skyblue;
}
(2)通过js完成
当用户鼠标移到哪个一级分类上就把它的索引值index给存起来,mouseenter事件,传参过来index,设置一个动态类名,谁被移上去谁就有那个类名
<div class="item" v-for="(c1,index) in categoryList" :key="c1.categoryId" :class="{cur:currentIndex==index}">
<h3 @mouseenter="changeIndex(index)">
<a href="">{{c1.categoryName}}</a>
</h3>
data(){
return {
currentIndex:-1
//用来记录鼠标在谁上,-1表示都不在
}
},
methods: {
//鼠标进入修改index
changeIndex(index){
//index是鼠标在的一级分类
this.currentIndex=index
}
},
.cur{
background-color: skyblue;
}
这样写实现了鼠标移上去背景颜色变蓝,但是鼠标移下来它还是蓝的(在鼠标最后待的h3)
所以还得有mouseleave事件,本来我还想着动颜色,结果发现直接index=-1更简单
<h3 @mouseenter="changeIndex(index)" @mouseleave="leaveIndex(index)">
、、、
leaveIndex(index){
//鼠标移出的index=-1
this.currentIndex=-1
}
最后老师又更改了一下,当鼠标从第一个h3移到h2的时候蓝色不变,移出h2蓝色才消失,那么这个时候事件不能添加给h3了,用到了事件的委托,移出事件添加给父标签
<div @mouseleave="leaveIndex">
<h2 class="all">全部商品分类</h2>
<div class="sort">
、、、、
<h3 @mouseenter="changeIndex(index)">
<a href="">{{ c1.categoryName }}</a>
</h3>
、、、
3.JS控制二、三级数据显示和隐藏
最开始是通过css样式display:none、block实现的
js实现:谁有背景颜色谁就有二三级分类
<!-- 二、三级分类 -->
<div class="item-list clearfix" :style="{display:currentIndex==index?'block':'none'}">
4.三级联动的防抖与节流
(1)防抖和节流是什么(面试频率高)
卡顿现象:事件触发非常频繁,而且每一次的触发,回调函数都要去执行(如果时间很短,而回调函数内部有计算,那么很可能出现浏览器卡顿)
节流:在规定的间隔时间范围内不会重复触发回调,只有大于这个时间间隔才会触发回调,把频繁触发变为少量触发
防抖:前面的所有的触发都被取消,最后一次执行在规定的时间之后才会触发,也就是说如果连续快速的触发 只会执行一次
防抖:写一个简单的页面来模拟,用户在搜索input表框时,直至输入停止再过一秒之后ajax才会发请求。(强调时刻)
//防抖:前面的所有的触发都被取消,最后一次执行 在规定的时间之后才会触发,也就是说如果连续快速的触发 只会执行一次
let input = document.querySelector('input');
//文本发生变化立即执行
input.oninput=_.debounce(function(){
console.log('ajax发请求')
},1000);
//lodash插件:里面封装函数的防抖与节流的业务【闭包+延迟器】
//1:1odash函数库对外暴露_函数
lodash原生代码 得看
节流:多么频繁的点击都是五秒钟加一次(强调时间)
//计数器:在一秒以内,数字只能加上1
button.onclick =_.throttle(function(){
//节流:目前这个回调函数5S执行一次,
//加入这里面有很多的业务代码,是不是可以给浏览器很充裕的时间去解析
count++;
span.innerHTML = count
console.log('执行')
},5000);
//防抖:用户操作很频繁,但是只是执行一次
//节流:用户操作很频繁,但是把频繁的操作变为少量操作【可以给浏览器有充裕的时间解析代码】
(2)三级联动导航节流
在项目中使用一下节流技术,防止用户操作过快
项目中node_module里有lodash,不用再自己下载
//这种是把lodash全部功能引入过来了
//import _ from 'lodash'
//按需引入,默认暴露所以不用再加{}了
import throttle from 'lodash/throttle'
、、、
methods: {
//鼠标进入修改index
// changeIndex(index) {
// //index是鼠标在的一级分类
// this.currentIndex = index;
// },
changeIndex:throttle(function(index){
this.currentIndex = index;
},50),
注意throttle回调函数不要使用箭头函数,容易产生this指向问题
明天继续更