Vue项目之登录注册

Vue项目之登录注册

  • 1. 注册
    • 1.1 注册页面的布局
    • 1.2 注册业务逻辑的实现
  • 2. 登录
  • 3.导航守卫
    • 3.1全局导航守卫,全部路由都会经过这里。一定要调用next方法向下执行
    • 3.2 路由独享的导航守卫
    • 3.3 组件内的导航守卫
    • 补充:个人中心的校验
  • 4.列表进入详情返回还在原位置
  • 5. 路由相关问题

1. 注册

1.1 注册页面的布局

需要用到Vant的Field组件
1.views下新建一个注册页面 ,完成基本布局。引入Vue和Field并使用。
2.在官方文档中搜索,查看自己需要的内容,将其直接粘贴到页面中。
3.给注册页面添加一个单独的路由,注册页面不需要底部。(注意,相关样式需要引入不同的组件,请细心查看官方文档,按需拿取内容)
4. 利用计算属性给输入框做条件判断。本案例以手机号为例。

<template>
  <div class="container">
    <header class="header">注册header>
    <div class="content">
      <van-field
        v-model="username"
        placeholder="请输入手机号"
        clearable
        :error-message="usernamemsg"
      />
      <van-field
        v-model="password"
        type="password"
        clearable
        placeholder="请输入密码"
        error-message="密码格式错误"
      />
      <van-field
        v-model="code"
        center
        clearable
        placeholder="请输入短信验证码"
        error-message="验证码格式错误"
      >
        <van-button slot="button" size="small" type="primary">发送验证码van-button>
      van-field>
    div>
  div>
template>

<script>
import Vue from 'vue'
import { Field, Button } from 'vant'

Vue.use(Field)
Vue.use(Button)
export default {
  data () {
    return {
      username: '18813007814',
      password: '123456',
      code: ''
    }
  },
  computed: {
    usernamemsg () {
      if (this.username === '') {
        return ''
      } else if (!/^[1][3,4,5,7,8][0-9]{9}$/.test(this.username)) {
        return '手机号码格式错误'
      } else {
        return ''
      }
    }
  }
}
script>

1.2 注册业务逻辑的实现

注:在此之前,记住要先去路由页面添加一个注册页面的路由。
1.补充了密码以及验证码的计算属性判断
密码:如果为空返回空,如果不符合正则,返回格式错误,否则表示可以直接返回空
验证码:如果为空返回空,否则不为五位,返回验证码格式错误,否则成功返回空

2.验证码

  • 绑定点击事件senCode
  • 将提示信息{{buttonmsg}}放在按钮内容中。
  • 定义函数:(定时器注意用箭头函数)每一秒钟定义一次,先提前声明时间,在计时器内让时间减减,当时间为零时,清除定时器,同时btn的内容改为发送验证码,最后return。未return时,让按钮内的内容为时间减减+后重新发送(注意:在开启定时器时,要让按钮禁止点击,在清除定时器后再让按钮可点。)

3.验证码跟接口做交互
在setCode最后定义一个getCode函数,在下方定义该函数(类似面向对象写法)。
定义getCode:

  • 需要引入Toast并使用,请提示
  • 在该函数中也可以去验证手机号是否正确,不正确显示相关提示信息
  • 否则正确,就去axios去post请求数据,然后打印数据,去判断返回信息,跟接口文档相对应,如果为1则是用户名已经注册,0为获取验证码失败,成功在data里再定义adminCode用来存放验证码,通过res.data.code可以获取,加入该值中。

4.引入一个按钮,作为注册按钮
定义register函数
1.判断:

  • 将手机号的验证再写一遍,如果不符合,则toast提示,直接return
  • 验证密码,不符合同样提示并return
  • 验证码验证,不符合toast提示,并return

2.当上面的判断都通过,再定义真正的注册函数

  • 调接口,修改注册按钮为不可点,并修改值为注册中,在注册按钮写上:loading=“loading” loading-text=“注册中” disabled=“flag”
  • this.flag=true,this.loading=true
  • axios的post请求数据,第二个参数为用户手机号和密码。请求成功后先去更改按钮的状态,this.flag,this.loading都改为false。 再去判断返回值(根据接口文档定义) 2->用户名已经注册,0->注册失败,否则注册成功
    5.在注册页面增加登录按钮,用来做跳转。
    记住不能用声明式跳转,用编程式跳转,用replace跳转到另一个页面(返回直接回到个人中心)
