vue项目-前端Vue 后台express

项目地址 https://github.com/hongchh/timeline-x

一、成品展示

二、项目需求

添加每一天的时间记录,修改某天的时间记录(因为可能记错或者忘了记某项活动)

每天的记录可以有多项活动,每项活动有对应的时间

每项活动划分到特定的类型

2016-01-20,星期五1. 学习Vue, 5h,[学习]2. 跑步,0.5h,[运动]3. 看霹雳布袋戏,2h,[休闲]

2、数据分析

按照月份统计每天的总时间,按照年份统计每月的总时间,按照分类统计各项内容的总时间

以图表形式展示月份时间支出和年份时间支出的变化情况

以图表形式展示各种类型的活动的时间支出情况

计算总时间和平均时间

3、时光展示

时光轴形式进行活动记录展示,便于回顾总结

轮播图形式进行活动记录展示,便于回顾总结

4、数据模型

{  "items":[{    "content":"string",    "time":"number",    "type":"string"}],  "year":"number",  "month":"number",  "date":"number",  "day":"number"}

数据示例

{  "items":[{    "content":"学习Vue",    "time":"5",    "type":"学习"}, {    "content":"跑步",    "time":"0.5",    "type":"运动"}, {    "content":"看霹雳布袋戏",    "time":"2",    "type":"休闲"}],  "year":"2016",  "month":"01",  "date":"20",  "day":"5"}

三、开发思路

1、界面构成

整个应用分成2个主要界面:【主界面】,【权限界面】

【权限界面】用于用户登录,也是应用的启动界面

【主界面】包含3个子界面:【管理】,【时光轴】,【时光展】

【时光轴】和【时光展】用于时光展示

【管理】用于展示数据分析结果以及编辑时光记录(添加/修改)

2、跳转关系

【权限界面】验证成功之后跳转到【主界面】

【主界面】默认展示【时光轴】界面

【主界面】顶栏可以选择跳转到【管理】、【时光轴】或【时光展】界面

【主界面】顶栏选择“锁屏”之后回到【权限界面】

3、组件划分

└─App:挂载整个应用

  ├─Auth:【权限界面】

  │  └─StarFlow:【权限界面】底部的动画

  └─Main:【主界面】的基本结构

      ├─Management:【管理】

      │    ├─TimeAnalysisPerMonth:月份时间分析组件

      │    ├─TimeAnalysisPerYear:年份时间分析组件

      │    ├─TimeAnalysisByType:分类时间分析组件

      │    └─EditTimeRecord:时间记录编辑组件

      ├─Timeline:【时光轴】

      └─TimeSlide:【时光展】

4、文件结构

└─build:构建用到的相关文件├─config:构建的配置文件├─server:应用的服务器源码│  ├─controller:服务端业务逻辑│  ├─model:数据存储逻辑│  ├─static:静态文件│  ├─views:应用的视图文件│  ├─app.js:express服务器配置文件│  └─server.js:服务器启动文件├─src:前端开发源码│  ├─assets:图片等静态资源│  ├─components:前端组件│  ├─router:前端路由│  ├─store:vuex的store│  ├─App.vue:应用的外层结构│  └─entry.js:应用的入口文件└─static:前端开发过程中用到的静态文件    └─data:存放伪数据以及伪数据生成器

四、开发历程

1、基础配置

使用vue-cli生成一个webpack项目模板

vue init webpack timeline-x

生成之后为了支持jade+sass开发,还需要安装相关的依赖

npm install node-sass --save-dev

npm install sass-loader --save-dev

npm install jade --save-dev

配置完成之后我们就可以在项目中使用jade和sass来进行开发了,.vue文件的模板如下,注意在template标签和style标签里面使用lang属性说明jade和sass。

div#auth  h1 Auth Pageexportdefault{  name:'auth'}#authborder: 1pxsolidred

2、准备组件

按照之前划分好的组件,在src/components文件夹里面准备好相关文件。暂时不需要写入各组件的内容,就按照上面的模板一样写个标题说明这是哪个组件即可。

3、配置路由

组件都准备好之后安装路由

npm install vue-router --save

安装完成之后需要在使用路由的时候将其导入,我这里选择在router/index.js文件里面导入。

import Vue from'vue'import Router from'vue-router'Vue.use(Router)

按照前面确定好的页面跳转关系配置路由信息,然后测试界面跳转是否正常。

constrouter =newRouter({  mode:'history',  routes: [    { path:'/auth', component: Auth },    {      path:'/main',      component: Main,      children: [        { path:'management', component: Management },        { path:'timeline', component: Timeline },        { path:'time-slide', component: TimeSlide },        { path:'', redirect:'timeline'}      ]    },    { path:'/', redirect:'/auth'}  ]})

