未分类

记一个Vue项目的开发历程

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

一、成品展示

二、项目需求

  • 添加每一天的时间记录,修改某天的时间记录(因为可能记错或者忘了记某项活动)
  • 每天的记录可以有多项活动,每项活动有对应的时间
  • 每项活动划分到特定的类型
1
2
3
4
2016-01-20,星期五
1. 学习Vue, 5h,[学习]
2. 跑步,0.5h,[运动]
3. 看霹雳布袋戏,2h,[休闲]

2、数据分析

  • 按照月份统计每天的总时间,按照年份统计每月的总时间,按照分类统计各项内容的总时间
  • 以图表形式展示月份时间支出和年份时间支出的变化情况
  • 以图表形式展示各种类型的活动的时间支出情况
  • 计算总时间和平均时间

3、时光展示

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

4、数据模型

1
2
3
4
5
6
7
8
9
10
11
{
"items": [{
"content": "string",
"time": "number",
"type": "string"
}],
"year": "number",
"month": "number",
"date": "number",
"day": "number"
}

数据示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"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、组件划分

1
2
3
4
5
6
7
8
9
10
11
└─App:挂载整个应用
├─Auth:【权限界面】
│ └─StarFlow:【权限界面】底部的动画
└─Main:【主界面】的基本结构
├─Management:【管理】
│ ├─TimeAnalysisPerMonth:月份时间分析组件
│ ├─TimeAnalysisPerYear:年份时间分析组件
│ ├─TimeAnalysisByType:分类时间分析组件
│ └─EditTimeRecord:时间记录编辑组件
├─Timeline:【时光轴】
└─TimeSlide:【时光展】

4、文件结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
└─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项目模板

1
vue init webpack timeline-x

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

1
2
3
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template lang="jade">
div#auth
h1 Auth Page
</template>

<script>
export default {
name: 'auth'
}
</script>

<style lang="sass">
#auth
border: 1px solid red
</style>

2、准备组件

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

文件结构

3、配置路由

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

1
npm install vue-router --save

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

1
2
3
4
import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const router = new Router({
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

1
npm install element-ui --save

1
2
3
4
5
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构建之后的产品代码就只有用到的图表的那部分代码了,可以较少产品代码体积。

1
npm install echarts --save

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

1
2
3
4
5
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

1
2
3
4
5
6
7
8
9
div#auth
star-flow
div#auth-input
el-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

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

1
2
3
4
5
6
7
8
9
10
11
#auth
width: 100%
height: 100%
background: url(../../assets/auth-bg.jpg) no-repeat
background-size: 100% 100%
background-attachment: fixed
#auth-input
position: absolute
width: 100%
height: 10%
top: 45%

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import StarFlow from './StarFlow'

export default {
name: 'auth',
components: { StarFlow },
data () {
return {
password: '',
disabled: true,
errorTip: ''
}
},
methods: {
setTip (tip) { // 消息提示,1.5秒后自动关闭
this.errorTip = tip
this.disabled = false
setTimeout(() => {
this.disabled = true
this.errorTip = ''
}, 1500)
},

signin () { // 验证密码解除锁屏
if (!this.password) {
this.setTip('密码不能为空')
} else {
this.$store.dispatch('unlockScreen', this.password)
.then((err) => {
if (err) this.setTip('密码错误')
else this.$router.replace('/main')
})
}
}
}
}

5、改善UI

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

6、数据状态管理

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

1
npm install vuex --save

1
2
3
4
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

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

1
2
3
4
5
6
7
8
const store = new Vuex.Store({
state: {
lockScreen: true,
timeRecords: []
},
mutations,
actions
})

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

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

1
npm install axios --save

1
import axios from 'axios'

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
export default {
unlockScreen ({commit}, password) { // 验证密码,接触锁屏
// 使用"npm run dev"启动时候请解除下面代码的注释,注释后面的POST代码
// return new Promise((resolve, reject) => {
// commit('UNLOCK')
// resolve(false)
// })
return axios.post('/api/check', {
password: password
}).then((res) => {
if (!res || res.status !== 200 || res.data.err) {
return true
} else {
commit('UNLOCK')
return false
}
})
},

addRecord ({commit}, record) { // 添加记录
// 使用"npm run dev"启动时候请解除下面代码的注释,注释后面的POST代码
// return new Promise((resolve, reject) => {
// commit('UPDATE_RECORD', record)
// resolve(false)
// })
return axios.post('/api/add', record).then((res) => {
if (!res || res.status !== 200 || res.data.err) {
return true
} else {
commit('UPDATE_RECORD', record)
return false
}
})
},

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也给importrouter里面去,这样就可以访问到了。不知道有没有其他更优秀的方案,欢迎交流。

1
2
3
4
5
6
7
8
9
10
11
12
13
import store from '../store'

router.beforeEach((to, from, next) => { // 对特定路径进行验证,增强锁屏功能
if (to.path === '/') {
next('/auth')
} else if (to.path === '/auth' && !store.state.lockScreen) {
next('/main')
} else if (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行即可。

1
2
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仓库的代码和commit记录。下面说说几点收获:

  • es6写起来感觉超棒
  • webpack比gulp优雅
  • vue值得尝试
分享到