uni-app微信小程序登录流程详解

发布于:2025-05-14 ⋅ 阅读:(10) ⋅ 点赞:(0)

uni-app微信小程序登录流程实战详解

作为前端开发者,理解微信小程序的登录流程对构建用户认证系统至关重要。本文将从前端开发角度,详细介绍如何在uni-app框架下实现微信小程序的登录流程,包括获取登录凭证、发送登录请求、管理登录状态等关键环节。

微信小程序登录流程概述

微信小程序的前端登录流程主要包括以下几个步骤:

  1. 调用 uni.login() 获取登录凭证(code)
  2. 将 code 发送到自己的业务服务器
  3. 接收服务器返回的登录态(token)
  4. 保存并管理登录态
  5. 在后续请求中使用登录态

接下来,我们将详细解析每个步骤的实现。

1. 获取登录凭证(code)

在小程序启动时,我们首先需要调用 uni.login() 方法获取登录凭证(code):

// 获取微信登录凭证
function getWxLoginCode() {
  return new Promise((resolve, reject) => {
    uni.login({
      provider: 'weixin',
      success: (res) => {
        if (res.code) {
          console.log('登录凭证获取成功');
          resolve(res.code);
        } else {
          console.error('登录凭证获取失败:', res.errMsg);
          reject(new Error(res.errMsg));
        }
      },
      fail: (err) => {
        console.error('登录接口调用失败:', err);
        reject(err);
      }
    });
  });
}

这段代码封装了 uni.login() 方法,返回一个 Promise 对象,便于我们使用 async/await 语法处理异步流程。

2. 发送登录请求

获取到 code 后,我们需要将其发送到自己的业务服务器,以换取自定义登录态:

// 发送登录请求
async function login() {
  try {
    // 显示加载提示
    uni.showLoading({
      title: '登录中...'
    });
    
    // 获取微信登录凭证
    const code = await getWxLoginCode();
    
    // 发送登录请求
    const result = await request({
      url: '/api/user/login',
      method: 'POST',
      data: { code }
    });
    
    // 处理登录结果
    if (result.code === 0 && result.data) {
      // 登录成功,保存登录态
      saveLoginState(result.data);
      return result.data;
    } else {
      // 登录失败
      uni.showToast({
        title: result.msg || '登录失败',
        icon: 'none'
      });
      return null;
    }
  } catch (error) {
    // 处理错误
    console.error('登录过程发生错误:', error);
    uni.showToast({
      title: '登录失败,请重试',
      icon: 'none'
    });
    return null;
  } finally {
    // 隐藏加载提示
    uni.hideLoading();
  }
}

这段代码完成了以下工作:

  • 调用 getWxLoginCode() 获取登录凭证
  • 将 code 发送到业务服务器
  • 处理服务器返回的登录结果
  • 提供用户友好的反馈(加载提示、错误提示)

3. 保存登录态

当服务器返回登录态(通常是 token)后,我们需要将其保存起来:

// 保存登录状态
function saveLoginState(loginData) {
  // 保存 token 到本地存储
  uni.setStorageSync('token', loginData.token);
  
  // 保存用户信息
  if (loginData.userInfo) {
    uni.setStorageSync('userInfo', loginData.userInfo);
  }
  
  // 记录登录时间
  uni.setStorageSync('loginTime', Date.now());
  
  console.log('登录状态已保存');
}

在这里,我们使用 uni-app 提供的存储 API 保存了 token、用户信息和登录时间。这些信息将在后续的请求和页面中使用。

4. 登录状态管理

为了有效管理登录状态,我们需要提供一套完整的工具函数:

// 检查是否已登录
function isLoggedIn() {
  return !!uni.getStorageSync('token');
}

// 获取当前token
function getToken() {
  return uni.getStorageSync('token') || '';
}

// 获取用户信息
function getUserInfo() {
  return uni.getStorageSync('userInfo') || null;
}

// 清除登录状态
function clearLoginState() {
  uni.removeStorageSync('token');
  uni.removeStorageSync('userInfo');
  uni.removeStorageSync('loginTime');
}

// 检查登录是否过期
function isLoginExpired() {
  const loginTime = uni.getStorageSync('loginTime') || 0;
  const currentTime = Date.now();
  // 假设登录有效期为7天
  const expiresIn = 7 * 24 * 60 * 60 * 1000;
  return currentTime - loginTime > expiresIn;
}

