这个项目主要是用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, };