当前项目进度已经上传到gitee开源远程仓库: https://gitee.com/Scurryniubi/project_mj
SSH码: git@gitee.com:Scurryniubi/project_mj.git
路由设计配置
但凡是单个页面,独立展示的,都是一级路由
路由设计:
登录页 (一级) login
注册页(一级) register
文章详情页(一级) detail
首页(一级) layout
面经(二级)article
收藏(二级)collect
喜欢(二级)like
我的(二级)my
一级路由
router/index.js配置一级路由
import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '@/views/login'
import Register from '@/views/register'
import Detail from '@/views/detail'
import Layout from '@/views/layout'
Vue.use(VueRouter)
const router = new VueRouter({
routes: [
{ path: '/login', component: Login },
{ path: '/register', component: Register },
{ path: '/article/:id', component: Detail },
{
path: '/',
component: Layout
}
]
})
export default router
清理 App.vue
<template>
<div id="app">
<router-view/>
</div>
</template>
<script>
export default {
async created () {
}
}
</script>
tabbar标签页
![](https://img-blog.csdnimg.cn/img_convert/71c17a5200fe89f0ed928374f337f783.png)
https://vant-contrib.gitee.io/vant/v2/#/zh-CN/tabbar
main.js 引入组件
import { Button, Icon, Tabbar, TabbarItem } from 'vant'
Vue.use(Tabbar)
Vue.use(TabbarItem)
layout.vue
<template>
<div class="layout-page">
首页架子 - 内容区域
<van-tabbar>
<van-tabbar-item icon="notes-o">面经</van-tabbar-item>
<van-tabbar-item icon="star-o">收藏</van-tabbar-item>
<van-tabbar-item icon="like-o">喜欢</van-tabbar-item>
<van-tabbar-item icon="user-o">我的</van-tabbar-item>
</van-tabbar>
</div>
</template>
二级路由
router/index.js配置二级路由
import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '@/views/login'
import Register from '@/views/register'
import Detail from '@/views/detail'
import Layout from '@/views/layout'
import Like from '@/views/like'
import Article from '@/views/article'
import Collect from '@/views/collect'
import User from '@/views/user'
Vue.use(VueRouter)
const router = new VueRouter({
routes: [
{ path: '/login', component: Login },
{ path: '/register', component: Register },
{ path: '/article/:id', component: Detail },
{
path: '/',
component: Layout,
redirect: '/article',
children: [
{ path: '/article', component: Article },
{ path: '/like', component: Like },
{ path: '/collect', component: Collect },
{ path: '/user', component: User }
]
}
]
})
export default router
layout.vue 配置路由出口, 配置 tabbar
<template>
<div class="layout-page">
<router-view></router-view>
<van-tabbar route>
<van-tabbar-item to="/article" icon="notes-o">面经</van-tabbar-item>
<van-tabbar-item to="/collect" icon="star-o">收藏</van-tabbar-item>
<van-tabbar-item to="/like" icon="like-o">喜欢</van-tabbar-item>
<van-tabbar-item to="/user" icon="user-o">我的</van-tabbar-item>
</van-tabbar>
</div>
</template>
效果图:
![](https://img-blog.csdnimg.cn/img_convert/28fa459964efe484894eee84fe8ad88d.png)
主题定制
整体网站风格,其实都是橙色的,可以通过变量覆盖的方式,制定主题色
https://vant-contrib.gitee.io/vant/v2/#/zh-CN/theme
babel.config.js 制定样式路径
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
plugins: [
['import', {
libraryName: 'vant',
libraryDirectory: 'es',
// 指定样式路径
style: (name) => `${name}/style/less`
}, 'vant']
]
}
vue.config.js 覆盖变量
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
css: {
loaderOptions: {
less: {
lessOptions: {
modifyVars: {
// 直接覆盖变量
blue: 'red'
},
},
},
},
},
})
重启服务器生效!
效果如图所示:
![](https://img-blog.csdnimg.cn/img_convert/5f27b2d97364a26f731482c9ed7eeb9e.png)
登录&注册功能
登录静态布局
![](https://img-blog.csdnimg.cn/img_convert/4acd99e28713439e8d933df586cbd9cb.png)
使用组件
van-nav-bar
van-form
van-field
van-button
vant-ui.js 注册
import Vue from 'vue'
import {
NavBar,
Form,
Field
} from 'vant'
Vue.use(NavBar)
Vue.use(Form)
Vue.use(Field)
login.vue 使用
结合正则表达式进行校验
同时注意将name中的中文换成英文,以方便更好地在获取对象时使用
<template>
<div class="login-page">
<!-- 导航栏部分 -->
<van-nav-bar title="面经登录" />
<!-- 一旦form表单提交了,就会触发submit,可以在submit事件中
根据拿到的表单提交信息,发送axios请求
-->
<van-form @submit="onSubmit">
<!-- 输入框组件 -->
<!-- \w 字母数字_ \d 数字0-9 -->
<van-field
v-model="username"
name="username"
label="用户名"
placeholder="用户名"
:rules="[
{ required: true, message: '请填写用户名' },
{ pattern: /^\w{5,}$/, message: '用户名至少包含5个字符' }
]"
/>
<van-field
v-model="password"
type="password"
name="password"
label="密码"
placeholder="密码"
:rules="[
{ required: true, message: '请填写密码' },
{ pattern: /^\w{6,}$/, message: '密码至少包含6个字符' }
]"
/>
<div style="margin: 16px">
<van-button block type="info" native-type="submit">提交</van-button>
</div>
</van-form>
</div>
</template>
<script>
export default {
name: 'login-page',
data () {
return {
username: 'zhousg',
password: '123456'
}
},
methods: {
onSubmit (values) {
console.log('submit', values)
}
}
}
</script>
login.vue添加 router-link 标签(跳转到注册)
<template>
<div class="login-page">
<van-nav-bar title="面经登录" />
<van-form @submit="onSubmit">
...
</van-form>
<router-link class="link" to="/register">注册账号</router-link>
</div>
</template>
login.vue调整样式
<style lang="less" scoped>
.link {
color: #069;
font-size: 12px;
padding-right: 20px;
float: right;
}
</style>
注册静态布局
register.vue
<template>
<div class="login-page">
<van-nav-bar title="面经注册" />
<van-form @submit="onSubmit">
<van-field
v-model="username"
name="username"
label="用户名"
placeholder="用户名"
:rules="[{ required: true, message: '请填写用户名' }]"
/>
<van-field
v-model="password"
type="password"
name="password"
label="密码"
placeholder="密码"
:rules="[{ required: true, message: '请填写密码' }]"
/>
<div style="margin: 16px">
<van-button block type="primary" native-type="submit"
>注册</van-button
>
</div>
</van-form>
<router-link class="link" to="/login">有账号,去登录</router-link>
</div>
</template>
<script>
export default {
name: 'register-page',
data () {
return {
username: '',
password: ''
}
},
methods: {
onSubmit (values) {
console.log('submit', values)
}
}
}
</script>
<style lang="less" scoped>
.link {
color: #069;
font-size: 12px;
padding-right: 20px;
float: right;
}
</style>
toast 轻提示
将来无论注册成功,还是失败,都是需要给用户提示的
https://vant-contrib.gitee.io/vant/v2/#/zh-CN/toast
import { Toast } from 'vant';
Vue.use(Toast)
两种使用方式
导入,调用
import { Toast } from 'vant';
Toast('提示内容');
**组件内 **通过this直接调用
this.$toast('提示内容')
封装api接口 - 注册功能(axios)
新建 api/user.js 提供注册 Api 函数
import request from '@/utils/request'
// 注册接口
export const register = (data) => {
return request.post('/user/register', data)
}
register.vue页面中调用测试
methods: {
async onSubmit (values) {
// 往后台发送注册请求了
await register(values)
this.$toast.success('注册成功')
this.$router.push('/login')
}
}
request.js响应拦截器,统一处理错误提示
import { Toast } from 'vant'
...
// 添加响应拦截器
request.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response.data
}, function (error) {
if (error.response) {
// 有错误响应, 提示错误提示
Toast(error.response.data.message)
}
// 对响应错误做点什么
return Promise.reject(error)
})
封装api接口 - 登录功能
api/user.js 提供登录 Api 函数
// 登录接口
export const login = (data) => {
return request.post('/user/login', data)
}
login.vue 登录功能
import { login } from '@/api/user'
import { setToken } from '@/utils/storage'
methods: {
async onSubmit (values) {
const { data } = await login(values)
setToken(data.token)
this.$toast.success('登录成功')
this.$router.push('/')
}
}
页面访问拦截
这个 面经移动端 项目,只对 登录用户 开放,如果未登录,一律拦截到登录
如果访问的是 首页, 无token, 拦走
如果访问的是 列表页,无token, 拦走
如果访问的是 详情页,无token, 拦走
....
分析:哪些页面,是不需要登录,就可以访问的! => 注册 和 登录 (白名单 - 游客可以随意访问的)
核心逻辑:
判断用户有没有token, 有token, 直接放行
没有token(游客),如果是白名单中的页面,直接放行
否则,无token(游客),且在访问需要权限访问的页面,直接拦截到登录
vue路由内置了一个语法
全局前置守卫:
可以在vue2官网中的路由相关文档中了解到更多详细的信息
所有的路由一旦被匹配到,在真正渲染解析之前,都会先经过全局前置守卫
只有全局前置守卫放行,才能看到真正的页面
// 全局前置守卫:
// 1. 所有的路由一旦被匹配到,在真正渲染解析之前,都会先经过全局前置守卫
// 2. 只有全局前置守卫放行,才能看到真正的页面
// 任何路由,被解析访问前,都会先执行这个回调
// 1. from 你从哪里来, 从哪来的路由信息对象
// 2. to 你往哪里去, 到哪去的路由信息对象
// 3. next() 是否放行,如果next()调用,就是放行 => 放你去想去的页面
// next(路径) 拦截到某个路径页面
const whiteList = ['/login', '/register'] // 白名单列表,记录无需权限访问的所有页面
router.beforeEach((to, from, next) => {
const token = getToken()
// 如果有token,直接放行
if (token) {
next()
} else {
// 没有token的人, 看看你要去哪
// (1) 访问的是无需授权的页面(白名单),也是放行
// 就是判断,访问的地址,是否在白名单数组中存在 includes
if (whiteList.includes(to.path)) {
next()
} else {
// (2) 否则拦截到登录
next('/login')
}
}
})