Vue实战开发旅游网站
- 项目搭建
-
- 搭建步骤
-
- 项目结构
- 首页拆解
-
- 首页组件拆分
- 步骤
- 网络请求库axios
-
- axios与ajax
-
- Promise
- 安装axios
- 如何使用axios
- 请求和响应的拦截
-
- 使用场景:
- 使用axios请求拦截的实现方法
- 跨域问题
-
- 什么是跨域
-
- 如何认定其他网站:同源策略
- 跨域的影响
- 解决跨域问题
-
- 在Vue.js中解决跨域问题
- 在Django中解决跨域问题
- 前端组件开发
-
- VantUI初探
- 轮播图组件开发
- 热门推荐组件开发
- 精选景点组件开发
- 页面底部组件开发
- 后台接口开发
-
- ORM模型设计
- 轮播图ORM模型
- 轮播图接口实现
- 景点列表ORM模型
- 景点列表接口实现
- 接口联调
- VueRouter实现多个页面
-
- Vue中的路由管理
-
- 路由的使用
- 路由的参数传递
-
- 动态路由匹配
-
- 按照path跳转
- 按照路由名称跳转
- js实现页面跳转
- 其他常见的跳转方式
- 景点详情页面开发
-
- 景点搜索页面
项目搭建 搭建步骤
文章图片
项目结构
公共的样式:src/assets/style/common.less
公共的js(工具函数,接口地址,配置文件)
- src/utils/apis.js(接口地址配置)
- src/utils/constants.js(常量配置)
- src/utils/filters.js(工具函数)
在src/assets/下创建style文件夹,并在style文件夹下创建common.less文件。
在src/下创建utils文件夹,并在utils文件夹下分别创建apis.js,constants.js,filters.js文件。
注!需要在main.js中注册全局过滤器到vue实例上:
import * as filters from ‘./utils/filters’
Object.keys(filters).forEach(k => Vue.filter(k, filters[k]))
最终项目结构图:
文章图片
首页拆解
文章图片
首页组件拆分
- 标题
- 轮播图(Banner图)
- 热门推荐
- 精选景点
- 底部导航(固定底部)
2.新建页面(views中)
3.新建对应组件(components)
注意思考:组件是否只用于当前页面(在src/components下新建文件夹用来存放对应页面所需的组件),或者组件用于多个页面(在src/components下新建文件夹用来存放公共的组件)
网络请求库axios axios与ajax ajax是通过浏览器后台与服务器通信的技术
axios是基于Promise的HTTP库(网络异步请求库),用来发送和处理http请求。
Promise
Promise是一种异步编程解决方案。
通过axios异步请求可以得到Promise对象,将它返回的结果进行处理。
文章图片
文章图片
安装axios
文章图片
文章图片
如何使用axios
文章图片
1.GET请求
文章图片
请求完毕后会返回一个响应,通过then函数获取响应(对结果进行处理)。如果返回异常则跳到catch函数。
2.POST请求
文章图片
url后面的参数是一个js对象,是需要post过去的数据。区别get请求的参数params。
请求和响应的拦截 1.了解请求响应拦截的使用场景
2.掌握如何对请求响应做统一的处理
使用场景:
- 设置自定义请求头
- 默认携带上次的cookie(主要解决用户登录的问题)
- 添加loading动画(请求发起前显示,完成后隐藏)
- 统一的错误处理
创建axios实例
const ajax = axios.create(options)options:js对象,例如:
- headers:设置请求头
- timeout:超时的毫秒数
- data:POST/PUT/PATCH请求的数据
- params:url中的参数
- responseType:默认响应json数据
文章图片
在请求发出之前进行拦截
文章图片
文章图片
文章图片
跨域问题 什么是跨域 跨域就是浏览器出于安全的考虑,要求你的网站不能执行或者不能访问其他网站的资源。
如何认定其他网站:同源策略
http://www.baidu.com:80
http(s):协议
www.baidu.com:域名/主机
80:端口(默认)
跨域的影响
- 浏览器本地存储无法使用(Cookie,LocalStorage,IndexDB)
- DOM对象,JS对象无法获取
- AJAX无法使用
- 利用HTML标签的特性(script,a,iframe,img等)获取其他域的资源。只能解决GET请求
- 在前端使用代理解决
- 在服务器端解决,CORS-跨域资源共享,通过添加响应头信息来解决
1.添加配置文件vue.config.js(项目根路径下)
2.添加配置
文章图片
target:目标地址
changeOrigin:变更请求头中设置的host(以xxx的名义进行访问)
pathRewrite:url的重写规则
例如要访问http://localhost:8080/api/test => 代理http://localhost:8000/test在Django中解决跨域问题
步骤:
- 安装第三方扩展 pip install django-cors-headers。
- 配置 settings.py INSTALLED_APPS=[‘corsheaders’]。
- 配置中间件 ,放在SessionMiddleware和CommonMiddleware之间。
文章图片
- 添加白名单
文章图片
- 可选配置
(请求头是指自定义的请求头)
文章图片
安装:npm i vant -S
导入所有的组件:
import Vue from ‘vue’;轮播图组件开发
import Vant from ‘vant’;
import ‘vant/lib/index.css’;
Vue.use(Vant);
文章图片
热门推荐组件开发
文章图片
精选景点组件开发
文章图片
页面底部组件开发
文章图片
后台接口开发
- 设计接口返回标准(定义接口的返回结构,接口的错误信息约定)
- 编写接口代码
- 模拟HTTP请求,测试验证接口
{
"meta": {},
"objects": []
}
【Vue实战开发移动端旅游网站】接口的错误信息返回结构
{
"error_code": '400000',
"error_meg": "该字段不能为空。",
"error_list": [
"password": [
"该字段不能为空。"
]
]
}
ORM模型设计
- 系统模块:轮播图,用户反馈
- 景点模块:景点,景点详情,景点评论
- 用户模块:用户,用户详细信息,登录历史
- 订单模块:订单,订单明细,支付相关
文章图片
文章图片
class Slider(models.Model):
""" 轮播图 """
name = models.CharField('名称', max_length=64)
desc = models.CharField('描述信息', max_length=256, null=True, blank=True)
types = models.SmallIntegerField('轮播图类型', default=10)
img = models.ImageField('图片地址', max_length=256, upload_to='%Y%m/slider')
start_time = models.DateTimeField('生效时间', null=True, blank=True)
end_time = models.DateTimeField('失效时间', null=True, blank=True)
reorder = models.SmallIntegerField('排序字段', default=0, help_text='数字越大靠前')
target_url = models.CharField('跳转地址', max_length=256, null=True, blank=True)
is_valid = models.BooleanField('是否有效', default=True)
created_at = models.DateTimeField('创建时间', auto_now_add=True)
updated_at = models.DateTimeField('修改时间', auto_now=True)class Meta:
db_table = 'system_slider'
# 按reorder降序排列
ordering = ['-reorder']
轮播图接口实现
def slider_list(request):
""" 轮播图接口 """
data = https://www.it610.com/article/{"meta": {},
"objects": []
}
query = Q(is_valid=True)
queryset = Slider.objects.filter(query)
for item in queryset:
data['objects'].append({
'id': item.id,
# img字段是ImageField,它有一个属性url,可以访问图片存在磁盘上的路径
'img_url': item.img.url,
'target_url': item.target_url,
'name': item.name,
'types': item.types
})
return JsonResponse(data)
景点列表ORM模型
文章图片
class Sight(models.Model):
""" 景点基础信息 """
name = models.CharField('名称', max_length=64)
desc = models.CharField('描述', max_length=64)
main_img = models.ImageField('主图', upload_to='%Y%m/sight', max_length=512)
banner_img = models.ImageField('详细主图', upload_to='%Y%m/sight', max_length=512)
content = models.TextField('详细信息')
score = models.FloatField('评分', default=5)
province = models.CharField('省份', max_length=32)
city = models.CharField('市区', max_length=32)
area = models.CharField('区/县', max_length=32, null=True, blank=True)
town = models.CharField('乡镇', max_length=32, null=True, blank=True)
min_price = models.FloatField('最低价格', default=0)
is_top = models.BooleanField('是否为精选', default=False)
is_hot = models.BooleanField('是否为热门', default=False)
is_valid = models.BooleanField('逻辑删除', default=True)
created_at = models.DateTimeField('创建时间', auto_now_add=True)
updated_at = models.DateTimeField('修改时间', auto_now=True)class Meta:
db_table = 'sight'
# 最新更新的排前面
ordering = ['-updated_at']
景点列表接口实现
class SightListView(ListView):
""" 景点列表接口 """
# 分页,每页放5条数据
paginate_by = 5def get_queryset(self):
""" 重写查询方法"""
query = Q(is_valid=True)
# 查询热门景点
is_hot = self.request.GET.get('is_hot', None)
if is_hot:
query = query & Q(is_hot=True)
# 查询精选景点
is_top = self.request.GET.get('is_top', None)
if is_top:
query = query & Q(is_top=True)
# TODO:按照景点名称搜索
queryset = Sight.objects.filter(query)
return querysetdef render_to_response(self, context, **response_kwargs):
page_obj = context['page_obj']
data = https://www.it610.com/article/{"meta": {
# 总共多少条记录
'total_count': page_obj.paginator.count,
# 总共多少页
'page_count': page_obj.paginator.num_pages,
# 当前页码
'current_page': page_obj.number,
},
"objects": []
}
for item in page_obj.object_list:
data['objects'].append({
'id': item.id,
'name': item.name,
'main_img': item.main_img.url,
'score': item.score,
'province': item.province,
'city': item.city,
'min_price': item.min_price,
# TODO:评论数量暂时无法获取
'comment_count': 0
})
return http.JsonResponse(data)
接口联调 步骤:
- 阅读文档
- 配置接口地址
- axios获取数据
- 将数据设置到模型层
const apiHost = "http://localhost:8080/api";
/**系统模块 */
const SystemApis = {
// 轮播图列表接口
sliderListUrl: apiHost + "/system/slider/list/"
};
/**景点模块 */
const SightApis = {
// 景点列表接口
sightListUrl: apiHost + "/sight/sight/list/"
};
export {
SystemApis,
SightApis
};
轮播图:
文章图片
精选景点:
文章图片
热门景点:
文章图片
设置代理解决跨域问题
vue.config.js:
module.exports = {
// 例如要访问http://localhost:8080/api/system/slider/list/ => 代理http://127.0.0.1:8000/system/slider/list/
devServer: {
proxy: {
"/api": {
target: "http://127.0.0.1:8000/",
changeOrigin: true,
pathRewrite: {
"^/api": ""
}
}
}
}
};
VueRouter实现多个页面
文章图片
Vue中的路由管理 安装:
npm install vue-router -S路由的使用
文章图片
1.设置路由规则
const routes = [
{ path:'/home',component:Home },
{ paht:'/about',component:About }
]
2.配置路由规则
// 在项目根路径下新建router.js
import Vue from 'vue'
import VueRouter from 'vue-router'Vue.user(VueRouter)
const router = new VueRouter({
routes //相当于routes:routes
})// 在main.js中配置
new Vue({
el:'#app'
router
})
3.设置路由切换后页面的显示位置
在模板中指定路由切换后组件的渲染位置
4.在模板中实现路由跳转
路由的参数传递 动态路由匹配
- 设置动态匹配规则
- 在组件中获取匹配参数和查询
动态路由参数 :name
const router = new VueRouter({
routes: [
{ path:'/detail/:id', component:Detail }
]
})
在组件中获取匹配参数
在JS中获取参数
let id = this.$route.params.id
响应参数变化
watch: {
$route(to, from){
// 对路由变化做出响应
}
}
在组件中获取查询参数
URL中的查询参数(/sight/list?/name=‘xxx’)
let name = this.$route.query.name
按照path跳转
文章图片
按照路由名称跳转
文章图片
文章图片
js实现页面跳转
文章图片
this:vue实例
$router:来自main.js中router
文章图片
文章图片
其他常见的跳转方式 页面的前进或后退
this.$router.go(n)
n:正数负数(整数)
-1:返回上次
替换浏览历史记录
this.$router.replace({name:'detail', parmas:{id:'123'}, query:{name:'xxx'}})
景点详情页面开发 景点搜索页面
推荐阅读
- 笔记|TypeScript简记(一)
- 笔记|数据挖掘经典十大算法_ID3算法
- 源程序|使用Python将数据库中的文本生成词云图
- 笔记|MySql数据库修改密码【详细教程】
- 源程序|SpringBoot框架入门(二)
- 笔记|基于Java+SpringMVC+MySql+Layui+H5网站后台管理系统
- 笔记|Nessus安裝教程
- 笔记|PaddleDetection-YOLOv3模型结构解析(二)
- 七七软件库