4、组件设计

UI方面使用了一些element-ui的组件,因此还需要先安装element-ui。同样地安装之后需要导入,导入方法类似之前的vue-router。注意这里还需要导入element-ui的样式文件index.css。

npm install element-ui --save

import Vue from'vue'import ElementUI from'element-ui'import'element-ui/lib/theme-default/index.css'Vue.use(ElementUI)

3个时间分析图表使用的是echarts,需要先安装echarts。安装之后import需要用到的图表就行了,这样经过webpack构建之后的产品代码就只有用到的图表的那部分代码了,可以较少产品代码体积。

npm install echarts --save

由于我只用到了柱状图和圆饼图,然后图表上面还使用了标题和提示等组件,所以只需要在entry.js里面导入这些组件即可。

import'echarts/lib/chart/pie'import'echarts/lib/chart/bar'import'echarts/lib/component/tooltip'import'echarts/lib/component/title'import'echarts/lib/component/legend'

到步骤【2】准备好的文件里面开始编写组件。在template标签中编写组件的HTML结构,然后在style标签中调节样式,业务逻辑则是放在script标签中。下面我用Auth.vue来作为例子,其他组件请参考github仓库里面的代码。

权限界面比较简单(参考上面的成品展示图),HTML结构如下,使用element-ui的栅格布局,同时用了另外一个动态背景组件star-flow。

div#authstar-flowdiv#auth-inputel-row(:gutter="20")      el-col(:span="8", :offset="8")        el-tooltip(:disabled="disabled", :content="errorTip", placement="bottom-start", effect="light")          el-input(placeholder="请输入密码", v-model="password",type="password")            template(slot="append")              el-button(@click="signin") Go

样式如下,设置一下背景图、宽高度之类的。

#authwidth:100%height:100%background: url(../../assets/auth-bg.jpg) no-repeatbackground-size:100%100%background-attachment: fixed#auth-inputposition: absolute    width:100%height:10%top:45%

下面是js部分了,使用es6的写法,为了使用另一个组件,需要先将其import进来然后添加到components里面。export是将当前的组件导出,其他模块才能使用到。data里面写该组件用到的数据项,methods则是这个组件里面需要的方法函数了。(有点类似angular的controller)

