Vue3+Vant實(shí)現(xiàn)簡(jiǎn)單的登錄功能
內(nèi)容/作用
知識(shí)點(diǎn)/設(shè)計(jì)/實(shí)驗(yàn)/作業(yè)/練習(xí)
學(xué)習(xí)
Vue3+Vant開(kāi)發(fā):登錄功能
1、創(chuàng)建登錄路由
npm install vue-router@4
以上安裝了最新版本的vue-router.
下面在src目錄下面創(chuàng)建router目錄,在該目錄下面創(chuàng)建index.js文件,在該文件中完成路由的配置。
index.js文件中代碼如下所示:()
在下面的代碼中,首先從vue-router中導(dǎo)入createRouter以及createWebHashHistory.
createRouter用來(lái)創(chuàng)建路由實(shí)例
createWebHashHistory創(chuàng)建hash路由
Layout組件用來(lái)完成整個(gè)后臺(tái)管理頁(yè)面的布局。
在routes數(shù)組中,定義了相應(yīng)的路由規(guī)則
import { createRouter, createWebHashHistory } from "vue-router"; const routes = [ { path: "/login", name: "login", component: () => import("../views/login/index.vue"), }, ]; //創(chuàng)建路由實(shí)例 const router = createRouter({ //history模式:createWebHistory history: createWebHashHistory(), //使用hash模式 routes, }); export default router;
下面在src目錄下面創(chuàng)建views目錄,在該目錄下面創(chuàng)建login目錄,然后在創(chuàng)建index.vue.
該組件中的內(nèi)容如下所示:
<template> <div>登錄</div> </template> <script> export default {}; </script> <style></style>
下面,返回到main.js文件中使用創(chuàng)建好的路由對(duì)象
import { createApp } from "vue"; import App from "./App.vue"; import Vant from "vant"; import "vant/lib/index.css"; import "amfe-flexible"; import router from "./router"; //導(dǎo)入路由對(duì)象 createApp(App).use(Vant).use(router).mount("#app"); //使用路由
同時(shí)在App.vue文件中,添加router-view,指定路由的出口。
<template> <router-view></router-view> </template> <script setup></script> <style></style>
在瀏覽器中輸入http://localhost:3000/#/login查看效果。
2、實(shí)現(xiàn)登錄布局結(jié)構(gòu)
<template> <div class="login-container"> <!-- 導(dǎo)航欄 --> <van-nav-bar title="登錄" /> <!-- 導(dǎo)航欄結(jié)束 --> <!-- 登錄表單 --> <van-form> <van-field name="userName" placeholder="請(qǐng)輸入用戶(hù)名" /> <van-field name="userPwd" placeholder="請(qǐng)輸入密碼" /> <div style="margin: 16px"> <van-button block type="primary" native-type="submit"> 提交 </van-button> </div> </van-form> <!-- 登錄表單結(jié)束 --> </div> </template> <script> export default {}; </script> <style></style>
3、登錄布局實(shí)現(xiàn)
這里我們首先設(shè)置一下,導(dǎo)航欄的樣式。由于很多頁(yè)面都會(huì)用到導(dǎo)航欄,所以可以將樣式定義在全局的文件中,而不是單獨(dú)的定義在登錄組件中。
在src目錄下面創(chuàng)建styles目錄,在該目錄下面創(chuàng)建index.css文件,該文件中的代碼如下所示:
.page-nav-bar { background-color: #3296fa; } .page-nav-bar .van-nav-bar__title {/* 這里的van-nav-bar__title,可以查看原有的導(dǎo)航欄的樣式*/ color: #fff; }
在登錄組件的導(dǎo)航欄中使用該樣式。(在main.js文件中導(dǎo)入import "./styles/index.css";)
<van-nav-bar title="登錄" class="page-nav-bar" />
下面給登錄表單的文本框與密碼框添加對(duì)應(yīng)的圖標(biāo)。
對(duì)應(yīng)的官網(wǎng):
https://vant-contrib.gitee.io/vant/v3/#/zh-CN/field
<van-form> <van-field name="userName" placeholder="請(qǐng)輸入用戶(hù)名" left-icon="manager" /> <van-field name="userPwd" placeholder="請(qǐng)輸入密碼" left-icon="lock"/> </van-field> <div style="margin: 16px"> <van-button block type="primary" native-type="submit"> 提交 </van-button> </div> </van-form>
這里給手機(jī)號(hào)與驗(yàn)證碼添加了left-icon設(shè)置了圖標(biāo)。同時(shí)設(shè)置了“發(fā)送驗(yàn)證碼”的按鈕
4、實(shí)現(xiàn)基本登錄功能
注意:這里只是實(shí)現(xiàn)基本登錄效果,把服務(wù)端返回的內(nèi)容打印出來(lái),不涉及到登錄狀態(tài)的提示與登錄狀態(tài)的存儲(chǔ)操作。
首先在src目錄下面創(chuàng)建api目錄,在該目錄下面創(chuàng)建user.js文件,該文件中封裝了用戶(hù)請(qǐng)求的相關(guān)處理模塊。
// 封裝用戶(hù)相關(guān)的請(qǐng)求模塊 import request from "../utils/request"; export const login = (data) => { return request({ method: "POST", url: "/user/login", data, }); };
下面修改login.vue組件中的內(nèi)容
<div class="login-container"> <!-- 導(dǎo)航欄 --> <van-nav-bar title="登錄" class="page-nav-bar" /> <!-- 導(dǎo)航欄結(jié)束 --> <!-- 登錄表單 --> <van-form @submit="onSubmit"> <van-field name="userName" placeholder="請(qǐng)輸入用戶(hù)名" v-model="userName" left-icon="manager" :rules="userFormRules.userName" /> <van-field name="userPwd" placeholder="請(qǐng)輸入密碼" v-model="userPwd" left-icon="lock" :rules="userFormRules.userPwd" > </van-field> <div style="margin: 16px"> <van-button block type="primary" native-type="submit"> 登錄 </van-button> </div> </van-form> <!-- 登錄表單結(jié)束 --> </div>
給每個(gè)van-field綁定了v-model,同時(shí)給van-form添加了@submit事件。
import { reactive, toRefs } from "vue"; import { login } from "../../api/user"; //導(dǎo)入login方法,進(jìn)行請(qǐng)求的發(fā)送 function useSubmit(user) { const onSubmit = async () => { //1、獲取表單數(shù)據(jù) //2、表單驗(yàn)證 //3、提交表單請(qǐng)求 Toast.loading({ message: "登錄中...", forbidClick: true, //禁用背景點(diǎn)擊 duration: 0, //持續(xù)時(shí)間,默認(rèn)是2000毫秒,如果為0則持續(xù)展示 }); const res = await login(user); if (res.data.code === 0) { store.commit("setUser", res.data); Toast.success("用戶(hù)登錄成功"); } else { Toast.fail("用戶(hù)名或密碼錯(cuò)誤"); } //4、根據(jù)請(qǐng)求響應(yīng)結(jié)果處理后續(xù)操作。 }; return { onSubmit, }; } export default { setup() { const user = reactive({ userName: "", //用戶(hù)名 userPwd: "", //用戶(hù)密碼 }); return { ...toRefs(user), ...useSubmit(user), }; }, };
在setup方法中,定義user響應(yīng)式對(duì)象,最后返回。同時(shí)將外部定義的useSubmit方法進(jìn)行調(diào)用。
5、登錄狀態(tài)提示
const onSubmit = async () => { //1、獲取表單數(shù)據(jù) //2、表單驗(yàn)證 //3、提交表單請(qǐng)求 Toast.loading({ message: "登錄中...", forbidClick: true, //禁用背景點(diǎn)擊 duration: 0, //持續(xù)時(shí)間,默認(rèn)是2000毫秒,如果為0則持續(xù)展示 }); const res = await login(user); if (res.data.code === 0) { store.commit("setUser", res.data); Toast.success("用戶(hù)登錄成功"); } else { Toast.fail("用戶(hù)名或密碼錯(cuò)誤"); } //4、根據(jù)請(qǐng)求響應(yīng)結(jié)果處理后續(xù)操作。 }; return { onSubmit, }; }
這里使用了Toast組件,同時(shí)需要進(jìn)行導(dǎo)入:
import { Toast } from "vant";
由于網(wǎng)絡(luò)比較快,可能看不到登錄中...這個(gè)提示,所以可以把網(wǎng)絡(luò)設(shè)置慢一些。
可以修改NetWork中的No throttling中的Slow 3G
6、表單驗(yàn)證功能
在setup函數(shù)中定義校驗(yàn)規(guī)則,并且將其返回。
setup() { const user = reactive({ userName: "", //用戶(hù)名 userPwd: "", //用戶(hù)密碼 }); //定義校驗(yàn)規(guī)則 const userFormRules = { userName: [{ required: true, message: "請(qǐng)?zhí)顚?xiě)用戶(hù)名" }], userPwd: [ { required: true, message: "請(qǐng)?zhí)顚?xiě)密碼", }, { pattern: /^\d{6}$/, message: "密碼格式錯(cuò)誤", }, ], }; return { ...toRefs(user), ...useSubmit(user), userFormRules, //返回校驗(yàn)規(guī)則 }; },
在表單中使用校驗(yàn)規(guī)則:
<van-field name="userName" placeholder="請(qǐng)輸入用戶(hù)名" v-model="userName" left-icon="manager" :rules="userFormRules.userName" /> <van-field name="userPwd" placeholder="請(qǐng)輸入密碼" v-model="userPwd" left-icon="lock" :rules="userFormRules.userPwd" > </van-field>
7、處理用戶(hù)Token
用戶(hù)登錄成功以后,會(huì)返回token數(shù)據(jù)。
Token是用戶(hù)登錄成功之后服務(wù)端返回的一個(gè)身份令牌,在項(xiàng)目中經(jīng)常要使用。
例如:訪問(wèn)需要授權(quán)的API接口。
校驗(yàn)頁(yè)面的訪問(wèn)權(quán)限等。
同時(shí),這里我們還需要將token數(shù)據(jù)進(jìn)行存儲(chǔ),這樣在訪問(wèn)其它的頁(yè)面組件的時(shí)候,就可以獲取token數(shù)據(jù)來(lái)進(jìn)行校驗(yàn)。
關(guān)于token數(shù)據(jù)存儲(chǔ)在哪兒呢?
可以存儲(chǔ)到本地:
存儲(chǔ)到本地的問(wèn)題是,數(shù)據(jù)不是響應(yīng)式的。
存儲(chǔ)到Vuex中,獲取方便,并且是響應(yīng)式的。但是存儲(chǔ)到Vuex中也是有一定的問(wèn)題的,就是當(dāng)我們刷新瀏覽器的時(shí)候,數(shù)據(jù)就會(huì)丟失,所以還是需要把token數(shù)據(jù)存放到本地,存儲(chǔ)到本地的目的就是為了進(jìn)行持久化。
所以這里我們需要在登錄成功以后,把token數(shù)據(jù)存儲(chǔ)到vuex中,這樣可以實(shí)現(xiàn)響應(yīng)式,在本地存儲(chǔ)就是為了解決持久化的問(wèn)題。
安裝最新版本的Vuex
npm install vuex@next --save
下面在src目錄下面創(chuàng)建store目錄,在store目錄中index.js文件,該文件中的代碼如下所示:
import { createStore } from "vuex"; const store = createStore({ state: { //存儲(chǔ)當(dāng)前登錄用戶(hù)信息,包含token等數(shù)據(jù) user: null, }, mutations: { setUser(state, data) { state.user = data; }, }, }); export default store;
在上面的代碼中,創(chuàng)建了store容器,同時(shí)指定了state對(duì)象,在該對(duì)象中定義user屬性存儲(chǔ)登錄用戶(hù)信息。
在mutations中定義setUser方法,完成用戶(hù)信息的更新。
下面,要實(shí)現(xiàn)的就是,當(dāng)?shù)卿洺晒σ院?,更新user這個(gè)狀態(tài)屬性。
當(dāng)然,這里首先要做的就是把store注入到Vue的實(shí)例中。
import { createApp } from "vue"; import App from "./App.vue"; import Vant from "vant"; import "vant/lib/index.css"; import "amfe-flexible"; import "./styles/index.css"; import router from "./router"; import store from "./store"; //導(dǎo)入store createApp(App).use(Vant).use(router).use(store).mount("#app"); //完成store的注冊(cè)操作
在main.js文件中,我們導(dǎo)入了store,并且注冊(cè)到了Vue實(shí)例中。
下面返回到views/login/index.vue頁(yè)面中,把登錄的信息存儲(chǔ)到store容器中。
import { reactive, toRefs, ref } from "vue"; import { login, sendSms } from "../../api/user"; import { Toast } from "vant"; import { useStore } from "vuex"; //導(dǎo)入useStore
在上面的代碼中導(dǎo)入useStore.
export default { setup() { const loginForm = ref(); //獲取store const store = useStore();
在setup函數(shù)中,調(diào)用useStore方法,獲取store容器。
return { ...toRefs(user), ...useSubmit(user, store),//在調(diào)用useSubmit方法的時(shí)候傳遞store容器 userFormRules, loginForm, };
//用戶(hù)登錄 function useSubmit(user, store) { const onSubmit = async () => { //1、獲取表單數(shù)據(jù) //2、表單驗(yàn)證 //3、提交表單請(qǐng)求 Toast.loading({ message: "登錄中...", forbidClick: true, //禁用背景點(diǎn)擊 duration: 0, //持續(xù)時(shí)間,默認(rèn)是2000毫秒,如果為0則持續(xù)展示 }); const res = await login(user); if (res.data.code === 0) { store.commit("setUser", res.data); Toast.success("用戶(hù)登錄成功"); } else { Toast.fail("用戶(hù)名或密碼錯(cuò)誤"); } //4、根據(jù)請(qǐng)求響應(yīng)結(jié)果處理后續(xù)操作。 }; return { onSubmit, }; }
登錄成功以后,獲取到返回的數(shù)據(jù),同時(shí)調(diào)用store中的commit方法完成數(shù)據(jù)的保存
現(xiàn)在,我們雖然把登錄成功的數(shù)據(jù),存儲(chǔ)到Vuex中,但是當(dāng)我們刷新瀏覽器的時(shí)候,Vuex中的數(shù)據(jù)還是會(huì)丟失的。所以這里,我們還需要將其存儲(chǔ)到本地中。
下面修改一下store/index.js文件中的代碼:
import { createStore } from "vuex"; const TOKEN_KEY = "TOUTIAO_USER"; const store = createStore({ state: { //存儲(chǔ)當(dāng)前登錄用戶(hù)信息,包含token等數(shù)據(jù) // user: null, user: JSON.parse(window.localStorage.getItem(TOKEN_KEY)), }, mutations: { setUser(state, data) { state.user = data; window.localStorage.setItem(TOKEN_KEY, JSON.stringify(state.user)); }, }, }); export default store;
在mutations中的setUser方法中,將登錄成功的用戶(hù)數(shù)據(jù)存儲(chǔ)到了localStorage中,在存儲(chǔ)的時(shí)候,將數(shù)據(jù)轉(zhuǎn)成了字符串。
同時(shí)在state中獲取數(shù)據(jù)的時(shí)候,就從localStorage中獲取,當(dāng)然獲取的時(shí)候,再將其轉(zhuǎn)換成對(duì)象的形式。
下面,我們可以在App.vue中做一下測(cè)試:
<template> <div> <router-view></router-view> </div> </template> <script> import { useStore } from "vuex"; export default { setup() { const store = useStore(); console.log(store.state.user); }, }; </script> <style></style>
通過(guò)查看瀏覽器的控制臺(tái),可以查看到對(duì)應(yīng)的登錄用戶(hù)的token數(shù)據(jù)
8、封裝本地存儲(chǔ)操作
在我們的項(xiàng)目中,有很多的地方都需要獲取本地存儲(chǔ)的數(shù)據(jù),如果每次都寫(xiě):
JSON.parse(window.localStorage.getItem(TOKEN_KEY)), window.localStorage.setItem(TOKEN_KEY, JSON.stringify(state.user));
就比較麻煩了。所以這里我們建議把操作本地?cái)?shù)據(jù)單獨(dú)的封裝到一個(gè)模塊中。
在utils目錄下面創(chuàng)建storage.js文件,該文件中的代碼如下所示:
// 存儲(chǔ)數(shù)據(jù) export const setItem = (key, value) => { //將數(shù)組,對(duì)象類(lèi)型的數(shù)據(jù)轉(zhuǎn)換為JSON格式的字符串進(jìn)行存儲(chǔ) if (typeof value === "object") { value = JSON.stringify(value); } window.localStorage.setItem(key, value); }; //獲取數(shù)據(jù) export const getItem = (key) => { const data = window.localStorage.getItem(key); //這里使用try..catch的,而不是通過(guò)if判斷一下是否為json格式的字符串,然后在通過(guò)parse進(jìn)行轉(zhuǎn)換呢,目的就是是為了方便處理,因?yàn)閷?duì)字符串進(jìn)行判斷看一下是否為json格式的字符串,比較麻煩一些。還需要通過(guò)正則表達(dá)式來(lái)完成。而通過(guò)try..catch比較方便 // 如果data不是一個(gè)有效的json格式字符串,JSON.parse就會(huì)出錯(cuò)。 try { return JSON.parse(data); } catch (e) { return data; } }; //刪除數(shù)據(jù) export const removeItem = (key) => { window.localStorage.removeItem(key);
下面返回到store/index.js文件中,修改對(duì)應(yīng)的代碼,這里使用我們上面封裝好的代碼。
import { createStore } from "vuex"; import { getItem, setItem } from "../utils/storage"; const TOKEN_KEY = "TOUTIAO_USER"; const store = createStore({ state: { //存儲(chǔ)當(dāng)前登錄用戶(hù)信息,包含token等數(shù)據(jù) // user: null, // user: JSON.parse(window.localStorage.getItem(TOKEN_KEY)), user: getItem(TOKEN_KEY), }, mutations: { setUser(state, data) { state.user = data; setItem(TOKEN_KEY, state.user); // window.localStorage.setItem(TOKEN_KEY, JSON.stringify(state.user)); }, }, }); export default store;
在上面的代碼中,我們導(dǎo)入getItem和setItem兩個(gè)方法,然后在存儲(chǔ)登錄用戶(hù)信息,和獲取登錄用戶(hù)信息的時(shí)候,直接使用這兩個(gè)方法,這樣就非常簡(jiǎn)單了。
下面返回瀏覽器進(jìn)行測(cè)試。
把以前l(fā)ocalStorage中存儲(chǔ)的內(nèi)容刪除掉。
然后重新輸入用戶(hù)名和密碼,發(fā)現(xiàn)對(duì)應(yīng)的localStorage中存儲(chǔ)了對(duì)應(yīng)的數(shù)據(jù)。
以上就是Vue3+Vant實(shí)現(xiàn)簡(jiǎn)單的登錄功能的詳細(xì)內(nèi)容,更多關(guān)于Vue3 Vant登錄的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解Vue 動(dòng)態(tài)組件與全局事件綁定總結(jié)
這篇文章主要介紹了詳解Vue 動(dòng)態(tài)組件與全局事件綁定總結(jié),從示例中發(fā)現(xiàn)并解決問(wèn)題,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-11-11vue.js實(shí)例對(duì)象+組件樹(shù)的詳細(xì)介紹
這篇文章主要介紹了vue.js實(shí)例對(duì)象+組件樹(shù)的相關(guān)資料,需要的朋友可以參考下2017-10-10Vue 監(jiān)聽(tīng)列表item渲染事件方法
今天小編就為大家分享一篇Vue 監(jiān)聽(tīng)列表item渲染事件方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-09-09Vue中使用require.context自動(dòng)引入組件的操作方法
require.context?是?webpack?提供的一個(gè)API,用于創(chuàng)建context,即一組具有相同上下文的模塊,使用?require.context?可以方便地加載多個(gè)模塊,并且可以靈活地控制模塊的加載順序和依賴(lài)關(guān)系,本文給大家講解Vue中使用require.context自動(dòng)引入組件的方法,感興趣的朋友一起看看吧2024-01-01Vue子組件內(nèi)的props對(duì)象參數(shù)配置方法
這篇文章主要介紹了?Vue?子組件內(nèi)的??props?對(duì)象里的?default?參數(shù)是如何定義Array、?Object?、或?Function?默認(rèn)值的正確寫(xiě)法說(shuō)明,感興趣的朋友跟隨小編一起看看吧2022-08-08Vue使用json-server進(jìn)行后端數(shù)據(jù)模擬功能
這篇文章主要介紹了Vue使用json-server進(jìn)行后端數(shù)據(jù)模擬功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2018-04-04關(guān)于vue 的slot分發(fā)內(nèi)容 (多個(gè)分發(fā))
這篇文章主要介紹了關(guān)于vue 的slot分發(fā)內(nèi)容 (多個(gè)分發(fā)),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03