Vue实战开发移动端旅游网站


Vue实战开发旅游网站

  • 项目搭建
    • 搭建步骤
      • 项目结构
      • 首页拆解
        • 首页组件拆分
        • 步骤
  • 网络请求库axios
    • axios与ajax
      • Promise
    • 安装axios
    • 如何使用axios
    • 请求和响应的拦截
      • 使用场景:
      • 使用axios请求拦截的实现方法
  • 跨域问题
    • 什么是跨域
      • 如何认定其他网站:同源策略
    • 跨域的影响
    • 解决跨域问题
      • 在Vue.js中解决跨域问题
      • 在Django中解决跨域问题
  • 前端组件开发
    • VantUI初探
    • 轮播图组件开发
    • 热门推荐组件开发
    • 精选景点组件开发
    • 页面底部组件开发
  • 后台接口开发
    • ORM模型设计
    • 轮播图ORM模型
    • 轮播图接口实现
    • 景点列表ORM模型
    • 景点列表接口实现
  • 接口联调
  • VueRouter实现多个页面
    • Vue中的路由管理
      • 路由的使用
    • 路由的参数传递
      • 动态路由匹配
        • 按照path跳转
        • 按照路由名称跳转
        • js实现页面跳转
        • 其他常见的跳转方式
  • 景点详情页面开发
    • 景点搜索页面

项目搭建 搭建步骤 Vue实战开发移动端旅游网站
文章图片

项目结构
公共的样式:src/assets/style/common.less
公共的js(工具函数,接口地址,配置文件)
  1. src/utils/apis.js(接口地址配置)
  2. src/utils/constants.js(常量配置)
  3. src/utils/filters.js(工具函数)
创建好项目后,将准备好的静态文件夹(static)放入public/目录下。
在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]))
最终项目结构图:
Vue实战开发移动端旅游网站
文章图片

首页拆解
Vue实战开发移动端旅游网站
文章图片

首页组件拆分
  • 标题
  • 轮播图(Banner图)
  • 热门推荐
  • 精选景点
  • 底部导航(固定底部)
步骤 1.分析首页结构
2.新建页面(views中)
3.新建对应组件(components)
注意思考:组件是否只用于当前页面(在src/components下新建文件夹用来存放对应页面所需的组件),或者组件用于多个页面(在src/components下新建文件夹用来存放公共的组件)
网络请求库axios axios与ajax ajax是通过浏览器后台与服务器通信的技术
axios是基于Promise的HTTP库(网络异步请求库),用来发送和处理http请求。
Promise
Promise是一种异步编程解决方案。
通过axios异步请求可以得到Promise对象,将它返回的结果进行处理。
Vue实战开发移动端旅游网站
文章图片

Vue实战开发移动端旅游网站
文章图片

安装axios Vue实战开发移动端旅游网站
文章图片

Vue实战开发移动端旅游网站
文章图片

如何使用axios Vue实战开发移动端旅游网站
文章图片

1.GET请求
Vue实战开发移动端旅游网站
文章图片

请求完毕后会返回一个响应,通过then函数获取响应(对结果进行处理)。如果返回异常则跳到catch函数。
2.POST请求
Vue实战开发移动端旅游网站
文章图片

url后面的参数是一个js对象,是需要post过去的数据。区别get请求的参数params。
请求和响应的拦截 1.了解请求响应拦截的使用场景
2.掌握如何对请求响应做统一的处理
使用场景:
  • 设置自定义请求头
  • 默认携带上次的cookie(主要解决用户登录的问题)
  • 添加loading动画(请求发起前显示,完成后隐藏)
  • 统一的错误处理
使用axios请求拦截的实现方法
创建axios实例
const ajax = axios.create(options)
options:js对象,例如:
  • headers:设置请求头
  • timeout:超时的毫秒数
  • data:POST/PUT/PATCH请求的数据
  • params:url中的参数
  • responseType:默认响应json数据
在src/utils/下新建文件ajax.js用来配置请求响应的拦截
Vue实战开发移动端旅游网站
文章图片

在请求发出之前进行拦截
Vue实战开发移动端旅游网站
文章图片

Vue实战开发移动端旅游网站
文章图片

Vue实战开发移动端旅游网站
文章图片

跨域问题 什么是跨域 跨域就是浏览器出于安全的考虑,要求你的网站不能执行或者不能访问其他网站的资源。
如何认定其他网站:同源策略
http://www.baidu.com:80
http(s):协议
www.baidu.com:域名/主机
80:端口(默认)
跨域的影响
  • 浏览器本地存储无法使用(Cookie,LocalStorage,IndexDB)
  • DOM对象,JS对象无法获取
  • AJAX无法使用
