用 Vue 写个移动 SPA 应用
栏目: JavaScript · 发布时间: 6年前
内容简介:最近看了 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 页面的
,让 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 属性改变 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在移动开发中的应用
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Python机器学习基础教程
[德]安德里亚斯·穆勒、[美]莎拉·吉多 / 张亮 / 人民邮电出版社 / 2018-1 / 79.00元
本书是机器学习入门书,以Python语言介绍。主要内容包括:机器学习的基本概念及其应用;实践中最常用的机器学习算法以及这些算法的优缺点;在机器学习中待处理数据的呈现方式的重要性,以及应重点关注数据的哪些方面;模型评估和调参的高级方法,重点讲解交叉验证和网格搜索;管道的概念;如何将前面各章的方法应用到文本数据上,还介绍了一些文本特有的处理方法。一起来看看 《Python机器学习基础教程》 这本书的介绍吧!