項目地址?https://github.com/hongchh/timeline-x
添加每一天的時間記錄,修改某天的時間記錄(因為可能記錯或者忘了記某項活動)
每天的記錄可以有多項活動,每項活動有對應的時間
每項活動劃分到特定的類型
2016-01-20,星期五1. 學習Vue, 5h,[學習]2. 跑步,0.5h,[運動]3. 看霹靂布袋戲,2h,[休閑]
按照月份統計每天的總時間,按照年份統計每月的總時間,按照分類統計各項內容的總時間
以圖表形式展示月份時間支出和年份時間支出的變化情況
以圖表形式展示各種類型的活動的時間支出情況
計算總時間和平均時間
時光軸形式進行活動記錄展示,便于回顧總結
輪播圖形式進行活動記錄展示,便于回顧總結
{? "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"}
整個應用分成2個主要界面:【主界面】,【權限界面】
【權限界面】用于用戶登錄,也是應用的啟動界面
【主界面】包含3個子界面:【管理】,【時光軸】,【時光展】
【時光軸】和【時光展】用于時光展示
【管理】用于展示數據分析結果以及編輯時光記錄(添加/修改)
【權限界面】驗證成功之后跳轉到【主界面】
【主界面】默認展示【時光軸】界面
【主界面】頂欄可以選擇跳轉到【管理】、【時光軸】或【時光展】界面
【主界面】頂欄選擇“鎖屏”之后回到【權限界面】
└─App:掛載整個應用
? ├─Auth:【權限界面】
? │? └─StarFlow:【權限界面】底部的動畫
? └─Main:【主界面】的基本結構
? ? ? ├─Management:【管理】
? ? ? │? ? ├─TimeAnalysisPerMonth:月份時間分析組件
? ? ? │? ? ├─TimeAnalysisPerYear:年份時間分析組件
? ? ? │? ? ├─TimeAnalysisByType:分類時間分析組件
? ? ? │? ? └─EditTimeRecord:時間記錄編輯組件
? ? ? ├─Timeline:【時光軸】
? ? ? └─TimeSlide:【時光展】
└─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:存放偽數據以及偽數據生成器
使用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
按照之前劃分好的組件,在src/components文件夾里面準備好相關文件。暫時不需要寫入各組件的內容,就按照上面的模板一樣寫個標題說明這是哪個組件即可。
組件都準備好之后安裝路由
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'}? ]})
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')? ? ? ? })? ? ? }? ? }? }}
調整組件的樣式,并且改進應用的樣式,添加過渡動畫。過渡動畫直接在router-view外面套一個transition,然后編寫相應的過渡樣式即可,很簡便。
該項目由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)? ? ? }? ? })? }}
為了讓應用有更多數據可以呈現,需要自行編造一些偽數據,我寫了一個偽數據生成器,即static/data/data-generator.js。運行之后產生100多條時間記錄并存放到json文件里面。具體請參考github倉庫。
實現鎖屏功能其實不難,只要在權限界面檢驗之后更新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()? }})
前端開發基本完成之后就可以開始后臺開發了,我后端使用的是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'),
對接前端和后臺的接口,調整代碼,修正bug。如果一開始將接口想清楚、設計好的話這里基本沒有什么難。本項目只有2個post數據和1個get數據的接口,沒有難度。
大概用了5天寫這個項目,基本入門了Vue,還是一次蠻不錯的體驗。為了多點練習強行加入了vuex,雖然官方推薦不要在小項目里面使用vuex,但我覺得引入vuex之后還是可以解決很多問題,思路也清晰了很多。在用vuex之前的想法是在一個外層的組件里面獲取數據然后傳遞給子組件。后來發現如果我在編輯記錄組件里面更新或者添加了一條記錄,父組件和其他兄弟組件的數據不會收到更新。如果通過$emit來讓父親組件更新數據然后再使得兄弟組件更新,整個過程又太過繁瑣。而Vuex的思路是弄一個全局狀態來對數據進行管理,組件樹上的組件都可以更新狀態,狀態變化之后會反饋到相應的其他組件上。這個思路很清晰,代碼寫起來也很順利,也體驗了vuex的強大,確實可以省事很多。
部分內容可能無法講得很清楚,有興趣了解的話請參考github倉庫的代碼。下面說說幾點收獲:
es6寫起來感覺超棒
webpack比gulp優雅
vue值得嘗試