解决跨域问题
  1. 利用HTML标签的特性(script,a,iframe,img等)获取其他域的资源。只能解决GET请求
  2. 在前端使用代理解决
  3. 在服务器端解决,CORS-跨域资源共享,通过添加响应头信息来解决
在Vue.js中解决跨域问题
1.添加配置文件vue.config.js(项目根路径下)
2.添加配置
Vue实战开发移动端旅游网站
文章图片

target:目标地址
changeOrigin:变更请求头中设置的host(以xxx的名义进行访问)
pathRewrite:url的重写规则
例如要访问http://localhost:8080/api/test => 代理http://localhost:8000/test
在Django中解决跨域问题
步骤:
  1. 安装第三方扩展 pip install django-cors-headers。
  2. 配置 settings.py INSTALLED_APPS=[‘corsheaders’]。
  3. 配置中间件 ,放在SessionMiddleware和CommonMiddleware之间。
    Vue实战开发移动端旅游网站
    文章图片
  4. 添加白名单
Vue实战开发移动端旅游网站
文章图片

  1. 可选配置
    (请求头是指自定义的请求头)
    Vue实战开发移动端旅游网站
    文章图片
前端组件开发 VantUI初探 VantUI:开源免费,轻量可靠的移动端Vue组件库。
安装:npm i vant -S
导入所有的组件:
import Vue from ‘vue’;
import Vant from ‘vant’;
import ‘vant/lib/index.css’;
Vue.use(Vant);
轮播图组件开发 Vue实战开发移动端旅游网站
文章图片

热门推荐组件开发 Vue实战开发移动端旅游网站
文章图片

精选景点组件开发 Vue实战开发移动端旅游网站
文章图片

页面底部组件开发 Vue实战开发移动端旅游网站
文章图片

后台接口开发
  1. 设计接口返回标准(定义接口的返回结构,接口的错误信息约定)
  2. 编写接口代码
  3. 模拟HTTP请求,测试验证接口
接口返回结构
{ "meta": {}, "objects": [] }

【Vue实战开发移动端旅游网站】接口的错误信息返回结构
{ "error_code": '400000', "error_meg": "该字段不能为空。", "error_list": [ "password": [ "该字段不能为空。" ] ] }

ORM模型设计
  • 系统模块:轮播图,用户反馈
  • 景点模块:景点,景点详情,景点评论
  • 用户模块:用户,用户详细信息,登录历史
  • 订单模块:订单,订单明细,支付相关
轮播图ORM模型 PDMAN建模
Vue实战开发移动端旅游网站
文章图片

Vue实战开发移动端旅游网站
文章图片

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模型 Vue实战开发移动端旅游网站
文章图片

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)

接口联调 步骤:
  1. 阅读文档
  2. 配置接口地址
  3. axios获取数据
  4. 将数据设置到模型层
const apiHost = "http://localhost:8080/api"; /**系统模块 */ const SystemApis = { // 轮播图列表接口 sliderListUrl: apiHost + "/system/slider/list/" }; /**景点模块 */ const SightApis = { // 景点列表接口 sightListUrl: apiHost + "/sight/sight/list/" }; export { SystemApis, SightApis };

轮播图:
Vue实战开发移动端旅游网站
文章图片

精选景点:
Vue实战开发移动端旅游网站
文章图片

热门景点:
Vue实战开发移动端旅游网站
文章图片

设置代理解决跨域问题
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实战开发移动端旅游网站
文章图片

Vue中的路由管理 安装:
npm install vue-router -S
路由的使用
Vue实战开发移动端旅游网站
文章图片

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.在模板中实现路由跳转

路由的参数传递 动态路由匹配
  1. 设置动态匹配规则
  2. 在组件中获取匹配参数和查询
设置动态匹配规则
动态路由参数 :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跳转 Vue实战开发移动端旅游网站
文章图片

按照路由名称跳转 Vue实战开发移动端旅游网站
文章图片

Vue实战开发移动端旅游网站
文章图片

js实现页面跳转 Vue实战开发移动端旅游网站
文章图片

this:vue实例
$router:来自main.js中router
Vue实战开发移动端旅游网站
文章图片

Vue实战开发移动端旅游网站
文章图片

其他常见的跳转方式 页面的前进或后退
this.$router.go(n)

n:正数负数(整数)
-1:返回上次
替换浏览历史记录
this.$router.replace({name:'detail', parmas:{id:'123'}, query:{name:'xxx'}})

景点详情页面开发 景点搜索页面

    推荐阅读