微信小程序登录popup,以及整个登录流程

所属分类:HtmlCssJs | 浏览:26 | 发布于 2026-05-11

先了解一下,微信小程序 Component 多单词命名规范(官方 + 行业最佳实践)

  • 组件标签名短横线 kebab-case(必须)
  • 文件 / 文件夹名短横线 kebab-case(推荐)
  • JS 内部构造名大驼峰 PascalCase(推荐)

1、自定义登录Component组件 login-popup

1.1、组件目录

components/
└─ login-popup/
   ├─ login-popup.js
   ├─ login-popup.json
   ├─ login-popup.wxml
   └─ login-popup.wxss

1.2、login-popup.wxml

<!-- 遮罩 -->
<view class="login-mask {{show ? 'mask-show' : ''}}" bindtap="close">
  <!-- 底部弹窗容器 带动画 -->
  <view class="login-popup-wrap {{show ? 'popup-show' : ''}}" catchtap="preventTouch">
    <view class="login-header">
        <!-- 顶部圆角拖拽条 -->
        <view class="drag-bar"></view>

        <view class="login-title">快捷登录</view>
    </view>
    <view class="login-content">
      
        <text class="tip-message">为优先保存您的数据,建议您登录</text>
      <!-- 微信授权登录(头像昵称) -->
      <button class="wx-login-btn" bindtap="wxAuthorLogin">
        微信账号登录
      </button>

      <!-- 微信一键获取手机号登录 -->
      <!-- <button class="phone-login-btn" open-type="getPhoneNumber" bindgetphonenumber="wxGetPhone">
        手机号一键登录
      </button> -->

      <!-- 协议勾选 -->
      <view class="agree-box">
        <checkbox class="checkbox" checked="{{isAgree}}" bindtap="toggleAgree" />
        <view class="agree-text">
          我已阅读并同意
          <text class="link" bindtap="goUserAgree">《用户协议》</text>
          及
          <text class="link" bindtap="goPrivacy">《隐私政策》</text>
        </view>
      </view>
    </view>
    <!-- 适配底部安全区 -->
    <view class="safe-area"></view>
  </view>
</view>

1.3、login-popup.wxss

/* components/login-popup/login-popup.wxss */
/* 遮罩 */
.login-mask {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: 9999;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.3s ease, visibility 0.3s ease;
  }
  .login-mask.mask-show {
    opacity: 1;
    visibility: visible;
  }
  
  /* 底部弹窗容器 初始在屏幕外下方 */
  .login-popup-wrap {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    background: #ffffff;
    border-radius: 24rpx 24rpx 0 0;
    transform: translateY(100%);
    transition: transform 0.3s ease-out;
    z-index: 10000;
  }
  /* 滑入动画 */
  .login-popup-wrap.popup-show {
    transform: translateY(0);
  }
  

  
  .login-header {
  }
    /* 顶部拖拽条 */
    .drag-bar {
        width: 80rpx;
        height: 8rpx;
        background: #e5e5e5;
        border-radius: 4rpx;
        margin: 20rpx auto;
      }

/* 标题 */
.login-title {
    font-size: 36rpx;
    font-weight: bold;
    text-align: center;
    margin-bottom: 20rpx;
    color: #333;
    background-color: #ffffff;
  }

  .login-content {
    padding: 40rpx 40rpx 20rpx;
    background-color: #f5f5f5;
  }
  
  .tip-message {
    display: block;
    color: #C6c6c6;
    text-align: center;
    font-size: 28rpx;
    padding-bottom: 20rpx;
  }
  
  /* 微信账号登录按钮 */
  .wx-login-btn {
    background: #07c160;
    color: #fff;
    border-radius: 10rpx;
    font-size: 32rpx;
    margin: 0 0 30rpx;
  }
  .wx-login-btn::after {
    border: none;
  }
  
  /* 手机号一键登录按钮 */
  .phone-login-btn {
    background: #fff;
    color: #07c160;
    border: 1rpx solid #07c160;
    border-radius: 10rpx;
    font-size: 32rpx;
    margin: 0 0 40rpx;
  }
  .phone-login-btn::after {
    border: none;
  }
  
  /* 协议区域 */
  .agree-box {
    display: flex;
    align-items: center;
    justify-content: center;
    margin-top: 20rpx;
  }
  .checkbox {
    transform: scale(0.7);
    margin-right: 10rpx;
  }
  .agree-text {
    font-size: 24rpx;
    color: #666;
    line-height: 1.5;
  }
  .link {
    color: #07c160;
  }
  
  /* 底部安全区适配 */
  .safe-area {
    height: env(safe-area-inset-bottom);
    background: #f5f5f5;
  }