<template>
    <div class="container">
        <header class="header">注册头部header>
         <div class="content">
             <van-field
                 v-model="tel"
                 placeholder="请输入手机号"
                 :error-message="usertel"
                 
                  clearable
             />
             <van-field
                 v-model="password"
                 type="password"
                 placeholder="请输入密码"
                 :error-message="pass"
                
                 clearable
             />
             <van-field
                 v-model="sms"
                 center
                 clearable                
                 placeholder="请输入短信验证码"
                 :error-message="test"
              >
             <van-button :disabled="flag" slot="button" size="small" type="primary" @click="sendCode">{{ buttonmsg }}van-button>
            
             van-field>
              <van-button type="primary" :loading="loading" loading-text="注册..." size="large" :disabled="zhud" @click="register">注册van-button>
              <van-divider @click="toLogin">去登录van-divider>
         div>
    div>
template>
<script>
import Vue from 'vue'
import { Field,Button,Toast,Divider } from 'vant'
import axios from 'axios'
Vue.use(Field)
Vue.use(Button)
Vue.use(Toast)
Vue.use(Divider)

export default {
    data () {
        return {
            tel:'',
            password:'',
            sms:'',
            buttonmsg:'点击发送验证码',
            flag:false,
            adminCode:'',
            zhud:false,
            loading:false
        }
    },
    computed: {
        usertel () {
            if (this.tel === ""){
                return ''
            }else if(!/^[1][3,4,5,7,8][0-9]{9}$/.test(this.tel)){
                return '手机号格式错误'
            }else {
                return ''
            }
        },
        pass () {
            if (this.password === ""){
                return ''
            }else if(this.password.length<6){
                return '密码不可小于6位'
            }else {
                return ''
            }
        },
        test () {
            if (this.sms === ""){
                return ''
            }else if(this.sms.length !== 5){
                return '验证码格式错误'
            }else {
                return ''
            }
        }
    },
    methods: {
        toLogin () {
           this.$router.replace('/login') 
        },
        sendCode () {
            let time = 4
            let timer
            timer = setInterval(()=>{                           
                time --
                if(time === 0){
                    clearInterval(timer)
                    this.flag=false
                    this.buttonmsg = '点击发送验证码'
                    return
                }
                this.flag = true  
                this.buttonmsg = time + '秒后重新发送' 
                              
            },1000)
            this.getCode()
        },
        getCode () {
            if(!/^[1][3,4,5,7,8][0-9]{9}$/.test(this.tel) || this.tel===""){
                Toast('手机号码输入有误')
            }else{
                axios.get('https://www.daxunxun.com/users/sendCode?tel='+this.tel).then(res=>{
                    if(res.data === 1){
                        Toast('用户名已注册,请更改')
                    }else if(res.data === 0){
                        Toast('获取验证码失败')
                    }else{
                        this.adminCode = res.data.code
                        console.log(this.adminCode)
                    }
                })
            }
        },
        register () {
            if (this.tel === '' || this.usertel === '手机号码格式错误') {
                Toast('手机号码输入有误')
                return
            }
            if (this.password === '' || this.pass === '密码格式错误,最少为6位') {
                Toast('密码输入有误')
                return
            }
            if (this.sms === '' || this.sms !== this.adminCode) {
                Toast('验证码输入有误')
                return
            }
            this.reallR()
        },
        reallR () {
            this.zhud=true
            this.loading=true
            axios.post('https://www.daxunxun.com/users/register', {
            username: this.tel,
            password: this.password
        }).then(res=>{
            this.zhud=false
            this.loading=false
            if (res.data === 2) {
                Toast('用户名已注册,请直接登录')
            } else if (res.data === 0) {
                Toast('注册失败')
            } else {
            Toast('注册成功')
            }
        })
        }
    }
}
script>

2. 登录

页面布局以及基本逻辑同注册基本相同,可直接复制注册,将相关注册字样更改为登录。删除相关的判断。更改相关函数。

<template>
    <div class="container">
        <header class="header">注册头部header>
         <div class="content">
             <van-field
                 v-model="tel"
                 placeholder="请输入手机号"
                 :error-message="usertel"
                 
                  clearable
             />
             <van-field
                 v-model="password"
                 type="password"
                 placeholder="请输入密码"
                 :error-message="pass"
                
                 clearable
             />
              <van-button type="primary" :loading="loading" loading-text="登录..." size="large" :disabled="zhud" @click="login">登录van-button>
              <van-divider @click="toRegister">去注册van-divider>
         div>
    div>
