项目简介css
蜘蛛电影主要是仿猫眼电影的一个vue项目 主要功能: 1. 城市定位 2. 展现正在热映的电影 3. 展现即将上映的电影 4. 搜索 5. 展现影院 6. 个人页面 7. 电影详情
项目开发环境html
编译工具:vscode 操做系统:win10 node环境:node 12.18.3 npm6.14.6 Vue脚手架:vue-cli 4.5.4 版本管理工具:git bash 2.28.0 服务器软件:Tomcat9.0(我设置Tomcat服务器对应的地址为:localhost:8082)
项目开发流程前端
1. 项目需求分析 2. 项目工期评估 3. 项目责任划分 前端: 静态页面制做 前端框架选型 前端页面架构 后端: 数据库开发 API接口文档 API接口实现
建立项目vue
在对应的项目目录下打开cmd 利用Vue-cli建立项目:(我选择的是Vue3.x版本) vue create 项目名 建立完后会生成一堆文件夹和文件:
在gitee建立项目对应的远程仓库node
将本地库与远程库进行关联ios
把master分支中初始的项目文件/目录都推送到远程仓库中css3
建立并切换到dev分支,把dev分支的初始项目文件/目录也推送到远程仓库nginx
建立并切换到一个新的分支(createComponents)用来专门建立组件git
如下部分在createComponents分支上开发↓↓↓↓↓↓es6
初始化路由的配置---电影页面,影院页面,个人页面
总共有三个页面:电影页面,影院页面,个人页面 电影页面的路由配置:
影院页面的路由配置:
个人页面的路由配置:
在路由主配置页面(index.js)中引入各路由:
路由重定向的配置
当跳转的路由不存在,利用重定向默认跳转到电影页面(路径:/movie) 重定向:redirect:'跳转的路由' 在router文件夹下的index.js进行配置:
对项目中初始的index.html文件进行一些修改
//增长了user-scalable=no,不容许用户放大缩小页面 <meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no"> //引入公共的css样式 <link rel="stylesheet" href="<%= BASE_URL %>css/common.css"> //引入图标 <link rel="stylesheet" href="https://at.alicdn.com/t/font_2446917_rga2fsr9ns.css">
App.vue文件的修改
留出一个路由出口:<router-view /> 加上keep-alive标签:实现页面缓存做用,由于切换组件被从新渲染时会影响性能,会显著提升用户体验(会缓存不活动的组件)
建立头部组件
在components文件夹下建立一个header文件夹,并在此文件夹下建立index.vue文件 1. 编写相关的HTML,CSS代码 2. 为实现动态值(对应页面对应标题)的切换(实现父子组件的数据传递),可利用在Vue中的props(数据单向传递。父组件值的会改变子组件的值,子组件的值改变不会影响父组件) 定义一个名称title,类型为String,默认值为'蜘蛛电影' 而后在对应标签(<h1></h1>)中的内容处写上{{title}},接收传递过来的数据
建立尾部组件
在components文件夹下建立一个footer文件夹,并在此文件夹下建立index.vue文件 编写相应的HTML,CSS代码
建立页面组件---电影页面
在views文件夹下建立一个movie文件夹,并在此文件夹下建立index.vue文件 1. 初始化页面代码 2. 在script标签中引入头部组件和尾部组件 import Header from "@/components/header"; import Footer from "@/components/footer"; 3. 为切换头部组件中的标题,在Header标签中添加属性title,值为'蜘蛛电影',传递给header组件 4. 编写相应的HTML,CSS代码 HTML中分为两部分: 1) 电影菜单栏(城市定位,正在热映,即将上映,搜索) 2) 正在上映和即将上映的电影页面展现(经过router-view标签进行相应的渲染) 5. 对电影菜单栏的每个功能都使用router-link标签,使其跳转到对应的子路由
配置电影页面中的子路由
电影页面中的子路由有:'city'(城市),'showing'(正在热映),'coming'(即将上映),'search'(搜索) 利用children来进行配置子路由(会拼接到/movie后面) 当在电影页面时,默认显示正在热映的页面,即路由重定向到'/movie/showing'
电影页面---建立城市的组件
在components文件夹下建立一个city文件夹,并在此文件夹下建立index.vue文件 1. 编写相应的HTML,CSS代码 HTML中分为三部分: 1) 热门城市 2) 城市分类 3) 首字母侧边栏
电影页面---建立正在热映的组件
在components文件夹下建立一个showing文件夹,并在此文件夹下建立index.vue文件 1. 编写相应的HTML,CSS代码 HTML中分为每一部电影的区域 而每个电影区域中分为三部分: 1) 电影图片 2) 电影信息:电影名,电影分数,主演,影院场次 3) 购票按钮
电影页面---建立即将上映的组件
在components文件夹下建立一个coming文件夹,并在此文件夹下建立index.vue文件 1. 编写相应的HTML,CSS代码 HTML中分为每一部电影的区域 而电影的每个区域中分为三部分: 1) 电影图片 2) 电影信息:电影名,想观看人数,导演,主演,上映时间 3) 预售按钮
电影页面---建立搜索的组件
在components文件夹下建立一个search文件夹,并在此文件夹下建立index.vue文件 1. 编写相应的HTML,CSS代码 HTML中分为两部分: 1) 搜索输入框 2) 搜索结果展现区域:大类型,每一部电影对应的信息(电影图片,电影名,电影分数,电影英文名,电影类型,电影日期)
建立页面组件---影院页面
在views文件夹下建立一个cinema文件夹,并在此文件夹下建立index.vue文件 1. 初始化页面代码 2. 在script标签中引入头部组件和尾部组件 import Header from "@/components/header"; import Footer from "@/components/footer"; 3. 为切换头部组件中的标题,在Header标签中添加属性title,值为'蜘蛛影院',传递给header组件 4. 编写相应的HTML,CSS代码 HTML中分为两部分: 1) 影院菜单栏(全城,品牌,特点) 2) 影院页面展现(经过router-view标签进行相应的渲染)
影院页面---建立展现影院的组件
在components文件夹下建立一个cinemalist文件夹,并在此文件夹下建立index.vue文件 1. 编写相应的HTML,CSS代码 HTML中分为每个影院的区域 而每个影院区域分为3部分: 1) 影院名和价钱 2) 地址和距离 3) 一些折扣卡之类的
建立页面组件---个人页面
在views文件夹下建立一个mine文件夹,并在此文件夹下建立index.vue文件 1. 初始化页面代码 2. 在script标签中引入头部组件和尾部组件 import Header from "@/components/header"; import Footer from "@/components/footer"; 3. 为切换头部组件中的标题,在Header标签中添加属性title,值为'个人蜘蛛',传递给header组件 4. 编写相应的HTML,CSS代码 HTML中就渲染一个登陆页面的组件
个人页面---建立登陆组件
在components文件夹下建立一个login文件夹,并在此文件夹下建立index.vue文件 1. 编写相应的HTML,CSS代码 HTML中分为五个部分 1) 帐户名输入框 2) 密码 3) 验证码 4) 登陆按钮 5) 找回密码和当即注册
到目前为止,在createComponents分支上的开发大概已完成了,能够把此分支上的东西合并到dev分支上,再推送到远程仓库中
1. 在createComponents分支:git add .,git commit提交代码到本地库 2. 切换到dev分支:git checkout dev 3. 合并到dev分支上:git merge createComponents --no-ff 4. 推送代码到gitee的远程库中:git push origin dev 5. 能够删除createComponents分支:git branch -d createComponents
接下来可继续进行相应的开发了
建立新分支setData,专门用来渲染数据的
建立并切换分支setData:git checkout -b setData
如下操做均在setData分支上开发↓↓↓↓↓↓
我把相关数据的api文件都放在Tomcat搭建的服务器上,因为数据存放的ip地址为localhost:8082,而项目运行的ip地址为localhost:8081
因此在渲染数据的时候就存在一个跨域问题
解决跨域问题
在项目目录中新建文件:vue.config.js 编写:(编写完后记得重启项目,才会生效)
渲染数据时使用axios来进行一个ajax的请求
1. 安装axios:npm i -S axios 2. 引入axios: 在main.js文件中编写import axios from 'axios' 3. 把axios添加在Vue的原型上,在开发中就能利用this直接调用axios: createApp(App).config.globalProperties.$axios=axios; 4. 使用axios:(利用getCurrentInstance方法的ctx来调用挂载的axiox) import { getCurrentInstance } from "vue"; const { ctx } = getCurrentInstance(); ctx.$axios.xxx
城市组件---渲染城市数据
部分城市数据:
1. 在data(){return {}}中定义两个数据:hotlist数组(接收传递过来的热门城市数据),citylist数组(接收传递过来的普通城市数据)
2. 在city组件中,在生命周期为mounted的钩子函数中,利用axios的get请求取得城市数据
3. 随后将数据渲染到页面上(利用v-for)
4. 在methods中编写一个方法,使得点击侧边栏字母能跳转到对应首字母的城市列表,而且对应点击的字母背景色变为灰色(在侧边栏字母对应的li上添加一个touchstart事件监听,此方法为handleToIndex) handleToIndex方法思路: 1) 先在类为city_sort的div上添加ref属性,属性值设为city_sort,便于在js中获取到此元素 2) 利用this.$refs.xxx(属性值)来获取到此div元素 3) 根据上一步获取到的div元素来取得它的子元素h2(即取到首字母的区域) 4) 利用offsetTop方法来得到每个首字母到达顶部的距离 5) 对城市列表的包裹层(即类为city_list的div,这里表示的是第二步获得div的父节点)用scrollTop的方法来改变到顶部滚动的距离,值为第四步获得的距离值 6) 而后是改变点击侧边栏字母里的每一项的背景颜色 7) 在类为city_index的div上添加ref属性,属性值设为city_index 8) 利用this.$refs.xxx(属性值)来获取到此div元素 9) 根据上一步获取到的div元素来取得它的子元素li(即每个字母项) 10) 先利用for循环把所有li的背景颜色变为透明色 11) 再对点击的li改变背景色为灰色(利用xxx.style.background='xxxx')
正在热映组件---渲染正在热映的电影数据
部分正在热映的电影数据:
1. 在data(){return {}}中定义一个数据:movielist数组(接收传递过来的正在热映的电影数据)
2. 在showing组件中,在生命周期为mounted的钩子函数中,利用axios的get请求取得正在热映的电影数据
3. 随后将数据渲染到页面上(利用v-for) 其中传递过来的图片路径须要通过一些宽高设置的处理(使用Vue3.x的过滤方法(在main.js文件编写)) 还有显示怎样版本的图片标识可用v-if来进行判断
即将上映组件---渲染即将上映的电影数据
部分即将上映的电影数据:
1. 在data(){return {}}中定义一个数据:cominglist数组(接收传递过来的即将上映的电影数据)
2. 在coming组件中,在生命周期为mounted的钩子函数中,利用axios的get请求取得即将上映的电影数据
3. 随后将数据渲染到页面上(利用v-for) 其中传递过来的图片路径须要通过一些宽高设置的处理(使用Vue3.x的过滤方法(在main.js文件编写)) 还有显示怎样版本的图片标识可用v-if来进行判断
搜索组件---渲染搜索结果的数据
部分搜索结果的电影数据:(这里只提供了有关字母a,b,aaa的数据)
1. 在data(){return {}}中定义一个数据:message字符串(输入框的值),movielist数组(接收传递过来的电影数据)
2. 在search组件中,在watch的钩子函数中,若message发生变化,则利用axios的get请求取得搜索获得的电影数据(在watch中,可作函数节流,Ajax异步数据获取,操做DOM;依赖固定的数据类型等。一个本来就存在的数据,发生变化就执行函数) 1) 在input标签中,利用v-model双向绑定message数据,监听输入框的值 2) 在监听message中进行axios的get请求
3. 随后将数据渲染到页面上(利用v-for) 其中传递过来的图片路径须要通过一些宽高设置的处理(使用Vue3.x的过滤方法(在main.js文件编写))
4. 在输入框输入数据的这种实时监测数据的状况下,只要最后一次肯定的结果,前面的都清除掉。(即在这期间axios会触发屡次请求,要把没用到的请求中断掉) 这个时候就要使用函数防抖的一个手段: 1. 可以使用定时器的方式 2. 也可利用axios终止请求的一些方法 1) 一开始可先判断终止请求的函数是否已经存在了,若存在就终止 2) 在axios的第二个参数中新建一个cancelToken的实例(利用axios的构造函数CancelToken)(使得请求具备能够取消的功能)
cinemalist组件---渲染影院的数据
部分影院的数据:(这里只提供了江门,成都的影院数据)
1. 在data(){return {}}中定义一个数据:cinemalist数组(接收传递过来的影院数据)
2. 在cinemalist组件中,在生命周期为mounted的钩子函数中,利用axios的get请求取得影院的数据
3. 随后将数据渲染到页面上(利用v-for) 还有显示怎样小卡片可用v-if来进行判断
4. 在后面的步骤,会实现根据不一样的城市地区渲染不一样地区的影院数据
到目前为止,在setData分支上的开发大概已完成了,能够把此分支上的东西合并到dev分支上,再推送到远程仓库中
1. 在setData分支:git add .,git commit提交代码到本地库 2. 切换到dev分支:git checkout dev 3. 合并到dev分支上:git merge setData --no-ff 4. 推送代码到gitee的远程库中:git push origin dev 5. 能够删除setData分支:git branch -d setData
接下来可继续进行相应的开发了
建立新分支getCity,专门用来城市关联数据的功能(不一样城市关联不一样的影院数据)
建立并切换分支getCity:git checkout -b getCity
如下操做均在getCity分支上开发↓↓↓↓↓↓
全局封装berrter-scroll组件
做用: 1. 使得滚动起来顺滑,没有那么生硬 2. 解决滑动时不会触发点击事件(实现tap事件的一种方法,还有能够利用zepto,vue-touch来实现) 组件生效的前提: 1. 包裹容器的高度或宽度必须小于内容的高度和宽度 2. 要等所要触发的内容都渲染完 步骤: 1. 先安装better-scroll插件:npm i -S better-scroll 2. 在components文件夹下建立一个scroller文件夹,并在此文件夹下建立index.vue文件 3. 编写HTML,CSS部分 HTML部分是有一个包裹层wrapper,里面是一个slot插槽(显示对应HTML模块) 4. 引入better-scroll插件:import BScroll from "better-scroll"; 5. 在钩子函数mounted中编写: 1) 新建一个better-scroll实例:this.scroll = new BScroll('对应的包裹层元素',{相关的一些配置}) 2) 实现上拉刷新,须要绑定一个scroll事件:this.scroll.on("scroll", (pos) => {}) 在scroll事件中执行实现上拉刷新的方法(利用props接收由父组件传过来的方法):handleToScroll(){} 还要绑定一个touchend事件(滑动结束会触发),来代表加载成功:this.scroll.on("touchEnd", (pos) => {}) 在touchEnd事件中执行相对应的方法(利用props接收由父组件传过来的方法):handleToTouchEnd(){}; 以上的这些都要在内容都加载完后才触发,因此须要用到vue中的.$nextTick(()=>{}),mounted中的内容都添加到这里面的函数中执行 6. 在钩子函数updated中编写: this.scroll.refresh(); //当数据更新时从新计算滑动值 7. 而在city组件中用到此滑动组件时,会发现侧边栏字母没法控制对应字母区域的跳转 由于此时better-scroll在控制着这一块区域,对应的方法就没法生效了 因此要利用better-scroll中的方法(scrollTo(x,y)),在better-scroll中添加此方法:toScrollTop(y) {this.scroll.scrollTo(0, y);} 随后在city组件中调用该方法 1) 在scroller元素上添加ref属性,属性值为city_list 2) 经过获取到这个元素来调用其身上的方法 8. 全局注册此组件,使得能够在文件中随时使用。在main.js文件中编写:
better-scroll组件全局的编写:
better-scroll组件的调用:
全局封装loading组件
因为有些内容都要经过请求加载出来,在等待数据响应的过程当中,能够在加载的过程当中添加一个loading组件,看上去也会更美观一点,用户的体验感也会更好 1. 在components文件夹下建立一个loading文件夹,并在此文件夹下建立index.vue文件 2. 编写HTML,CSS部分 (可本身写,也可直接去找一些css3实现loading效果的组件,推荐网址:https://codepen.io/trending) 3. 全局注册此组件,使得能够在文件中随时使用。在main.js文件中编写:
loading组件全局的编写:
loading组件的调用: (可在data中定义一个Boolean类型的数据:isLoading,为true就显示loading组件,不然就显示better-scroll组件)
设置城市数据的本地存储与状态管理
对于一些不怎样变化的数据,能够存储到本地(如:localStorage) 城市数据的本地存储:(利用localStorage) 1. 当经过ajax请求获得的数据后,利用window.localStorage.stetItem('xx',xx)来存储数据(注意要先用JSON.stringfy方法来变为字符串再存放) 2. 在ajax请求前,能够先作一个判断,若本地存储中有相关数据,则直接进行赋值(注意取出数据时先要用JSON.parse方法来变为JSON对象再赋值),不然就进行ajax请求取得数据
城市数据的状态管理:(这里就要用到store文件夹下的东西) 1. 先在store文件夹下新建一个名为city的文件夹,在city文件夹下新建一个index.js文件(专门用来对城市数据进行状态管理) 2. 在store的index.js文件中,引入上面的city模块文件:import city from './city' 3. 把上面引入的city子模块添加到modules中 4. 在city文件中的配置:(每一个子模块都有本身的state、mutation、action等方法) 1) 在state中定义两个变量:nm(城市名),id(城市id) 2) 在mutation中定义一个方法用来接收传递过来的值并更新state状态里的值: CITY_INFO(state,payload){ state.nm=payload.nm; state.id=payload.id; } 3) exports default{}的配置: export default { namespaced:true, //使其成为带命名空间的模块。保证在变量名同样的时候,添加一个父级名拼接(在使用模块中的mutations、getters、actions和获取属性时,要加上模块名) state, actions, mutations }
5. 在电影组件(movie)的城市标签显示中,获取到store状态里的city模块的城市名来实现动态显示城市数据(获取对应的属性值:$store.state.模块名.模块属性)
6. 在city组件中,实现点击每个城市就会更新当前的城市状态信息并存储起来 1) 为每一个城市li绑定click事件,并执行handleToCity()方法 2) 在handleToCity方法中: 经过this.$store.commit("模块名/模块中的mutations", {对应的值})方法把接收到对应的城市名和城市id都传给city的状态管理并更新 利用localStorage存储当前状态的城市名和城市id 选择完城市后,利用this.$router.push('路由路径')来自动跳转到正在上映的电影页面
经过当前城市的id关联渲染相关的即将上映的电影数据和影院数据
这里渲染的数据只提供了成都和江门地区 要想选择完对应的城市后从新渲染相关的数据,无非就是从新发起ajax请求接收数据 但在生命周期mounted中,一旦有缓存系统keep-alive存在,那么再次请求是不会进行触发了 因此就要在actived生命周期中进行相关的请求 (activated生命周期是在keep-alive组件激活时调用的) 常规来讲,是应该在activated生命周期中执行的,但我这里反而要在mounted生命周期中执行才生效(目前我也不太清楚这个缘由,好奇怪) 渲染对应城市的即将上映的电影数据和影院数据的一些步骤: 1. 若是没有切换城市的话,咱们则无需发送ajax请求 1) 在data中新定义一个数据prevCityId(表示上一次选择的城市id),一开始赋值为-1 2) 经过this.$store.state方法来获取到城市状态管理中的城市id 3) 把prevCityId与获取到当前状态的城市id做比较,若相等(即没有切换城市),则直接return退出,不须要发送请求,直接渲染缓存的数据 2. 若切换了城市,要发送请求,则要在请求的地址拼接上当前状态的城市id进行请求数据,而且要把当前状态的城市id赋值给prevCityId
全局封装弹窗组件
若利用vue来建立这个组件,在传参数或者复用的时候会有点麻烦,因此咱们这里能够利用JS来封装此组件,并接收对应的参数 1. 在components文件夹下建立一个名为JS的文件夹,在JS文件夹下建立一个index.js文件(做为JS主模块文件),在JS文件下建立一个名为msgBox的文件夹,在msgBox文件夹下建立一个index.vue 2. 在msgBox对应的index.vue中编弹窗的HTML,CSS部分 3. 编写JS对应的index.js文件 1) 引入vue对应的createApp和h函数:import { createApp,h } from 'vue' 2) 引入刚才编写的msgBox vue组件:import msgBox from './msgBox' 3) 定义导出一个名为messageBox的闭包函数,为引入此组件提供一个接口:export var messageBox=(function(){})() 4) 在此闭包函数中,首先定义一个默认配置的变量:defaults 5) 接下来就是编写返回出去的一个函数 6) 接收传过来的配置参数,并覆盖掉默认的参数 7) 定义对应的动态组件并挂载到DOM上(Vue2.0中能够用Vue.extend({})方法,Vue3.0中能够用createApp({})方法) i) 利用createApp定义一个名为app的实例:const app=createApp({}) ii) 利用render(){return h()}来进行渲染组件及相关的props属性和插槽属性等 (h返回一个"虚拟节点",包含向 Vue 描述它应在页面上渲染哪一种节点的信息,包括全部子节点的描述。共接收三个参数:1. HTML 标签名、组件或异步组件。使用返回 null 的函数将渲染一个注释(必选),2. 一个对象,与咱们将在模板中使用的 attribute、prop 和事件相对应(可选),3. 子代 VNode,使用h()生成,或者使用字符串来获取“文本 VNode”,或带有插槽的对象(可选)) iii) 新建立一个div节点,将此组件添加到body,并挂载
4. 而后继续编写msgBox对应的index.vue文件 要接收传过来的插槽属性值,可利用v-slot和slot的配合 要接收props的值,先在props:{}中定义那两个函数值,经过this.$props.xxx来调用对应的函数方法
这里是关于弹窗的一下小改进: 1. 在cancel和ok对应的区域中,判断哪一个有值就显示哪一个区域(利用v-if) 2. 点击ok区域后也要移除掉弹窗并解除绑定 3. cancel和ok值都当作props属性传进去,并在对应的msgBox vue组件中进行接收
实现城市定位
城市的定位是在movie组件(电影页面)中进行的 实现城市定位的方法是经过高德地图开放的API实现的(详情可查看网址:https://blog.csdn.net/qq_42817227/article/details/98303058?ops_request_misc=%7B%22request%5Fid%22%3A%22161728232116780357267112%22%2C%22scm%22%3A%2220140713.130102334..%22%7D&request_id=161728232116780357267112&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-98303058.first_rank_v2_pc_rank_v29&utm_term=使用高德地图定位) 1. 在movie组件中,先引入弹窗组件:import { messageBox } from "@/components/JS"; 2. 在methods中定义一个方法handleOk(实现定位的方法),编写此方法的前提是须要在public文件夹中的index.html中引入对应的js连接 handleOk的函数方法: 1) 编写对应提供的定位函数方法 2) 在成功获取到的函数方法中,把定位获得的城市名和城市id都存储在localStorage中,同时利用this.$store.commit()方法更新城市的当前状态 3. 在生命周期mounted中,先执行handleOk方法进行定位,再判断当前的城市状态和当前定位的城市是否相等,若是不相等,则弹出弹窗提示切换定位(2s后再弹出) 弹窗的参数值: messageBox({ title: "定位", content: this.nowCityName, cancel: "取消", ok: "切换定位", handleOk: () => { this.handleOk(); window.location.reload(); }, }); 4. 在生命周期updated中,数据更新了就从新判断一下当前的城市状态和当前定位的城市是否相等,看是否须要从新定位
到目前为止,在getCity分支上的开发大概已完成了,能够把此分支上的东西合并到dev分支上,再推送到远程仓库中
1. 在getCity分支:git add .,git commit提交代码到本地库 2. 切换到dev分支:git checkout dev 3. 合并到dev分支上:git merge getCity --no-ff 4. 推送代码到gitee的远程库中:git push origin dev 5. 能够删除getCity分支:git branch -d getCity
接下来可继续进行相应的开发了
建立新分支detailPage,专门开发电影详情页面
建立并切换分支detailPage:git checkout -b detailPage
如下操做均在detailPage分支上开发↓↓↓↓↓↓
建立组件---电影详情页面组件
1. 电影详情页面的渲染入口: 电影详情页面的组件渲染,能够经过在movie组件中经过router-view命令视图来渲染出来 但这样一来,就会和以前写的渲染正在上映和即将上映的组件的router-view产生冲突了,这个时候咱们能够经过name来把它们区分开来(即命名视图),致使不让它们渲染混乱 咱们把渲染电影详情组件的命令视图的name设为detail
2. 接下来就要在movie的路由中设置详情页的路由 因为使用了命名视图,因此在对应子路由渲染组件的时候,写法也会稍微不同 并且可经过路由传参的方式把在电影页面获得的电影id值传给电影详情页面,电影详情页经过props接收传过来的id值,可请求到对应的电影详情数据并渲染出来 如下图片是相关的路由配置
3. 编写电影详情页的组件 1) 在views文件夹下的movie文件夹中,新建一个名为detail.vue的文件 2) 编写相关的HTML,CSS 引入header组件,由于要在头部的左侧加上一个回退上一级功能,因此须要增长一个图标,在header组件中新增长一个插槽用来渲染图标
3) 定义props属性来接收从电影页面传过来的电影id(路由中定义的属性名是什么,这里的props属性名就定义什么) 4) 定义一个handleToBack方法实现返回上一级路由,利用 this.$router.back()方法来实现 5) 而后就是利用ajax请求接收数据了,经过和接收到的电影id配合,来取得相应的电影详情数据 6) 其中在演职人员的展现中,咱们能够利用swiper插件来实现移动端的滑动展现(要引入swiper相关的CSS,JS文件) (swiper中文网:https://www.swiper.com.cn/)
到目前为止,在detailPage分支上的开发大概已完成了,能够把此分支上的东西合并到dev分支上,再推送到远程仓库中
1. 在detailPage分支:git add .,git commit提交代码到本地库 2. 切换到dev分支:git checkout dev 3. 合并到dev分支上:git merge detailPage --no-ff 4. 推送代码到gitee的远程库中:git push origin dev 5. 能够删除detailPage分支:git branch -d detailPage
以上基本就是客户端的一些开发了,你也能够自行完善一些其余的功能
也能够进行一下真机测试,即在手机上浏览开发的这个项目
进行真机测试的前提,就是手机和电脑要链接到同一个网络 查看运行项目时的Network的地址,直接在手机浏览器中输入这个地址就能够查看了
若你这里显示的Network地址为unavailable 解决方法: 在vue.config.js文件中,添加一个public属性,属性值为此网络的IPv4地址,并加上相应的端口号,重启项目,就有显示啦
项目的一些优化
1. 能够在此项目开始的时候,加一个等待加载的动画(在index.html文件中添加) 2. 修改基础的公共根路径(能够指定一个根目录,在此目录下访问全部的文件,组件) 利用publicPath来修改生产模式和开发模式的基础公共根路径(在vue.config.js文件下配置) 这是个人配置修改:
项目的打包
项目打包命令:npm run build 打包后的文件放在dist目录中
下面是后端的操做↓↓↓↓↓↓
咱们的后端开发须要用到的技术
1. Node.js 2. Express 3. MongoDB 4. 其余模块
后端开发的前提准备
为实现关于个人页面的对应功能,须要进行后台管理的开发,利用数据库存储用户的对应信息 安装node (可查看个人另外一篇文章:关于node) 安装express:npm i -s express 可全局安装express-generator:npm install -g express-generator (express-generator会快速建立应用程序框架) 在项目目录spider的同级目录下,打开cmd 输入命令:express -e 服务框架名
而后就会生成对应的一个目录
进入此目录,打开package.config文件,若是你想实时修改(监听)文件 可把scripts中的"start": "node ./bin/www"改成"start": "nodemon ./bin/www"(前提是要安装nodemon的这个插件) 运行后,在浏览器输入对应的地址(我这里设置的端口是3000),若出现下图的内容,则证实已经打开成功
因为咱们要用到数据库来存储咱们的用户数据,咱们这里就使用MongoDB数据库 咱们能够安装mongoose模块,利用express来对其操做数据 安装mongoose:npm i -S mongoose 而后在spiderserver目录下新建一个名为db的文件夹,用来存放数据库的数据 打开数据库:(若你已经配置了mongodb的全局路径,可在任意一处输入命令。若没有,则要在对应的mongodb的bin目录下打开cmd输入命令) 输入命令:mongod --dbpath=db文件夹的路径 咱们能够新建一个名为untils的文件夹,在此文件夹下新建一个名为config.js的文件,这个文件是专门用来编写数据库和一些模块的相关配置 编写config.js文件:
而后再app.js文件中引入刚编写的mongoose模块文件:var {Mongoose}=require('./untils/config'); 并调用它的链接数据库方法:Mongoose.connect(); 随后可经过命令行或者可视化的数据库工具Robo 3T来建立咱们对应的数据库(具体操做可查看个人另外一篇文章《关于MongoDB》) 我这里就使用可视化的数据库工具来建立
建立完成后,咱们就能够再次启动咱们的服务器,看数据库是否链接成功,若出现以下图的状况,则表示已链接成功
关于个人页面须要编写的相关接口
1. login:登录接口 2. register:注册接口 3. logout:登出借口 4. verify:验证接口 5. getUser:获取用户信息的接口 6. findPassword:找回密码的接口 ... 因为后端采用的是MVC框架,咱们须要把数据和控制器分离出来,咱们能够在此目录下新建对应的文件夹models和controllers来分别处理相关的东西和编写相关的接口 controllers是用来编写相关的处理方法 models是用来管理相关的数据库数据 在这两个文件夹下分别新建文件users.js
编写个人页面的接口---验证接口verify
咱们这里的验证接口是经过邮箱发送验证码验证(会使用到nodemailer模块) 准备一个发验证码的邮箱(开通SMTP服务)和一个接收验证码的邮箱 在untils文件夹下的config.js文件中编写发送邮箱验证码的配置(先引入nodemailer模块):
在controllers文件夹下的users.js文件中先引入untils下的users.js模块的Email对象,再编写verify接口的处理方法:
在routers文件夹下的index.js文件中先引入controllers文件模块,调用其verify接口的处理方法,而后配置verify接口的路由:(get请求)
设置验证码的时效性: 1. 在spiderserver/config/untils下的Email对象中新增一个获取当前时间的方法
2. 在spiderserver/controllers/users.js下的验证接口处理方法中,把发送验证码的时间存入session中
3. 在spiderserver/controllers/users.js下的注册接口处理方法中,用当前的时间减掉刚才存入session中的发送验证码的时间,判断有没有超过60秒,若超过60s则证实验证码已过时
编写个人页面的接口---注册接口register
为了能在注册的时候拿到验证时的验证码,须要对数据进行持久化操做,存入session中(利用express的中间件express-session) 安装express-session:npm i -S express-session 在app.js文件中引入express-session模块:var session=require('express-session'); 引入模块后可进行相关的配置:(记住要在路由声明前进行配置)
在验证接口中把接收邮箱和验证码都存入session中: req.session.verify=verify; req.session.email=email; 由于用户的注册信息须要保存到数据库中,因此在models/users.js下进行相关的编写: 1. 定义注册接口的数据库骨架 2. 定义对应的数据库模型(注意让惟一值生效的作法) 3. 定义一个数据保存的方法
随后在controllers/users.js下编写注册接口的处理方法: 1. 引入刚定义的数据库模型 2. 获取到用户进行post请求的注册数据 3. 判断传进来的邮箱或者验证码是否与session中存放的相等 4. 利用定义的数据保存方法保存相应的用户名,密码,邮箱 5. 而后根据保存的结果判断是否注册成功
在routers文件夹下的index.js文件中配置rigister接口的路由:(post请求) router.post('/register',usersController.register);
编写个人页面的接口---登陆接口login
在models/users.js下编写数据库查找数据的方法:
在controllers/users.js下编写注册接口的处理方法: 1. 获取到用户进行post请求的登陆数据 2. 根据用户传过来的登陆数据在数据库进行查找匹配 3. 而后根据查找的结果判断是否登陆成功,若登陆成功则把username存入session中
在routers文件夹下的index.js文件中配置login接口的路由:(post请求) router.post('/login',usersController.login);
编写个人页面的接口---登出的接口logout
在controllers/users.js下编写登出的处理方法:直接把存储在session的用户名变为空
在routers文件夹下的index.js文件中配置logout接口的路由:(get请求) router.get('/logout',usersController.logout);
编写个人页面的接口---获取用户信息的接口getUser
在controllers/users.js下编写获取用户信息的处理方法: 判断session中的username是否为空,不为空则获取成功并把相应的用户数据存放到session中,不然获取失败
在routers文件夹下的index.js文件中配置getUser接口的路由:(get请求) router.get('/getUser',usersController.getUser);
编写个人页面的接口---找回密码的接口findPassword
在models/users.js下编写更新数据库数据的方法:(根据传过来的对应邮箱进行更新)
在controllers/users.js下编写找回密码的处理方法: 1. 获取到用户进行post请求的相关数据 2. 判断传过来的邮箱和验证码是否和session存储的同样 3. 若同样,则调用usreModel下的updatePassword方法对密码进行修改,不然失败
截止目前,有关个人页面的用户接口已经写完了
接下来继续开发个人页面的组件↓↓↓↓↓↓
配置个人页面的子路由
在src/router/mine/index.js中配置个人页面相关的子路由(包括登陆组件,我的中心组件,注册组件,找回密码组件) 当显示个人页面时,重定向路由到我的中心页面
完善以前建立的登陆组件的功能
1. 在data中定义两个数据:username(用户名),password(密码) 2. 对用户名的输入框和密码的输入框都利用v-model分别对username,password数据进行双向绑定 3. 给登陆按钮添加一个touchstart事件,执行handleToLogin方法 4. 编写handleToLogin方法: 1) 向以前编写的登陆login接口进行ajax请求(记得先进行对应的反向代理,要否则会出现跨域没法访问的问题,反向代理时本地编写的接口最好放在前面,以避免有些没必要要的错误) 2) 根据status来判断是否登陆成功(登陆成功则跳转到我的中心的页面) 3) 引入弹窗组件,利用弹窗组件对其进行一个反馈信息的做用
设置用户的一些状态管理
在store文件夹下新建一个名为user的文件夹,在此文件夹下新建一个index.js文件并编写。随后在store/index.js中引入该文件模块
建立个人页面的相关组件---我的中心组件
1. 在views/mine下新建一个名为center.vue的文件 2. 编写相应的HTML,CSS HTML部分中,当前用户名经过$stor.state.xxx.xxx显示出来
3. 对退出按钮添加一个touchstart事件,并编写handleToLogout方法: 4. 经过对logout接口进行ajax请求,经过status判断是否登出成功,登出成功则跳转到登陆页面并更新用户状态管理的用户名为空
5. 在进入我的中心页面前能够先利用"导航守卫"判断一下当前用户的登陆状态再决定是否能够进入此组件(用户的登陆状态可利用向getUser接口发起请求来判断) 若此时用户为登陆状态则能够进入我的中心页面并更新用户状态管理的用户名为当前用户的用户名,反之则跳转到登陆页面
建立个人页面的相关组件---注册组件
1. 在components新建一个名为register的文件夹,在此文件夹下新建一个index.vue文件 2. 编写相应的HTML,CSS
3. 在data中定义六个数据:username(用户名),password(密码),email(邮箱),verify(验证码),verifyInfo(验证码的相关信息),disabled(判断按钮是否禁用状态) 4. 对用户名的输入框,密码的输入框,邮箱的输入框和验证码的输入框都利用v-model分别对username,password,email,verify数据进行双向绑定 5. 引入弹窗组件,利用弹窗组件对其进行一个反馈信息的做用 6. 对发送验证码的按钮添加一个touchstart事件,并编写handleToVerify方法:(利用ajax对verify接口进行请求) 点击肯定后触发countDown方法
7. 对注册的按钮添加一个touchstart事件,并编写handleToRegister方法:(利用ajax对register接口进行请求)
建立个人页面的相关组件---修改密码组件
1. 在components新建一个名为findPassword的文件夹,在此文件夹下新建一个index.vue文件 2. 编写相应的HTML,CSS
3. 在data中定义三个数据:password(密码),email(邮箱),verify(验证码) 4. 对密码的输入框,邮箱的输入框和验证码的输入框都利用v-model分别对password,email,verify数据进行双向绑定 5. 引入弹窗组件,利用弹窗组件对其进行一个反馈信息的做用 6. 对发送验证码的按钮添加一个touchstart事件,并编写handleToVerify方法:(利用ajax对verify接口进行请求)
7. 对确认修改的按钮添加一个touchstart事件,并编写handleToPassword方法:(利用ajax对findPassword接口进行请求)
接下来是开发后台管理页面↓↓↓↓↓↓
设置后台管理的管理员接口与权限
1. 先在spiderserver/models/users.js中的数据库骨架新增一个值为isAdmin,此值为Boolean类型。用于判断是否有管理权限
2. 在spiderserver/contorllers/user.js中,在登陆接口的处理方法中,当登录成功时,也把其管理权限的值也存入到session中,以便之后判断是否有管理权限。在获取用户信息接口的处理方法中,当获取成功时,把其管理权限的值存入到data中
3. 在spiderserver/routes下新建一个名为admin.js的文件,配置管理页面的接口路由 1) 先设置一个拦截器,用于判断其用户的管理权限,才可进入管理页面 2) 配置管理页面的主页路由
4. 在spiderserver/app.js中引入该管理页面的路由
后台管理页面是利用element-ui组件库搭建的
element-ui中文官网:https://element-plus.gitee.io/#/zh-CN 1. 安装element-ui插件:npm i -S element-ui 2. 在main.js文件中引入element-ui: import ElementPlus from 'element-plus'; import 'element-plus/lib/theme-chalk/index.css'; app.use(ElementPlus);
配置后台管理页面的路由
在spider/src/router下建立一个名为admin的文件夹,在此文件夹下建立一个index.js文件 其子路由包括用户管理页面路由,电影管理页面路由,影院管理页面路由
随后在spider/src/router下的index.js文件下引入该路由文件模块
建立管理后台组件---主页面组件
1. 在spider/src/views下新建一个名为admin的文件夹,在此文件夹下新建一个index.vue文件 2. 编写相应的HTML,CSS(布局为头部,侧边栏,主区域)
3. 在spider/store/user/index.js下,新增用户的一个管理员状态值isAdmin,默认值为false
4. 在我的中心页面根据其是否为管理员渲染用户的身份,若为管理员则添加一个router-link标签来跳转到后台管理页面 1) 在获取到用户信息后,把用户的管理权限的值更新到用户的状态管理中 2) 登出的时候,用户状态管理中的管理权限的值为false
5. 在管理后台的主页面中,利用导航守卫来判断当前用户是否有管理权限来进入管理后台
建立管理后台组件---用户管理组件
1. 在spider/src/views/admin下新建一个user.vue文件 2. 编写相应的HTML,CSS(表格区域分为帐号建立日期,用户名,邮箱,操做(未冻结和删除))
3. 在data中定义一个数据:tableData(数组,用于存放所有的用户信息) 4. 在生命周期mounted中利用ajax对用户管理接口发起请求(把获得的所有用户数据都利用tableData数据渲染到页面上) 5. 对冻结操做的按钮监听一个click事件,并执行编写的handleToFreeze方法: 1) 传入参数(利用scope上的属性:scope.$index, scope.row) 2) 利用ajax对冻结帐号操做的接口发起请求,根据status的值判断冻结操做是否成功,利用element-ui的信息弹窗组件来反馈信息(若冻结操做成功,则把对应的isFreeze值取反便可) 6. 对删除操做的按钮监听一个click事件,并执行编写的handleToDelete方法: 1) 传入参数(利用scope上的属性:scope.$index, scope.row) 2) 利用ajax对删除帐号操做的接口发起请求,根据status的值判断删除操做是否成功,利用element-ui的信息弹窗组件来反馈信息(若删除操做成功,则利用splice方法把对应的tableData指定的index项删除掉)
7. 对于冻结的帐号,登陆时应提示帐号已冻结,登陆失败 在spiderserver/controllers/users.js下编写一些相应判断
在login组件中也要在弹窗中反馈对应的信息
8. element-ui对数据进行分页处理 1) 引入分页组件
2) 在data中定义两个数据:currentPage(当前页),pageSize(每页显示的数据) 3) 经过computed根据当前页和每页显示的数据来计算nowTableData的每一页渲染的数据,此时table应该是渲染nowTableData的数据
4) 编写handleCurrentChange方法来改变当前页的值
建立管理后台的接口---用户管理接口
在用户管理接口中,要实现三个方法: 1. 显示全部用户数据的方法 1) 在spiderserver/models/users.js下编写一个返回显示全部用户数据的方法
2) 在spiderserver/controllers/admin.js下编写显示全部用户信息的操做的处理方法(引入models中刚编写的方法)
3) 在spiderserver/routes/admin.js下配置显示用户数据的接口路由:router.get('/userList', adminController.userList); 2. 帐号冻结操做的方法 1) 在spiderserver/models/users.js的数据库骨架中,新增一个值为isFreeze,此值为Boolean类型。用于判断用户帐号是否被冻结
在spiderserver/models/users.js下编写一个数据库更新帐号的冻结状态的方法(以邮箱号为标识来更新isFreeze的值)
2) 在spiderserver/controllers/admin.js下编写更新帐号的冻结状态操做的处理方法(引入models中刚编写的方法)
3) 在spiderserver/routes/admin.js下配置帐号冻结的接口路由:router.post('/updateFreeze', adminController.updateFreeze); 3. 帐号删除的方法 1) 在spiderserver/models/users.js下编写一个数据库删除用户帐号的方法(以邮箱号为标识来删除)
2) 在spiderserver/controllers/admin.js下编写删除用户帐号操做的处理方法(引入models中刚编写的方法)
3) 在spiderserver/routes/admin.js下配置帐号删除的接口路由:router.post('/deleteUser', adminController.deleteUser);
关于用户头像上传的操做
编写用户头像上传的接口: 1. 在spiderserver/models/users.js下的数据库骨架中添加一个值为userHead,类型为String,默认值为http://localhost:3000/images/default.jpg,表示用户的头像
2. 在spiderserver/models/users.js下编写数据库更新用户头像的方法
3. 在spiderserver/untils/config.js下编写用户头像上传的基础公共路径
4. 在spiderserver/controllers/users.js下编写更新用户头像的方法(先引入刚编写的config.js的Head对象,fs模块)
5. 后端上传东西能够利用node中的multer模块(详情用法可参考文档:http://expressjs.com/en/resources/middleware/multer.html) 1) 在spiderserver/routes/users.js下引入multer模块,并指定头像的上传路径
2) 配置上传文件接口的路由
前端的操做: 1. 在用户的管理状态中新增一个状态量userHead,初始值为空串
2. 在进入我的中心页面前,若进入成功,也要更新一下用户头像状态的值
3. 登出页面的时候,把用户头像状态的状态值变为空串
4. 在我的中心页面组件中添加一个区域,是负责上文头像文件的
5. 在上传文件的按钮上绑定一个touchstart事件,执行编写的handleToUpload方法 须要注意的是在调用用户头像上传的这个接口时,要传入文件的相关参数(利用FormData()对象,更多FormData的用法可参考:https://www.cnblogs.com/wssdx/p/11244766.html)和传入"Content-Type"的值是为"multipart/form-data" 更新用户头像的状态值时记得在后面加个随机数来清除缓存
6. 在后台管理页面中,新增一列来存放用户上传的头像
项目优化---密码加密
加密可以使用node中的crypto模块 1. 在spiderserver/untils下新建一个base.js,用来编写加密的相关方法 1) 引入crypto模块:const crypto = require('crypto'); 2) 编写一个加密方法setCrypto
2. 在spiderserver/controllers/users.js,先引入上面编写的文件模块,再去设置存放密码为加密的密码形式 如:
项目优化---登陆时防攻击验证码
利用node的trek-captcha模块生成图形验证码 1. 在spiderserver/untils/base.js下引入trek-captcha模块,再编写图形验证码生成方法
2. 在spiderserver/controllers/users.js,先引入上面编写的文件模块,再去编写生成图形验证码的处理方法
3. 在spiderserver/routes/users.js下配置生成图形验证码的接口路由:
在login组件中使用此组件: 1. HTML中新增长一个区域:填写验证码
2. 为img标签绑定一个touchstart事件,执行编写的handleToVerifyImg方法实现点击图片更新验证码
3. 当登录失败点击肯定后,也要从新刷新图片验证码(在img标签添加一个ref属性,属性值为verImg,用于获取到该img元素)
关于项目的线上部署
把打包好的项目交给后端,后端要选取一个web服务器(http服务器) 咱们此次选取的一个web服务器是nginx 什么是nginx? 为何要选择nginx呢? nginx的好处有哪些呢? 以上的种种问题可移步至另外一篇文章《关于nginx》!!! 而上线项目,则须要有一个云服务器(对应的域名或一个IP地址),能够去阿里云,腾讯云等看看 云服务器都设置好之后,可经过windows的远程链接来链接远程服务器,在远程服务器中也安装nginx,配置对应的反向代理 截止目前,就能够在浏览器上访问公有的IP地址,就能够访问到咱们的这个项目了
项目过程当中遇到的一些问题
1. 在gitee建立远程仓库时没有把readme初始化仓库那一项去掉,致使一开始的远程库不是一个空仓库,因此没法把本地库中的初始项目推送到远程库中,一直卡住 git push时出现的报错:(大概以下)
解决方法也可参考网址:https://blog.csdn.net/cuomer/article/details/81142159 2. 在router下的index.js编写路由重定向的时候,如有任意不匹配的路由时,则跳转到电影路由 我原本写的path值为'/*'来表示任意不匹配的路由,以前都是能够生效的,但此次作项目的时候不行 因此我在网上找了一下解决的方案,把path的值改成'/:catchAll(.*)'就能够了 3. 在解决跨域问题,编写vue.config.js文件时,输出模块我写成了export default{}形式,结果从新运行项目时报错了,可能我没有新增长一些模块来支持这种es6的写法,因此改为了module.exports={}的形式 require: node 和 es6 都支持的引入(CommonJS规范) export / import / export default: 只有es6 支持的导出引入 module.exports / exports: 只有 node 支持的导出(CommonJS规范) 4. Vue3.x挂载全局及调用挂载上的方法 在之前是能够直接使用如下方法来进行全局的挂载 import Vue from 'vue' Vue.prototype.xxx=xxx; 在我想要全局挂载axios的时候,就利用了上面那个方法,报错了 我上网搜了一下,发现Vue3.x中Vue不能直接这样挂载全局,下面是挂载的方法: import { createApp } from 'vue' import App from './App.vue' createApp(App).config.globalProperties.$xxx=xxx; 调用全局挂载里的方法:this.$xxx 5. 关于Vue3.x的过滤器定义和使用 在Vue2.x的时候,定义过滤器: import Vue from 'vue' Vue.filter('过滤方法名',(xx)=>{ return xxx; }) 使用的时候xx | 过滤方法名(xx) 在我渲染正在热映的组件时,想要设置一个过滤器来改变传过来的图片的宽高,使用了上面的方法结果就报错了 原来在Vue3.x中,这个方法已经废除掉了 下面是Vue3.x中用的方法: 定义一个过滤方法,挂载到全局上: import { createApp } from 'vue' import App from './App.vue' createApp(App).config.globalProperties.$filters = { 过滤方法名(xx) { return xxx; } } 使用时:$filters.过滤方法名(xx) 6. computed和watch的区别: 详情可参考网址:https://blog.csdn.net/weixin_42757570/article/details/112214606 7. 在封装better-scroll为全局组件时,会出现监听的scroll事件没法生效 查看了一下致使scroll事件没法生效的一些缘由: 1) DOM层级关系(wrapper里面不能存在多个同级div,若出现多个同级div,则须要有一个div包裹住) 2) content是否被成功添加滚动相关style 3) 只有content的高度大于wrapper高度时候,才能够滚动 4) .wrapper元素上要给定位 但调试来调试去,都好像不是这些缘由 而后就试一下有没有多是数据更新了,致使没有从新计算 better-scroll,结果在钩子函数updated中添加上了this.scroll.refresh(),就能够了 (当 DOM 结构发生变化的时候务必要调用确保滚动的效果正常) 8. 本来在有keep-alive缓存系统的状况下更新数据,要把请求等操做都在activated生命周期中执行。但我在mounted中执行能够,在activated中就只执行了一次,好奇怪(有知道致使这个问题的大佬能够在评论区评论哦!!!) 9. 修改基础的公共根路径: 在vue-cli3.3以前的版本可利用baseUrl属性 在vue-cli3.3以后的版本可利用publicPath属性 (baseURL在vue-cli_3.3后面的版本中被弃用) 10. 在app.js文件中配置session时要注意放在路由声明前,不然不会生效
以上就是蜘蛛电影项目的大概内容了,有更多的功能可自行去完善
最后附上此项目大概的思惟导图:
如有什么表达不当的地方,也欢迎指出,一块儿进步哦!!!