1.4、login-popup.js(核心:获取手机号 + 授权逻辑)

获取手机号如果不需要的话,可以忽略

// components/login-popup/login-popup.js
const app = getApp()

Component({
  properties: {
    show: {
      type: Boolean,
      value: false
    }
  },

  data: {
    isAgree: false
  },

  methods: {
    // 阻止冒泡
    preventTouch() {},

    // 关闭弹窗
    close() {
      app.$closeLogin()
      setTimeout(() => {
        this.setData({
          isAgree: false
        })
      }, 300)
    },

    // 切换协议勾选
    toggleAgree() {
      this.setData({
        isAgree: !this.data.isAgree
      })
    },

    // 跳转协议页面
    goUserAgree() {
      wx.navigateTo({ url: '/pages/agreement/agreement' })
    },
    goPrivacy() {
      wx.navigateTo({ url: '/pages/privacy/privacy' })
    },

    // 微信授权 获取昵称头像
    wxAuthorLogin() {
      if (!this.data.isAgree) {
        wx.showToast({ title: '请先勾选同意协议', icon: 'none' })
        return
      }
      wx.getUserProfile({
        desc: '用于完善用户个人资料',
        success: (res) => {
          wx.login({
            success: loginRes => {
              // 回调给页面:微信账号登录
              app.globalData.loginCallback?.({
                type: 'userInfo',
                userInfo: res.userInfo,
                code: loginRes.code
              })
              this.close()
            }
          })
        },
        fail: () => {
          wx.showToast({ title: '授权已取消', icon: 'none' })
        }
      })
    },

    // 微信一键获取手机号
    wxGetPhone(e) {
      if (!this.data.isAgree) {
        wx.showToast({ title: '请先勾选同意协议', icon: 'none' })
        return
      }

      // 用户拒绝授权手机号
      if (e.detail.errMsg !== 'getPhoneNumber:ok') {
        wx.showToast({ title: '获取手机号失败', icon: 'none' })
        return
      }

      // 拿到加密的 phoneCode,传给后端解密拿真实手机号
      const phoneCode = e.detail.code
      wx.login({
        success: loginRes => {
          app.globalData.loginCallback?.({
            type: 'phoneNumber',
            phoneCode: phoneCode,
            wxCode: loginRes.code
          })
          this.close()
        }
      })
    }
  }
})

1.5、login-popup.json

{
  "component": true,
  "usingComponents": {}
}

1.6、app.json 全局注册

{
  "usingComponents": {
    "login-popup": "/components/login-popup/login-popup"
  }
}

1.7、app.js 全局方法

App({
  globalData: {
    loginCallback: null
  },

  $login(callback) {
    this.globalData.loginCallback = callback
    const pages = getCurrentPages()
    const currPage = pages[pages.length - 1]
    currPage.setData({ showGlobalLogin: true })
  },

  $closeLogin() {
    const pages = getCurrentPages()
    const currPage = pages[pages.length - 1]
    currPage.setData({ showGlobalLogin: false })
  }
})

1.8、页面调用

页面 wxml 加一行即可:

<login-popup show="{{showGlobalLogin}}" />

1.9、页面Js调用

// 打开登录弹窗
openLogin() {
  getApp().$login(res => {
    console.log('登录回调结果', res)
    if (res.type === 'userInfo') {
      // 微信登录:res.userInfo、res.code
      console.log('微信用户信息', res.userInfo)
      console.log('wx.login code', res.code)
      // 传给后端登录
    } else {
      // 手机号验证码登录
      console.log('手机号', res.phone, '验证码', res.code)
    }
  })
}

2、小程序登录流程

2.1、登录完成流程(时序)

  • 小程序前端:调用 wx.login(),拿到临时 code(5 分钟有效期、一次性)。
  • 前端→开发者服务器:把 code 发给自己后端。
  • 开发者服务器→微信接口:请求 jscode2session,参数 appid+secret+code+grant_type=authorization_code
    • 微信返回:openid(用户唯一标识)、session_key(会话密钥,不可下发前端)、unionid(开放平台绑定后才有)。
     
  • 服务器生成登录态:用 openid 生成自定义 token(如 JWT),关联用户会话。
  • 服务器→前端:返回 token,前端存到 localStoragecookie
  • 后续请求:前端带 token 访问业务接口,服务器验证 token 识别用户。