template>
<script>
import Vue from 'vue'
import { Field,Button,Toast,Divider} from 'vant'
import axios from 'axios'
// import { setInterval, clearInterval } from 'timers';
// import func from '../../../vue-temp/vue-editor-bridge';
Vue.use(Field)
Vue.use(Button)
Vue.use(Toast)
Vue.use(Divider)

export default {
    data () {
        return {
            tel:'',
            password:'',
            zhud:false,
            loading:false
        }
    },
    computed: {
        usertel () {
            if (this.tel === ""){
                return ''
            }else if(!/^[1][3,4,5,7,8][0-9]{9}$/.test(this.tel)){
                return '手机号格式错误'
            }else {
                return ''
            }
        },
        pass () {
            if (this.password === ""){
                return ''
            }else if(this.password.length<6){
                return '密码不可小于6位'
            }else {
                return ''
            }
        }
    },
    methods: {
        toRegister () {
            this.$router.replace('/register')
        },
        login () {
            if (this.tel === '' || this.usertel === '手机号码格式错误') {
                Toast('手机号码输入有误')
                return
            }
            if (this.password === '' || this.pass === '密码格式错误,最少为6位') {
                Toast('密码输入有误')
                return
            }
            if (this.sms === '' || this.sms !== this.adminCode) {
                Toast('验证码输入有误')
                return
            }
            this.reallR()
        },
        reallR () {
            this.zhud=true
            this.loading=true
            axios.post('https://www.daxunxun.com/users/login', {
            username: this.tel,
            password: this.password
        }).then(res=>{
            this.zhud=false
            this.loading=false
            if (res.data === 2) {
                Toast('用户未注册')
            } else if(res.data === -1){
                Toast('密码错误')
            }else if (res.data === 0) {
                Toast('登录失败')
            } else {
            Toast('登录成功')
            localStorage.setItem('isLogin','ok') //登录标识
            this.$router.back()  //登陆成功返回上一页
            }
        })
        }
    }
}
script>

3.导航守卫

3.1全局导航守卫,全部路由都会经过这里。一定要调用next方法向下执行

除了登陆路由,其他页面都需要登录验证。

