我们来详细讲解如何使用 Vue 3 开发 Web 端的这三种核心登录方式:微信扫码登录、手机验证码登录、账号密码登录

我将提供清晰的代码结构、关键代码片段以及详尽的讲解,帮助你理解整个流程。

项目结构与准备工作

假设我们有一个 Vue 3 项目(使用 Vite 创建),并安装了 axios 用于网络请求,vue-router 用于路由管理。

src/
|-- api/
|   |-- login.js       # 封装所有登录相关的 API 请求
|-- views/
|   |-- Login.vue      # 登录页面组件
|-- router/
|   |-- index.js       # 路由配置
|-- store/
|   |-- user.js        # (可选) 使用 Pinia 或 Vuex 管理用户状态
|-- main.js
1. 后端接口约定

在开始前端开发前,你需要和后端约定好以下接口:

  • 微信登录:
    • GET /api/wechat/login/url: 获取微信登录二维码的跳转 URL。
    • POST /api/wechat/login/callback: 微信扫码成功后,后端接收 codestate 进行处理,并返回 token
  • 手机验证码登录:
    • POST /api/sms/send: 发送短信验证码,需要手机号作为参数。
    • POST /api/sms/login: 使用手机号和验证码进行登录,返回 token
  • 账号密码登录:
    • POST /api/account/login: 使用账号和密码进行登录,返回 token
2. api/login.js 封装请求

将所有网络请求封装起来,便于管理和复用。

// src/api/login.js
import axios from 'axios'; // 假设你已配置好 axios 实例

// 获取微信登录二维码 URL
export const getWechatLoginUrl = () => {
    return axios.get('/api/wechat/login/url');
};

// 发送短信验证码
export const sendSmsCode = (phone) => {
    return axios.post('/api/sms/send', { phone });
};

// 手机验证码登录
export const loginBySms = (phone, code) => {
    return axios.post('/api/sms/login', { phone, code });
};

// 账号密码登录
export const loginByAccount = (username, password) => {
    return axios.post('/api/account/login', { username, password });
};

views/Login.vue 组件实现

这是我们的核心登录页面,它将包含三种登录方式的切换和逻辑处理。

<template> 部分
<template>
  <div class="login-container">
    <div class="login-box">
      <div class="tabs">
        <span @click="activeTab = 'account'" :class="{ active: activeTab === 'account' }">账号登录</span>
        <span @click="activeTab = 'sms'" :class="{ active: activeTab === 'sms' }">短信登录</span>
        <span @click="activeTab = 'wechat'" :class="{ active: active-tab === 'wechat' }">微信登录</span>
      </div>

      <!-- 账号密码登录表单 -->
      <div v-if="activeTab === 'account'" class="form-section">
        <input type="text" v-model="accountForm.username" placeholder="请输入账号" />
        <input type="password" v-model="accountForm.password" placeholder="请输入密码" />
        <button @click="handleAccountLogin">登录</button>
      </div>

      <!-- 手机验证码登录表单 -->
      <div v-if="activeTab === 'sms'" class="form-section">
        <input type="text" v-model="smsForm.phone" placeholder="请输入手机号" />
        <div class="sms-code-wrapper">
          <input type="text" v-model="smsForm.code" placeholder="请输入验证码" />
          <button @click="handleSendSms" :disabled="isCountingDown">
            {{ countdownText }}
          </button>
        </div>
        <button @click="handleSmsLogin">登录</button>
      </div>

      <!-- 微信扫码登录 -->
      <div v-if="activeTab === 'wechat'" class="wechat-section">
        <p>请使用微信扫描二维码登录</p>
        <div id="wechat-login-container"></div>
      </div>
    </div>
  </div>
