案例来源:SYSU SE305 课程大作业。互联网售票软件是比较常见的软件系统。通常由多个零售系统和多个供给系统系统。 机票、酒店房间、电影票似乎是风马牛大相关的系统,但它们之间存在极其相似的业务模型。 以电影票为例,格瓦拉、蜘蛛网、腾讯等等都做类似的电影票分销、推广业务,但票是由各大院线排期提供的。 分销-院线-影院形成了一个完整的生态体系。 本课程以大家熟悉的订票为例,学习分析、设计、开发的方法。
我所在的小组做的是一个叫做MonkeyEye的项目,实现一个简单的电影购票系统,并在项目完成过程中学习系统分析与设计,学习UML建模等技能。本系列文章将会以此项目为案例,总结整个项目的设计、建模与开发过程。
前端项目地址:https://github.com/SYSUMonkeyEye/MonkeyEye-FE
关于项目的功能和项目结构的介绍请查看上一篇博客。
【前端技术框架】
- vue
- vue-router
- vuex
- vue-material
- axios
【构建工具】
- webpack
一、预编译语言(预处理语言)
项目中使用pug
预处理语言来编写前端的HTML,非常简洁和方便。用pug
来编写HTML结构,编写完后经过处理器处理之后会变成标准的HTML,代码上少了很多闭合标签显得更加简洁,而且结构也很清晰。类名和id的写法或者其他HTML标签属性的写法也很简洁。
至于编写样式的CSS,则是使用了sass
。缩进嵌套选择器,不用打花括号等特性用起来都特别爽。
pug
和sass
支持的功能还有很多,例如pug
的mixin
,sass
的模块化样式等等,此处不作介绍,官方文档已经有了详细的描述。
二、Vue 组件化
Vue支持前端组件化,可以将前端界面或者界面中的某些模块划分为组件,每个组件可以使用一个Vue文件来实现,也可以将代码分散在html/css/js
文件中。我们这里都是采取Vue文件的方式,每个Vue文件实现一个组件。Vue文件通过template、script、style
三种标签将HTML、js、css
三种类型的代码进行划分,三种放在同一个文件里,根据组件定位到代码,很方便进行代码管理。通过在标签中指定lang
属性,可以指定预编译语言。
项目将每个界面抽象为一个Vue组件来进行开发和管理,虽然每个界面上可以根据界面内容继续细分组件,例如顶栏组件、表单组件、标签页组件。但是由于本应用的界面并不是非常复杂,而且每个页面的业务逻辑都是连贯的,我们认为一个组件包含一个界面这种把握刚刚好,一个界面组件的HTML结构、js逻辑和css样式全部代码加起来一般在100到200+行之间,加上有template、script、style
三个标签进一步将代码分块,每一个Vue文件里面的每一大块代码(html/js/css
)的代码量基本都在100行以内,可读性还是很强的,很有利于进行开发和维护。
三、前端路由
由于本应用是一个单页应用,所以需要配置前端的路由。利用vue-router
库来实现前端路由控制。下面是部分代码截图,主要配置URL路径和与之绑定的组件,当浏览器地址栏中的路由变成相应的路径时候,就跟渲染与之绑定的组件,然后更新界面内容。
由于部分界面需要权限才能进入,例如,需要登录后才能查看和修改用户信息、需要登录后才能下单购票。这里可以利用vue-router
提供的钩子(hook
)进行权限控制,实现方式如下。
首先在配置路由的时候,给需要权限的界面加一个meta
字段,即元数据,里面的requireAuth
设置为true
,表示这个界面需要权限才能进入(这里的权限指的是已登录用户才具备的权限)。
vue-router
提供了一个beforeEach
钩子,如果给这个钩子绑定一个处理函数,那么在进入任何一个路由之前,都会先调用这个钩子所绑定的处理函数。在处理函数中,参数to
表示要进入的路由,通过检查要进入的路由的元数据便可以知道该路由是否需要权限。如果需要权限则检查store
里面的用户状态(后面将介绍store
,用户的登录状态会保存在store
中)。如果用户未登录则将它重定向到登录界面。
四、异步通信
前端界面经常需要跟服务器进行通信,发送GET、POST
等HTTP请求获取数据或者提交数据。为了提高用户体验,不阻塞页面流程,一般都是采取ajax
方式来异步发送请求。本项目使用了axios
这个库来发送HTTP请求。如官方文档介绍,“Promise based HTTP client for the browser and node.js”,这个库有一个很大的优点,基于Promise
。其次,他还封装好了多种HTTP方法,直接通过get、post、delete
等API即可发送一个对应HTTP方法的请求。promise
的写法使得代码可以通过.then
和.catch
的方式来处理异步请求得到的结果或者处理异常,相对于嵌套回调函数的方式,代码更加简洁优雅。
五、数据状态管理
应用的数据状态管理使用的是vuex
这个库。下面先简单总结一下vuex
的思想。Vue使用Vuex
实现状态管理,从概念上来看Vuex
遵从Flux
,即将应用的数据视为状态,放在一个全局的store
里面进行管理,所有组件共享同一个store
的数据,组件无法直接对数据进行修改,要更新store
里面的数据需要发起一个action
才能对store
里面的数据进行更新,当store
里面的数据更新之后会反馈所有使用到store
里面的数据的组件,使得其他组件也进行更新。
Vuex基本按照Flux的概念来,唯一不同之处就是Vuex除了action
之外还有mutation
。mutation
对store
里面的数据进行同步
更新,所有操作都必须是同步的。当需要异步
操作的时候就才会用到action
,在action
里面进行异步操作之后再通过mutation
来对store
里面的数据进行更新。
使用vuex
可以更好地管理应用的数据和接口,下面以电影数据为例子进行说明。电影的数据包括正在热映和即将上映的电影列表以及对应的推荐列表,将这些数据存放在store
的state
里面,电影列表界面和电影详情界面都会共享这一份数据,这份数据改变的时候,两个界面的内容都会更新。除了数据是存在全局的单例可以节省内存之外,界面的更新同步也变得非常容易。
下面则是电影模块的actions
部分,可以将所有与服务端API通信的异步操作接口封装在这里,接口不会分散在各个界面的代码中,非常方便我们对接口进行管理和调整。
最后则是mutations
,这里面封装了对数据进行同步修改的代码,在派发action
获取数据之后,如果需要对数据进行分类,例如获取全部电影列表然后分成正在热映和即将上映两类,都可以将相应的代码封装在这里。
六、数据状态模块化
vuex
数据状态管理,可以理解为是把整个应用的数据抽象出来,形成一棵状态树。这棵状态树位于一个全局的位置并且是单例的,所有界面都引用这棵树上的某一部分的数据。但是,当应用界面较多,数据模型变得复杂的时候,这棵状态树也会变得很庞大。如果将所有代码都放在一个位置,那么将很不利于代码的管理。所以,可以根据数据模型来对这棵树进行划分,变成多棵子树,每棵子树代表一个数据模型,并且将对应的API接口和操作封装到一起。这就是vuex
的modules
。
我们根据应用的数据模型将数据分为以下几个module
,然后在顶层将所有module
组装起来形成完整的数据状态树。
每个module
里面都有相应的state、mutations、actions
,将各模块的数据变量、接口等都封装到一起。这样每个module
的代码都是完整的,管理起来很方便。并且每个module
的代码量都很小,可读性可维护性大大提高。
七、HTTP代理与前后端分离
这个项目是一个前端开发项目,因此没有服务端的代码。前端的代码和服务端的代码运行在不同的位置。例如,开发的时候,服务端可能运行在服务器上,而前端的代码是跑一个本地静态服务器(dev-server
)来提供。静态服务器只能提供HTML/JS/CSS/图片等资源给浏览器使用,但是应用中还有不少地方需要访问服务器的API接口,这里可以通过HTTP代理来实现。
前端发送/api
请求到dev-server
,通过配置HTTP代理
,dev-server
将这个/api
请求代理到真正的API服务端,等拿到数据后再给回前端。这样,除了可以解决访问API的问题,还可以让我们不用去处理跨域的问题,非常方便。
HTTP代理使用http-proxy-middleware
中间件来实现,dev-server
中的代码已经写好,只需要在config
中配置一下代理规则即可。写好真正的API服务器和静态资源服务器的地址,然后在代理规则表中写好代理规则。例如下面图中的/api
,表示所有/api
开头的请求都会被代理到apiServer
。这时候在应用里面请求localhost:8080/api/movies
的效果就跟请求[apiServer]/api/movies
的效果时一样的。
dev-server
在这里充当了代理的角色,前端并不知道代理的存在,响应报文里面的服务器主机依然是localhost
,所以也不会造成前端的跨域问题。
有了代理之后,前后端分离的实践也变得容易起来。后端和前端可以分离开发,也不用管对方写了什么代码,甚至不需要运行对方的代码。后端把代码写好部署好接口,前端只要知道服务器地址即可。前后端之间的交互通过RESTful API
。