购物车
购物车时多对多的关系
- 用户
- 商品
- 订单
问题:一个购物车有多个商品,只买其中一部分商品,如何将这多个商品变成一个订单
答:一个订单对应的是多个已经购买的商品,一个商品只对应一个订单,而不是一个商品可以对应多个订单,比如两个人同时在一家店都买了两个苹果,那么第一个人买的苹果和第二个人买的苹果不是相同的。
表关系
订单表
- 属于哪个用户
订单商品表
- 订单商品表的数据从购物车里下单的来,下单时,将购物车中对应的商品删除,放入订单表中
地址
- 每个订单对应一个地址
- 一个地址可以对应多个订单
- 订单会级联收获地址表
- 优惠卷
添加购物车
需要用户
- 如果用户未登录,直接跳转到登录界面
需要商品
- 传递商品唯一标识
添加的合法性
- 新商品首次加入购物车,数据不存在,创建购物车数据
- 此商品数据在购物车中存在,直接将数量加一。
购物车模块表单之间的关系图:
这里的关系图只是其中一部分,还有其他比如评价表,优惠卷,积分表表等等,可以额外扩展。
下面我们先来实现,Goods,Cart,User,order,ordergoods这五张表。
Models
Cart
class Cart(models.Model):
c_user = models.ForeignKey(peiqi1user)
c_goods = models.ForeignKey(Goods)
c_goods_num = models.IntegerField(default=1)
c_is_select = models.BooleanField(default=True)
class Meta:
db_table = 'peiqi1_cart'
然后迁移数据库。
接着我们要将商城中商品的数量控制按钮完善好:
market.css:
.subShopping .addShopping {
font-size: 0.4rem;
font-weight: 100;
}
market.html:(76-80)给按钮标签添加选择器
<section>
<button class="subShopping">—</button>
<span>0</span>
<button class="addShopping">+</button>
</section>
在market.js中添加点击事件:
$(function () {
$('#all_types').click(function(){
console.log('全部类型');
var $all_types_container = $('#all_types_container'); /* 找到all_types_container的div标签 */
$all_types_container.show(); /* 显示 */
var $all_type = $(this); /* 找到箭头的位置 */
var $span = $all_type.find('span').find('span'); /* 取出箭头对应的class */
$span.removeClass('glyphicon-chevron-down').addClass('glyphicon-chevron-up');
/* 打开全部分类时,收起综合排序 */
var $sort_rule_container = $('#sort_rule_container');
$sort_rule_container.slideUp();
var $sort_rule = $('#sort_rule');
var $span_sort_rule = $sort_rule.find('span').find('span');
$span_sort_rule.removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down');
})
$('#all_types_container').click(function () {
var $all_type_container = $(this);
$all_type_container.hide();
var $all_type = $('#all_types'); /* 找到箭头的位置 */
var $span = $all_type.find('span').find('span'); /* 取出箭头对应的class */
$span.removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down');
})
$('#sort_rule').click(function(){
console.log('排序规则');
var $sort_rule_container = $("#sort_rule_container");
$sort_rule_container.slideDown() /* 下弹特效显示 */
var $sort_rule = $(this);
var $span = $sort_rule.find('span').find('span');
$span.removeClass('glyphicon-chevron-down').addClass('glyphicon-chevron-up');
/* 打开综合排序,自动关闭全部分类 */
var $all_type_container = $('#all_types_container');
$all_type_container.hide();
var $all_type = $('#all_types'); /* 找到箭头的位置 */
var $span_all_type = $all_type.find('span').find('span'); /* 取出箭头对应的class */
$span_all_type.removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down');
})
$('#sort_rule_container').click(function () { /* 点击事件,上拉收起菜单 */
var $sort_rule_container = $(this);
$sort_rule_container.slideUp();
var $sort_rule = $('#sort_rule');
var $span = $sort_rule.find('span').find('span');
$span.removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down');
})
$('.subShopping').click(function (){
console.log('sub');
})
$('.addShopping').click(function (){
console.log('add');
})
})
生成购物车数据,即商品数据和用户的关系,哪个商品,登录的是哪个用户,然后生成一条购物车数据,还要考虑这条数据在我们的购物车中有没有,有的话改变数量,没有就重新建。
商品的唯一标识是id,用户的唯一标识id、token、session都可以。
下面我们要实现购物车中商品数量的改变,就要在js中获得对应商品的id:
market.html:
<button class="subShopping">—</button>
<span>0</span>
<button goodsid="{{ goods.id }}" class="addShopping">+</button>
market.js:
$('#sort_rule_container').click(function () { /* 点击事件,上拉收起菜单 */
var $sort_rule_container = $(this);
$sort_rule_container.slideUp();
var $sort_rule = $('#sort_rule');
var $span = $sort_rule.find('span').find('span');
$span.removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down');
})
$('.subShopping').click(function (){
console.log('sub');
})
$('.addShopping').click(function (){
console.log('add');
var $add = $(this);
var goodsid = $add.attr('goodsid'); /* 获得goodsid */
/*将goodsid发送给后端*/
$.get('/peiqi1/addtocart/', {'goodsid':goodsid}, function(data){
console.log(data);
})
})
})
添加url:
url(r'^addtocart/', views.addtocart, name='add_to_cart'),
函数封装判断用户登录
要改动购物车中商品的数量,保证用户要登录
所以我们要验证用户是在登录状态下对购物车进行操作:
views: redirect重定向的行为是浏览器才能识别详细请看下方浏览器行为介绍
def addtocart(request):
goodsid = request.GET.get('goodsid')
# print(request.user)
user_id = request.session.get('user_id')
if user_id:
return HttpResponse('ADD SUCESS')
else:
# 因为我们是利用ajax请求来返回数据,所以直接使用redirect ajax不会识别出这个是重定向请求,所以只会在控制台打印出html代码
data = {
'status': 302,
'msg': 'not login'
}
return JsonResponse(data=data)
market.js:
$(function () {
$('#all_types').click(function(){
console.log('全部类型');
var $all_types_container = $('#all_types_container'); /* 找到all_types_container的div标签 */
$all_types_container.show(); /* 显示 */
var $all_type = $(this); /* 找到箭头的位置 */
var $span = $all_type.find('span').find('span'); /* 取出箭头对应的class */
$span.removeClass('glyphicon-chevron-down').addClass('glyphicon-chevron-up');
/* 打开全部分类时,收起综合排序 */
var $sort_rule_container = $('#sort_rule_container');
$sort_rule_container.slideUp();
var $sort_rule = $('#sort_rule');
var $span_sort_rule = $sort_rule.find('span').find('span');
$span_sort_rule.removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down');
})
$('#all_types_container').click(function () {
var $all_type_container = $(this);
$all_type_container.hide();
var $all_type = $('#all_types'); /* 找到箭头的位置 */
var $span = $all_type.find('span').find('span'); /* 取出箭头对应的class */
$span.removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down');
})
$('#sort_rule').click(function(){
console.log('排序规则');
var $sort_rule_container = $("#sort_rule_container");
$sort_rule_container.slideDown() /* 下弹特效显示 */
var $sort_rule = $(this);
var $span = $sort_rule.find('span').find('span');
$span.removeClass('glyphicon-chevron-down').addClass('glyphicon-chevron-up');
/* 打开综合排序,自动关闭全部分类 */
var $all_type_container = $('#all_types_container');
$all_type_container.hide();
var $all_type = $('#all_types'); /* 找到箭头的位置 */
var $span_all_type = $all_type.find('span').find('span'); /* 取出箭头对应的class */
$span_all_type.removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down');
})
$('#sort_rule_container').click(function () { /* 点击事件,上拉收起菜单 */
var $sort_rule_container = $(this);
$sort_rule_container.slideUp();
var $sort_rule = $('#sort_rule');
var $span = $sort_rule.find('span').find('span');
$span.removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down');
})
$('.subShopping').click(function (){
console.log('sub');
})
$('.addShopping').click(function (){
console.log('add');
var $add = $(this);
var goodsid = $add.attr('goodsid'); /* 获得goodsid */
/*将goodsid发送给后端*/
$.get('/peiqi1/addtocart/', {'goodsid':goodsid}, function(data){
console.log(data);
if (data['status'] === 302){ /* 如果状态是302,那么就 */
window.open('/peiqi1/login/', target='_self')
}
})
})
})
82-93行判断如果返回302,也就是用户未登录的状态下,那么就重定向到登录界面
中间件封装判断用户登录
这里我们也可以用中间件封装用户登录
新建middleware 的python包,然后在该文件夹中新建Middleware的python文件
middleware.py:
from django.http import JsonResponse
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.deprecation import MiddlewareMixin
from App.models import peiqi1user
# 需要验证登录的接口
REQUIRE_LOGIN = [
'/peiqi1/addtocart/',
]
class LoginMiddleware(MiddlewareMixin):
def process_request(self, request):
if request.path in REQUIRE_LOGIN: # 如果登录的用户在列表中就执行下方登录操作
user_id = request.session.get('user_id') # 获取用户登录状态信息
if user_id: # 如果user_id存在
try:
user = peiqi1user.objects.get(pk=user_id)
request.user = user # 这样之后的操作就不需要去数据库中进行查询
except: # 出现异常则重新登录
# return redirect(reverse('peiqi1:login'))
data = {
'status': 302,
'msg': 'user not avaliable',
}
return JsonResponse(data=data)
else: # 如果id不存在则返回登陆界面
# return redirect(reverse('peiqi1:login'))
data = {
'status': 302,
'msg': 'user not login',
}
return JsonResponse(data=data)
然后在settings中注册该文件:
srttings.py:
MIDDLEWARE = [
'debug_toolbar.middleware.DebugToolbarMiddleware',
'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',
'middleware.middleware.LoginMiddleware',
]
这样我们只要能执行addtocart函数的对象,都是经过中间件验证的登录成功的对象:
views:
def addtocart(request):
goodsid = request.GET.get('goodsid')
print(request.user) # 打印出用户
data = {
'status': 200,
}
return JsonResponse(data=data)
商品数量增/删实现
增实现
url:
url(r'^addtocart/', views.addtocart, name='add_to_cart'),
在改动商品数量之前我们还要判断商品是否已经存在在购物车中:
views:
def addtocart(request):
goodsid = request.GET.get('goodsid')
# 从数据库中取出对应用户的购物车
carts = Cart.objects.filter(c_user=request.user).filter(c_goods_id=goodsid)
if carts.exists(): # 如果商品在购物车表单中存在
cart_obj = carts.first()
cart_obj.c_goods_num = cart_obj.c_goods_num + 1
else:
cart_obj = Cart()
cart_obj.c_goods_id = goodsid # 添加到商品表单中
cart_obj.c_user = request.user
cart_obj.save()
data = {
'status': 200,
'msg': 'add_success',
'c_goods_num': cart_obj.c_goods_num
}
return JsonResponse(data=data)
测试:
当我们点击加号时,控制台会返回添加成功
并且当我们观察数据库时,发现cart表中生成了对应的商品,及其数量:
减实现
url:
url(r'^subtocart/', views.subtocart, name='sub_to_cart'),
views:
记得subtocart函数要放入中间件的用户验证中!!!否则会报TypeError: 'AnonymousUser' object is not iterable
def subtocart(request):
goodsid = request.GET.get('goodsid')
# 从数据库中取出对应用户的购物车
carts = Cart.objects.filter(c_user=request.user).filter(c_goods_id=goodsid)
if carts.exists(): # 如果商品在购物车表单中存在
cart_obj = carts.first()
data = {
'status': 200,
'msg': 'sub_success',
'c_goods_num': cart_obj.c_goods_num
}
if cart_obj.c_goods_num > 1: # 如果数量大于1就减少1
cart_obj.c_goods_num = cart_obj.c_goods_num - 1
cart_obj.save()
data['c_goods_num'] = cart_obj.c_goods_num
return JsonResponse(data=data)
else: # 如果数量小于等于1就从数据库中删除
cart_obj.delete()
data['c_goods_num'] = 0
return JsonResponse(data=data)
else: # 如果数量为0,则直接返回0
return JsonResponse(data={'status': 200, 'msg': 'sub_success', 'c_goods_num': 0})
market.js:
$('.subShopping').click(function (){
console.log('sub');
var $sub =$(this);
var goodsid = $sub.attr('goodsid');
$.get('peiqi1/subtocart/',{'goodsid':goodsid}, function (data){
console.log(data);
if (data['status'] === 302){ /* 如果状态是302,那么就重定向到登陆界面 */
window.open('/peiqi1/login/',target='_self');
}else if(data['status'] === 200){
$sub.next('span').html(data['c_goods_num']); /*前端前端对应商品的数量标签*/
}
})
})
前端商品数量同步显示
现在我们要将商品的数量同步显示在用户的前端页面上:
market.js:
$('.addShopping').click(function (){
console.log('add');
var $add = $(this);
var goodsid = $add.attr('goodsid'); /* 获得goodsid */
/*将goodsid发送给后端*/
$.get('/peiqi1/addtocart/', {'goodsid':goodsid}, function(data){
console.log(data);
if (data['status'] === 302){ /* 如果状态是302,那么就重定向到登陆界面 */
window.open('/peiqi1/login/', target='_self');
}else if(data['status']===200){
$add.prev('span').html(data['c_goods_num']); /*前端前端对应商品的数量标签*/
}
})
})
测试:
Cart页面检查是否登录
只有登录的用户可以进入购物车页面,未登录者跳转到登录页面
之前我们在中间件中实现了ajax中验证用户是否登录
为了实现这个功能,我们需要在中间件中添加一个新的功能,用来验证用户在访问一个普通页面时是否登录:
REQUIRE_LOGIN_JSON = [
'/peiqi1/addtocart/',
]
REQUIRE_LOGIN = [
'/peiqi1/cart/',
]
class LoginMiddleware(MiddlewareMixin):
def process_request(self, request):
if request.path in REQUIRE_LOGIN_JSON: # 如果登录的用户在列表中就执行下方登录操作
user_id = request.session.get('user_id') # 获取用户登录状态信息
if user_id: # 如果user_id存在
try:
user = peiqi1user.objects.get(pk=user_id)
request.user = user # 这样之后的操作就不需要去数据库中进行查询
except: # 出现异常则重新登录
# return redirect(reverse('peiqi1:login'))
data = {
'status': 302,
'msg': 'user not avaliable',
}
return JsonResponse(data=data)
else: # 如果id不存在则返回登陆界面
# return redirect(reverse('peiqi1:login'))
data = {
'status': 302,
'msg': 'user not login',
}
return JsonResponse(data=data)
if request.path in REQUIRE_LOGIN:
user_id = request.session.get('user_id') # 获取用户登录状态信息
if user_id: # 如果user_id存在
try:
user = peiqi1user.objects.get(pk=user_id)
request.user = user # 这样之后的操作就不需要去数据库中进行查询
except: # 出现异常则重新登录
return redirect(reverse('peiqi1:login'))
else: # 如果id不存在则返回登陆界面
return redirect(reverse('peiqi1:login'))
Cart页面的搭建
下面我们要将商品在cart页面中展示出来:
views:
def cart(request):
carts = Cart.objects.filter(c_user=request.user)
data = {
'title': '购物车',
'carts': carts
}
return render(request, 'main/cart.html', context=data)
cart.css:
/* 底部图标 */
footer .cart span {
background: url(/static/img/cart_selected.png) no-repeat;
background-size: 0.513889rem;
}
footer .cart dd{
color: orange;
}
/*
Container
*/
#cart {
padding: 1.5rem 0;
z-index: +15;
width: 100%;
background: #fafafa;
overflow: hidden;
}
h3 {
text-align: center;
position: fixed;
z-index: +100;
width: 100%;
border-bottom: 0.04rem solid lightgrey;
line-height: 1.5rem;
background: yellow;
top: 0;
}
.full > section {
background: lightpink;
}
.full > section > ul {
border: 0.2rem dashed lightgreen;
border-width: 0.1rem 0;
margin-bottom: 0.2rem;
}
.full > section > ul > li {
padding-left: 0.3rem;
line-height: 0.8rem;
font-size: 0.375rem;
}
.clear:after {
content: "";
display: block;
visibility: hidden;
clear: both;
height: 0;
}
.full > section > ul > li > div > p:last-child {
float: right;
width: 78%;
}
.full > section > ul > li > div > p:last-child > span {
padding: 0.15rem;
}
.infoJustify {
float: left;
width: 20%;
height: 0.8rem;
overflow: hidden;
text-align: justify;
}
.infoJustify > b {
display: inline-block;
width: 100%;
}
.change {
float: right;
padding-right: 0.2rem;
}
/*
闪送超市
*/
.bill {
line-height: 0.75rem;
position: relative;
border-bottom: 0.04rem solid lightgrey;
}
.bill > p {
padding: 0 0.3rem;
font-size: 0.3rem;
color: green;
}
.bill > p:first-child:before {
width: 0.2rem;
height: 0.3rem;
background: yellow;
content: ".";
color: yellow;
margin-right: 0.2rem;
}
.bill > a {
border: 0.05rem solid orangered;
position: absolute;
border-radius: 0.3rem;
padding: 0 0.3rem;
font-size: 0.35rem;
line-height: 0.65rem;
top: 0.2rem;
right: 0.5rem;
}
/*************收货时间*****收货备注************/
.delivery {
line-height: 1.5rem;
border-bottom: 0.04rem solid lightgrey;
font-size: 0.4rem;
padding: 0 0.3rem;
}
.delivery > span:first-child {
padding-right: 0.3rem;
}
.delivery > span:nth-child(2) {
color: orangered;
}
.delivery > a:last-child {
float: right;
}
.delivery > input {
height: 0.8rem;
line-height: 0.8rem;
border-radius: 0.1rem;
border-width: 0.04rem;
width: 70%;
}
/**************menu***************/
.menuList {
border-bottom: 0.04rem solid lightgrey;
height: 2.5rem;
position: relative;
}
.confirm, .all_select{
padding: 0.95rem 0;
width: 15%;
height: 2.5rem;
display: inline-block;
box-sizing: border-box;
text-align: center;
float: left;
}
.confirm > span, .all_select>span{
box-sizing: border-box;
border: 0.04rem solid orange;
background: white;
display: inline-block;
width: 0.6rem;
height: 0.6rem;
overflow: hidden;
border-radius: 50%;
line-height: 0.6rem;
}
.confirm > span > span, .all_select>span>span {
background: yellow;
font-size: 0.5rem;
display: block;
}
.menuList > a {
width: 84%;
display: inline-block;
font-size: 0.4rem;
line-height: 1rem;
}
.menuList > a > img {
margin-top: 0.25rem;
width: 25%;
height: 100%;
float: left;
}
.menuList > a > p {
width: 70%;
height: 1rem;
float: right;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.menuList > section {
position: absolute;
right: 0.4rem;
bottom: 0.4rem;
height: 0.75rem;
border-radius: 1rem;
}
.menuList > section > button {
background: white;
border: 1px solid orange;
border-radius: 1111px;
color: red;
display: inline-block;
text-align: center;
line-height: 0.65rem;
font-weight: 900;
width: 0.75rem;
height: 0.75rem;
}
.menuList > section > span {
display: inline-block;
width: 0.5rem;
text-align: center;
line-height: 0.5rem;
font-size: 0.4rem;
}
.presentPrice:before {
content: "¥";
font-size: 0.33rem;
}
/*************payTheBill 买单*************/
.payTheBill {
height: 1.5rem;
position: relative;
}
.payTheBill .all_select {
width: 10%;
padding-top: 0.4rem;
padding-left: 0.4rem;
}
.payTheBill > p {
line-height: 1.5rem;
text-indent: 0.3rem;
}
.payTheBill > p > span:first-child {
padding-right: 0.8rem;
}
.payTheBill > p > span:last-child {
padding-left: 0.3rem;
color: red;
}
.payTheBill > p > span:last-child:before {
content: "¥";
font-size: 0.35rem;
}
.payTheBill > a {
background: yellow;
position: absolute;
right: 0;
line-height: 1.5rem;
top: 0;
padding: 0 0.7rem;
}
cart.html:
{% extends 'base_main.html' %}
{% load static %}
{% block ext_css %}
{{ block.super }}
<link rel="stylesheet" href="{% static 'peiqi1/main/css/cart.css' %}">
{% endblock %}
{% block content %}
<div id="cart">
<h3>Cart</h3>
<div class="full">
<section>
<ul>
<li>收 货 人: PEIQI</li>
<li>电 话: 123456789</li>
<li>地 址: CHINA</li>
</ul>
<div class="bill">
<p>闪送超市</p>
<p>1元起送,满30免运费,22:00前可送达</p>
<a href="">凑单专区</a>
</div>
<div class="delivery">
<span>收获时间</span>
<span>一小时之内送达</span>
<a href="#">可预订></a>
</div>
<div>
<div class="delivery">
<span>收货备注</span>
<input type="text" placeholder="可输入100字以内的特殊要求">
</div>
</div>
<ul>
{% for cart in carts %}
<li class="menuList">
<div class="confirm">
<span>
{% if cart.c_is_select %} {# 判断购物车中商品是否被选中 #}
<span>√</span>
{% else %}
<span></span>
{% endif %}
</span>
</div>
<a href="#">
<img src="{{ cart.c_goods.productimg }}" alt="{{ cart.c_goods.productlongname }}">
<p>{{ cart.c_goods.productlongname }}</p>
<p class="presentPrice">{{ cart.c_goods.price}}</p>
</a>
<section>
<button>-</button>
<span>{{ cart.c_goods_num }}</span>
<button>+</button>
</section>
</li>
{% endfor %}
</ul>
<div class="payTheBill">
<div class="confirm">
<span>
<span></span>
</span>
</div>
<p>
<span>全选</span>
<span>共计:</span>
<span>0</span>
</p>
<a href="#">下单</a>
</section>
</div>
</div>
{% endblock %}
测试:
Cart页面的点击事件
下面我们要实现点击事件
新建cart.js
cart.html导入cart.js:
{% block ext_js %}
{{ block.super }}
<script type="text/javascript" src="{% static 'peiqi1/main/js/cart.js'%}"></script>
{% endblock %}
为了方便管理cart页面的点击事件,我们不采用之前直接给每一个标签都添加id,而是在最小父标签添加id
<li class="menuList" cartid="{{ cart.id }}">
cart.js:
$(function(){
$('.confirm').click(function(){
console.log('change state');
/*修改选中状态*/
var $confirm = $(this);
var $li = $confirm.parents('li');
var cartid = $li.attr('cartid');
/*利用ajax,找到对应标签之后发送给服务器修改状态*/
$.getJSON('/peiqi1/changecartstate/',{'cartid': cartid}, function (data){
console.log(data);
if (data['status']===200){
if(data['c_is_select']){
$confirm.find('span').find('span').html('√');
}else{
$confirm.find('span').find('span').html('');
}
}
})
})
})
添加修改商品是否被选中状态的url:
url(r'^changecartstate/', views.change_cart_state, name='changecartstate'),
因为这个url需要是用户登录状态,所以我们要在中间件上加上该url:
middleware:
REQUIRE_LOGIN_JSON = [
'/peiqi1/addtocart/',
'/peiqi1/changecartstate/',
]
views:
def change_cart_state(request):
# 从ajax获得商品id,但是这里有个隐藏漏洞,这里没有验证这个cart_id是用户已经存在的。
cart_id = request.GET.get('cartid')
# 去数据库中匹配对应的商品id
cart_obj = Cart.objects.get(pk=cart_id)
# 将原本选中的状态取反
cart_obj.c_is_select = not cart_obj.c_is_select
# 记得保存,如果不保存,数据库的状态不会改变
cart_obj.save()
data = {
'status':200,
'msg':'change ok',
'c_is_select':cart_obj.c_is_select
}
return JsonResponse(data=data)
小技巧
js获取jquery对象属性
js获取jquery对象属性
attr
- 可以获取任意属性
prop
- 只能获取内置属性,自定义的属性不认
项目中多块逻辑有相同操作
例如我们开发很多接口都要求用户登录,那这个操作就可以封装起来。
而比如移动端,客户端实际上识别不了301是什么意思。
- 跨域也是浏览器的行为
此处评论已关闭