NavigationDuplicated
的警告错误路由跳转由两种形式:编程式导航(push|replace
)、声明式导航router-link
编程式导航才会有这种情况的异常,声明式导航是没有这种问题,因为声明式导航内部已经解决这种问题
这种异常,对于程序没有任何影响的
由于vue-router
最新版本3.5.2
,引入了promise
,当传递参数多次且重复,会抛出异常,因此出现上面现象
我们可以把push看作一个函数,返回的是一个Promise函数,需要给函数传入成功或者失败的回调,没传 就会警告
function push(){
return new Promise( resolve, reject ) => {
}
}
第一种解决方案:是给push
函数,传入相应的成功的回调与失败的回调(空的箭头函数就可以),还可以捕获当前的错误
this.$router.push(
{
name: "search",
params: { keyword: this.keyword },
query: { k: this.keyword.toUpperCase() },
},
//解决编程式导航多次点击报错的问题 这个方案不能根治,以后再用`push|replace`还是会出现类似现象
() => {},
() => {}
);
push
this
:当前组件实例(search
)
this.$router属性
:是VueRouter
类的一个实例,当在入口文件注册路由时,给组件实例添加$router|$route
属性
push
:VueRouter
实例身上没有push
,是在VueRouter
实例原型对象身上的一个方法
简单来说就是
$router
是VueRouter
类的一个实例,push
是VueRouter
原型身上的方法所以对原型方法
push
进行修改,修改结果就会作用于组件实例的$router
(重写push
方法)
//说明实例 和 原型对象上的方法
function VueRouter(){}
//原型对象身上的方法
VueRouter.prototype.push = function(){
//函数上下文为VueRouter类的一个实例
}
//相当于
let $router = new VueRouter();
//就可以使用原型对象身上的方法
$router.push()
console.log(VueRouter)----->是一个构造函数
console.log(VueRouter.prototype)------>是一个原型---->VueRouter的原型对象----->push就是在这里面
//先把VueRouter原型对象的push方法,先保存一份
let originPush = VueRouter.prototype.push
//重写push和replace方法
//第一个参数:告诉原来的push方法,你该往哪跳转(传递哪些参数)
//第二个参数是成功的回调,第三个是失败的回调
VueRouter.prototype.push = function (location, resolve, reject) {
//这里面的this还是VueRouter的实例
if (resolve && reject) {
// originPush()这样调用是这不行的,上下文变成是window
originPush.call(this, location, resolve, reject)//这行代码保证了上下文还是VueRouetr类的一个实例 $router
} else {
//缺少resolve或reject或都缺少,我们就手动传入两个回调函数,治根了,就不会报错
originPush.call(this, location, () => { }, () => { })
}
}
//同样的重写replace方法
//编程式导航有这个问题
call
和apply
区别相同点:都可以调用函数一次,都可以篡改函数上下文一次
不同点:
call
传递参数用逗号分隔,apply
用数组传递
三级联动、轮播图与快报、猜你喜欢、商品展示
很多地方都使用(主页,搜索页,详情页),所以拆分为一个全局组件,优点就是只需注册一次,每个地方都可以使用
创建全局组件TypeNav
在路口文件main.js
中注册全局组件,如下:
import TypeNav from '@/pages/Home/TypeNav'
Vue.component('TypeNav', TypeNav) //第一个是注册组件的名字,第二个参数是要注册的组件
在Home组件中使用一下
ctrl+h
全局替换
注意:
HTML
+CSS
+图片资源
轮播图和快报一个组件
- 创建静态组件(结构、样式、图片资源)
ListContainer
- 不需要注册为全局组件,在
Home组件
中引入和注册,再使用
//Home组件中
import ListContainer from "./ListContainer"; //引入
components: { ListContainer }, //注册组件
<ListContainer /> //使用
同理,今日推荐组件
拆分好
Recommend
组件,在Home
中注册就可以
import Recommend from "./Recommend";
components: { ListContainer, Recommend },
<Recommend />
同理,商品排行组件
Rank
、猜你喜欢组件Like
、楼层组件Floor
…
使用最新服务器地址:
http://gmall-h5-api.atguigu.cn
服务器返回的数据
code
字段是200
表示服务器返回数据成功整个项目,接口前缀都有
/api
字段
axios
二次封装向服务器发请求的方式有很多:XMLHttpRequest
原生的构造函数、fecth
、JQ
、axios
axios
目的:
请求拦截器:可以在发送请求之前处理一些业务
响应拦截器:当服务器返回数据后,可以处理一些业务
项目中一般用src下的 API
文件夹放axios
首先安装axios
npm i axios
接口当中路径都带有/api,如:http:// xxx.xxx:8080/api
//对axios进行二次封装
//引入axios
import axios from 'axios'
//第一步:利用axios对象的方法create,去创建一个axios实例
//requests其实就是axios,只不过稍微配置一下
const requests = axios.create({
//配置对象
//基础路径,发请求的时候路径就会带上api,就不用一个一个去写了
baseURL: "/api",
//代表请求超时的时间5s
timeout: 5000,
});
//请求拦截器:在发请求之前,请求拦截器可以检测到,在请求发出之前处理一些事情
requests.interceptors.request.use((config) => {
//config:是一个配置对象,对象里面有个属性很重要,headers请求头
return config
});
//响应拦截器:成功与失败回调
requests.interceptors.response.use((res) => {
//成功回调函数:服务器返回相应数据后,响应拦截器可以检测到,做一些事情
//比如:我们只需要data数据就可以,其他不用,那就返回res.data
return res.data
}, (error) => {
//失败的回调函数:也可以做一些事情,返回错误信息啊,或者终止promise链
return Promise.reject(new Error('failure'))
});
export default requests
项目很小:完全可以在组件的生命周期函数中发请求
项目较大:axios.get('xxx')
api文件下的index.js就是用来管理接口的
//对API接口进行统一管理
//发请求需要用到我们封装好的axios----requests,先引入
import requests from './request'
//三级联动的接口
//请求地址:/api/product/getBaseCategoryList 请求方式:GET 无参数
//对外暴露一个函数,其他组件需要发请求获取数据时,调用函数就可以
export const reqCategoryList = () => {
//发请求:返回的是一个promise对象
return requests({
//不需要写/api了,之前在baseUrl中配置过
url: '/product/getBaseCategoryList',
method: 'get',
})
}
//简写形式:
export const reqCategoryList = () => requests({ url: '/product/getBaseCategoryList', method: 'get', });
在mian.j入口文件中测试
//测试发送请求
import { reqCategoryList } from './api' //注意看看使用什么暴露,对应使用什么引入
reqCategoryList()
//会发现上面报404错误----跨域了----需要去配置代理服务器
什么是跨域:协议、域名、端口号不一致,就叫做跨域
前端项目本地服务器:http://localhost:8080/#/home
后台服务器:http://gmall-h5-api.atguigu.cn
解决跨域问题的方案:JSONP
、CROS
、代理
//代理跨域---webpack提供了,就是vue.config.js文件
devServer: {
proxy: {
'/api': {
target: 'http://gmall-h5-api.atguigu.cn'
}
}
}
//测试发送请求
import { reqCategoryList } from './api' //注意看看使用什么暴露,对应使用什么引入
reqCategoryList()
//现在就可以成功返回数据了
有一个报错需要注意一下
nprogress
进度条的使用npm i nprogress
start
:是进度条的开始
done
:是进度条的结束
在哪里调用?
在请求拦截器和响应拦截器中使用
进度条的样式我们可以在源样式文件中自行修改
//第一步:引入进度条
import nprogress from 'nprogress'
//start:进度条开始 done:进度条结束
//第三步:引入进度条样式才可以看见
import "nprogress/nprogress.css"
//第二步:
//在请求成功的拦截器里开始
nprogress.start();
//在响应拦截器的成功返回情况下
nprogress.done();
vuex
状态管理库vuex
是什么vuex
是官方提供的一个插件:状态管理库,集中管理项目中组件共有的数据,达到共享资源的目的
注意:
不是所有项目都需要
vuex
,小的项目可以不用;如果项目很大,组件很多,数据很多,数据维护费力是,可以使用本项目安装的是
[email protected]
vuex
的核心:state
、 mutations
、 actions
、getters
、modules
vuex
的基本使用复习vuex
新建store文件夹–index.js
import Vue from 'vue'
import Vuex from 'vuex'
//使用插件
Vue.use(Vuex)
//state:仓库存储数据的地方
const state = {}
//actions:响应组件中用户的需求,可以书写业务逻辑
const actions = {}
//mutations:处理state的唯一手段
const mutations = {}
//getters:相当于计算属性,简化仓库数据,让要使用数据的组件更加方便的拿取
const getters = {}
//Vuex对象身上有一个store构造函数,需要暴露Store类一个实例
export default new Vuex.Store({
state,
actions,
mutations,
getters
})
引入了vuex,这样在创建vm实例的时候就多了一个全新配置项 store
在main.js中创建vm时传入store配置项
......
//引入store
import store from './store'
......
//创建vm
new Vue({
el:'#app',
render: h => h(App),
//注册仓库:每个组件的实例vc身上就会多一个$store属性
store
})
将大仓库拆分为小仓库
在大仓库中引入并注册就可以使用了
就是命名空间+模块化(可以看基础笔记
modules: {
home,
search
}
TypeNav
三级联动展示数据业务小仓库准备好,三级联动组件也准备好,那就在三级联动组件中,通知仓库发请求获取数据
第一步
//组件挂载完毕之后,向服务器发请求
mounted() {
//通知vuex发请求,获取数据,存储于仓库当中
this.$store.dispatch("home/categoryList");//注意!!!!!要告诉仓库派发到哪个模块小仓库,加上home/
},
第二步:对应的小仓库中要处理这次组件的派发请求
//这边我们向服务器发请求的操作,之前封装好了一个函数,在小仓库中导入然后调用就可以
//注意:axios返回的是promise,所以我们需要的是promise成功的结果
//注意:返回数据的时候看一下result.data是什么类型,这样我们就可以在state中准备好相应的空数据
//home小仓库中:
state: {
//3:state中的数据默认初始值不可以瞎写,根据返回的data来初始化
categoryList: [],
},
actions: {
//1:通过API里面的接口函数调用,向服务器发请求,获取服务器的数据
async categoryList(context) {
const result = await reqCategoryList();
//成功返回的话我们要修改仓库中的数据了
if (result.code == 200) {
context.commit('CATEGORYLIST', result.data)
}
}
},
mutations: {
CATEGORYLIST(state, categoryList) {
//2:修改state中的categoryList---事先准备好空的categoryList
state.categoryList = categoryList
}
},
//ok现在仓库拿到数据了,接下来就是在组件从仓库拿数据,然后展示即可
//三级联动组件中:我自己用的命名空间的写法,老师没有开命名空间,用的是对象的写法,vuex提供了右侧的一个函数
computed: {
...mapState("home", ["categoryList"]),
},
老师的写法:
computed: {
...mapState({
//右侧是一个vuex提供的函数,使用计算属性时,会执行
//注入一个参数state,其实即为大仓库中的数据(就是小仓库啦)
categoryList:sate=>state.home.categoryList
}),
},
最后展示数据
<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>
今日的错误点