import StarFlow from'./StarFlow'exportdefault{  name:'auth',  components: { StarFlow },  data () {return{      password:'',      disabled:true,      errorTip:''}  },  methods: {    setTip (tip) {// 消息提示,1.5秒后自动关闭this.errorTip = tipthis.disabled =falsesetTimeout(() => {this.disabled =truethis.errorTip =''},1500)    },    signin () {// 验证密码解除锁屏if(!this.password) {this.setTip('密码不能为空')      }else{this.$store.dispatch('unlockScreen',this.password)        .then((err) => {if(err)this.setTip('密码错误')elsethis.$router.replace('/main')        })      }    }  }}

5、改善UI

调整组件的样式,并且改进应用的样式,添加过渡动画。过渡动画直接在router-view外面套一个transition,然后编写相应的过渡样式即可,很简便。

6、数据状态管理

该项目由6个组件需要用到同一份数据,1个记录编辑组件、2个时光展示组件和3个数据分析组件。为了方便数据管理,同时也好制造机会学习Vuex,因此我这里引入vuex来进行应用的状态管理。安装vuex,然后在store/index.js里面导入vuex。

npm install vuex --save

import Vue from'vue'import Vuex from'vuex'Vue.use(Vuex)

定义1个store存放应用的全局状态:锁屏变量和时间记录。默认开启锁屏,所以lockScreen设置为true,而timeRecords则是一个时间记录数据,里面包含多条记录。这些数据在应用启动接触锁屏之后向服务器获取,在还没有开发服务器的时候,为了完成前端开发可以先在这里写一些伪数据进去。

conststore =newVuex.Store({  state: {    lockScreen:true,    timeRecords: []  },  mutations,  actions})

在store/actions.js和store/mutations.js里面定义这些全级状态可能发生的变化。如果涉及到异步操作,则将这些涉及异步的变化放到actions.js里面,例如向服务器获取数据这个操作就是一个异步的,应该将其代码放在action里面。根据Vuex文档的介绍,mutation的代码必须要是同步的。

由于涉及跟服务器的交互,所以我们还需要一个提供http服务模块。上网查了查,有vue-resource这个东西可以用,不过好像更推荐使用axios,因此我这里也选择使用axios。下面进行安装和导入。

npm install axios --save

import axios from'axios'

准备好了之后可以开始编写了,下面是actions.js的代码,其他代码请参考github仓库。实现了后台交互功能,然后通过commit相应的mutation来更新store里面的状态。注释部分的代码是在前端开发还没有服务器的时候使用的开发代码。

exportdefault{  unlockScreen ({commit}, password) {// 验证密码,接触锁屏// 使用"npm run dev"启动时候请解除下面代码的注释,注释后面的POST代码// return new Promise((resolve, reject) => {//  commit('UNLOCK')//  resolve(false)// })returnaxios.post('/api/check', {      password: password    }).then((res) => {if(!res || res.status !==200|| res.data.err) {returntrue}else{        commit('UNLOCK')returnfalse}    })  },  addRecord ({commit}, record) {// 添加记录// 使用"npm run dev"启动时候请解除下面代码的注释,注释后面的POST代码// return new Promise((resolve, reject) => {//  commit('UPDATE_RECORD', record)//  resolve(false)// })returnaxios.post('/api/add', record).then((res) => {if(!res || res.status !==200|| res.data.err) {returntrue}else{        commit('UPDATE_RECORD', record)returnfalse}    })  },  fatchData ({commit}) {// 获取数据axios.get('/static/data/data.json').then((res) => {if(res.status ===200) {        commit('FATCH_DATA', res.data)      }    })  }}

7、准备伪数据

为了让应用有更多数据可以呈现,需要自行编造一些伪数据,我写了一个伪数据生成器,即static/data/data-generator.js。运行之后产生100多条时间记录并存放到json文件里面。具体请参考github仓库

8、完善锁屏

实现锁屏功能其实不难,只要在权限界面检验之后更新store里面的锁屏状态为false,在主界面点击顶栏的锁屏之后更新store里面的锁屏状态为true即可。但为了更完善,还需要在路由里面设置一个全局钩子,在进入相应界面之前检查一下store的锁屏状态,例如,在访问主界面的时候需要检查store里面的锁屏状态是否为true,如果是的话强行给它重定向到权限界面,因为这可能是用户通过手动修改URL来访问的。

这里有一个坑,在router的全局钩子里面访问不到store里面的状态。在stack-overflow里面找到了这个解决方案,把store也给import到router里面去,这样就可以访问到了。不知道有没有其他更优秀的方案,欢迎交流。

import store from'../store'router.beforeEach((to, from, next) => {// 对特定路径进行验证,增强锁屏功能if(to.path ==='/') {    next('/auth')  }elseif(to.path ==='/auth'&& !store.state.lockScreen) {    next('/main')  }elseif(to.path !=='/auth'&& store.state.lockScreen) {    next('/auth')  }else{    next()  }})

9、后台开发

前端开发基本完成之后就可以开始后台开发了,我后端使用的是express框架,按照MVC模式进行开发。由于本项目关注的是前端开发,所以后端就写得比较简单,也没有用上数据库,没有什么好讲的。主要有一点需要注意,为了方便,我更改了build的目标路径。用vue-cli生成的webpack项目build之后是在主目录下面生成一个dist文件夹来存放产品文件,我将其改成build之后静态文件放到server里面的static文件夹,而index.html则放到server里面的views文件夹,这样就不用手动去搬dist里面的文件给服务器用了。改动很简单,只需要该config/index.js里面的7-8行即可。

index: path.resolve(__dirname,'../server/views/index.html'),assetsRoot: path.resolve(__dirname,'../server'),

10、前后对接

对接前端和后台的接口,调整代码,修正bug。如果一开始将接口想清楚、设计好的话这里基本没有什么难。本项目只有2个post数据和1个get数据的接口,没有难度。

11、改进,修正小问题

五、结束

大概用了5天写这个项目,基本入门了Vue,还是一次蛮不错的体验。为了多点练习强行加入了vuex,虽然官方推荐不要在小项目里面使用vuex,但我觉得引入vuex之后还是可以解决很多问题,思路也清晰了很多。在用vuex之前的想法是在一个外层的组件里面获取数据然后传递给子组件。后来发现如果我在编辑记录组件里面更新或者添加了一条记录,父组件和其他兄弟组件的数据不会收到更新。如果通过$emit来让父亲组件更新数据然后再使得兄弟组件更新,整个过程又太过繁琐。而Vuex的思路是弄一个全局状态来对数据进行管理,组件树上的组件都可以更新状态,状态变化之后会反馈到相应的其他组件上。这个思路很清晰,代码写起来也很顺利,也体验了vuex的强大,确实可以省事很多。

部分内容可能无法讲得很清楚,有兴趣了解的话请参考github仓库的代码。下面说说几点收获:

es6写起来感觉超棒

webpack比gulp优雅

vue值得尝试

推荐阅读更多精彩内容