router.beforeEach((to,from,next)=>{
  // console.log(to)
  // console.log(from)
  // next()   //至此就可以显示页面了
  //做业务逻辑,如果是登录状态,就进行下一步(注意登录页面不可做这个判断)
  if(to.name = 'login'){  //如果要去的页面是登录页面,就进行下一步,不做判断
    next()
  }else{
    if(localStorage.getItem('isLogin')==='ok'){
      next()
    }else{
      next('/login')
    }
  }

3.2 路由独享的导航守卫

路由独享的导航守卫—一般不推荐直接使用
比如在购物车路由下方加入下面一段代码,可以实现购物车页面的相关判断。
没有登录状态就直接去到登录页面,有登录状态就继续下一步。

beforeEnter (to,from,next) {
          if(localStorage.getItem('isLogin')==="ok"){
            next()
          }else{
            next('/login')
          }
        }

3.3 组件内的导航守卫

先来简单看一下组件内的导航守卫相关介绍:

const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}

beforeRouteEnter 守卫 不能 访问 this,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。

不过,你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。

beforeRouteEnter (to, from, next) {
  next(vm => {
    // 通过 `vm` 访问组件实例
  })
}

本文以购物车页面为例,在路由进入之前,做一个判断,有登录状态进行下一步,没有登录状态就直接去到登录页面。组件内的导航守卫有两张写法,请看案例,且不同写法有不同的效果,按需而定。

<template>
    <div class="container">
        <header class="header">购物车头部</header>
         <div class="content">购物车内容</div>
    </div>
</template>
<script>
export default {
    beforeRouteEnter (to,from,next) {
        // if (localStorage.getItem('isLogin')==='ok'){
        //     next()
        // }else{
        //     next('/login')  //这种登陆成功返回点击购物车之前的页面
        // }
        next(vm => {
            if (localStorage.getItem('isLogin')==='ok'){
            next()
        }else{
           // next('./login')
           vm.$router.push('/login')  //这种登录成功直接返回到了购物车
        }
        })
    }
}
</script>

补充:个人中心的校验

  • 在个人中心写业务逻辑,如果本地读取登录状态则跳到相关页面,没有登录的话就跳转到未登录页面
  • 涉及到重定向,一开始就将路由盖过去了,因此未登录页面同样需要业务处理
  • 注意,要在未登录页面部署同样的业务逻辑,有登陆跳转登录,没有登录next
  • 想让业务更加严谨就去登录页面设置业务逻辑,(虽然效果已经实现了)

4.列表进入详情返回还在原位置

基本思路:在离开页面前将内容高度存储到本地,在返回页面后在去本地获取并设置内容高度。

  • 添加meta标签,keepAlive,表示当前路由涉及到缓存
  • 在app.vue修改路由的显示部分,将视图放进标签,并且做一个判断,注意需要缓存的才放在标签中(可以避免组件的重新渲染)
  • 找位置:离开时记录位置,返回时回到这个位置
    利用keepAlive,可以在activated中进行操作
  1. 在需要 缓存的列表中定义路由时添加如下代码 router.js中针对于 首页的路由

Vue项目之登录注册_第1张图片
2.修改App.vue中路由的显示部分
Vue项目之登录注册_第2张图片
3.在离开首页时,记录滚动条的位置,返回时滚动到该位置即可,下方案例有一句代码是为了实现其他业务需求,可以忽略不看。
Vue项目之登录注册_第3张图片
4.利用keepAlive,可以在activated中进行返回后的操作,设置内容高度为本地存储的值。
在这里插入图片描述

5. 路由相关问题

对于导航守卫还需要补充一些相关知识,上面步骤执行完会发现个人中心存在一些问题。当在个人中心页面显示已经登录时,无法后退。
通过vue开发者工具,我们可以观察到,当我们未登录时,先去到首页,再去到个人中心,此时个人中心应该是未登录页面(/user/nologin),我们点击登录,会去到登录页面,点击登录后,在开发者工具中,我们会看到,我们先到了个人中心的未登录页面,再去到了个人中心的已登录页面,并且每次后退,都是先后退到个人中心未登录页面,又跳转到个人中心登录页面。(因为未登录页面有一个判断,所以我们每次点击后退,在视觉上感觉无法后退,实际上是退回到了未登录页面,这个页面又做了个判断,又回到已登录页面,如此循环往复。)
Vue项目之登录注册_第4张图片
解决:

  • 在个人中心的未登录页面,将之前的next、vm删去。
  • 在个人中心的登录页面,将之前的判断删去,此页面不做判断也可以执行业务逻辑。

执行完以上解决方法,发现我们可以后退了,但是,需要后退两次才能返回主页。通过开发者工具我们可以看到页面路由跳转的流程,见下图:
Vue项目之登录注册_第5张图片
相关原因以及解决方法:
1.在路由文件中将重定向删除。
2.个人中心未登录页面到登录页面的跳转是声明式挑战,需要更改为编程式跳转。在编程式跳转中,运用replace方法()
3.在个人中心页面,我们之前用的是beforeRouteEnter,如果是该页面下的其它页面做跳转,此方法无法触发。所以我们此处应该将该方法替换成侦听属性。(侦听属性可以侦听路由的变化,打印可以获取相关的路由,因此我们可以做判断。)
4.但是我们发现侦听属性在别的页面跳转到该页面时,第一次不会触发监听。因此第一次监听还是需要用到beforeRouteEnter,再此方法中也需要用到replace,至此可以解决问题了。

步骤详情
1.在路由文件中,将个人中心页面的重定向删除

Vue项目之登录注册_第6张图片
2.在nologin页面将路由守卫删去,改变登录跳转方式。在个人中心的login页面,直接将业务逻辑删去。
Vue项目之登录注册_第7张图片
3.个人中心页面,将beforeRouteEnter中的next改为replace方法。(因为没有重定向,默认跳转为/user替换为/user/login或者/user/nologin)在下面利用侦听属性侦听路由的变化,并做判断。 (replace和push页面显示不同)

<template>
  <div class="container">
    <header class="header">个人中心头部header>
    <div class="content">
      <router-view>router-view>
      个人中心内容
    div>
  div>
template>
<script>
export default {
  beforeRouteEnter (to, from, next) {
    next(vm => {
      if (localStorage.getItem('isLogin') === 'ok') {
        // 因为没有重定向,默认跳转为 /user 替换为/user/loging
        vm.$router.replace('/user/loging')
      } else {
        vm.$router.push('/user/nologin')
      }
    })
  },
  watch: {
    $route (val) {
      console.log(val)
      if (localStorage.getItem('isLogin') === 'ok') {
        this.$router.replace('/user/loging')
      } else {
        this.$router.replace('/user/nologin')
      }
    }
  }
}
script>

你可能感兴趣的:(VUE)