// 刷新登录状态
async function refreshLogin() {
  if (isLoginExpired()) {
    clearLoginState();
    return await login();
  }
  return true;
}

这组函数提供了:

  • 检查用户是否已登录
  • 获取 token 和用户信息
  • 清除登录状态
  • 检查登录是否过期
  • 刷新登录状态

5. 应用登录状态

有了上述工具函数,我们需要在应用中合理使用它们:

请求拦截器中添加 token

// 请求拦截器
const request = (options) => {
  // 克隆原始选项
  const requestOptions = {
    ...options,
    header: options.header || {}
  };
  
  // 添加 token 到请求头
  const token = getToken();
  if (token) {
    requestOptions.header.Authorization = `Bearer ${token}`;
  }
  
  // 发送请求
  return new Promise((resolve, reject) => {
    uni.request({
      ...requestOptions,
      success: (res) => {
        // 检查响应状态
        if (res.statusCode === 200) {
          resolve(res.data);
        } else if (res.statusCode === 401) {
          // token 无效或过期
          handleUnauthorized();
          reject(new Error('未授权'));
        } else {
          reject(res);
        }
      },
      fail: (err) => {
        reject(err);
      }
    });
  });
};

// 处理未授权情况
function handleUnauthorized() {
  // 清除登录状态
  clearLoginState();
  
  // 提示用户
  uni.showToast({
    title: '登录已过期,请重新登录',
    icon: 'none'
  });
  
  // 跳转到登录页
  setTimeout(() => {
    uni.navigateTo({
      url: '/pages/login/login'
    });
  }, 1500);
}

这个请求拦截器确保了:

  • 每个请求都带上 token
  • 处理 401 未授权响应
  • 在 token 无效时引导用户重新登录

自动登录

在应用启动时,我们可以实现自动登录功能:

// App.vue 的 onLaunch 钩子
export default {
  async onLaunch() {
    // 检查本地是否有登录态
    if (isLoggedIn()) {
      // 检查登录是否过期
      if (isLoginExpired()) {
        console.log('登录已过期,尝试重新登录');
        await login();
      } else {
        console.log('已登录状态');
      }
    } else {
      console.log('未登录状态');
      await login();
    }
  }
}

这段代码在应用启动时检查登录状态,并在必要时进行自动登录。

页面路由守卫

在需要登录才能访问的页面,我们可以添加路由守卫:

// 需要登录的页面
export default {
  onLoad() {
    this.checkLogin();
  },
  methods: {
    checkLogin() {
      if (!isLoggedIn()) {
        uni.showToast({
          title: '请先登录',
          icon: 'none'
        });
        
        // 跳转到登录页
        setTimeout(() => {
          uni.navigateTo({
            url: '/pages/login/login'
          });
        }, 1500);
        
        return false;
      }
      return true;
    }
  }
}

使用 Vuex 集中管理登录状态

为了更好地管理状态,我们可以使用 Vuex:

// store/modules/user.js
const state = {
  token: uni.getStorageSync('token') || '',
  userInfo: uni.getStorageSync('userInfo') || null
};

const mutations = {
  SET_TOKEN(state, token) {
    state.token = token;
    uni.setStorageSync('token', token);
  },
  SET_USER_INFO(state, userInfo) {
    state.userInfo = userInfo;
    uni.setStorageSync('userInfo', userInfo);
  },
  CLEAR_USER(state) {
    state.token = '';
    state.userInfo = null;
    uni.removeStorageSync('token');
    uni.removeStorageSync('userInfo');
    uni.removeStorageSync('loginTime');
  }
};

