1、基础知识点
1.1 常见ES6
(1)变量和常量
var函数作用域,vue中较少用到
<script>
function show(){
if(1==1){
var name = "武沛齐"; //函数作用域=Python
}
consolo.log(name);
}
show();
</script>
let块级作用域
<script>
if(1==1){
let age = 19; // 块级作用域
}
console.log(age);
</script>
const块级作用域+常量(ref),info不可以被重新赋值,但是info.value可以被重新赋值;
<script>
if(1==1){
const age = 19; // 块级作用域 + 常量(ref)
const info = {id:1,value:18}; // 块级作用域 + 常量(ref)
info.value = 999;
}
console.log(age);
</script>
(2)模版字符串
- js写法
let info = "我是" + "?" + "今年技术";
- 模版字符串拼接
<script>
let name = "张开";
let age = 73;
let info = `我叫${name},今年${age}岁`; // 反引号
</script>
(3)动态参数
...类似于python中的*args、**kwargs,表示可以传入0或多个参数,示例代码如下:
<script>
function info(v1,...data){
console.log(v1,data);
}
info(11);
info(11,22);
info(11,22,333,444,55);
</script>
<script>
function info(v1,v2,v3,v4){
console.log(v1,v2,v3,v4);
}
info(11,22,333,444);
nums = [22,33,44,55,66,77,88];
info(11,...nums) // 将nums打散,传入到info中,若数据多余函数定义参数,则取前面部分数据;若数据少于函数定义参数,则少的部分为未定义;
</script>
(4)解构赋值
<script>
let info = {name:"武沛齐",email:"wupeiqi@live.com",addr:"北京"};
let {name,addr} = info; //花括号中的键对应info中的键
console.log(name);
console.log(addr);
</script>
- Vue3中需要什么就要导入什么,不像vue2中
this.$routerthis.$route。
import {name,addr} from 'vue'
- 解构赋值示例代码【对象解构赋值】
<script>
function getData(n1,{name,addr}){
//let {name,addr} = info;
console.log(name);
console.log(addr);
}
let info = {name:"武沛齐",email:"wupeiqi@live.com",addr:"北京"};
getData(111,info); //将info中的name、addr提取并传入到函数中
</script>
- 解构赋值示例代码【数组解构赋值】
<script>
function getData(n1,[n2,n3,n4]){
console.log(n1,n2,n3,n4)
}
let nums = [11,22,33,44];
getData(100,nums);
</script>
(5)箭头函数
- 发送网络请求时,回调函数必须要使用箭头函数。
<script>
function f1(name,age){
console.log(name,age);
}
f1("张开",99);
//箭头函数
let f2 = (name,age) =>{
console.log(name,age);
}
f2("张开",99);
</script>
- 示例代码【一】
<script>
var name = "源代码";
let info = {
name: "武沛齐",
func: function () {
console.log(this.name); // 函数内部默认都有this关键字; 输出结果为:武沛齐
}
}
info.func();
function getData() {
console.log(this.name); // 函数内部默认都有this关键字,this=window/全局
}
getData(); //输出结果为:源代码
</script>
- 示例代码【二】
<script>
var name = "源代码";
let info = {
name: "武沛齐",
func: function () {
console.log(this.name); // 函数内部默认都有this关键字,this=info对象
// 函数内的函数
function getData() {
console.log(this.name); // 函数内部默认都有this关键字,this=window/全局
}
getData();
}
}
info.func();
</script>
- 示例代码【三】
<script>
var name = "源代码";
let info = {
name: "武沛齐",
func: function () {
console.log(this.name); // 函数内部默认都有this关键字,this=info对象
let getData = () => {
console.log(this.name); // 函数内部默认都有this关键字
}
getData();
}
}
info.func();
</script>
- 示例代码【四】
info.data获取不到数据
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
<script>
let info = {
data: null,
send: function () {
axios({
method: "post",
url: "https://api.luffycity.com/api/v1/auth/password/login/?loginWay=password",
data: {
username: "alex",
password: "dsb"
},
headers: {
"content-type": "application/json"
}
}).then(function (res) {
console.log(this,res);
this.data = res;
})
}
}
info.send();
</script>
- 示例代码【五】
info.data可以获取到数据
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
<script>
let info = {
data: null,
send: function () {
axios({
method: "post",
url: "https://api.luffycity.com/api/v1/auth/password/login/?loginWay=password",
data: {
username: "alex",
password: "dsb"
},
headers: {
"content-type": "application/json"
}
}).then((res) => { // 改动之处
console.log(this,res);
this.data = res;
})
}
}
info.send();
</script>
(6)模块
- 代码
// index.js
// 常量、变量、函数等
export {} // 导出
<script type='module'>
// 引入常量、变量、函数等
</script>
- 截图
- 截图
- 截图
1.2 项目部署
- 第一步:项目编译
npm run build
- 第二步:将编译后的代码dist文件上传到服务器(阿里云、腾讯云)
- 第三步:安装nginx + 配置 + 启动
yum install nginx
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 4096;
include /etc/nginx/mime.types;
default_type application/octet-stream;
include /etc/nginx/conf.d/*.conf;
server {
listen 80;
listen [::]:80;
server_name _;
# 项目目录
root /data/mysite;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
error_page 404 /404.html;
location = /404.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
}
>>>systemctl start nginx
>>>systemctl restart nginx
- 第四步:访问
2、flex【CSS知识点】
- 传统的页面布局:div+css+float实现。
- flex布局更简单。
2.1 容器
(1)布局
<div class='menu'>
<div class='item'>北京</div>
<div class='item'>上海</div>
</div>
<style>
.menu{
display:flex;
}
</style>
(2)元素方向
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.menu {
border: 1px solid red;
width: 500px;
display: flex;
flex-direction: row; /*主轴=横向*/
}
</style>
</head>
<body>
<div class='menu'>
<div class='item'>北京</div>
<div class='item'>上海</div>
</div>
</body>
</html>
(3)元素排列方式
justify-content: 主轴
align-items: 副轴
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.menu {
border: 1px solid red;
width: 500px;
height: 500px;
display: flex;
flex-direction: row; /*主轴=横向*/
/*主轴方向设置*/
/*justify-content: space-evenly;*/
justify-content: space-between;
/*副轴方向设置*/
align-items: center;
/*align-items: flex-start;*/
/*align-items: flex-end;*/
}
.menu .item{
width: 45px;
height: 50px;
border: 1px solid green;
}
</style>
</head>
<body>
<div class='menu'>
<div class='item'>北京</div>
<div class='item'>上海</div>
<div class='item'>深圳</div>
</div>
</body>
</html>
(4)换行、多行控制
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.menu {
border: 1px solid red;
width: 500px;
height: 500px;
display: flex;
flex-direction: row; /*主轴=横向*/
/*justify-content: space-evenly;*/
justify-content: flex-start;
/*align-items: center;*/
align-items: flex-start;
/*align-items: flex-end;*/
/*换行*/
flex-wrap: wrap;
/*多行控制*/
align-content: center;
align-content: flex-start; /*多行文本,从顶部开始*/
}
.menu .item{
width: 45px;
height: 50px;
border: 1px solid green;
}
</style>
</head>
<body>
<div class='menu'>
<div class='item'>北京</div>
<div class='item'>上海</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
</div>
</body>
</html>
- 案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.menu {
border: 1px solid red;
width: 500px;
height: 500px;
display: flex;
flex-direction: row; /*主轴=横向*/
/*justify-content: space-between;*/
justify-content: space-around; /*横轴*/
align-items: flex-start; /*纵轴*/
flex-wrap: wrap;
align-content: flex-start; /*多行文本,从顶部开始*/
}
.menu .item {
width: 150px;
height: 50px;
border: 1px solid green;
}
</style>
</head>
<body>
<div class='menu'>
<div class='item'>北京</div>
<div class='item'>上海</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
<div class='item'>深圳</div>
</div>
</body>
</html>
2.2 元素
(1)顺序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.menu {
border: 1px solid red;
width: 500px;
height: 500px;
display: flex;
flex-direction: row; /*主轴=横向*/
/*justify-content: space-between;*/
justify-content: space-around; /*横轴*/
align-items: flex-start; /*纵轴*/
flex-wrap: wrap;
align-content: flex-start; /*多行文本,从顶部开始*/
}
.menu .item {
width: 50px;
height: 50px;
border: 1px solid green;
}
</style>
</head>
<body>
<div class='menu'>
<!--通过order设置顺序-->
<div class='item' style="order: 1">北京</div>
<div class='item' style="order: 0">上海</div>
<div class='item' style="order: 2">深圳</div>
</div>
</body>
</html>
(2)剩余空间
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.menu {
border: 1px solid red;
width: 500px;
height: 500px;
display: flex;
flex-direction: row; /*主轴=横向*/
/*justify-content: space-between;*/
justify-content: flex-start; /*横轴*/
}
.menu .item {
width: 50px;
height: 50px;
border: 1px solid green;
}
</style>
</head>
<body>
<div class='menu'>
<!--通过flex-grow控制剩余空间【剩余空间占满】-->
<div class='item' style="">北京</div>
<div class='item' style="flex-grow: 2">上海</div>
<div class='item' style="flex-grow: 1">深圳</div>
</div>
</body>
</html>
(3)案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.container {
width: 1100px;
margin: 0 auto;
}
.row1 {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.row1 .company {
width: 210px;
height: 180px;
background-color: saddlebrown;
}
.row1 .pic {
width: 266px;
height: 180px;
background-color: cadetblue;
}
.row2 .title {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.row2 .pic-list {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.row2 .pic-list .big {
background-color: aquamarine;
height: 610px;
width: 210px;
margin-right: 20px;
}
.row2 .pic-list .right-list {
background-color: antiquewhite;
flex-grow: 1;
}
.row2 .pic-list .right-list .group {
display: flex;
flex-direction: row;
justify-content: space-between;
flex-wrap: wrap;
}
.row2 .pic-list .right-list .phone {
margin-bottom: 10px;
border: 1px solid red;
width: 200px;
height: 300px;
}
.course-list {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
.course-list .item {
width: 24%;
height: 100px;
background-color: skyblue;
margin-top: 15px;
}
/*如果最后一个元素,是第3个,右边距=一个位置 + 所有空白位置/3(有三个空白位置)*/
.course-list .item:last-child:nth-child(4n - 1) {
margin-right: calc(24% + 4% / 3);
}
.course-list .item:last-child:nth-child(4n - 2) {
margin-right: calc(48% + 8% / 3);
}
</style>
</head>
<body>
<div class="container">
<div class="row1">
<div class="company"></div>
<div class="pic"></div>
<div class="pic"></div>
<div class="pic"></div>
</div>
<div class="row2">
<div class="title">
<div>手机</div>
<div>查看更多</div>
</div>
<div class="pic-list">
<div class="big"></div>
<div class="right-list">
<div class="group">
<div class="phone"></div>
<div class="phone"></div>
<div class="phone"></div>
<div class="phone"></div>
</div>
<div class="group">
<div class="phone"></div>
<div class="phone"></div>
<div class="phone"></div>
<div class="phone"></div>
</div>
</div>
</div>
</div>
<div class="course-list">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>
</body>
</html>
3、vue-router
3.1 安装
- 方法一
npm install vue-router --save
手动创建文件+配置
- 方法二
vue add router
3.2 必备操作
(1)url传值(get)、动态参数
- App.vue
<template>
<div>
<div class="menu">
<div class="container">
<router-link to="/">源代码教育</router-link>
<router-link to="/home">首页</router-link>
<router-link to="/course">课程1</router-link>
<router-link :to="{path:'/course'}">课程2</router-link>
<router-link :to="{name:'Course'}">课程3</router-link>
<!--url传值(get)-->
<router-link :to="{name:'Course',query:{size:21,page:9}}">课程-21-9</router-link>
<router-link :to="{path:'/course',query:{size:22,page:19}}">课程-22-19</router-link>
<!--url动态参数-->
<router-link :to="{name:'Detail', params:{id:99},query:{size:22,page:3}}">详细-99-22-3</router-link>
<router-link :to="{name:'Detail', params:{id:1},query:{size:122,page:32}}">详细-1-122-32</router-link>
<router-link to="/news">资讯</router-link>
</div>
</div>
<div class="container">
<router-view/>
</div>
</div>
</template>
- CourseView.vue
<template>
<div>
<h1>课程页面</h1>
<h3>讲师:{{name}}</h3>
<div>当前页:{{page}}</div>
<div>数量:{{size}}</div>
</div>
</template>
<script setup>
/* eslint-disable */
/* 引入ref,实现name、page等数据双向绑定 */
import {ref} from 'vue'
import {useRoute, onBeforeRouteUpdate} from 'vue-router'
const route = useRoute();
const name = ref("武沛齐");
const page = ref(route.query.page || 0);
const size = ref(route.query.size || 0);
/* onBeforeRouteUpdate 实现页面渲染数据与地址栏中数据同步变化 */
onBeforeRouteUpdate((to, from) => {
page.value = to.query.page || 0;
size.value = to.query.size || 0;
})
</script>
(2)路由嵌套
- router/index.js
const routes = [
{
path: '/',
name: "Index",
component: HomeView
},
{
path: '/home',
name: 'Home',
component: HomeView
},
/* 路由嵌套 */
{
path: '/pins',
name: 'Pins',
component: HomeView,
children:[
{
path: 'new',
name: 'New',
component: HomeView
},
{
path: 'hot',
name: 'Hot',
component: HomeView
}
]
},
{
path: '/course',
name: 'Course',
component: () => import('../views/CourseView.vue')
},
{
path: '/detail/:id',
name: 'Detail',
component: () => import('../views/DetailView.vue')
},
{
path: '/news',
name: 'News',
component: () => import('../views/NewsView.vue')
}
]
(3)编程式导航
如果想要是跳转+加载组件:
<template>
<div>
<h1>新闻页面</h1>
<input type="button" value="跳转" @click="doClick"/>
</div>
</template>
<script setup>
/* 注意与获取参数时引入的{useRoute}不同 */
import {useRouter} from 'vue-router'
const router = useRouter();
// push与replace在操作浏览器回退按钮时结果不同:push跳转时会建立类似工作栈保存跳转记录[Course、Home、Detail...]的功能,回退时会依据跳转记录逐步往回;而repalce不会;
function doClick() {
//跳转到首页 [Course,]
// router.push({path:"/home"})
// router.push({name:"Home"})
// router.push({name: "Course", query: {page: 10, size: 20}})
router.push({name: "Detail", params: {id: 100}, query: {page: 10, size: 20}})
// router.replace({path:"/home"})
// router.replace({name:"Home"})
// router.replace({name: "Course", query: {page: 10, size: 20}})
// router.replace({name: "Detail", params: {id: 100}, query: {page: 10, size: 20}})
// router.replace({name: "Course", query: {page: 10, size: 20}})
}
</script>
<style scoped>
</style>
- 登录跳转案例,含顶部和不含顶部主要区别在于路由嵌套设置部分。
// 登录跳转【含顶部】
const routes = [
{
path: '/',
name: "Index",
component: HomeView
},
{
path: '/home',
name: 'Home',
component: HomeView
},
{
path: '/login',
name: 'Login',
component: () => import('../views/LoginView.vue'),
},
{
path: '/pins',
// name: 'Pins',
component: () => import('../views/PinsView.vue'),
children: [
{
path: '',
//component: () => import('../views/NewView.vue'),
redirect: {name: "New"},
},
{
path: 'new',
name: 'New',
component: () => import('../views/NewView.vue'),
},
{
path: 'hot',
name: 'Hot',
component: () => import('../views/HotView.vue'),
},
{
path: 'following',
name: 'Following',
component: () => import('../views/FollowingView.vue'),
}
]
},
{
path: '/course',
name: 'Course',
component: () => import('../views/CourseView.vue')
},
{
path: '/detail/:id',
name: 'Detail',
component: () => import('../views/DetailView.vue')
},
{
path: '/news',
name: 'News',
component: () => import('../views/NewsView.vue')
}
]
// 登录跳转【不含顶部】
const routes = [
{
path: '/login',
name: 'Login',
component: () => import('../views/LoginView.vue'),
},
{
path: '/',
component: HomeView,
children: [
{
path: '',
// name: 'Index',
// component: () => import('../views/IndexView.vue'),
redirect: {name: "Index"}
},
{
path: 'index',
name: 'Index',
component: () => import('../views/IndexView.vue'),
},
{
path: '/course',
name: 'Course',
component: () => import('../views/CourseView.vue')
},
{
path: '/news',
name: 'News',
component: () => import('../views/NewsView.vue')
},
{
path: '/pins',
component: () => import('../views/PinsView.vue'),
children: [
{
path: '',
//component: () => import('../views/NewView.vue'),
redirect: {name: "New"},
},
{
path: 'new',
name: 'New',
component: () => import('../views/NewView.vue'),
},
{
path: 'hot',
name: 'Hot',
component: () => import('../views/HotView.vue'),
},
{
path: 'following',
name: 'Following',
component: () => import('../views/FollowingView.vue'),
}
]
},
]
},
]
(4)导航守卫(全局)
- router/index.js
router.beforeEach((to, from, next) => {
// to,即将访问路由对象
// from,当前正要离开路由
// next() 继续向后执行,去to的页面
// next(false) 不跳转,还在当前页面。
// next("/xxx") next({name:"xxx"}) next({pat:"/xxx"})
let token = sessionStorage.getItem("isLogin");
if (token) {
// 已登录,可以向目标地址访问
next();
return
}
// 未登录,登录页面
if (to.name === "Login") {
next();
return;
}
// 未登录,访问的其他地址
next({name: "Login"});
})
4、存储数据
- cookie,超时时间+发送请求自动携带。
- sessionStorage,当浏览器关闭时,自动清除。
- localStorage,长时间存储浏览器。
localStorage.setItem(key,value)
localStorage.getItem(key)
localStorage.removeItem(key)
localStorage.clear()
5、vuex
5.1 安装
vue add vuex (推荐)
5.2 vuex+localStorage存储+导航守卫
- vuex中的js部分
// store/index.js
import {createStore} from 'vuex'
export default createStore({
state: {
// 将localStorage中数据赋值到内存中
username: localStorage.getItem('username'),
token: localStorage.getItem('token')
},
getters: {},
mutations: {
login(state, {username, token}) {
state.username = username;
state.token = token;
//保存在localStorage中
localStorage.setItem('username', username);
localStorage.setItem('token', token);
},
// 注销时清除state及localStorage中数据
logout(state) {
state.username = "";
state.token = "";
//localStorage清除
localStorage.removeItem('username');
localStorage.removeItem('token');
},
},
actions: {},
modules: {}
})
- login中的js部分
// views/LoginView.vue
<script setup>
import {useRouter} from 'vue-router'
import {useStore} from 'vuex'
import {ref} from 'vue'
const router = useRouter();
const store = useStore();
const username = ref("");
const password = ref("");
function doLogin() {
//...
if (username.value.length > 0 && password.value.length > 0) {
console.log("登录成功");
// 调用login方法
const response = {username: username.value, token: "ed5cf238-d8ce-49f9-a1cf-cef55d689a2b", id: 100};
store.commit("login", response)
router.replace({name: "Index"})
} else {
console.log("登录失败");
}
}
</script>
- 登录后页面中的js部分
// views/HomeView.vue
<script setup>
import {ref, computed} from 'vue'
import {useStore} from 'vuex'
import {useRouter} from 'vue-router'
const router = useRouter();
const store = useStore();
const name = ref(store.state.username);
function doLogout() {
//localStorage清空 + state值清空 + 跳转登录
store.commit("logout");
router.push({name: "Login"});
}
</script>
- “登录”/“用户名” 之间切换
// views/HomeView.vue
<div style="float: right">
<span>购物车有{{carNum}}件</span>
<a v-if="name" @click="doLogout">{{name}}</a>
<router-link v-else to="/login">登录</router-link>
</div>
6、vue3-cookies
实现将数据在cookie中进行存取。
6.1 安装
npm install vue3-cookies --save
- main.js
import {createApp} from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import VueCookies from 'vue3-cookies'
createApp(App).use(store).use(router).use(VueCookies).mount('#app')
- 使用
import {useCookies} from 'vue3-cookies'
const {cookies} = useCookies();
cookies.set("ts", "123123", "10s")
- 案例代码
// plugins/cookie.js
import {useCookies} from "vue3-cookies";
const {cookies} = useCookies();
export const getToken = () => {
return cookies.get("token") || "";
}
export const getUserName = () => {
return cookies.get("username") || "";
}
export const setUserToken = (user, token) => {
cookies.set('username', user, "30s");
cookies.set('token', token, "30s");
}
export const clearUserToken = () => {
cookies.remove("username");
cookies.remove("token");
}
// store/index.js
import {createStore} from 'vuex'
// 引入cookie
import {getToken, getUserName,setUserToken,clearUserToken} from "@/plugins/cookie";
export default createStore({
state: {
// username: localStorage.getItem('username'),
// token: localStorage.getItem('token'),
// 从cookie中获取
username: getUserName(),
token: getToken(),
car: localStorage.getItem('carNum') || 0,
},
getters: {},
mutations: {
login(state, {username, token}) {
state.username = username;
state.token = token;
//保存在localStorage中
// localStorage.setItem('username', username);
// localStorage.setItem('token', token);
// cookie中设置
setUserToken(username,token);
},
logout(state) {
state.username = "";
state.token = "";
//localStorage清除
// localStorage.removeItem('username');
// localStorage.removeItem('token');
//cookie中清除
clearUserToken();
},
addCar(state) {
state.car = parseInt(state.car) + 1;
localStorage.setItem('carNum', state.car);
}
},
actions: {},
modules: {}
})
7、axios
7.1 安装
vue add axios
7.2 使用示例
利用axios发起请求时,then(() => {})后加上catch(() => {接收请求异常的回调}),可避免前端出现类似以下红色报错页面;
(1)快速发送
<script setup>
import {useRouter} from 'vue-router'
import {useStore} from 'vuex'
import {ref} from 'vue'
import _axios from "@/plugins/axios";
const router = useRouter();
const store = useStore();
const username = ref("");
const password = ref("");
function doLogin() {
//发送网络请求
_axios.post("/v1/auth/password/login/?loginWay=password", {
username: "alex",
password: "dsb"
}).then((res) => {
// {"code":-1,"msg":"校验错误","data":{"global_error":["密码错误"]}}
console.log("成功", res.data);
}).catch((error) => {
console.log("失败", error);
})
if (username.value.length > 0 && password.value.length > 0) {
console.log("登录成功");
//调用login方法
const response = {username: username.value, token: "ed5cf238-d8ce-49f9-a1cf-cef55d689a2b", id: 100};
store.commit("login", response)
router.replace({name: "Index"})
} else {
console.log("登录失败");
}
}
</script>
(2)拦截器
/* eslint-disable */
import axios from "axios";
import {useRouter} from "vue-router";
import {useStore} from "vuex";
import {getToken} from "@/plugins/cookie";
const router = useRouter();
const store = useStore();
// 设置基本路由
axios.defaults.baseURL = 'https://api.luffycity.com/api/';
// axios.defaults.headers.common['Authorization'] = getToken();
// axios.defaults.headers.post['Content-Type'] = 'application/json';
let config = {
// baseURL: process.env.baseURL || process.env.apiUrl || ""
// timeout: 60 * 1000, // Timeout
// withCredentials: true, // Check cross-site Access-Control
};
const _axios = axios.create(config);
// 请求前拦截器
_axios.interceptors.request.use(
function (config) {
// Do something before request is sent
// console.log("请求前执行");
const token = getToken();
if (token) {
config.headers['token'] = token;
}
return config;
}
);
// 请求返回拦截器,包含两个函数
// 浏览器上有Token,但是Token在后端已经失效
_axios.interceptors.response.use(
// 请求成功拦截
function (response) {
// Do something with response data
// 请求成功 200成功(登录失效了){code:-1,msg:"登录失效"} {code:0,msg:data} {code:1000,msg:"认证失败"}
if (response.data.code === 1000) {
//认证失败,token过期,登录失败 -> 登录页面
store.commit("logout");
router.replace({name: "Login"});
return Promise.reject();
}
return response;
},
// 请求错误拦截
function (error) {
// Do something with response error
// 请求失败自动执行此处的代码,返回的状态码:500(认证401)
if (error.response.status === 401) {
store.commit("logout");
router.replace({name: "Login"});
}
return Promise.reject(error);
}
);
export default _axios;
8、后端API
8.1 跨域问题
- 当前浏览器访问地址:
http://localhost:8080/xxxx/xxxx/ - 点击发送网络请求:
https://api.luffycity.com/api/xxxx/xxxx/ - 本质上想要处理跨域,添加一些响应头即可。
from django.shortcuts import render
from django.http import JsonResponse
def user_list(request):
info = {"code": 0, 'data': "success"}
response = JsonResponse(info)
# 响应头
print(request.method)
# 任意网址
response["Access-Control-Allow-Origin"] = "*"
# 任意的请求方式
response["Access-Control-Allow-Methods"] = "*" # "PUT,DELETE,GET,POST"
# 允许任意的请求头
response["Access-Control-Allow-Headers"] = "*"
return response
注意:测试时一定要移除csrf认证。
8.2 现象(2个请求)
(1)跨域时发送的是:
- 简单请求:1个请求-
- 复杂请求:2个请求
- OPTIONS请求,预检
- 真正的请求
条件:
``1``、请求方式:HEAD、GET、POST
``2``、请求头信息:
``Accept
``Accept``-``Language
``Content``-``Language
``Last``-``Event``-``ID
``Content``-``Type` `对应的值是以下三个中的任意一个
``application``/``x``-``www``-``form``-``urlencoded
``multipart``/``form``-``data
``text``/``plain
注意:同时满足以上两个条件时,则是简单请求,否则为复杂请求
(2)如果非要避免这种情况,那就别跨域
- 主域名+API域名
https://www.luffycity.com/free-course
https://api.luffycity.com/api/v1/course/category/actual/?courseType=actual
- 主域名+API域名
https://www.luffycity.com/free-course
https://www.luffycity.com/api/...
8.3 最后跨域
- 写在中间件的process_response中。
(1)project/md/cors.py
from django.utils.deprecation import MiddlewareMixin
class CorsMiddleware(MiddlewareMixin):
def process_response(self, request, response):
# 任意网址
response["Access-Control-Allow-Origin"] = "*"
# 任意的请求方式
response["Access-Control-Allow-Methods"] = "*" # "PUT,DELETE,GET,POST"
# 允许任意的请求头
response["Access-Control-Allow-Headers"] = "*"
return response
(2)project/settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
# 添加以下代码
'md.cors.CorsMiddleware'
]