1.传参可能会出现的问题
编程式导航在你多次跳转的时候会出现报错,这个问题时因为下载的路由有问题。我们有两个办法解决:
第一种:治标,通过传入相应成功和失败回调来解决
this.$router.push({name:"search",params:{keyword:this.keyword},query:{k:this.keyword.toUpperCase()}},()=>{},()=>{});
第二种:根治,重写push和replace方法:
在路由里面router/index下重写
//需要重写VueRouter.prototype原型对象身上的push|replace方法
//先把VueRouter.prototype身上的push|replace方法进行保存一份
let originPush = VueRouter.prototype.push;
let originReplace = VueRouter.prototype.replace;
//重写VueRouter.prototype身上的push方法了
VueRouter.prototype.push = function(location, resolve, reject) {
//第一个形参:路由跳转的配置对象(query|params)
//第二个参数:undefined|箭头函数(成功的回调)
//第三个参数:undefined|箭头函数(失败的回调)
if (resolve && reject) {
//push方法传递第二个参数|第三个参数(箭头函数)
//originPush:利用call修改上下文,变为(路由组件.$router)这个对象,第二参数:配置对象、第三、第四个参数:成功和失败回调函数
originPush.call(this, location, resolve, reject);
} else {
//push方法没有产地第二个参数|第三个参数
originPush.call(
this,
location,
() => {},
() => {}
);
}
};
//重写VueRouter.prototype身上的replace方法了
VueRouter.prototype.replace = function(location, resolve, reject) {
if (resolve && reject) {
originReplace.call(this, location, resolve, reject);
} else {
originReplace.call(
this,
location,
() => {},
() => {}
);
}
};
call和apply的区别:
相同点:都可以调用函数一次,都可以篡改函数上下文一次;
不同点:call传递参数用逗号隔开,而apply用数组传递;
2.三级联动组件
先看看设置为什么组件,因为我们有很多歌页面都在使用三级联动组件,所有把他拆分成一个全局组件,这样就可以只注册一次,每个地方都可以使用。
注册全局组件:
- 在Vue项目中
src/components
路径下补充创建文件夹名(xxx)/ index.vue
,在此vue组件中写入自己需要服用多次的代码结构 - 在
src/main.js
文件中 -
import Vue from 'vue' // 引入封装的复用的组件 注意xxx组件名应采用大驼峰命名 import TypeNav from '@/components/TypeNav' //第一个参数:全局组件的名字 第二个参数:哪一个组件 Vue.component('typenav',TypeNav) // 在全局用Vue.component()方法全局注册
然后在另一个组件上使用
-
<div> <!-- 因为已经注册为全局组件了,所以这里不要import --> <typenav/> </div>
这里是在Home组件中使用的
3.完成首页其他静态组件
注意拉代码的时候:HTML + CSS + 图片资源
这些静态组件只在首页使用,所有直接在局部注册使用就好了
<template>
<div>
<!-- 因为已经注册为全局组件了,所以这里不要import -->
<typenav/>
<ListCotainer/>
<Like/>
<Rank/>
<Recommend/>
<Brand/>
<Floor/>
</div>
</template>
<script>
import ListCotainer from '@/pages/Home/ListContainer';
import Like from '@/pages/Home/Like';
import Rank from '@/pages/Home/Rank';
import Recommend from'@/pages/Home/Recommend';
import Brand from '@/pages/Home/Brand'
import Floor from '@/pages/Home/Floor'
export default {
name:'',
components:{ListCotainer,Like,Rank,Recommend,Brand,Floor},
}
</script>
4.用postman软件测试接口
测试最新的端口:http://gmall-h5-api.atguigu.cn/api/product/getBaseCategoryList
服务器返回的code是200就表示返回数据成功
整个项目,接口前缀都有/api字段
5.axios二次封装
我们向服务器发送请求的方式有很多种:XMLHttpRequest原生的构造函数、fecth、JQ、axios;
一般我们是使用axios的
为什么要二次封装axios呢?
可以设置请求拦截器和响应拦截器:
请求拦截器:可以在发送请求之前处理一些业务
响应拦截器:当服务器返回数据之后,可以处理一些业务
项目一般用src下的api文件放axios
//首先安装axios
npm i axios
//接口当中路劲都带有/api 例如:http://xxxxxxx/api
//对于axios进行二次封装
import axios from "axios"
//引入进度条
import nProgress from "nprogress";
//start:进度条开始 done:进度条结束
//引入进度条样式
import "nprogress/nprogress.css"
//1.利用axios对象方法创造一个实例
//requests就是axios
const requests=axios.create({
//配置对象//基础路径,发请求是,路径会出现/api
baseURL:"/api",
//请求时间超过时间为5s
timeout:5000,
})
//请求拦截器:在请求发出前,可以侦测到,可以再发出去前做点事情
requests.interceptors.request.use((config)=>{
//进度条开始
nProgress.start();
//config:配置对象,对象里面有一个属性很重要,headers请求头
return config;
});
//响应拦截器,数据返回之后还能干的事情
requests.interceptors.response.use((response)=>{
//进度条结束
nProgress.done();
//状态码是2xx,就执行这个
return response.data;
},(error)=>{
return Promise.reject(new Error('faile'));
});
//对外暴露
export default requests;
6.接口统一管理
项目小:可以在组件的生命周期函数种发请求
项目大:axios.get(‘xxx’)
api文件下的index.js就是用来管理接口的
//当前这个模块:API进行统一管理
import requests from './request';
//三级联动接口
// /api/product/getBaseCategoryList GET 无参数
// export const reqCategoryList= ()=>{
// //发请求:axios发请求返回结果promise对象
// return requests({url:'/product/getBaseCategoryList'})
// }
//测试,用postman测试,对外暴露一个函数,只要外部调用这个函数,就向服务器发起ajax请求来获取我们的三级菜单数据;这个是上面方式的简写
export const reqCategoryList= ()=>requests({url:'/product/getBaseCategoryList'});
//!!!在网上有看到其他版本的但是没试过,就是export const reqCategoryList= ()=>requests({url:'/product/getBaseCategoryList',method:'get',});
在入口文件main.js下测试
// //测试那个端口有没有用的,用postman那个软件
// import {reqCategoryList} from '@/api'
// reqCategoryList();//这是在api下的一个方法
7.跨域问题
所谓跨域就是:协议、域名、端口号不一致,就叫做跨域
前端项目的本地服务器是:http://localhost:8080/#/home
后台项目服务器:http://gmall-h5-api.atguigu.cn
这就是跨域,想要解决跨域问题的方案:JSONP、cros、代理
//代理跨域
//配置代理跨域
devServer: {
proxy: {
"/api": {
target: "http://gmall-h5-api.atguigu.cn",
},
},
},
8.Vuex
这是vuex的原理。
原本的vuex是这样的,然后再在main.js中引入
//该文件用于创建Vuex中最为核心的store
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
//准备actions——用于响应组件中的动作!!!!!!!!!!!等于餐厅的服务员,如果是固定的东西可以直接跳过他,但是如果是后台发的信息不可以跳过他
const actions = {
jia(context,value){
console.log('actions中的jia被调用了')
context.commit('JIA',value)
},
//commit是actions传递给mutations的方法,用了这个要保证mutations里面也有一个(’jia‘)
//context是上下文的意思,编译器通过看上下文
}
//准备mutations——用于操作数据(state) !!!!等于厨师
const mutations = {
JIA(state,value){
console.log('mutations中的JIA被调用了')
state.sum += value
},
}
//准备state——用于存储数据 等于后厨
const state = {
sum:0 //当前的和
}
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
})
//在main.js中写的
import store from './store'
new Vue({
render: h => h(App),
// $route路由对象 $routers路由器
// 注册路由信息:当这里写router的时候,组件身上都拥有$route,$router
router,
//注册仓库:组件实例身上会多一个属性$store属性
store
}).$mount('#app')
但是我们这个项目要用vuex模块化开发
所谓的模块化就是将创建一个个小仓库分别当不同组件的仓库,大仓库统一管理
第一步:在组件中挂载
//因为在三级联动上向服务器发请求,所有发送装置放在三级联动组件
mounted() {
//通知vuex发请求,获取数据,储存在仓库中
// this.$store.dispatch('home/getCategoryList');//开启命名空间就这样写,不开就写getCategoryList
// 这个之所以不用是因为其他组件调用它就会发一次请求,跳转到其他页面后就自动销毁前面的组件,导致后面还要用又要发请求,所以放到app。vue里面,它只执行一次,就不会反复调用了
},
// 因为跳转页面后会销毁上一个组件,后面不希望每次跳转页面都要重写加载一次数据,所有放在app。vue
mounted(){
//因为只有main.js和app.vue的只执行一次,这也可以减低服务器的压力
//派发一个action||获取商品分类的三级列表的数据,这是发送异步请求,需要在路由那边写好异步请求的东西
//开了命名空间才要home/getCategoryList,如果没开命名空间就直接getCategoryList
this.$store.dispatch("getCategoryList")
}
第二步:创建home的小仓库就是store/home/index.js
//home的仓库
import {reqCategoryList} from '@/api'
//getters:理解为计算属性,用于简化仓库数据,让组件获取仓库的数据更加方便
const getters = {};
//action:处理action,可以书写自己e的业务逻辑,也可以处理异步
const actions = {
//通过API里面的接口函数调用,向服务器发请求,获取服务器的数据
async getCategoryList({commit}){
let result = await reqCategoryList();
//成功返回时,我们要修改仓库中的数据
if(result.code==200){
commit("CETEGORYLIST",result.data);
}
},
};
//mutations:修改state的唯一手段
const mutations = {
CETEGORYLIST(state,categoryList){
//修改state中的categoryList---事先准备好空的categoryList
state.categoryList = categoryList;
}
};
//state:仓库存储数据的地方
const state ={
//state中的数据默认初始值别乱写,服务器返回对象,服务器返回数组【根据接口返回值初始化的】
categoryList:[],
}
//对外暴露store类的实例
export default ({
//开启命名空间
namespaced:true,
state,mutations,actions,getters
});
然后统一大仓库就是文件夹store/index.js管理
import Vue from 'vue';
import Vuex from 'vuex';
//需要使用插件一次
Vue.use(Vuex);
//引入小仓库
import home from './home';
//对外暴露store类的实例
export default new Vuex.Store({
//模块化开发
modules:{
home
}
});
第三步:展示,从仓库中获取到数据之后就可以拿出来展示了。就是下面的c1.categoryId。。。。
<div class="item bo" 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>
<script>
import {mapState} from 'vuex';
import {throttle} from 'lodash';
export default {
name:"typeNav",
//组件挂在完毕:可以向服务器发请求
data(){
return{
currentIndex:-1,
show:true
}
},
computed:{
// xxx为自定义命名,这里是指把state仓库里的search小仓库里的searchList里的total的值拿到,
// 赋值给左边xxx的身上,这样xxx身上就有数据里,在组件里就
// 可以用{{xxx}}拿到仓库里的total数据了
// ...mapState({
// xxx:state=>state.search.searchList.total,
// }),
//这个。。。mapState的作用就是获取仓库中的数据,然后上面展示的时候用。
...mapState({
//没开命名空间的写法
categoryList:state=>state.home.categoryList
//开了命名空间就是。。。mapState("home",["categoryList"])
})
},
</script>