从0学习Vue3(6)

136 阅读2分钟

3.11 实现登录功能

用所学知识来做一个简单的登录功能吧。首先创建一个Login.vue组件,然后再router.js中添加一个路由路径:

 <!-- Login.vue -->
 <template>
   <h1>登录页</h1>
   <form>
     <div>
       <label for="username">用户名:</label>
       <input id="username" type="text" />
     </div>
     <div>
       <label for="password">密码:</label>
       <input id="password" type="password" />
     </div>
     <button>登录</button>
   </form>
 </template>
 const router = createRouter({    
   history:createWebHashHistory(),
   routes:[
     {path:"/",component:Home},   
     {path:'/blog/:id',component:Blog},
     {path:'/login',component:Login}
   ]
 })

好了,现在我们能够在我们的网址后面手动加上login就可以跳转到登录页了。

接下来实现在登录页,点击登录后 跳转到首页的方法:

 <!-- Login.vue -->
 <template>
   <h1>登录页</h1>
   <!-- 记得我们的form有默认事件嘛,需要用prevent取消默认事件 -->
   <form @submit.prevent="login">
     <div>
       <label for="username">用户名:</label>
       <input id="username" type="text" v-model="username" />
     </div>
     <div>
       <label for="password">密码:</label>
       <input id="password" type="password" v-model="password" />
     </div>
     <button>登录</button>
   </form>
 </template>
 ​
 <script>
 export default {
   data(){
     return{
       username:'',
       password:''
     }
   },
   methods:{
     login(){  //本应该是由后端进行判定,这里就先在前端写一个函数来判定
       if(this.username==='admin'&&this.password==='123456'){
         //如果满足以上条件,登陆成功,会跳转到首页
         this.$router.push("/")
       }else{
         //否则就会提示
         alert("用户名或密码错误")
       }
     }
   }
 }
 </script>

3.11.1 token验证登录状态

这里要知道一个概念是localStorage本地存储。我们可以通过localStorage.setItem('token','111')在本地设置一个token,并在每个页面都能用localStorage.getItem("token")可以拿到‘111’这个值。但真正在开发中,这个token一般是后端发送给前端的一个十六进制很长的一个字符串。

这里可以在前端模拟一下,我们登录完成后设置一个token:

 login(){  //本应该是由后端进行判定,这里就先在前端写一个函数来判定
       if(this.username==='admin'&&this.password==='123456'){
         this.$router.push("/")
         //设置token
         localStorage.setItem("token","111")
       }else{
         alert("用户名或密码错误")
       }
 }

下面要介绍一个路由守卫(也可以叫做导航守卫路由拦截):

 //router.js
 //每次路由切换都会执行一次这个函数beforeEach()
 ​
 router.beforeEach((to,from,next)=>{//可以不写next
     //三个参数分别代表 去哪个url  从哪个url来  如果在参数里面写了next而下方不用它 页面就不会跳转了
     console.log(to)
     console.log(from)
     next() //若参数里写了next,这里不执行,那么页面将不跳转
 })

我们知道了路由守卫,在每次路由切换时都会执行,就可以在这里面检测我们的token。如果有token说明登录成功了,就允许他到达首页,而如果没有token 就把他强制跳转到登录页。这是为了防止没有登录就直接通过修改网址跳到主页去。

 //router.js
 //路由守卫
 router.beforeEach((to,_from,next)=>{//这里的from下面不使用它的话,这里可以在前面加_
   //验证token,只有存在token的时候,才能跳转到其他页面,否则只能去登录页
   let token = localStorage.getItem("token")
   if(token || to.path==='/login'){
     next()
   }else{
     next('/login')
   }
 })

这样就是先纯前端的一个登陆验证的功能,当然实际项目是要跟后端配合的。

3.12 组合api

3.12.1 响应式变量

首先回顾一下之前写的vue代码:

 <template>
   <h1>{{msg}}</h1>
 </template>
 ​
 <script>
 export default {
     data(){
         return{
             msg:'hello'
         }
     },
     methods:{
         changeMsg(){
             this.msg = 'hello world'
         }
     }
 }
 </script>

这里我们是将一个一个的属性,传给到当前组件里,这个呢就是option api(选项api)的语法。

现在要学习的是composition api(组合api) :把所有东西写到一个setup( ){ }里面。

 <template>
   <h1>{{title}}</h1>
   <button @click="sayHi">按钮</button>
 </template>
 ​
 <script>
 export default {
   setup(){
     let title = 'hello world'
     const sayHi = ()=>{
       alert('hello vue')
     }
     return {   //把数据暴露出来给上面的模板用
       title,sayHi
     }
   }
 }
 </script>

