axios的二次封装
后台管理系统
import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
// create an axios instance
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 5000 // request timeout
})
// 请求拦截器 携带的token字段
service.interceptors.request.use(
config => {
// do something before request is sent
if (store.getters.token) {
// let each request carry token
// ['X-Token'] is a custom headers key
// please modify it according to the actual situation
config.headers['token'] = getToken()
}
return config
},
error => {
// do something with request error
console.log(error) // for debug
return Promise.reject(error)
}
)
// 相应拦截器
service.interceptors.response.use(
/**
* If you want to get http information such as headers or status
* Please return response => response
*/
/**
* Determine the request status by custom code
* Here is just an example
* You can also judge the status by HTTP Status Code
*/
response => {
const res = response.data
// 服务器响应失败在干什么 真实服务器返回的可能是20000 也可能是200
if (res.code !== 20000 && res.code !== 200) {
Message({
message: res.message || 'Error',
type: 'error',
duration: 5 * 1000
})
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// to re-login
MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
confirmButtonText: 'Re-Login',
cancelButtonText: 'Cancel',
type: 'warning'
}).then(() => {
store.dispatch('user/resetToken').then(() => {
location.reload()
})
})
}
return Promise.reject(new Error(res.message || 'Error'))
} else {
// 服务器响应成功在干什么
return res
}
},
error => {
console.log('err' + error) // for debug
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
export default service
前台管理系统(尚品汇)
// 对于axios进行二次封装
import axios from 'axios'
// 引入进度条
import nprogress from 'nprogress'
// 引入进度条样式
import 'nprogress/nprogress.css'
// start:进度条开始 done:进度条结束
//在当前模块中引入store
import store from '@/store'
// 1.利用axios对象的方法create 去创建一个axios实例
// 2.request就是axios 只不过稍微配置一下
const requests = axios.create({
// 配置对象
// 基础路径 发请求的时候 路径当中会出现api
baseURL:'/api',
// 代表请求超时的时间5s
timeout:5000,
});
// 请求拦截器:在发请求之前 请求拦截器可以检测到 可以在请求发出去之前做一些事情
requests.interceptors.request.use((config)=>{
// config:配置对象 对象里面有一个属性很重要 header请求头
if(store.state.detail.uuid_token){
// 请求头添加一个字段(userTempId) 和后台老师商量好了
config.headers.userTempId = store.state.detail.uuid_token
}
//需要携带token带给服务器
if(store.state.user.token){
config.headers.token = store.state.user.token
}
// 进度条开始动
nprogress.start();
return config;
})
// 响应拦截器
requests.interceptors.response.use((res)=>{
// 成功的回调函数:服务器响应数据回来以后 响应拦截器可以检测到 可以做一些事情
// 进度条借宿
nprogress.done()
return res.data
},(error)=>{
// 响应失败的回调函数
return Promise.reject(new Error('faile'))
})
export default requests
Axios发送请求时params和data的区别
1,params是添加到url的请求路径中后面用于get请求; 2,data是添加到请求体(body)中用于post请求。
浅拷贝和深拷贝
如何区分深拷贝与浅拷贝,简单点来说,就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,拿人手短,如果B没变,那就是深拷贝,自食其力。
浅拷贝
我们来举个浅拷贝例子:
let a=[0,1,2,3,4],
b=a;
console.log(a===b);
a[0]=1;
console.log(a,b);
嗯?明明b复制了a,为啥修改数组a,数组b也跟着变了,这里我不禁陷入了沉思。
深拷贝
1.我们怎么去实现深拷贝呢,这里可以递归递归去复制所有层级属性。
这么我们封装一个深拷贝的函数(PS:只是一个基本实现的展示,并非最佳实践)
采用递归去拷贝所有层级属性
//单级层次的对象或数组
function deepClone(obj){
//Array.isArray()用来确定传递的值是否是一个Array
let objClone = Array.isArray(obj)?[]:{};
if(obj && typeof obj==="object"){
for(key in obj){
//hasOwnProperty()方法返回一个布尔值,只是对象自身属性中是否具有指定属性(此处是key)
if(obj.hasOwnProperty(key)){
//判断ojb子元素是否为对象,如果是,递归复制
if(obj[key]&&typeof obj[key] ==="object"){
objClone[key] = deepClone(obj[key]);
}else{
//如果不是,简单复制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
let a=[1,2,3,4],
b=deepClone(a);
a[0]=2;
console.log(a,b);
//多级层次的对象或数组
function deepClone(obj) {
//Array.isArray()用来确定传递的值是否是一个Array
let objClone = Array.isArray(obj) ? [] : {};
if (obj && typeof obj === "object") {
for (key in obj) {
console.log(key)
//hasOwnProperty()方法返回一个布尔值,只是对象自身属性中是否具有指定属性(此处是key)
if (obj.hasOwnProperty(key)) {
//判断ojb子元素是否为对象,如果是,递归复制
if (obj[key] && typeof obj[key] === "object") {
objClone[key] = deepClone(obj[key]);
} else {
//如果不是,简单复制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
let a = [1, [1,2,3], 3, 4],
b = deepClone(a);
a[1][1] = 1;
console.log(a, b);
console.log(a.hasOwnProperty(4)) // 对于数组来说是判断是否有这个下标
let a = {
name:'123',
age:[18,19,20],
}
b = deepClone(a)
a.age[1]=21
console.log(a,b)
JSON对象的parse和stringify
function deepClone(obj){
let _obj = JSON.stringify(obj),
objClone = JSON.parse(_obj);
return objClone
}
let a=[0,1,[2,3],4],
b=deepClone(a);
a[0]=1;
a[2][0]=1;
console.log(a,b);
注意!!! 缺点: 无法实现对对象中方法的深拷贝,会显示为undefined
slice()和concat()方法不传参
slice()测试例子:
var arr1 = ["1","2","3"];
var arr2 = arr1.slice(0);
arr2[1] = "9";
console.log("数组的原始值:" + arr1 );
console.log("数组的新值:" + arr2 );
concat()测试例子:
var arr1 = ["1","2","3"];
var arr2 = arr1.concat();
arr2[1] = "9";
console.log("数组的原始值:" + arr1 );
console.log("数组的新值:" + arr2 );
注意!!!!!!slice和concat这两个方法,仅适用于对不包含引用对象的一维数组的深拷贝 只适用于一层的结构 如果数组里包含数组以及对象等多层次的结构 这两个方法只能对最外层的结构实现
拓展运算符实现深拷贝
let oldObj = { id: 1 }
let newObj = { ...oldObj }
注意!!!! 拓展运算符的深拷贝同样不能运用于多层次的结构!!!
lodash函数库实现深拷贝
项目中常用
let result = _.cloneDeep(test)
// 按需引入lodash当中的深拷贝
import cloneDeep from 'lodash/cloneDeep'
this.attrInfo=cloneDeep(row)
总结:深拷贝最好使用lodash的cloneDeep方法或者JSON数据转换 其他方法都有坑
项目进度条的使用
1.下载进度条插件
npm i nprogress
2.在axios二次封装的时候引入进度条以及进度条的样式
// 对于axios进行二次封装
import axios from 'axios'
// 引入进度条
import nprogress from 'nprogress'
// 引入进度条样式
import 'nprogress/nprogress.css'
// start:进度条开始 done:进度条结束
3.在请求拦截器和响应拦截器中使用进度条
请求拦截器中
nprogress.start();
响应拦截器中
nprogress.done()
常用的数组的一些方法(高级)
array.map()用法
语法:array.map(function(currentValue,index,arr))
currentValue | 必选,当前元素 |
---|---|
index | 可选,当前元素的索引值 |
arr | 可选,该数组 |
定义:对数组中的每个元素进行处理,得到新的数组;
特点:不改变原有数据的结构和数据
1.map()方法使用return,进行回调;其他方法可不需要。
2.map()方法直接对数组的每个元素进行操作,返回相同数组长度的数组;其他方法可扩展数组的长度。
3.map() 不会对空数组进行检测。
const arr = [
{name:'123',age:18},
{name:'456',age:20},
]
const newArr = arr.map((item)=>{
return{
name1:item.name,
age1:item.age
}
})
console.log(newArr)
const array = [1, 3, 6, 9];
const newArray = array.map((item)=>{
return item + 1;
});
console.log(newArray);
console.log(array);
.filter()
filter方法是对数据中的元素进行过滤,也就是说是不能修改原数组中的数据,只能读取原数组中的数据,callback需要返回布尔值
为true的时候,对应的元素留下来,
为false的时候,对应的元素过滤掉
let obj = [
{name:'123',age:18},
{name:'234',age:19},
{name:'345',age:20},
{name:'456',age:21},
]
let newObj = obj.filter((item)=>{
// return item.age>=19
if(item.age>=19){
return true
}
})
console.log(newObj)
.some()
some 检测数组中是否有满足条件的 返回结果是一个布尔值 如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测。 如果没有满足条件的元素,则返回false。
let arr = [{
name: '小明',
sex: '男',
age: 23
},
{
name: '小红',
sex: '女',
age: 18
},
{
name: '小兰',
sex: '女',
age: 21
},
{
name: '小黑',
sex: '男',
age: 23
}
];
const someResult = arr.some((value, index, arr) => {
console.log(value);
return value.age <= 20;
});
console.log(someResult); // true
-
以上代码给定条件,判断数组中是否存在值的age属性小于等于20时,只要有一个值满足该条件,就返回true
.every()
every方法用于检测数组所有元素是否都符合指定条件 如果数组中检测到有一个元素不满足,则整个表达式返回 false ,且剩余的元素不会再进行检测。 如果所有元素都满足条件,则返回 true。
let arr = [{
name: '小明',
sex: '男',
age: 23
},
{
name: '小红',
sex: '女',
age: 18
},
{
name: '小兰',
sex: '女',
age: 21
},
{
name: '小黑',
sex: '男',
age: 23
}
];
const everyResult = arr.every((value, index, arr) => {
console.log(value);
return value.age <= 20;
});
console.log(everyResult); // false
-
以上代码给定条件,判断数组中是否所有值的age属性小于等于20时,只要有一个值不满足该条件,就返回false
.find()
给定条件,返回数组中第一个满足该条件的值,之后的值不再进行检测,当没有找到满足该条件的值时,返回undefined
let arr = [
{
name: '小明',
sex: '男',
age: 23
},
{
name: '小红',
sex: '女',
age: 18
},
{
name: '小兰',
sex: '女',
age: 21
},
{
name: '小黑',
sex: '男',
age: 23
}
];
const findResult = arr.find((item) => {
console.log(item);
return (item.age === 23);
});
console.log(findResult); // {name: "小明", sex: "男", age: 23}
//return value.age = 16 则返回的值为undefined
-
以上代码给定条件,当数组当前的值的age属性等于23时,返回该值。从数据中我们可以看到,有2项值符合该条件,但是find会找到第一个符合该条件的值,最终返回了第一个符合该条件的所对应的数据 小明,且后面的值不再进行检测
.indexOf()
-
数组的indexOf方法是查找 查找元素在数组中是否存在 存在则返回第一个找到的下标 找不到则返回-1
let arr = ['orange', '2016', '2016']; arr.indexOf('orange'); //0 arr.indexOf('o'); //-1 arr.indexOf('2016'); //1 arr.indexOf(2016); //-1
//这里用的是严格等于(===)。大家做类似判断的时候多留意。不要误认为数字会转成字符串,同理字符串也不会转换成数字。
-
实战
-
var arr = [
{"id":1,"name":"Tom"},
{"id":2,"name":"Cathy"},
{"id":3,"name":"Jack"}
];
var obj = arr[0];
// console.log(obj)
var theObj = {"id":1,"name":"Tom"};
// console.log(theObj)
console.log(obj==theObj) //false
console.log(arr.indexOf(theObj)); // -1
console.log(arr.indexOf(obj)); // 0
//上面这题想了很久才想通
// obj的值是arr数组里的第一项 所以他们的内存空间地址指向的是同一个地址
// 而theObj看起来是数组的第一项 数据也都一样 但是它的内存空间地址指向的是它新建的
// 因为 obj和theObj都是 Object类型的 他们之间的比较相等 比较的是指向的地址 他俩地址不同 所以比较为false
// obj指向的地址和arr[0] 是一样的 所以能indexOf能找到
// theObj则是新开辟了一个地址 在arr中找不到这个地址 所以 indexOf返回的是-1
-
字符串也有这个方法 但不可以匹配正则 匹配正则需要用search
reduce()方法详解及高级技巧
详情博客
JS数组reduce()方法详解及高级技巧_行走在边缘的博客-CSDN博客_js中数组的reduce方法
深度选择器
-
scoped属性的作用 ------ 加上scoped的作用是只是对于当前的组件有用(样式) 对于某一个组件,如果style添加上scoped属性,给当前子组件的结构中都添加上一个data-v-xxx自定属性 会发现vue是通过属性选择器,给需要添加的元素添加上样式 例如:h3[data-v-7ba5bd90]
-
子组件的根标签(拥有父组件当中自定义属性:一样的),如果子组件的根节点和父组件中书写的样式相同,也会添加上相应的样式
-
注意!!! 如果父组件的样式(scoped) 而且还想影响到子组件的样式 像这种情况我们可以使用深度选择器
61)深度选择器
>>> 一般用于原生CSS /deep/ 一般用于less ::v-deep 一般用户scss
echarts
在非vue项目中
-
引入echarts依赖包
-
准备一个容器:容器就是显示图标的区域 (可以通过style来设置容器的宽和高)
-
基于准备好的dom,初始化echarts实例
-
创建echarts实例
-
指定图表的配置项和数据
<body> <!-- 准备一个容器:容器就是显示图标的区域 --> <div></div> <script> // 基于准备好的dom,初始化echarts实例 let dom = document.querySelector("div") // 创建echarts实例 let mycharts = echarts.init(dom) // 指定图表的配置项和数据 mycharts.setOption({ // 图标的标题 title:{ // 主标题的设置 text:'数据可视化', // 子标题 subtext:'echarts基本使用', // 主标题的颜色 textStyle:{ color:'#bfa' }, left: "center", }, // x轴的配置项 xAxis:{ // 数据 data:['衣服','直播','游戏','电影'] }, // y轴的配置项 yAxis:{ // 设置y轴的线 axisLine: { show: true }, // 显示y轴的刻度 axisTick: { show: true } }, // 系列的设置:绘制什么类型的图表 数据的展示在这里设置 series:[ { // 图表类型的设置 type:'bar', // bar是柱状图 line折线图 pie饼图 // 图表的数据 data:[10,20,30,40], color:'red' } ] }) </script> </body>
-
具体各种的配置项查看官方文档 Apache ECharts
-
在Vue项目中使用echarts
-
安装插件
npm i echarts@4.9.0
注意如果不指定安装版本的话是安装最新的版本 版本号是5.多 但是5.多的版本在vue项目中会出现问题 -
引入
import echarts from 'echarts'
对容器打ref 在mounted() 中获取对应图表的dom然后进行操作 以及要对图表初始化echarts实例echart.init()
<template> <!-- 容器 --> <div class="charts" ref="charts"></div> </template> <script> // 引入echarts import echarts from "echarts"; export default { name: "", mounted() { // 初始化echarts实例 let lineCharts = echarts.init(this.$refs.charts); lineCharts.setOption({ xAxis: { // 隐藏x轴 show: false, type: "category", }, yAxis: { // 隐藏y轴 show: false, }, series: [ { type: "line", data: [10, 7, 33, 12, 48, 9,29,10,44,], // 曲线平滑 smooth:true, // 拐点的样式的设置 itemStyle: { opacity: 0, }, // 线条的样式 lineStyle: { color: "purple", }, // 填充颜色 areaStyle: { color: { type: "linear", x: 0, y: 0, x2: 0, y2: 1, colorStops: [ { offset: 0, color: "purple", // 0% 处的颜色 }, { offset: 1, color: "#fff", // 100% 处的颜色 }, ], global: false, // 缺省为 false }, }, }, ], // 布局的调试 grid: { left: 0, top: 0, right: 0, bottom: 0, }, }); }, }; </script> <style> .charts { width: 100%; height: 100%; } </style>
-
动态展示echarts
-
例子:切换图标的标题 数据 颜色
// 准备一个容器 <div class="charts" ref="charts"></div> //因为mounted 只是在页面加载完毕后挂载 它只能挂载一次 如果需要修改echarts里的数据 就需要将echarts转化为data里的动态展示的数据 让它成为vm里的响应式数据 //所以需要在data里return mycharts: null, //所以在mounted中可以直接使用this.mycharts //在mounted中挂载该容器 该容器处于初始化阶段 mounted() { this.mycharts = echarts.init(this.$refs.charts); // 配置数据 this.mycharts.setOption({ title: { text: "销售额趋势", }, tooltip: { trigger: "axis", axisPointer: { type: "shadow", }, }, grid: { left: "3%", right: "4%", bottom: "3%", containLabel: true, }, xAxis: [ { type: "category", data: [ "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月", ], axisTick: { alignWithLabel: true, }, }, ], yAxis: [ { type: "value", }, ], series: [ { name: "Direct", type: "bar", barWidth: "60%", data: [10, 52, 200, 334, 390, 330, 220, 60, 80, 90, 60, 44], color: "pink", }, ], }); }, //修改mycharts的配置项和数据 //因为要对修改数据 所以要对数据进行监听 //activeName是v-model获得的 而title是对activeName计算出来的 所以对title进行监听 当然也可以其他的进行监听 这里就只是举一个例子 //例如:对title进行监听 只要title一发生变化 就重新修改图表的配置数据 //注意!!!!!!图表配置数据可以再次修改 如果有新的数值 新的数值 没有新的数值 还是用以前的 this.mycharts.setOption({ title: { text: this.title, }, series: [ { name: "Direct", type: "bar", barWidth: "60%", data: this.list, color: this.color }, ], }); }, //在绑定标签的时候会v-model获取到title的内容 //computed里的数据 computed: { //计算属性之标题 title() { return this.activeName == "sale" ? "销售额" : "访问量"; }, list(){ return this.activeName == "sale" ?[10, 52, 200, 334, 390, 330, 220, 60, 80, 90, 60, 44]:[1320, 5232, 2300, 3334, 3905, 3630, 2202, 6150, 80322, 9120, 640, 444] }, color(){ return this.activeName == "sale" ? "pink" : "blue"; } },
-
-
注意!!!! 使用echarts要多看官方的文档! 配置项手册
路由懒加载
正常的写路由的方法
-
例子
import Home from '@/views/Home'
const routes=[
{
name:'home',
path:'/home',
component:Home,
}
]
优化的方法
-
路由懒加载(按需加载)
const routes=[
{
name:'home',
path:'/home',
component:()=>import('@/views/Home')
}
]
为什么需要懒加载
-
像vue这种单页面应用,如果没有应用懒加载,运用webpack打包后的文件将会异常的大,造成进入首页时,需要加载的内容过多,时间过长,会出啊先长时间的白屏,即使做了loading也是不利于用户体验,而运用懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用时
-
不需要在最上面引入import
防抖与节流
防抖和节流是针对响应跟不上触发频率这类问题的两种解决方案。 在给DOM绑定事件时,有些事件我们是无法控制触发频率的。 如鼠标移动事件onmousemove, 滚动滚动条事件onscroll,窗口大小改变事件onresize,瞬间的操作都会导致这些事件会被高频触发。 如果事件的回调函数较为复杂,就会导致响应跟不上触发,出现页面卡顿,假死现象。
防抖(debounce)
防抖:就是将一段时间内连续的多次触发转化为一次触发
debounce的特点是当事件快速连续不断触发时,动作只会执行一次。 延迟debounce,是在周期结束时执行,前缘debounce,是在周期开始时执行。但当触发有间断,且间断大于我们设定的时间间隔时,动作就会有多次执行。
function debounce(fn, wait) {
let timeout = null
return function() {
if(timeout){
clearTimeout(timeout)
}
timeout = setTimeout(fn, wait);
}
}
function handle() {
console.log(Math.random())
}
window.addEventListener('scroll', debounce(handle, 1000))
节流(throttle)
节流:减少一段时间内触发的频率
当高频事件触发时,第一次会立即执行(给scroll事件绑定函数与真正触发事件的间隔一般大于delay,如果你非要在网页加载1000毫秒以内就去滚动网页的话,我也没办法o(╥﹏╥)o),而后再怎么频繁地触发事件,也都是每delay时间才执行一次。而当最后一次事件触发完毕后,事件也不会再被执行了 (最后一次触发事件与倒数第二次触发事件的间隔小于delay,为什么小于呢?因为大于就不叫高频了呀(╹▽╹))。
-
函数节流主要有两种实现方法:时间戳和定时器。接下来分别用两种方法实现throttle~
//时间戳
function throttle(fn, delay) {
var prev = Date.now()
return function() {
if (Date.now() - prev > delay) {
fn()
prev = Date.now()
}
}
}
function handle() {
console.log(Math.random())
}
window.addEventListener('scroll', throttle(handle, 1000))
//定时器
var throttle = function(func, delay) {
var timer = null;
return function() {
var context = this;
var args = arguments;
if (!timer) {
timer = setTimeout(function() {
func.apply(context, args);
timer = null;
}, delay);
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
区别
-
防抖是将多次执行变为最后一次执行,节流是将多次执行变为每隔一段时间执行
-
debounce和throttling 各有特点,在不同 的场景要根据需求合理的选择策略。如果事件触发是高频但是有停顿时,可以选择debounce; 在事件连续不断高频触发时,只能选择throttling,因为debounce可能会导致动作只被执行一次,界面出现跳跃。
-
区别:两者区别在于函数节流是固定时间做某一件事,比如每隔1秒发一次请求。而函数防抖是在频繁触发后,只执行一次(两者的前提都是频繁触发)
-
防抖是在某个时间段内只会执行一次,如果间隔时间内再次出发事件,则清除上次定时器,重新开始定时器,只有最后一次操作会被执行。节流是间隔时间执行,不管触发频率多频繁,都会保证在规定时间内执行一次。
做尚品汇项目中有用到,在依赖中lodash可以直接用
import throttle from 'lodash/throttle' <h3 @mouseenter="changeIndex(index)"></h3> // throttle回调函数别用箭头函数 可能出现上下文this问题 changeIndex:throttle(function(index){ // index鼠标移上某一个一级分类的元素的索引值 this.currentIndex = index; // console.log("鼠标进入"+index) },50),