這個項目主要是用vue+vuex實現一個單頁面應用,純粹是熟悉vue全家桶相關開發模式,用於練手很是合適。javascript
着手開發完了以後能夠學的東西:css
若是想學vue的不妨進來看看。vue
開始以前,仍是有必要去vue官網學習一下vue,至少得有個大體的瞭解,後面在用到vue-router和vuex時,再去對應的倉庫看文檔就能夠了。java
建立項目能夠用vue-cli,具體看這裏git
項目結構通常來講很是重要,定義好的目錄結構,很是利於後續的項目維護,以及別人閱讀理解。下面就是這個項目的結構,應該看一下就知道是幹什麼的,大體說一下。github
項目結構分爲靜態資源目錄,api接口請求目錄,組件目錄,插件目錄,路由配置目錄,公共樣式目錄,狀態維護目錄,工具類目錄,頁面視圖目錄。vue-router
vue開發通常都是單頁面組件的方式,即一個以vue爲後綴的文件就是一個組件,組件裏包含了template模版,script腳本,style樣式,組件內的邏輯能夠徹底封裝在裏面,對外能夠提供接受的Props數據,能夠對外發射一個事件emit,或者將外部組件組合到本身內部的slot裏面。vuex
<template> <div class="topNav"> <ul class="list"> <li class="item left"> <app-icon :link="left" @click.native.stop="clickLeft" /> </li> </ul> </div> </template> <script lang="ts"> import { Component, Prop, Emit , Vue } from 'vue-property-decorator'; import AppIcon from './AppIcon.vue'; import {PREFIX} from '@/store/modules/user/CONSTANTS'; @Component({ components: { AppIcon, }, }) export default class TopNav extends Vue { @Prop({required: true}) private left!: string; private get avatar() { return this.$store.state[PREFIX].avatar; } private clickLeft() { this.$emit('left'); } } </script> <style lang="scss" scoped> @import '../scss/theme.scss'; .topNav { background: $topBarBgColor; position: fixed; } </style>
因爲在客戶端渲染的單頁面應用,須要在客戶端配置路由,實現頁面間的切換。開發vue時官方推薦使用vue-router,在配置這個項目時,因爲考慮登陸態的維護,因此對路由配置加了meta數據,並增長了路由跳轉鉤子函數,進行鑑權控制受登陸態的頁面。vue-cli
import Vue from 'vue'; import Router from 'vue-router'; import Sign from '@/views/Sign.vue'; import Me from '@/views/Me.vue'; import { hasLogin } from '@/util/session'; Vue.use(Router); const router = new Router({ mode: 'history', routes: [ { path: '/', name: 'sign', component: Sign, }, { path: '/me', name: 'me', component: Me, meta: { requiredAuth: true }, }, ], }); router.beforeEach((to, from, next) => { if (to.matched.some((record) => record.meta.requiredAuth)) { // this route requires auth, check if logged in // if not, redirect to login page. if (!hasLogin()) { next({ path: '/', query: { redirect: to.fullPath }, }); } else { next(); } } else { next(); // 確保必定要調用 next() } }); export default router;
對於那種須要全組件共享,或者全局注入的方法等可使用vue插件。其實,vue-router和vuex實際就是vue的插件,在入口處,調Vue.use(Router);
就能夠了,好比 Vue.use(Router);
typescript
一個插件,能夠是一個函數,或者一個包含install
方法的對象,在調用Vue.use
時,會調用install
方法。
在插件裏,咱們能夠
import Vue, { VueConstructor, PluginObject } from 'vue'; import Loading from './Loading.vue'; type ShowFunc = () => () => void; const plugin: PluginObject<{}> = { install(Vue: VueConstructor, options = {}) { const CONSTRUCTOR = Vue.extend(Loading); let cache: Vue & { show: ShowFunc } | null = null; function loading(): () => void { const loadingComponent = cache || (cache = new CONSTRUCTOR()); if (!loadingComponent.$el) { const vm = loadingComponent.$mount(); (document.querySelector('body') as HTMLElement).appendChild(vm.$el); } return loadingComponent.show(); } Vue.prototype.$loading = loading; }, }; export default plugin;
單頁面應用的狀態管理使用vuex,上面提到了,它就是一個vue的插件,會在組件實例上注入$store對象,這個對象就是new Vuex.Store()
,相比redux ,我以爲vuex簡單不少。使用須要注意一下幾點就能夠了,
本次項目也是用模塊化的管理狀態的方式,把整個應用的狀態以業務劃分爲子狀態,最後在modules中合併
modules: { user, list, filter, },
對於單個模塊的state,按照上面的注意點便可以。
// user模塊的state import { ActionTree, MutationTree, ActionContext } from 'vuex'; import { login, loginOut, LoginInfo } from '@/api/login'; import { getUserInfo, getUserActions } from '@/api/user'; import { User } from './user'; import { RootState } from '../../rootstate'; const namespaced = true; /* initial state */ const state = () => ({ id: null, username: null, email: null, avatar: null, likes_count: null, goings_count: null, past_count: null, }); /* user actions */ const actions: ActionTree<User, RootState> = { login({ commit, state }: ActionContext<User, RootState>, payload: LoginInfo) { return login(payload).then( ({ token, user }: { token: string; user: User }) => { commit('saveToken', token, { root: true }); commit('saveUser', user); }, ); }, getUserInfo({ commit, state }: ActionContext<User, RootState>) { return getUserInfo().then((user: User) => { commit('saveUser', user); }); }, }; /* user mutations */ const mutations: MutationTree<User> = { saveUser(state, user) { state.id = user.id; state.username = user.username; state.email = user.email; state.avatar = user.avatar; state.likes_count = user.likes_count; state.goings_count = user.goings_count; state.past_count = user.past_count; }, }; export default { state, actions, mutations, namespaced, };