const actions = {
  // 登录
  async login({ commit }) {
    try {
      uni.showLoading({ title: '登录中...' });
      
      // 获取微信登录凭证
      const code = await getWxLoginCode();
      
      // 发送登录请求
      const result = await request({
        url: '/api/user/login',
        method: 'POST',
        data: { code }
      });
      
      if (result.code === 0 && result.data) {
        // 保存登录状态
        commit('SET_TOKEN', result.data.token);
        commit('SET_USER_INFO', result.data.userInfo);
        uni.setStorageSync('loginTime', Date.now());
        
        return result.data;
      } else {
        uni.showToast({
          title: result.msg || '登录失败',
          icon: 'none'
        });
        return null;
      }
    } catch (error) {
      console.error('登录失败', error);
      uni.showToast({
        title: '登录失败,请重试',
        icon: 'none'
      });
      return null;
    } finally {
      uni.hideLoading();
    }
  },
  
  // 登出
  logout({ commit }) {
    commit('CLEAR_USER');
    uni.reLaunch({
      url: '/pages/login/login'
    });
  },
  
  // 检查登录状态
  checkLogin({ state, dispatch }) {
    if (!state.token) {
      return false;
    }
    
    const loginTime = uni.getStorageSync('loginTime') || 0;
    const currentTime = Date.now();
    const expiresIn = 7 * 24 * 60 * 60 * 1000;
    
    if (currentTime - loginTime > expiresIn) {
      // 登录过期,重新登录
      return dispatch('login');
    }
    
    return true;
  }
};

export default {
  namespaced: true,
  state,
  mutations,
  actions
};

使用 Vuex 管理登录状态有以下优势:

  • 集中管理状态
  • 状态变化更加可预测
  • 方便组件之间共享状态
  • 提供统一的 API 接口

登录组件示例

最后,我们来看一个完整的登录组件示例:

<!-- pages/login/login.vue -->
<template>
  <view class="login-container">
    <image class="logo" src="/static/logo.png"></image>
    <view class="welcome">欢迎使用向明天小程序</view>
    
    <button 
      class="login-btn" 
      type="primary" 
      :loading="loading"
      @click="handleLogin"
    >
      微信一键登录
    </button>
    
    <view class="tips">登录后即可使用完整功能</view>
  </view>
</template>

<script>
import { mapActions } from 'vuex';

export default {
  data() {
    return {
      loading: false
    };
  },
  methods: {
    ...mapActions('user', ['login']),
    
    async handleLogin() {
      this.loading = true;
      
      try {
        const result = await this.login();
        
        if (result) {
          uni.showToast({
            title: '登录成功',
            icon: 'success'
          });
          
          // 登录成功后跳转
          setTimeout(() => {
            const redirectUrl = uni.getStorageSync('redirectUrl') || '/pages/index/index';
            uni.reLaunch({
              url: redirectUrl
            });
            uni.removeStorageSync('redirectUrl');
          }, 1500);
        }
      } catch (error) {
        console.error('登录操作失败', error);
      } finally {
        this.loading = false;
      }
    }
  }
};
</script>

<style>
.login-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 60rpx;
  height: 100vh;
  background-color: #f8f8f8;
}

.logo {
  width: 200rpx;
  height: 200rpx;
  margin-bottom: 40rpx;
}

.welcome {
  font-size: 36rpx;
  font-weight: bold;
  margin-bottom: 80rpx;
  color: #333;
}

.login-btn {
  width: 80%;
  height: 88rpx;
  line-height: 88rpx;
  border-radius: 44rpx;
  font-size: 32rpx;
  background-color: #07c160;
}

.tips {
  margin-top: 40rpx;
  font-size: 28rpx;
  color: #999;
}
</style>

登录流程最佳实践

  1. 静默登录:在用户无感知的情况下完成登录流程,提升用户体验。

  2. 登录状态检查:在应用启动和页面切换时检查登录状态,确保用户处于登录状态。

  3. 错误处理:提供友好的错误提示,引导用户成功完成登录。

  4. 登录重试:在登录失败时提供重试机制。

  5. 登录过期处理:及时处理登录过期情况,引导用户重新登录。

  6. 登录态持久化:使用适当的存储方式保存登录态,确保用户下次打开应用时能够保持登录状态。

  7. 登录态安全:不在前端存储敏感信息,设置合理的 token 过期时间。

总结

作为前端开发者,实现微信小程序登录流程需要关注以下关键点:

  1. 正确调用 uni.login() 获取登录凭证
  2. 将登录凭证发送到业务服务器换取登录态
  3. 安全地保存和管理登录态
  4. 在网络请求中使用登录态
  5. 处理登录过期和未授权情况
  6. 优化用户登录体验

通过本文介绍的方法和最佳实践,你应该能够在 uni-app 项目中实现一个完整、安全、用户友好的微信小程序登录流程。

希望这篇文章对你实现微信小程序登录功能有所帮助!