</template>
<style> 部分 (简单样式)
<style scoped>
.login-container {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
}
.login-box {
  width: 350px;
  padding: 20px;
  border: 1px solid #ccc;
  border-radius: 8px;
}
.tabs {
  display: flex;
  justify-content: space-around;
  margin-bottom: 20px;
}
.tabs span {
  cursor: pointer;
  padding-bottom: 5px;
}
.tabs span.active {
  color: #409eff;
  border-bottom: 2px solid #409eff;
}
.form-section input {
  width: 100%;
  padding: 10px;
  margin-bottom: 15px;
  box-sizing: border-box;
}
.sms-code-wrapper {
  display: flex;
}
.sms-code-wrapper input {
  flex-grow: 1;
}
.sms-code-wrapper button {
  margin-left: 10px;
}
.wechat-section {
  text-align: center;
}
#wechat-login-container {
  display: inline-block; /* 必须,否则 WxLogin 可能无法正确渲染 */
}
</style>
<script setup> 部分 (Vue 3 Composition API)

这是所有逻辑的核心。

<script setup>
import { ref, reactive, watch, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { getWechatLoginUrl, sendSmsCode, loginBySms, loginByAccount } from '@/api/login';
// 引入微信 JS-SDK,你可能需要先 npm install weixin-js-sdk
// 对于扫码登录,我们使用微信官方提供的 JS 构造器
// 需要在 index.html 中引入: <script src="http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"></script>

const router = useRouter();
const activeTab = ref('account');

// --- 账号密码登录 ---
const accountForm = reactive({
  username: '',
  password: '',
});

const handleAccountLogin = async () => {
  if (!accountForm.username || !accountForm.password) {
    alert('请输入账号和密码');
    return;
  }
  try {
    const res = await loginByAccount(accountForm.username, accountForm.password);
    handleLoginSuccess(res.data.token);
  } catch (error) {
    console.error('账号登录失败:', error);
    alert('账号或密码错误');
  }
};

// --- 手机验证码登录 ---
const smsForm = reactive({
  phone: '',
  code: '',
});
const isCountingDown = ref(false);
const countdown = ref(60);
const countdownText = ref('获取验证码');
let timer = null;

const handleSendSms = async () => {
  if (!/^1[3-9]\d{9}$/.test(smsForm.phone)) {
    alert('请输入正确的手机号');
    return;
  }
  isCountingDown.value = true;
  countdownText.value = `${countdown.value}s后重试`;
  timer = setInterval(() => {
    countdown.value--;
    countdownText.value = `${countdown.value}s后重试`;
    if (countdown.value <= 0) {
      clearInterval(timer);
      isCountingDown.value = false;
      countdown.value = 60;
      countdownText.value = '获取验证码';
    }
  }, 1000);

  try {
    await sendSmsCode(smsForm.phone);
    alert('验证码已发送');
  } catch (error) {
    console.error('发送验证码失败:', error);
    alert('发送失败,请稍后重试');
    // 发送失败时也应该重置倒计时
    clearInterval(timer);
    isCountingDown.value = false;
    countdown.value = 60;
    countdownText.value = '获取验证码';
  }
};

const handleSmsLogin = async () => {
  if (!smsForm.phone || !smsForm.code) {
    alert('请输入手机号和验证码');
    return;
  }
  try {
    const res = await loginBySms(smsForm.phone, smsForm.code);
    handleLoginSuccess(res.data.token);
  } catch (error) {
    console.error('短信登录失败:', error);
    alert('登录失败,验证码错误或已过期');
  }
};

// --- 微信扫码登录 ---
const initWechatLogin = async () => {
  try {
    // 1. 从后端获取微信登录所需的参数
    const res = await getWechatLoginUrl();
    const { appid, scope, redirect_uri, state, style, href } = res.data;

    // 2. 使用 WxLogin 构造器生成二维码
    // WxLogin 是全局对象,来自我们引入的 JS 文件
    new WxLogin({
      self_redirect: false, // 是否内嵌二维码,false 表示跳转
      id: "wechat-login-container", // 容器 ID
      appid: appid,
      scope: scope,
      redirect_uri: redirect_uri, // 扫码成功后,微信会携带 code 和 state 跳转到这个地址
      state: state,
      style: style, // "black" or "white"
      href: href, // 自定义样式表的 URL
    });
  } catch (error) {
    console.error('初始化微信登录失败:', error);
  }
};

// 监听 tab 切换,当切换到微信登录时才初始化
watch(activeTab, (newVal) => {
  if (newVal === 'wechat') {
    // DOM 更新后再执行,确保容器已渲染
    nextTick(() => {
      initWechatLogin();
    });
  }
});

// --- 公共登录成功处理 ---
const handleLoginSuccess = (token) => {
  // 1. 保存 token (例如到 localStorage 或 Pinia/Vuex store)
  localStorage.setItem('token', token);
  
  // (可选) 可以在这里保存用户信息到 Pinia/Vuex
  // userStore.setUserInfo(...)

  // 2. 跳转到首页或其他页面
  router.push('/');
  alert('登录成功!');
};

// 处理微信登录回调
// 微信扫码成功后,会跳转到你在后端配置的 redirect_uri,
// 页面 URL 会变成这样:http://yourdomain.com/login-callback?code=xxx&state=yyy
// 你需要一个专门的页面或在当前页面处理这个回调
onMounted(() => {
    const urlParams = new URLSearchParams(window.location.search);
    const code = urlParams.get('code');
    const state = urlParams.get('state');

    if (code && state) {
        // 通常这里会调用后端的 callback 接口,用 code 换取 token
        // loginByWechatCode(code, state).then(res => handleLoginSuccess(res.data.token));
        // 这里只是演示,实际项目需要一个专门的回调处理逻辑或页面
        console.log('接收到微信回调:', { code, state });
    }
});

</script>

代码讲解与核心要点

1. 账号密码登录
  • 流程: 用户输入 -> 点击登录 -> 调用 loginByAccount API -> 成功后调用 handleLoginSuccess
  • 代码: handleAccountLogin 函数负责整个流程,非常直接。
2. 手机验证码登录
  • 流程:
    1. 用户输入手机号,点击“获取验证码”。
    2. 前端进行手机号格式校验。
    3. 调用 sendSmsCode API,后端发送短信。
    4. 前端启动一个 60 秒倒计时,防止用户频繁点击。
    5. 用户收到短信,输入验证码,点击登录。
    6. 调用 loginBySms API,后端校验验证码并完成登录。
  • 代码: handleSendSms 函数处理发送和倒计时逻辑。isCountingDowncountdownref 变量用于控制按钮状态和文本。
3. 微信扫码登录 (PC端)
  • 核心原理: 这是基于 OAuth 2.0 协议的。前端的角色是引导用户到微信的授权页面
  • 流程:
    1. 前端向后端请求参数: 前端不应暴露 AppID、AppSecret 等敏感信息。前端先调用 getWechatLoginUrl 接口,后端会返回初始化二维码所需的参数,包括 appidscope(授权范围)、redirect_uri(扫码成功后的回调地址)和 state(一个随机字符串,用于防止 CSRF 攻击)。
    2. 前端生成二维码:
      • 首先,在 public/index.html 中引入微信官方的 JS 文件:<script src="http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"></script>
      • 在 Vue 组件中,调用 new WxLogin({...}) 构造函数。它会在指定的容器 (id: "wechat-login-container") 内生成一个 iframe,里面就是微信的登录二维码。
    3. 用户扫码授权: 用户使用手机微信扫码,并在手机上确认登录。
    4. 微信重定向: 确认后,微信服务器会将页面重定向到你在后端配置的 redirect_uri,并在 URL 参数中附带 codestate
    5. 后端用 codetoken: 你的前端回调页面(或逻辑)拿到 code 后,需要再将其发送给后端(例如 POST /api/wechat/login/callback)。后端用这个 code 和你的 AppSecret 去请求微信服务器,换取用户的 access_tokenopenid,最终生成你自己系统的 token 并返回给前端。
  • 代码:
    • initWechatLogin 负责从后端获取参数并初始化二维码。
    • watch(activeTab, ...) 确保只有在用户切换到微信登录时才加载二维码,避免不必要的性能开销。
    • onMounted 中的代码演示了如何处理回调 URL 中的 codestate。在实际项目中,你可能需要一个专门的路由和页面来处理这个回调,用户体验会更好。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