1:编程式路由跳转到当前路由(参数不变),多次执行会抛出NavigationDuplicated的警告错误?
--路由跳转有两种形式:声明式导航、编程式导航
--声明式导航没有这类问题的,因为vue-router底层已经处理好了
(通俗来说,多点击两次搜索会警告错误)(该警告对于程序没有任何影响)
1.1 为什么编程式导航进行路由跳转的时候,就有这种警告错误?
"vue-router":"^3.5.3":最新的vue-router引入promise
function push(){
rerurn new Promise((resolve,reject)=>{
})
}
比如有个push函数,返回Promise,Promise需要传入成功与失败回调,没传会警告
1.2 通过给push方法传递相应的成功、失败的回调函数,可以捕获到当前错误,可以解决。( 加上 ()=>{},()=>{} )
1.3 通过底部的代码,可以实现解决错误
this.$router.push({name:"search",params:{keyword:this.keyword},query:{k:this.keyword.toUpperCase()}},()=>{},()=>{});
这种写法:治标不治本,将来在别的组件当中push | replace,编程式导航还有类似错误。
1.4
this:当前组件实例(search)
this.$router属性:当前的这个属性,属性值VueRouter类的一个实例,当在入口文件注册路由的时候,给组件实例添加$router| $route属性
push:VueRouter类的一个实例
如:
function VueRouter(){
}
//原型对象的方法
VueRouter.prototype.push=function(){
//函数的上下文为VueRouter类的一个实例
}
let $router = new VueRouter();
$router.push(xxx);
console.log(this);
console.log(this.$router);
call | | apply区别
相同点:都可以调用函数一次,都可以纂改函数的上下文一次
不同点:call与apply传递参数:call传递参数用逗号隔开,apply方法执行,传递数组
!!!编程式导航进行路由跳转的 “治本” 方法:
//先把VueRouter原型对象的push,先保存一份 //router文件夹->index.js
let originPush = VueRouter.prototype.push;
//重写push|replace
//第一个参数:告诉原来push方法,你往哪里跳转(传递哪些参数)
//第二个参数:成功的回调
//第三个参数:失败的回调
VueRouter.prototype.push=function(location,resolve,reject){
if(resolve && reject){ //成功与失败的回调都传了
//this是VueRouter实例,originPush.call(this)调用的还是VueRouter实例
originPush.call(this,location,resolve,reject);//(originPush()是错误的,调用的上下文是Windows)
}else{
//否则,手动传入两个回调函数
originPush.call(this,location,()=>{},()=>{});
}
}
重写replace与push方法类似(只需将其中的push改为replace)。
2:Home模块组件拆分
--先把静态页面完成
--拆分出静态组件
--获取服务器的数据进行展示
--动态业务
3:三级联动组件完成
---由于三级联动,在Home、Search、Detail,把三级联动注册为全局组件。
好处:只需要注册一次,就可以在项目任意地方使用
<script> <!-- Home文件夹--TypeNav文件夹--index.vue文件 -->
export default {
name: "TypeNav",
};
</script>
import Vue from 'vue' //main.js文件
import App from './App.vue'
//三级联动组件--全局组件
import TypeNav from '@/pages/Home/TypeNav'; //@是src别名
//第一个参数:全局组件的名字 第二个参数:哪一个组件
Vue.component(TypeNav.name,TypeNav)//TypeNav.name可以获取到名字 name: "TypeNav"
<template>
<div>
<!-- main.js:三级联动全局组件:三级联动已经注册为全局组件,因此不需要引入(可直接使用) -->
<TypeNav/>
</div>
</template>
4:完成其余静态组件
HTML + CSS + 图片资源 ---信息【结构、样式、图片资源】
如:商品分类ListContainer
(index.vue文件编写其template和style)
<script> <!-- Home文件夹下的index.vue -->
//引入其余的组件(不是全局组件)
import ListContainer from '@/pages/Home/ListContainer';
export default {
name:'',
components:{
//注册
ListContainer
}
}
</script>
(其余组件,如今日推荐Recommend,商品排行Rank,猜你喜欢Like,商标Brand,底层Floor类似以上实现步骤)
5:POSTMAN测试接口
--刚刚经过postman工具测试,接口是没有问题的
--如果服务器返回的数据code字段200,代表服务器返回数据成功
--整个项目,接口前缀都有/api字样
6:axios二次封装
XMLHttpRequest、fetch、JQ、axios 都是用来向服务器端发送请求,并获得响应
安装axios
报错1
cnpm : 无法加载文件 C:\Users\abc\AppData\Roaming\npm\cnpm.ps1,因
为在此系统上禁止运行脚本。有关详细信息,请参阅 https:/go.microsoft
.com/fwlink/?LinkID=135170 中的 about_Execution_Policies。
所在位置 行:1 字符: 1
+ cnpm install --save axios
+ ~~~~
+ CategoryInfo : SecurityError: (:) [],PSSecurityExc
eption
+ FullyQualifiedErrorId : UnauthorizedAccess
解决办法
从命令提示符(CMD)打开PowerShell
1、单击任务栏上的搜索按钮,输入CMD,启动命令提示符,然后输入:start powershell
2、输入“ set-ExecutionPolicy RemoteSigned”回车
3、根据提示,输入A,回车
(输入get-ExecutionPolicy来验证是否成功,此时系统回复Restricted,表示状态是禁止的)
报错2
set-ExecutionPolicy : 对注册表项“HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Mi
crosoft.PowerShell”的访问被拒绝。 要更改默认(LocalMachine)作用域的执行策略,请使用“以管理员身
份运行”选项启动 Windows PowerShell。要更改当前用户的执行策略,请运行 "Set-ExecutionPolicy -Scop
e CurrentUser"。
所在位置 行:1 字符: 1
+ set-ExecutionPolicy RemoteSigned
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (:) [Set-ExecutionPolicy], UnauthorizedAccessEx
ception
+ FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.PowerShell.Commands.
SetExecutionPolicyCommand
解决方法
1、在Windows PowerShell中执行命令:Set-ExecutionPolicy -Scope CurrentUser
2、接着提示我们输入执行策略(ExecutionPolicy),我们把此策略RemoteSigned输入进去
3、根据提示,输入A,回车
4、输入get-ExecutionPolicy来验证是否成功,若系统回复Restricted,表示状态是禁止的,反之是成功的,如图
5、再次在VS code的终端terminal中输入cnpm install --save axios命令,安装axios成功
6、同时可在package.json文件可以查看axios是否安装成功
6.1 为什么需要进行二次封装axios?
请求拦截器、响应拦截器:
请求拦截器:可以在发请求之前可以处理一些业务
响应拦截器:当服务器数据返回以后,可以处理一些事情
6.2 在项目当中经常API文件夹【axios】
接口当中:路径都带有/api
baseURL:"/api"
(作用:在发请求的路径http://xxx.xxx:8080后面自动带上/api,就不用自己书写/api了,默认变为:http://xxx.xxx:8080/api)
6.3 有的同学axios基础不会,可以参考git|npm 关于axios文档
对于axios进行二次封装 步骤
src文件夹-->api文件夹-->request.js文件
//先安装axios
//对于axios进行二次封装
//引入axios
import axios from 'axios';
//1:利用axios对象的方法create,去创建一个axios实例
//2:request就是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)=>{
//成功的回调函数,服务器响应数据回来以后,响应拦截器可以检测到,可以做一些事情
return res.data;
}),(error=>{
//响应失败的回调函数
return Promise.reject(new Error('false'));//(终止Promise)
});
//(将axios对象requests)对外暴露
export default requests;
src文件夹-->api文件夹-->index.js文件
//当前这个模块:API接口进行统一管理
import requests from "./request";
//三级联动的接口
///api/product/getBaseCategoryList get请求 无参数
//发请求:axios发请求返回结果Promise对象
/*
export const reqCategoryList = ()=> {
return requests({url:'/product/getBaseCategoryList',method:'get'})
};
可以简化:去掉{},去掉return
*/
export const reqCategoryList = ()=> requests({
url:'/product/getBaseCategoryList',
method:'get'});//baseURL已经带上api
入口文件main.js进行测试
//测试
import {reqCategoryList} from '@/api';//分别暴露reqCategoryList,需要带 {}
reqCategoryList();
结果是该请求将出现404错误(跨域问题)
7:接口统一管理
项目很小(如:两个组件,接口1-2个):完全可以在组件的生命周期函数中发请求
项目大(如:数百组件,接口数十个):axios.get('xxx')
7.1 跨域问题
什么是跨域 :协议、域名、端口号不同请求,称之为跨域
http://localhost:8080/#/home ---前端项目本地服务器
http://gmall-h5-api.atguigu.cn ---后台服务器
跨域解决方案:JSONP、CROS、代理
解决跨域
vue.config.js文件中->代理跨域:
module.exports = {
//关闭eslint
lintOnSave:false,
//代理跨域
devServer:{
proxy:{
'/api':{//'/api'(可以其它路径):若路径带有/api,则进行转发
target:' http://gmall-h5-api.atguigu.cn',//获取数据的ip地址
// pathRewrite:{'^/api':''},路径重写(本项目路径中需要有/api,不用重写)
},
},
},
}
配置完vue.config.js(配置文件)需要重新启动(npm run serve),但Edge浏览器无法查看该跨域问题是否解决,需用谷歌浏览器查看。
Edge浏览器:
谷歌浏览器:
报错(可忽略)
Refused to apply style from 'http://localhost:8080/iconfont.css' because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled.
解决
CSS文件中有个import引入,将其删除即可
8:nprogress进度条的使用
start:进度条开始,nProgress.start();
done:进度条结束,nProgress.done();
进度条颜色可以修改的,当然需要修改人家的样式。
进度条使用步骤
src文件夹->api文件夹->request.js文件:
//引入进度条
import nProgress from 'nprogress';
//引入进度条的样式
import 'nprogress/nprogress.css';
请求拦截器代码中添加 nProgress.start();
//进度条开始动
nProgress.start();
响应拦截器代码中添加 nProgress.done();
//进度条结束
nProgress.done();
安装nprogress
同时可在package.json文件可以查看nprogress是否安装成功
修改nprogress进度条颜色
9:vuex状态管理库
9.1 vuex是什么?
vuex是官方提供的一个插件,状态管理库,集中式管理项目中组件共用的数据。
切记,并不是全部项目都需要Vuex,如果项目很小,完全不需要Vuex,如果项目很大,组件很多、数据很多,数据维护很费劲,则可使用Vuex
Vuex的几大核心概念:
state:仓库存储数据的地方
mutations:修改state的唯一手段
action:处理action,可以书写自己的业务逻辑,也可以处理异步
getters:理解为计算属性,用于简化仓库数据,让组件获取仓库的数据更加方便
9.2 Vuex基本使用
(src文件夹下新建store文件夹)
( mapState和mapGetters是映射在计算属性里面的,
而mapActions和mapMutations是映射在methods里面的)
( state中的数据,组件中如何获取?------- this.$store.state.xxx属性
store.js中的getters,组件中如何获取?--------this.$store.getters.xxxx(getters名称) )
安装vuex
报错
peerDependencies WARNING vuex@latest requires a peer of vue@^3.2.0 but vue@2.7.14 was installed
解决
在vuex后添加@3(指定版本为3)
例子(帮助理解)(可忽略)
Home文件夹下的index.vue(可忽略)
<template>
<div>
<button @click="add">点击我加上1</button>
<span>仓库的数据{{count}}</span>
<button>点击我减去1</button>
</div>
</template>
<script>
import {mapState} from 'vuex';//引入mapState辅助函数
export default {
name:'',
computed:{
...mapState(['count']), // 等同于下面的这种方式,ES6 ... 扩展运算符(对象展开符)
// count: function () {
// return this.$store.state.count
// }
},
methods:{
add(){
//派发action
this.$store.dispatch('add');//dispatch调用的是actions里的add方法,commit调用的是mutations里的add方法
}
}
}
</script>
知识点
...mapState(['count']), // 等同于下面的这种方式,ES6 ... 扩展运算符(对象展开符)
// count: function () {
// return this.$store.state.count
// }
//dispatch调用的是actions里的add方法,commit调用的是mutations里的add方法
store文件夹下的index.js(可忽略)
import Vue from 'vue';
import Vuex from 'vuex';//使用vuex前需先引入
//需要使用插件一次
Vue.use(Vuex);
//state:仓库存储数据的地方
const state = {
count:1
};
//mutations:修改state的唯一手段
const mutations = {
ADD(state){
state.count++;
}
};
//action:处理action,可以书写自己的业务逻辑,也可以处理异步
const actions = {
//这里可以书写业务逻辑,但是不能修改state
add({commit}){
commit("ADD");
}
};
//getters:理解为计算属性,用于简化仓库数据,让组件获取仓库的数据更加方便
const getters = {};
//对外暴露Store类的一个实例
export default new Vuex.Store({
state,
mutations,
actions,
getters,
});
main.js文件(可忽略)
//引入仓库
import store from '@/store';
new Vue({
render: h => h(App),
...
//注册仓库:组件实例的身上会多一个书写$store属性
store
...
}).$mount('#app')
9.3 vuex实现模块化开发
如果项目过大,组件过多,接口也很多,数据也很多,可以让vuex实现模块式开发
步骤
创建store文件夹->Search文件夹->index.js文件(Home文件夹同样)
//search模块的小仓库
const state = {};
const mutations = {};
const actions = {};
const getters = {};
export default{//上面写完仍是常量(对象),需对外暴露别的模块才能使用
state,
mutations,
actions,
getters
}
store文件夹->index.js
import Vue from 'vue';
import Vuex from 'vuex';//使用vuex前需先引入
//需要使用插件一次
Vue.use(Vuex);
//引入小仓库
import home from './home';
import search from './search';
//对外暴露Store类的一个实例
export default new Vuex.Store({
//实现Vuex仓库模块式开发存储数据
modules:{
home,
search,
}
});
TypeNav文件夹->index.vue文件
import { mapState } from "vuex";
export default {
name: "TypeNav",
//组件挂载完毕,可以向服务器发请求
mounted() {
//通知Vuex发请求,获取数据,存储于仓库当中
this.$store.dispatch("categoryList");
},
computed: {
...mapState({
//对象写法:右侧需要的是一个函数,当使用这个计算属性的时候,右侧函数会立即执行一次
//注入一个参数state,其实即为大仓库的数据
/* categoryList:(state)=>{
return state.home.categoryList;
} 只有一个参数可去除(),可简化为以下形式*/
categoryList: (state) => state.home.categoryList,
}),
},
};
</script>
store文件夹->home文件夹->index.js文件
import { reqCategoryList } from "@/api";
//home模块的小仓库
const state = {
//state中的数据(data)默认初始值别瞎写,服务器返回对象,起始值则为对象。
//【根据接口返回值确定】
categoryList:[],
}
const mutations = {
CATEGORYLIST(state, categoryList) {
state.categoryList = categoryList;
}
};
const actions = {
//通过API里面的接口函数调用,向服务器发请求,获取服务器的数据
async categoryList({ commit }) {
let result = await reqCategoryList();//await与async需同时存在
//(计算结果是 Promise 对象或者其它值)
if (result.code = 200) {
commit("CATEGORYLIST", result.data);
}
}
};
const getters = {};
export default {
state,
mutations,
actions,
getters
}
快捷键
Ctrl + h 全局替换