前端路由
此篇是基于
vue2.x VueRouter3
的开发环境
有关路由:动态组件进行切换的时候地址栏地址没有发生变化而路由可以解决这一缺点
我们通过vue-cil脚手架工具创建的项目是单页面应用,我们把单页面应用称为single page app 简称spa,单页面就是只有一个html代码
Vue.js 路由允许我们通过不同的 URL 访问不同的内容(不同的组件), 实现单页面应用的多页面切换效果。(通过url的改变切换不同的组件)
Vue.js 路由需要载入 vue-router
库.vue-router
库是独立vue之外的,可以有也可以没有,若要实现不同的 URL 访问不同的内容(不同的组件)可以用vue-router,如果不用vue-router也是可以动态组件实现的只是地址栏没有变化还是可以实现页面的切换。
1.安装vue-router
在vue后面加载vue-router核心文件,它会自动安装的:
<script src="/path/to/vue.js"></script>
<script src="/path/to/vue-router.js"></script>
或者 , 用npm模块化引入( 了解 )
npm install 'vue-router'
如果在一个模块化工程中使用它,必须要通过 Vue.use()
明确地安装路由功能:
import Vue from 'vue'
import VueRouter from 'vue-router'
如果使用全局的 script 标签,则无须如此 (手动安装).
如果是基于@vue/cli脚手架创建的项目,可以在vue create创建项目时勾选VueRouter, 这样在创建的项目中会自动引入VueRouter.
1.1安装步骤
在要创建项目的文件夹下打开cmd创建项目
vue create vue-router-demo
在创建时需选:Manually select features -->Babel,Router,CSS Pre-processors --> 2.x --> Yes —> Sass/SCSS(with dart-sass) --> In package.json --> Yes
2.创建好含有 vue-router 库的项目
比普通的项目多了router文件夹
router文件夹下的index.js文件
import Vue from 'vue'
// 1.导入vue-router插件
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'
// 2.注册vue-router插件
Vue.use(VueRouter)
// 3.定义路由表
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
}
]
// 4.实例化路由对象
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
// 5.导出路由对象
export default router
index.js文件在main.js文件中使用
import Vue from 'vue'
import App from './App.vue'
// 1.导入路由对象
import router from './router'
Vue.config.productionTip = false
new Vue({
router,//2.挂载路由对象到根实例上(在整个项目的所有组件中都可以通过组件实例this获取到这个路由对象:this.$router)
render: h => h(App)
}).$mount('#app')
3.基础路由示例
放置两个跳转链接是vue-router插件内置的组件
<div id="app">
<nav>
<!-- router-link 是uve-router插件内置的组件,最终渲染为a标签,用于跳路由 , to表示要跳去往的地址 -->
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</nav>
<router-view/>
</div>
4.路由核心
运行的实现过程:拿到点击router-link中的地址放在地址栏中,再拿地址和将来可能会切换的地址进行比较和哪个相等就自动添加router-link-active类,同时通过类router-link-exact-activ实现高亮效果
路由相关组件
router-link 路由库内置的组件,最终会渲染成可以点击的a标签,点击即可跳路由,可以在任何组件中使用
router-view 路由库内置的组件,只是个占位组件,组件本身不会渲染出任何标签,能成功匹配当前路由地址的组件会渲染在router-view
router-view是个动态组件,负责占位可以用动画组件包裹起来
路由相关对象
通过在vue根实例的router配置传入router实例,下面这些属性成员会被注入到每个子组件。
** r o u t e r 路由管理对象 , 借助该对象可以跳路由 , ∗ ∗ 在任何一个组件内通过组件实例都可以获取该对象 t h i s . router 路由管理对象, 借助该对象可以跳路由,** 在任何一个组件内通过组件实例都可以获取该对象 this. router路由管理对象,借助该对象可以跳路由,∗∗在任何一个组件内通过组件实例都可以获取该对象this.router, 它的核心方法有:
this.$router.push()
this.$router.go()
this.$router.replace()
$route 路由对象, 借助该对象可以获取路由参数. 它的核心属性有:
路由对象属性 | 描述 |
---|---|
$route.path | 对应当前路由的路径,总是解析为绝对路径 |
$route.params | 一个 key/value 对象,包含了动态片段和全匹配片段,如果没有路由参数,就是一个空对象 |
$route.query | 一个 key/value 对象,表示 URL 查询参数。例如,对于路径 /foo?user=1 ,则有 $route.query.user == 1 ,如果没有查询参数,则是个空对象 |
$route.hash | 当前路由的 hash 值 (带 # ) ,如果没有 hash 值,则为空字符串 |
---|---|
$route.fullPath | 完成解析后的 URL,包含查询参数和 hash 的完整路径 |
$route.name | 当前路由的名称,如果有的话 |
$route.redirectedFrom | 如果存在重定向,即为重定向来源的路由的名字 |
5.路由表
路由表位于index.js文件中
一个对象,就是一个路由规则
因为提前在路由表中定义好了路由规则所以可以跳路由
一个路由规则中有路由地址,路由名字,导入路由地址对应的组件
为什么可以实现跳路由:根本原因是你在路由表中已经定义好了,按钮可以不可以高亮和有没有跳路由是没有关系的
导入路由地址对应的组件有两种方法:
1.非动态导入,主要特点是所有组件会被打包成一个js,一开始就会将所有组件加载
// import HomeView from '../views/HomeView.vue'
component: HomeView//路由地址对应的组件
2.动态导入,在实际跳路由的时候才会临时加载组件的内容,被叫做动态加载又叫懒加载又叫延迟加载
动态导入:什么时候跳路由什么时候加载组件,临时加载
component: () => import('../views/HomeView.vue')
示例代码
// 3.定义路由表
const routes = [
{
//一个对象,就是一个路由规则
path: '/',//路由地址
name: 'home',//路由名字
// 路由地址对应的组件有两种写法
// 第一种:非动态导入,主要特点所有组件会被打包成一个js,一开始就会将所有组件加载
// component: HomeView//路由地址对应的组件
// 第二种:动态导入组件:在实际跳路由的时候才会临时加载组件的内容,被叫做动态加载又叫懒加载又叫延迟加载
component: () => import('../views/HomeView.vue')
},
{
path: '/about',
name: 'about',
component: () => import('../views/AboutView.vue')
}
]
斜杠开头的路由地址都是绝对路由地址
放在views中的组件都是路由级的组件,放在components中的组件是页面内的组件
6.跳路由的二种方法
返回:@click='$router.go(-1)
**第一种:**router-link,必须用router-link包裹需要点击的元素
<router-link to="/index/home">首页</router-link>
**第二种:**借助路由管理对象this.$router 通过js代码跳路由
<div @click="$router.push('/login')">去登陆</div>
跳路由的方式一共有:
- router-link必须将点击的元素包裹在router-link内,router-link最终会渲染成a标签
- $router.push(路由地址)会往浏览器历史记录中添加新的历史记录
- $router.replace()会替换浏览器历史记录中当前历史记录
- $router.go(n)切换历史记录,在浏览器已有历史记录中切换
7.跳路由传参的三种方式
路由传参的作用:
- 跳路由之后显示的组件,需要动态渲染;
- 组件需要动态渲染的数据和用户操作有关,当页面的内容是动态渲染的点击之后跳转路由又和点击相关,点击之后传参
- 传参的多或少,可以把商品全部数据传过去也可以只把id传过去
**第一种:**动态路由,刷新页面参数不会丢失,参数显示在地址栏
路由表中:
{
// 路由地址中带有 :xxx 的路由地址 都属于动态路由
// path: '/detail/:name', // :name 是个占位符, 实际在跳路由的时候,会动态拼接参数 例如: '/detail/'+100
path: '/detail/:name',
name: 'detail',
component: () => import('../views/Detail.vue')
},
vue文件中:
<router-link :to="'/detail/'+888">详情</router-link>
<div @click="$router.push('/detail/'+888)">详情</div>
取路由参数this.$router.params.name
**第二种:**path + query , 刷新页面参数不会丢失,参数以查询字符串的形式显示在地址栏
路由表中:
{
path: '/detail',
name: 'detail',
component: () => import('../views/Detail.vue')
},
vue文件中:
<router-link :to="{path:'/detail',query:{name:888}}">详情</router-link>
<div @click="$router.push({path:'/detail',query:{name:888}})">详情</div>
取路由参数this.$router.query.name
**第三种:**name + params , 刷新页面参数会丢失,参数无法看到
路由表中:
{
path: '/detail',
name: 'detail',
component: () => import('../views/Detail.vue')
},
vue文件中:
<router-link :to="{name:'detail',params:{name:888}}">详情</router-link>
<div @click="$router.push({name:'detail',params:{name:888 }})">详情</div>
取路由参数this.$router.params.name
8.嵌套路由
要在嵌套的出口中渲染组件,需要在 VueRouter 的参数中使用 children 配置:
var router = new VueRouter({
routes:[
{
path:'/article',
component:Article,
/*通过children设置嵌套路由规则*/
children:[
{
path:'/article/cate',component: Cate },
{
path:'/article/list',component: List }
]
}
]
});
9.重定向和别名
//一级路由重定向
{
path:'/',
redirect:'/index'
},
//404路由
{
path:'*',
component: () => import('../views/NotFound.vue')
}
//二级路由重定向
{
path:'/index',
redirect:'/index/home'
},
//404路由
{
path:'*',
component: () => import('../views/NotFound.vue')
}
10.路由模式
history模式和hash模式
11. r o u t e r 和 router和 router和route的区别
$router:路由管理对象,负责管理路由(负责跳路由)
$route:路由对象,包含当前路由的所有信息(当前的地址,携带的参数)
12.路由守卫函数和元信息
任何一个合法的路由,在路由表中存在的路由都可以控制当前路由能否跳转,比如说点击的时候判断是否可以跳转,我们在哪里写这些可以进行判断之后可以跳路由的代码?
在vue中提供的路由的守卫函数正好可以用来写判断是否可以跳路由的代码
router的实例方法(是必须添加到router实例身上)也叫导航守卫方法
-
路由的全局前置守卫方法
router.beforeEach((to, from, next) => { //参数一: to 新的路由对象 //参数二: from 旧的路由对象 //参数三: next 路由控制方法, 调用该方法则允许路由跳转, 未调用该方法则不允许路由跳转 next();//必须手动调用该函数,否则无法完成路由跳转 })
-
路由的全局后置守卫方法
router.afterEach((to,from)=>{ console.log('afterEach'); })
-
路由独享的守卫函数(只是加给某个路由决定单个路由是否可以跳转)
beforeEnter: (to, from, next) => { console.log('beforeEnter'); next(); }
注意:全局守卫函数和独享守卫函数的书写位置不一样,名字也不一样,但是作用是类似的都是守卫路由的跳转
12.1路由守卫函数的案例
1.动态更新网页标题
动态更新网页标题:可以告诉用户当前位于哪个页面,配合路由元信息使用,通过js设置当前的title
要实现上述功能就得给每个路由对象添加路由元信息,路由元信息时会永远保存在当前路由对象身上
比如:
{
path: '/index',
name: 'index',
meta: {
title:'index',
auth:true//用户登录后才可以跳路由,没有加是不需要登录就可以
}, //路由元信息( 会永远保存在当前路由对象身上 )
component: () => import('../views/Index.vue'),
}
案例代码:
//路由的全局前置守卫方法
router.beforeEach((to, from, next) => {
//因为这个函数跳任何一个路由都会执行,所以当路由跳转的时候就会执行将路由元信息中的title的值赋给网页的标题,这样就可以实现动态更新网页标题
//动态更新网页标题
document.title = to.meta.title;
next();
})
2.控制个别路由的访问
想要跳往订单页得和我的页面的时候要先登录才可以跳转
//路由的全局前置守卫方法
router.beforeEach((to, from, next) => {
//参数一: to 新的路由对象
//参数二: from 旧的路由对象
//参数三: next 路由控制方法, 调用该方法则允许路由跳转, 未调用该方法则不允许路由跳转
//获取localStorage中的登陆凭证
var token = localStorage.getItem('token');
//只有在登陆以后 才可以跳转到 订单页
//if(to.path == '/index/order'){ //想要跳往 订单页
/* if(to.path == '/index/order' || to.path == '/index/mine'){ //想要跳往 订单页 或者 我的页面
if( token ){ //已经登陆
next();
}else{ //未登录
next('/login');
}
}else{ //其他页面( 非订单页 )
next();//必须手动调用该函数, 否则无法完成路由跳转
} */
})
3.只有登录页是可以随意访问的,其他页面都是必须先登录以后才可以访问
//路由的全局前置守卫方法
router.beforeEach((to, from, next) => {
//参数一: to 新的路由对象
//参数二: from 旧的路由对象
//参数三: next 路由控制方法, 调用该方法则允许路由跳转, 未调用该方法则不允许路由跳转
//获取localStorage中的登陆凭证
var token = localStorage.getItem('token');
// 对于后台管理系统这种应用, 只有登录页是可以随意访问的, 但是其他所有页面都是必须在登陆以后才可以访问
if( to.path == '/login' ){
//跳往 登录页
if( token ){
//已登录 , 重定向到 "/"
next('/');
}else{
//未登录 , 允许跳往 登录页
next();
}
// next();
}else{
//跳往 非登录页
if( token ){
//已登录 , 允许访问
next();
}else{
//未登录 , 重定向到 "/login"
next('/login');
}
}
})
综合代码
项目src文件夹下router文件夹中的index.js文件
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [ //定义多个一级路由
{
path: '/index',
name: 'index',
meta: {
title:'index' }, //路由元信息( 会永远保存在当前路由对象身上 )
component: () => import('../views/Index.vue'),
children:[ //嵌套路由( 二级路由 )
{
path: '/index/home',
name: 'home',
meta: {
title:'首页' }, //路由元信息( 会永远保存在当前路由对象身上 )
component: () => import('../views/Index/Home.vue'),
},
{
path: '/index/order',
name: 'order',
meta: {
title:'订单' }, //路由元信息( 会永远保存在当前路由对象身上 )
component: () => import('../views/Index/Order.vue')
},
{
path: '/index/mine',
name: 'mine',
meta: {
title:'我的' },
component: () => import('../views/Index/Mine.vue')
},
//二级路由的重定向
{
path:'/index',
redirect:'/index/home'
},
//404路由
{
path:'*',
component: () => import('../views/NotFound.vue')
}
]
},
{
path: '/login',
name: 'about',
meta: {
title:'登陆' },
component: () => import('../views/Login.vue'),
//路由独享的守卫函数
// beforeEnter: (to, from, next) => {
// console.log('beforeEnter');
// next();
// }
},
{
// 路由地址中带有 :xxx 的路由地址 都属于动态路由
// path: '/detail/:name', // :name 是个占位符, 实际在跳路由的时候,会动态拼接参数 例如: '/detail/'+100
path: '/detail',
name: 'detail',
meta: {
title:'详情' },
component: () => import('../views/Detail.vue'),
},
//一级路由重定向
{
path:'/',
redirect:'/index'
},
//404路由
{
path:'*',
component: () => import('../views/NotFound.vue')
}
]
const router = new VueRouter({
mode: 'history', //设置路由模式, history , hash
base: process.env.BASE_URL,
routes
})
//设置实例方法( 导航守卫方法 )
//路由的全局前置守卫方法
router.beforeEach((to, from, next) => {
//参数一: to 新的路由对象
//参数二: from 旧的路由对象
//参数三: next 路由控制方法, 调用该方法则允许路由跳转, 未调用该方法则不允许路由跳转
//动态更新网页标题
document.title = to.meta.title;
//获取localStorage中的登陆凭证
var token = localStorage.getItem('token');
//只有在登陆以后 才可以跳转到 订单页
//if(to.path == '/index/order'){ //想要跳往 订单页
/* if(to.path == '/index/order' || to.path == '/index/mine'){ //想要跳往 订单页 或者 我的页面
if( token ){ //已经登陆
next();
}else{ //未登录
next('/login');
}
}else{ //其他页面( 非订单页 )
next();//必须手动调用该函数, 否则无法完成路由跳转
} */
// 对于后台管理系统这种应用, 只有登录页是可以随意访问的, 但是其他所有页面都是必须在登陆以后才可以访问
if( to.path == '/login' ){
//跳往 登录页
if( token ){
//已登录 , 重定向到 "/"
next('/');
}else{
//未登录 , 允许跳往 登录页
next();
}
// next();
}else{
//跳往 非登录页
if( token ){
//已登录 , 允许访问
next();
}else{
//未登录 , 重定向到 "/login"
next('/login');
}
}
})
//路由的全局后置守卫方法
// router.afterEach((to,from)=>{
// console.log('afterEach');
// })
export default router
12.2自己封装的登录注册组件
项目文件中src文件下router文件下的index文件配置路由表
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [ //定义多个一级路由
{
path: '/index',
name: 'index',
meta: {
title:'index' }, //路由元信息( 会永远保存在当前路由对象身上 )
component: () => import('../views/Index.vue'),
children:[ //嵌套路由( 二级路由 )
{
path: '/index/home',
name: 'home',
meta: {
title:'首页' }, //路由元信息( 会永远保存在当前路由对象身上 )
component: () => import('../views/Index/Home.vue'),
},
{
path: '/index/order',
name: 'order',
meta: {
title:'订单' }, //路由元信息( 会永远保存在当前路由对象身上 )
component: () => import('../views/Index/Order.vue')
},
{
path: '/index/mine',
name: 'mine',
meta: {
title:'我的' },
component: () => import('../views/Index/Mine.vue')
},
//二级路由的重定向
{
path:'/index',
redirect:'/index/home'
},
//404路由
{
path:'*',
component: () => import('../views/NotFound.vue')
}
]
},
{
path: '/login',
name: 'about',
meta: {
title:'登陆' },
component: () => import('../views/Login.vue'),
//路由独享的守卫函数
// beforeEnter: (to, from, next) => {
// console.log('beforeEnter');
// next();
// }
},
{
path: '/register',
name: 'register',
meta: {
title:'注册' },
component: () => import('../views/Register.vue')
},
{
// 路由地址中带有 :xxx 的路由地址 都属于动态路由
// path: '/detail/:name', // :name 是个占位符, 实际在跳路由的时候,会动态拼接参数 例如: '/detail/'+100
path: '/detail',
name: 'detail',
meta: {
title:'详情' },
component: () => import('../views/Detail.vue'),
},
//一级路由重定向
{
path:'/',
redirect:'/index'
},
//404路由
{
path:'*',
component: () => import('../views/NotFound.vue')
}
]
const router = new VueRouter({
mode: 'history', //设置路由模式, history , hash
base: process.env.BASE_URL,
routes
})
//设置实例方法( 导航守卫方法 )
//路由的全局前置守卫方法
router.beforeEach((to, from, next) => {
//参数一: to 新的路由对象
//参数二: from 旧的路由对象
//参数三: next 路由控制方法, 调用该方法则允许路由跳转, 未调用该方法则不允许路由跳转
//动态更新网页标题
document.title = to.meta.title;
//获取localStorage中的登陆凭证
var token = localStorage.getItem('token');
//只有在登陆以后 才可以跳转到 订单页
//if(to.path == '/index/order'){ //想要跳往 订单页
/* if(to.path == '/index/order' || to.path == '/index/mine'){ //想要跳往 订单页 或者 我的页面
if( token ){ //已经登陆
next();
}else{ //未登录
next('/login');
}
}else{ //其他页面( 非订单页 )
next();//必须手动调用该函数, 否则无法完成路由跳转
} */
//对于后台管理系统这种应用, 只有登录页是可以随意访问的, 但是其他所有页面都是必须在登陆以后才可以访问
if( to.path == '/register' ){
//跳往 注册页 , 允许跳转
next();
}else{
if( to.path == '/login' ){
//跳往 登录页
if( token ){
//已登录 , 重定向到 "/"
next('/');
}else{
//未登录 , 允许跳往 登录页
next();
}
// next();
}else{
//跳往 非登录页
if( token ){
//已登录 , 允许访问
next();
}else{
//未登录 , 重定向到 "/login"
next('/login');
}
}
}
})
//路由的全局后置守卫方法
// router.afterEach((to,from)=>{
// console.log('afterEach');
// })
export default router
在项目文件夹中views文件中创建注册组件
<template>
<div class="register">
<div class="title">注册页</div>
<div class="block">
<input type="text" v-model="formData.phone" placeholder="输入手机号">
</div>
<div class="block">
<input type="password" v-model="formData.pass" placeholder="输入密码">
</div>
<div class="block">
<input type="password" v-model="formData.checkpass" placeholder="输入确认密码 ">
</div>
<div class="block">
<input type="button" class="btn" value="注册" @click="register">
<input type="button" class="btn" value="登录" @click="login">
</div>
</div>
</template>
<script>
// 导入封装的请求函数
import {
user_register} from '../utils/api'
import {
Toast } from 'vant';
export default {
data(){
return {
formData:{
phone:'',
pass:'',
checkpass:''
}
}
},
methods:{
// 点击时先对输入的内容进行判断如果都符号再去发请求
register(){
// 先对输入的手机号通过正则进行判断,如果不符合则弹出弹框
if(/^1[3-9]\d{9}$/.test(this.formData.phone) == false){
Toast.fail({
message: '请输入合法的手机号' });
// 对密码和确认密码进行判断是否为空为空就提示
}else if( !this.formData.pass || !this.formData.checkpass ){
Toast.fail({
message: '请输入密码/确认密码' });
}else{
//验证通过
Toast.fail({
message: '密码和确认密码不一致' });
// 因为注册请求时要传账号和密码,formData中多了一个确认密码需要将多的内容删掉
// 深拷贝formData,防止对原数据进行修改
var newFormData = JSON.parse(JSON.stringify(this.formData));
// 删除对象的checkpass属性
delete newFormData.checkpass;
// 发起注册请求,账号phone,密码pass必须传
user_register(newFormData).then((res)=>{
if(res.data.code == 200){
//注册成功
Toast.success('登陆成功!')
// 跳转到登录页
this.$router.push('/login');
}else{
Toast.fail('登陆失败!')
}
})
}
},
login(){
this.$router.push('/login');
}
}
}
</script>
<style lang="scss" scoped>
.register{
margin: 50px 20px;
}
.register .title{
font-weight: bold;
text-align: center;
line-height: 40px;
font-size: 32px;
}
.register .block{
margin: 20px 0;
}
.register .block input{
height: 40px;
border: 1px solid #ccc;
border-radius: 20px;
padding-left: 20px;
box-sizing: border-box;
outline: none;
width: 100%;
margin-bottom: 10px;
font-size: 14px;
color: #ccc;
}
.register .block .btn{
padding-left: 0;
height: 40px;
border: 1px solid #ccc;
border-radius: 20px;
box-sizing: border-box;
outline: none;
width: 100%;
margin-bottom: 15px;
color: black;
font-size: 16px;
}
</style>
在项目文件夹中views文件中创建登录组件
<template>
<div class="login">
<div class="title">登陆页</div>
<div class="block">
<input type="text" v-model="formData.phone" placeholder="输入手机号">
</div>
<div class="block">
<input type="password" v-model="formData.pass" placeholder="输入密码">
</div>
<div class="block">
<input type="button" value="登陆" @click="login">
</div>
</div>
</template>
<script>
import {
Toast } from 'vant';
import {
user_login} from '../utils/api'
export default {
data(){
return {
formData:{
phone:'',
pass:''
}
}
},
methods:{
login(){
if( /^1[3-9]\d{9}$/.test( this.formData.phone ) == false ){
alert('请输入合法的手机号');
}else if( !this.formData.pass ){
alert('请输入密码')
}else{
//验证通过
//发起登陆请求
user_login( this.formData ).then((res)=>{
if( res.data.code == 200 ){
//保存用户信息,token
localStorage.setItem('token',res.data.token);
localStorage.setItem('userinfo', JSON.stringify( res.data.userinfo ) );
//自动跳转到首页
this.$router.push('/');
Toast.success('登陆成功!')
}else{
Toast.fail('登陆失败!')
}
})
}
}
}
}
</script>
<style lang='scss' scoped>
.login{
margin: 50px 20px;
}
.login .title{
font-weight: bold;
text-align: center;
line-height: 40px;
}
.login .block{
margin: 20px 0;
}
.login .block input{
height: 40px;
border: 1px solid #ccc;
border-radius: 20px;
padding-left: 20px;
box-sizing: border-box;
outline: none;
width: 100%;
}
</style>
用表单组价封装的登录组件
<template>
<div class="login">
<div class="title">登录页</div>
<van-form @submit="login">
<van-field
class="input-box"
v-model="formData.phone"
placeholder="输入手机号"
:rules="[{ required: true, pattern:/^1[3-9]\d{9}$/,message: '请输入手机号' }]"
/>
<van-field
class="input-box"
v-model="formData.pass"
type="password"
placeholder="输入密码"
:rules="[{ required: true, pattern:/^\d{6}$/, message: '请输入密码' }]"
/>
<div style="margin: 16px;">
<van-button round block type="info" native-type="submit">登录</van-button>
</div>
</van-form>
<button round block @click="reguster" class="zhuce" >点此注册新账号</button>
</div>
</template>
<script>
// 导入组件
import {
Toast } from 'vant';
import {
user_login} from '../utils/api'
export default {
data(){
return {
formData:{
phone:'',
pass:'',
}
}
},
methods:{
login(){
// 发起登录请求
user_login( this.formData ).then((res)=>{
if( res.data.code == 200 ){
//保存用户信息,token
localStorage.setItem('token',res.data.token);
localStorage.setItem('userinfo', JSON.stringify( res.data.userinfo ) );
}else{
Toast.fail('登陆失败!')
}
})
},
reguster(){
this.$router.push('/register');
}
}
}
</script>
<style lang='scss' scoped>
.login{
margin: 50px 20px;
}
.login .title{
font-weight: bold;
text-align: center;
line-height: 40px;
font-size: 32px;
}
.login .input-box{
border: 1px solid #ccc;
margin: 30px 0;
border-radius: 30px;
}
.zhuce{
position: absolute;
right: 20px;
padding: 5px 20px;
border: none;
font-size: 14px;
color: lightblue;
background-color: white;
}
</style>
项目中的axios二次封装,统一管理API请求
这两个文件都在项目的src文件中创建一个utils文件
在utils文件中创建文件来实现
request.js文件
//对axios进行二次封装
import axios from 'axios'
import {
Toast } from 'vant';
//实例化axios
var service = axios.create({
timeout: 10*1000,
baseURL: '/api'
})
//设置拦截器( 请求拦截器 )
service.interceptors.request.use((config)=>{
//统一携带请求头
config.headers['Authorization'] = 'Bearer' + ' ' + localStorage.getItem('token');
//显示loading
Toast.loading({
message: '加载中...', duration: 0});
return config;
},(error)=>{
return Promise.reject(error);
})
//设置拦截器( 响应拦截器 )
service.interceptors.response.use((res)=>{
//隐藏loading
Toast.clear();
return res;
},(error)=>{
//隐藏loading
Toast.clear();
if( error.response.status == 401 ){
alert('身份认证失败,登陆过期!')
}
else if( error.response.status == 404 ){
alert('访问路径错误!')
}
else if( error.response.status == 500 ){
alert('服务器内部错误')
}
return Promise.reject(error);
})
export default service;
api.js文件,用使用封装的请求就得导入到你要用到的组件中,再进行使用
//导入设置过 拦截器的axios
import service from './request'
//一个请求封装一个函数
export function user_login( params = {
} ){
return service.post('/user/login', params );
}
export function user_register( params = {
} ){
return service.post('/user/register', params );
}
//...
//导出所有函数
设置代理在项目的vue.config.js文件中
const {
defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
devServer:{
proxy:{
'/api':{
target:'http://waimai.yantianfeng.com/',
changeOrigin:true,
pathRewrite:{
}
}
}
}
})