2.2、关键概念

  • code:临时凭证,前端获取,一次性有效。
  • openid:用户在当前小程序的唯一 ID,不可跨小程序
  • unionid:用户在开放平台下的唯一 ID,可跨小程序 / 公众号(需绑定开放平台)。
  • session_key:微信加密密钥,用于解密用户敏感数据(如手机号),仅后端存储

2.3、常见变体

  • 静默登录:只拿 openid,不获取用户信息,自动完成。
  • 用户信息授权:调用 wx.getUserProfile,前端拿到用户昵称 / 头像,后端用 session_key 校验签名。
  • 手机号登录:调用 wx.getPhoneNumber 拿到加密数据,后端用 session_key 解密获取手机号。

2.4、安全要点

  • code 只能用一次,防止重放攻击。
  • session_key 绝不返回前端,避免泄露。
  • token 建议短期有效,配合刷新机制。

3、登录(获取昵称/头像) | 退出 功能实现

上面登录弹窗已经写好了,流程也清楚了,下面开始写登录及退出功能,由于登录可能在任意页面,其它页面页有可能要判断登录状态,所以登录功能要写成全局的。

3.1、app.js

在globalData中新增userInfo字段,提供setUserInfo(userInfo)用户设置用户信息到本地,提供logout()函数用于退出登录

globalData: {
        userInfo: null,
        loginCallback: null
    },

onLaunch() {
        // 小程序启动时,读取本地缓存,初始化全局用户信息
        const storageUser = wx.getStorageSync('userInfo')
        if (storageUser) {
            this.globalData.userInfo = storageUser
        }
    },

    // 登录:设置用户信息
    setUserInfo(userInfo) {
        this.globalData.userInfo = userInfo;
        console.log(userInfo);
        wx.setStorageSync('userInfo', userInfo);
        this._refreshAllPages();
    },

    // 退出登录:设置为 null
    logout() {
        this.globalData.userInfo = null;
        wx.removeStorageSync('userInfo');
        this._refreshAllPages();
    },

    // 刷新所有页面(核心:自动更新 WXML)
    _refreshAllPages() {
        const pages = getCurrentPages();
        const user = this.globalData.userInfo;

        pages.forEach(page => {
            if (page.setData) {
                page.setData({
                    userInfo: user
                });
            }
        });
    },

3.2、utils/watchUserInfo.js

这个js是用来观测用户信息变化的,这样可以及时更新用户状态

export function watchUserInfo(page) {
  const app = getApp();
  const oldOnShow = page.onShow || function () {};

  page.onShow = function () {
    this.setData({
      userInfo: app.globalData.userInfo
    });
    oldOnShow.call(this);
  };

  return page;
}

3.3、页面js调用

import { watchUserInfo } from '../../utils/watchUserInfo.js';

Page(watchUserInfo({
  data: {},

  getUserProfile() {
    wx.getUserProfile({
      desc: '登录',
      success: (res) => {
        console.log('微信返回:', res.userInfo);
        const app = getApp();
        app.setUserInfo(res.userInfo);
      }
    });
  },

  logout() {
    const app = getApp();
    app.logout();
  }
}));

3.4、结合 login-popup 组件调用

上面3.3的代码没有结合 login-popup 组件,这里结合 第一部分写的login-popup组件来登录

// 点击用户信息卡片,没有登录时,弹出登录窗;已登录状态则跳转到个人信息页面
    tapUserCard() {
        const app = getApp()
        if (app.globalData.userInfo) {
            console.log("has logined") // 可调转到个人资料页面,也可啥都不做直接返回
        } else {
            getApp().$login((res) => {
                console.log('登录成功', res)

                if (res.type === 'userInfo') {
                    // ✅ 只需要这一句!
                    // 全局更新 + 本地缓存 + 当前页面自动刷新,这是最新版的
                    app.setUserInfo(res.userInfo);
                }

                if (res.type === 'phone') {
                    // 手机号登录
                }
            })
        }
    },

 

 

 

海涛博客(https://haitaoblog.com)属于海涛个人博客,欢迎浏览使用

联系方式:qq:52292959 邮箱:52292959@qq.com W22

备案号:粤ICP备18108585号 友情链接