用 Vue 写个移动 SPA 应用
栏目: JavaScript · 发布时间: 5年前
内容简介:最近看了 Vue 的文档,想着应该写点什么加深下印象,能力有限,就照着葫芦画下吧:joy:,这次的葫芦是图灵社区 移动端页面Github:预览:
最近看了 Vue 的文档,想着应该写点什么加深下印象,能力有限,就照着葫芦画下吧:joy:,这次的葫芦是图灵社区 移动端页面
Github: github.com/Jimzjy/itur…
预览: jimzjy.github.io/ituring-mob…
前端新手,CSS / TS / JS 写的很烂,望见谅
准备工作
使用 vue-cli 创建项目,我的配置是
vue-router vuex dart-sass babel typescript eslint 复制代码
删除自动创建的 HelloWorld,About 等组件、页面以及路由
页面结构
剖析页面
我们看下要做的部分的大体导航结构
概括为(上面的图少 more-books 的页面)
- pages - [pages-content] - header-bar - home / book / article - [user-info] - bottom-navbar - login - more-books 复制代码
创建基本结构组件
新建以下文件
views/ Pages.vue PgaesContent.vue Login.vue MoreBooks.vue NotFound.vue pagesContents/ Home.vue Book.vue Article.vue User.vue // 对应上文结构中的 user-info, 按照结构其实应该直接放在 views/ 目录下 components/ HeaderMenu.vue HeaderNav.vue // 两个 Header 组件一起对应 header-bar BottomNavbar.vue 复制代码
添加 route
routePageNames = ['home', 'book', 'article', 'user'] { path: '/', component: Pages, children: [ { path: '', component: PagesContent, children: [ { path: '', component: Home, name: routePageNames[0] }, { path: 'book', component: Book, name: routePageNames[1], }, { path: 'article', component: Article, name: routePageNames[2], } ] }, { path: 'user', component: User, name: routePageNames[3], } ] }, { path: '/login', component: Login, name: 'login', }, { path: '/more-books', component: MoreBooks, name: 'more-books', } { path: '*', component: NotFound, name: 'not-found' } 复制代码
OK,现在文件结构已经满足了上面结构,接下来开始填充结构内容
开始填充架构
准备工作
用 MockJS 来模拟数据
npm install mockjs npm install @types/mockjs -D 复制代码
新建文件
src/ mock/ index.ts // mock 数据 service/ index.ts // 获取数据的 Url 复制代码
头部
头部由两个组件组成 HeaderMenu 和 HeaderNav, 代码可以在最上面的链接中找到
HeaderMenu
HeaderNav有两种样子
在 Home 页面的
在 Book 和 Article 页面的
我们使用 BottomNavbar 来切换页面,但是 HeaderNav 和 BottomNavbar 既不是父子也不是兄弟,那么我们不能直接传参,那么我们可以通过 Vuex 设置一个判断 Home 和 其它 两种状态的 state,但是这样需要在 route 上设置守卫或者在 Home 和 Article 组件上操作,或者还可以通过监听 $route,让 HeaderNav 自己去处理,这里我选择后者
service/index.ts const topicsUrl = api + '/topics' mock/index.ts Mock.mock(`${topicsUrl}/${routePageNames[0]}`, shuffle(topicsHome).concat('最新上线', '每周特价')) Mock.mock(`${topicsUrl}/${routePageNames[1]}`, topicsBook) Mock.mock(`${topicsUrl}/${routePageNames[2]}`, topicsArticle) // 内容都可以在源码中找到,篇幅有限就只列重点了,下同 HeaderNav.vue @Watch('$route') updateTopics () { this.isHome = this.$route.name === routePageNames[0] // 通过 isHome 来判断是否是 Home,routePageNames 是之前在 route 里写的 this.$http.get(`${topicsUrl}/${this.$route.name}`).then((resposne: any) => { this.topicsData = resposne.data }) } <div v-if="isHome">...</div> <div v-if="!isHome">...</div> 复制代码
在 PagesContent.vue 添加 HeaderNav 和 HeaderMenu
<header> <header-menu></header-menu> <header-nav></header-nav> </header> <router-view/> // Home / Book / Article <div id="bottom-space"></div> // 为 BottomNavbar 留底部空间 复制代码
底部
BottomNavbar 用到了 vue-awesome 这个 icon 库
npm install vue-awesome 复制代码
在这个项目中我用的都是自定义icon,可以在 src/asstes/icon/customIcons.ts 中找到
通过 fill 和 .router-link-active 一起配合,可以很轻松的实现根据不同路由改变 icon 的颜色
.fa-icon { ... fill: #C8CDD4; } .navbar-tab { ... &.router-link-active { color: $primary-color; .fa-icon { fill: $primary-color; } } } 复制代码
在 Pages.vue 中添加 BottomNavbar
<router-view/> // PagesContent <bottom-navbar></bottom-navbar> 复制代码
用户和登录
登录状态可以用 Vuex 来设置一个 loginStatus(true为登录)
state: { loginStatus: false }, mutations: { login (state) { state.loginStatus = true }, logout (state) { state.loginStatus = false } }, 复制代码
当用户登录状态为 false 时,不允许用户进入 /user ,所以我们在 /user 上添加一个路由守卫
{ path: 'user', component: User, name: routePageNames[3], + beforeEnter: authGuard } function authGuard (to: Route, from: Route, next: Function) { if ($store.state.loginStatus) { next() return } next({ name: 'login', query: { to: to.name } }) } 复制代码
在上面的代码中给到 login 的路由加了 query: { to: to.name } } ,之后我们在 Login 组件中就可以获取到本来用户想要去的路由,在登录后就可以跳转到用户原来想要取得路由,我们可以通过 $route.query 获取到信息,或者可以使用
route.ts { path: '/login', component: Login, name: 'login', + props: (route) => ({ to: route.query.to }) } Login.vue @Prop({ default: 'home' }) readonly to!: string 复制代码
在路由中添加 props 将 query.to 变成添加给 Login 的属性
在 Login 中添加 login 方法,在 User 中添加 logout 方法
Login.vue onLoginClick () { this.$store.commit('login') this.$router.push({ name: this.to }) } User.vue onLogoutClick () { this.$store.commit('logout') this.$router.push('/') } 复制代码
开始填充内部
开始填充 PagesContent 内部,MoreBooks 中的组件会复用内部用过的,所以会和内部一起说明
Home
Swiper
Home 中的第一个组件是一个 Swiper,使用 vue-swipe
npm install vue-swipe 复制代码
再新建 components/Swiper.vue 再封装下 vue-swipe
Swiper.vue <swipe :showIndicators=false :speed=3000> <swipe-item v-for="(item, index) in data" :key="index" class="swipe-item"> <p class="title">{{ item.title }}</p> <p class="content">{{ item.content }}</p> </swipe-item> </swipe> @Prop() readonly data!: Array<InfoSwipe> 复制代码
在 Home 中通过 Mock 获取数据,传给 Swiper
created () { this.updateData() } updateData () { this.$http.get(homeDataUrl).then((resposne: any) => { this.data = resposne.data }) } <swiper :data="data.infoSwipe"></swiper> 复制代码
SpecialView
在 Home 中有两种组件有着同样的外组件
书的列表
文章的列表新建 components/SpecialView.vue
SpecialView.vue <div class="title"> <div class="title-content"> ... </div> <router-link class="more" v-if="more" :to="{ name: 'more-books', query: { title } }">更多</router-link> </div> <slot></slot> Home.vue <special-view :title="item.title" :more="item.books.length > 4"> ... </special-view> 复制代码
这里我偷了下懒,更多按钮只去 /more-books
BookListView
书的列表也有两种样子
横向滚动
wrap通过 wrap 属性改变 class 来改变样式
components/BookListView.vue <div :class="['book-list-view', wrap ? 'wrap-list' : '']"> ... </div> @Prop() readonly books!: Array<Book> @Prop({ default: false }) readonly wrap!: boolean Home.vue <div v-for="(item, index) in data.booksWithTitle" :key="index" > <special-view :title="item.title" :more="item.books.length > 4"> <book-list-view :books="item.books"></book-list-view> </special-view> <sepline></sepline> </div> MoreBooks.vue <book-list-view :books="books" :wrap=true></book-list-view> 复制代码
Book
TopicTabView
在 Book 和 Article 中都有一个 tabview
点击 tab 时要更新 Book 的内容,所以在 TopicTabView 上可以加一个监听
components/TopicTabView.vue @Emit() onTopicClick (n: number) { this.topicChecked = n return n } Book.vue <topic-tab-view @on-topic-click="onTopicChange"></topic-tab-view> onTopicChange (n: number) { this.updateData() } updateData () { this.$http.get(moreBooksUrl).then((response: any) => { this.books = response.data }) } 复制代码
内容
Book 的内容可以直接复用 BookListView 的 wrap 模式
Article
ArticleListView
Article 和 Home 中都有文章的列表,样式相差并不多,可以像之前一样通过属性改变 class
components/ArticleListView.vue <div v-if="showTag"> ... </div> <div v-if="!showTag"> ... </div> Article.vue <article-list-view :articles="articles" class="articles"></article-list-view> Home.vue <special-view title="推荐文章"> <article-list-view :articles="data.articles" :showTag=false></article-list-view> </special-view> 复制代码
内容更新
现在从样子的角度来说已经构建完了,但是还有一个问题,当在 Book 和 Article 中点击 HeaderNav 的选项不会有任何效果,因为 HeaderNav 和 Book 以及 Article 的 内容部分,既不是父子也不是兄弟,我们又不能直接传参或者监听了
那我们可以通过在 route 上加属性,HeaderNav 通过 route 上的属性切换,点击后改变 route 的属性刷新页面,嗯...,感觉消耗有点大
那我们还可以通过 Vuex
state: { + currentHeaderNav: 0 }, mutations: { + changeCurrentHeaderNav (state, n) { state.currentHeaderNav = n } }, 复制代码
我们添加了一个 currentHeaderNav 用于表示现在的 HeaderNav 序号
<div :class="[isCurrentNav(index) ? 'topic-content-foucused' : 'topic-content']" @click="onTopicClick(index)">{{ topic }}</div> isCurrentNav (n: number): boolean { return this.currentHeaderNav === n } onTopicClick (n: number) { this.$store.commit('changeCurrentHeaderNav', n) } 复制代码
在 Book 和 Article 中添加监听
created () { this.updateData() this.subscription = this.$store.subscribe(mutation => { if (mutation.type === 'changeCurrentHeaderNav') { this.updateData() } }) } destroyed () { this.subscription() } 复制代码
现在已经可以正常更新内容了,但是还有一个问题,在 Book 和 Article 中序号是共享的,所以我们要在进入 Book 和 Article 前初始化序号,不然的话序号就乱套了,添加路由守卫
{ path: 'book', component: Book, name: routePageNames[1], + beforeEnter: refreshHeaderNav }, { path: 'article', component: Article, name: routePageNames[2], + beforeEnter: refreshHeaderNav } function refreshHeaderNav (to: Route, from: Route, next: Function) { $store.commit('changeCurrentHeaderNav', 0) next() } 复制代码
OK!现在就完成了
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 创建一个促进社区合作的移动应用
- 在出行途中对移动图像应用认知技术
- 移动应用三方 SDK 安全质量保障实践
- 为 Android 开发全栈移动应用
- [译] PWA 会取代原生移动应用吗?
- AOP在移动开发中的应用
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
数据结构算法与应用
塞尼 / 机械工业出版社 / 1999-3 / 49.00元
数据结构、算法与应用—C++语言描述(英文版),ISBN:9787111070177,作者:(美)塞尼 著一起来看看 《数据结构算法与应用》 这本书的介绍吧!