以上就是基本写法,那么如果想要通过点击按钮,改变title的值呢,如果直接在sayHi里面改,会发现根本没有改变,这里就要讲到响应式了。

因为我们以上代码中的title只是一个普通变量,不是一个响应式变量,因此就算在sayHi中将其赋值为其他字符串也不会在页面显示改变:

 <template>
   <h1>{{title}}</h1>
   <button @click="sayHi">按钮</button>
 </template>
 ​
 <script>
 //引入vue的ref
 import {ref} from 'vue'
 ​
 export default {
   setup(){
     //title变为响应式变量,会拥有一个value属性
     let title = ref('hello world')
     const sayHi = ()=>{
       title.value = 'hello vue'
     }
     return {   //把数据暴露出来给上面的模板用
       title,sayHi
     }
   }
 }
 </script>

那么上面代码中,是使用vue中的ref将一个字符串变成了响应式变量,vue中还有一个reactive可以将对象变成响应式对象:

 <template>
   <h1>{{student.name}}</h1>
   <button @click="changeName">按钮</button>
 </template>
 ​
 <script>
 //引入vue的reactive
 import {reactive} from 'vue'
 ​
 export default {
   setup(){
     //将student变成一个响应式对象
     const student = reactive({name:'小明',age:2})
     const changeName = ()=>{
       student.name = '大明'
     }
     return {
       student,changeName
     }
   }
 }
 </script>

这样在页面就可以实时修改页面的展示数据了。

3.12.2 组件的生命周期

这里有几个常用生命周期的对应写法:

option apicomposition api
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeUnmountonBeforeUnmount
unmountedonUnmounted

那我们会发现,beforeCreate和created咋没了捏,因为我们的setup() 运行的时间呢就是created的时间,他其实代替了created。

3.12.3 计算属性

需要从vue中引入{computed}

 <template>
   <h1>{{number}}</h1>
 </template>
 ​
 <script>
 //引入vue的reactive
 import {computed} from 'vue'
 ​
 export default {
   setup(){
     const number = computed(()=>{ //computed是一个函数
       return 100
     })
     return {   
       number
     }
   }
 }
 </script>

3.12.4 组合api的优势

我们先用选项api(composition api)写加减按钮的方法:

 <template>
   <button @click="minus">-</button>
   <h1>{{num}}</h1>
   <button @click="add">+</button>
 </template>
 ​
 <script>
 export default {
   data(){
     return {
       num:0
     }
   },
   methods:{
     add(){
       this.num++
     },
     minus(){
       this.num--
     }
   }
 }
 </script>

接着用组合api来写:

 <template>
   <button @click="minus">-</button>
   <h1>{{num}}</h1>
   <button @click="add">+</button>
 </template>
 ​
 <script>
 import {ref} from 'vue'
 export default {
   setup(){
     const num = ref(0) //响应式变量
     const add = ()=>{
       num.value++
     }
     const minus = ()=>{
       num.value--
     }
     return {num,add,minus}
   }
 }
 </script>

而且在使用组合api的时候,甚至可以把里面的东西拿到setup()外面来,到export外面来:

 <template>
   <button @click="minus">-</button>
   <h1>{{num}}</h1>
   <button @click="add">+</button>
 </template>
 ​
 <script>
 import {ref} from 'vue'
 ​
 const num = ref(0)
 const add = ()=>{
       num.value++
 }
 const minus = ()=>{
       num.value--
 }
     
 export default {
   setup(){
     //就留 return 在里面
     return {num,add,minus}
   }
 }
 </script>

既然这样子,那我们甚至都不需要写到这里来,甚至是可以另起 js文件,从外部导入即可,比如我们在components文件夹下建立一个 count.js :

 //count.js
 export default function(num){  //暴露一个函数,接收一个参数num(是一个响应式变量)
   const add = ()=>{
     num.value++
   }
 ​
   const minus = ()=>{
     num.value--
   }
   return{
     add,minus                 //这个函数的返回值是add()和minus()
   }
 }

然后看看我们vue文件那边:

 <template>
   <button @click="minus">-</button>
   <h1>{{num}}</h1>
   <button @click="add">+</button>
 </template>
 ​
 <script>
 import {ref} from 'vue'
 import count from './components/count' // 引入刚写的count.js
 ​
 export default {
   setup(){
     const num = ref(0)
     //这种取值方式是 解构赋值
     const {add,minus} = count(num) //这里为什么不能直接传num.value呢,因为响应式变量是num而不是num.value
     return {num,add,minus}
   }
 }
 </script>

这样子我们的代码就可以很简洁了。可以用组合api把之前写的水果列表重新实现一遍,会有所进步!