跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
TypeScriptNode.js大前端

UniApp 集成鸿蒙华为一键登录 Account Kit 实战

在 UniApp 项目中集成鸿蒙华为一键登录(Account Kit)的完整流程。文章涵盖了从华为开发者平台配置、插件结构设计、UTS 插件层核心实现(JS API 与原生按钮组件)、客户端 Vue 页面编写到后端 Express 验证示例的全链路开发指南。重点讲解了 authorizationCode 的安全流转机制、state 防 CSRF 措施、条件编译处理以及 OpenID 与 UnionID 的区别。通过封装通用插件,实现了用户无需手动输入即可快速获取手机号并完成登录,显著提升了鸿蒙生态下的用户体验与转化率。

DevOpsTeam发布于 2026/3/22更新于 2026/10/25120 浏览
UniApp 集成鸿蒙华为一键登录 Account Kit 实战

什么是华为一键登录

华为 Account Kit 提供的一键登录能力,本质上是这样一个流程:用户点击登录按钮 → 华为系统弹出授权面板,展示当前华为账号绑定的手机号(脱敏显示)→ 用户确认授权 → 客户端拿到一个 authorizationCode → 把这个 code 发给你的后端 → 后端拿 code 去华为服务器换取真实手机号 → 完成登录。

整个过程用户不需要手动输入任何信息,体验非常丝滑。

Account Kit 提供两种主要能力:

能力说明获取内容
华为账号授权登录获取用户基本资料昵称、头像、OpenID、UnionID
一键登录(quickLoginAnonymousPhone)获取手机号匿名手机号 + authorizationCode(后端换真实号码)

整体架构

先看整体的技术架构,理解各层之间的关系:

  • 服务端:Express 后端,处理 JWT Token 签发与手机号验证。
  • 鸿蒙原生层:jack-hwilogin 插件,包含 index.uts (JS API) 和 login.ets (原生按钮组件)。
  • 客户端:Vue 页面,通过 embed 标签或 JS API 调用。
  • 华为授权服务:提供 AccountKit - AuthenticationController 与 LoginWithHuaweiIDButton。

关键点在于:客户端永远拿不到真实手机号,只能拿到匿名手机号和授权码。真实手机号的获取必须走服务端,这是华为出于安全考虑的设计。

前置准备

在写代码之前,需要先在华为开发者平台完成一些配置工作:

  1. 登录 AppGallery Connect 创建你的应用。
  2. 在「API 管理」中开通 Account Kit(帐号服务)。
  3. 记录下你的 Client ID 和 Client Secret,后端验证要用。
  4. 配置应用的签名证书指纹(SHA-256),这个很重要,指纹不匹配的话授权页面拉不起来。

重点说一下容易忽略的:签名证书指纹一定要和你实际打包用的证书一致,开发阶段用的调试证书和发布证书的指纹是不同的,别搞混了。

插件结构

插件目录结构很简洁:

uni_modules/jack-hwilogin/ └── utssdk/ └── app-harmony/ ├── index.uts # JS API(huaweiLogin / getPhoneNumber) └── login.ets # 原生登录按钮组件(embed 方式) 

两个文件各司其职:index.uts 提供函数式调用,适合你想自定义按钮样式的场景;login.ets 封装了华为官方的登录按钮组件,通过 embed 标签嵌入 Vue 页面,样式合规且用户体验更好。

核心实现:UTS 插件层

JS API 方式 (index.uts)

这个文件导出两个核心函数:huaweiLogin(获取用户资料)和 getPhoneNumber(获取手机号,一键登录场景)。

import './login.ets'
import { authentication } from '@kit.AccountKit'
import { hilog } from '@kit.PerformanceAnalysisKit'
import { util } from '@kit.ArkTS'
import { BusinessError } from '@kit.BasicServicesKit'
import { common } from '@kit.AbilityKit'

const TAG = 'JackHWILogin'

// ========== 类型定义 ==========
interface HuaweiUserInfo {
  authorizationCode: string
  openID: string
  unionID: string
  nickName: string
  avatarUri: string
}

interface HuaweiLoginOptions {
  success: (data: HuaweiUserInfo) => void
  fail: (err: string) => void
}

interface PhoneInfo {
  authorizationCode: string
  openID: string
  unionID: string
  anonymousPhone?: string
  isLocalPhone?: boolean
}

interface GetPhoneOptions {
  success: (data: PhoneInfo) => void
  fail: (err: string) => void
}

/**
 * 华为账号登录 —— 获取用户基本信息
 */
export function huaweiLogin(options: HuaweiLoginOptions): void {
  try {
    const loginRequest = new authentication.HuaweiIDProvider().createAuthorizationWithHuaweiIDRequest()
    loginRequest.forceAuthorization = true
    loginRequest.scopes = ['profile']
    loginRequest.permissions = ['serviceauthcode']
    loginRequest.state = util.generateRandomUUID()
    loginRequest.nonce = util.generateRandomUUID()
    loginRequest.idTokenSignAlgorithm = authentication.IdTokenSignAlgorithm.PS256

    const context = getContext() as common.UIAbilityContext
    const controller = new authentication.AuthenticationController(context)

    controller.executeRequest(loginRequest, (error: BusinessError<Object>, data) => {
      if (error) {
        hilog.error(0x0000, TAG, `Login failed: ${JSON.stringify(error)}`)
        options.fail && options.fail(`登录失败:${error.message || '未知错误'}`)
        return
      }
      const response = data as authentication.AuthorizationWithHuaweiIDResponse
      // 验证 state,防止 CSRF 攻击
      if (response.state && loginRequest.state !== response.state) {
        hilog.error(0x0000, TAG, `State mismatch`)
        options.fail && options.fail('状态验证失败')
        return
      }
      const credential = response?.data
      const userInfo: HuaweiUserInfo = {
        authorizationCode: credential?.authorizationCode ?? '',
        openID: credential?.openID ?? '',
        unionID: credential?.unionID ?? '',
        nickName: credential?.nickName ?? '',
        avatarUri: credential?.avatarUri ?? ''
      }
      options.success && options.success(userInfo)
    })
  } catch (error) {
    hilog.error(0x0000, TAG, `Exception: ${error}`)
    options.fail && options.fail(`登录异常:${error}`)
  }
}

/**
 * 获取手机号 —— 一键登录场景
 */
export function getPhoneNumber(options: GetPhoneOptions): void {
  try {
    const loginRequest = new authentication.HuaweiIDProvider().createAuthorizationWithHuaweiIDRequest()
    loginRequest.forceAuthorization = true
    loginRequest.scopes = ['quickLoginAnonymousPhone']
    loginRequest.permissions = ['serviceauthcode']
    loginRequest.state = util.generateRandomUUID()

    const context = getContext() as common.UIAbilityContext
    const controller = new authentication.AuthenticationController(context)

    controller.executeRequest(loginRequest, (error: BusinessError<Object>, data) => {
      if (error) {
        hilog.error(0x0000, TAG, `Get phone failed: ${JSON.stringify(error)}`)
        options.fail && options.fail(`获取手机号失败:${error.message}`)
        return
      }
      const response = data as authentication.AuthorizationWithHuaweiIDResponse
      if (response.state && loginRequest.state !== response.state) {
        options.fail && options.fail('状态验证失败')
        return
      }
      const credential = response?.data
      const phoneInfo: PhoneInfo = {
        authorizationCode: credential?.authorizationCode ?? '',
        openID: credential?.openID ?? '',
        unionID: credential?.unionID ?? '',
        anonymousPhone: credential?.extraInfo?.quickLoginAnonymousPhone as string | undefined,
        isLocalPhone: credential?.extraInfo?.localNumberConsistency as boolean | undefined
      }
      options.success && options.success(phoneInfo)
    })
  } catch (error) {
    hilog.error(0x0000, TAG, `Exception: ${error}`)
    options.fail && options.fail(`获取手机号异常:${error}`)
  }
}

说几个关键设计点:

  1. scopes 的区别是两个函数最核心的差异。huaweiLogin 用的是 ['profile'],请求用户的昵称头像等基本资料;getPhoneNumber 用的是 ['quickLoginAnonymousPhone'],请求手机号授权。两者返回的数据结构不同,所以我分成了两个独立函数,调用方按需选择。
  2. state 防 CSRF 是个容易被忽略但很重要的安全措施。每次请求我都用 util.generateRandomUUID() 生成一个随机 state,回调时校验 response 里的 state 是否一致。如果不一致,说明响应可能被篡改,直接拒绝。
  3. forceAuthorization = true 表示每次都强制拉起授权页面,即使用户之前已经授权过。这样做的好处是每次都能拿到新的 authorizationCode,避免 code 过期的问题。
原生按钮组件 (login.ets)

除了 JS API,我还封装了华为官方的 LoginWithHuaweiIDButton 组件。这个组件的好处是样式由华为官方提供,符合品牌规范,而且用户看到熟悉的华为登录按钮会更有信任感。

import { defineNativeEmbed, NativeEmbedBuilderOptions } from '@dcloudio/uni-app-runtime'
import { loginComponentManager, LoginWithHuaweiIDButton } from '@kit.AccountKit'
import { hilog } from '@kit.PerformanceAnalysisKit'
import { BusinessError } from '@kit.BasicServicesKit'

const TAG = 'HuaweiLoginButton'

interface LoginButtonOptions extends NativeEmbedBuilderOptions {}

interface LoginSuccessData {
  authorizationCode?: string
  unionID?: string
  openID?: string
  success: boolean
  err: string
}

interface LoginEvent {
  type: string
  detail: LoginSuccessData
}

@Component
struct HuaweiLoginButtonComponent {
  onSuccess?: Function
  onFail?: Function

  private controller: loginComponentManager.LoginWithHuaweiIDButtonController = new loginComponentManager.LoginWithHuaweiIDButtonController()
    .setAgreementStatus(loginComponentManager.AgreementStatus.ACCEPTED)
    .onClickLoginWithHuaweiIDButton((error: BusinessError, response: loginComponentManager.HuaweiIDCredential) => {
      if (error) {
        hilog.error(0x0000, TAG, `Login failed: ${error.code}${error.message}`)
        this.handleFail(`登录失败:${error.code}${error.message}`)
      } else {
        hilog.info(0x0000, TAG, `Login success`)
        this.handleSuccess(response)
      }
    })
    .onClickEvent((error: BusinessError, clickEvent: loginComponentManager.ClickEvent) => {
      hilog.info(0x0000, TAG, `Button clicked: ${clickEvent}`)
    })

  private handleSuccess(response: loginComponentManager.HuaweiIDCredential): void {
    if (this.onSuccess) {
      const event: LoginEvent = {
        type: 'success',
        detail: {
          authorizationCode: response.authorizationCode,
          unionID: response.unionID,
          openID: response.openID,
          success: true,
          err: 'ok'
        }
      }
      this.onSuccess(event)
    }
  }

  private handleFail(errorMsg: string): void {
    if (this.onFail) {
      const event: LoginEvent = {
        type: 'fail',
        detail: { success: false, err: errorMsg }
      }
      this.onFail(event)
    }
  }

  build() {
    LoginWithHuaweiIDButton({
      params: {
        style: loginComponentManager.Style.BUTTON_CUSTOM,
        loginType: loginComponentManager.LoginType.QUICK_LOGIN,
        supportDarkMode: true
      },
      controller: this.controller
    }).width('100%').height('100%')
  }
}

// 组件构建器
@Builder
function HuaweiLoginButtonBuilder(opts: LoginButtonOptions) {
  HuaweiLoginButtonComponent({
    onSuccess: opts?.on?.get('success'),
    onFail: opts?.on?.get('fail')
  }).width(opts.width).height(opts.height)
}

// 注册为原生嵌入组件,Vue 页面通过 <embed tag="hwilogin" /> 使用
defineNativeEmbed('hwilogin', { builder: HuaweiLoginButtonBuilder })

这里有个 uni-app 鸿蒙开发的知识点:defineNativeEmbed 是 uni-app 运行时提供的 API,用于将鸿蒙原生组件注册为可在 Vue 页面中通过 <embed> 标签使用的组件。tag 参数就是你在 Vue 模板里写的标签名,事件通过 opts.on.get('eventName') 获取。

loginType: LoginType.QUICK_LOGIN 表示使用快速登录模式,系统会自动展示当前华为账号的手机号。setAgreementStatus(ACCEPTED) 表示用户已同意隐私协议——这个状态需要你在业务层自己判断,我在 Vue 页面里做了隐私政策勾选的校验。

客户端完整页面

下面是我实际项目中的注册/登录页面完整代码,直接可用:

<template>
  <view>
    <!-- Logo 区域 -->
    <view class="logo-section">
      <image src="/static/logo.png" mode="aspectFit"></image>
      <text class="app-name">示例 APP</text>
      <text class="slogan">成长好帮手</text>
    </view>

    <!-- 登录表单卡片 -->
    <view class="form-card">
      <view class="form-title">用户注册</view>

      <!-- 华为账号登录按钮(原生组件) -->
      <!-- #ifdef APP-HARMONY -->
      <view class="huawei-login-section">
        <embed tag="hwilogin" :options="huaweiOptions" @success="handleHuaweiLoginSuccess" @fail="handleHuaweiLoginFail"></embed>
        <text>请使用华为账号登录</text>
      </view>
      <!-- #endif -->

      <!-- 登录链接 -->
      <view class="login-link">
        <text>已有账号?</text>
        <text class="link-btn" @click="goToLogin">立即登录</text>
      </view>

      <!-- 隐私政策勾选 -->
      <view class="privacy-check">
        <view class="checkbox-wrapper" @click="agreePrivacy = !agreePrivacy">
          <view class="checkbox" :class="{ checked: agreePrivacy }">
            <text v-if="agreePrivacy">✓</text>
          </view>
        </view>
        <text class="privacy-text">我已阅读并同意</text>
        <text class="privacy-link" @click.stop="goToPrivacy">《隐私政策》</text>
      </view>
    </view>
  </view>
</template>

<script>
import { huaweiLogin } from '@/utils/api.js'
import { setToken, setUserInfo } from '@/utils/auth.js'

// #ifdef APP-HARMONY
import '@/uni_modules/jack-hwilogin'
// #endif

export default {
  name: 'Register',
  data() {
    return {
      agreePrivacy: false,
      huaweiOptions: {}
    }
  },
  methods: {
    // 华为登录成功回调
    async handleHuaweiLoginSuccess({ detail }) {
      console.log('华为登录成功:', detail)
      // 必须先同意隐私政策
      if (!this.agreePrivacy) {
        uni.showToast({ title: '请先同意隐私政策', icon: 'none', duration: 2000 })
        return
      }
      try {
        uni.showLoading({ title: '登录中...' })
        // 将 authorizationCode 发送到后端验证
        const res = await huaweiLogin({
          authorizationCode: detail.authorizationCode,
          unionId: detail.unionID,
          openId: detail.openID
        })
        if (res.success) {
          setToken(res.token)
          setUserInfo(res.user)
          uni.hideLoading()
          if (res.isNewUser) {
            uni.showToast({ title: '注册成功', icon: 'success' })
          } else {
            uni.showToast({ title: '登录成功', icon: 'success' })
          }
          setTimeout(() => {
            uni.switchTab({ url: '/pages/index/index' })
          }, 1500)
        }
      } catch (err) {
        uni.hideLoading()
        console.error('华为登录失败:', err)
      }
    },
    // 华为登录失败回调
    handleHuaweiLoginFail(err) {
      console.error('华为登录失败:', err)
      uni.showToast({ title: '华为登录失败,请重试', icon: 'none' })
    },
    goToLogin() {
      uni.navigateBack()
    },
    goToPrivacy() {
      uni.navigateTo({ url: '/pages/privacy/index' })
    }
  }
}
</script>

<style scoped>
.register-page {
  min-height: 100vh;
  background: #f5f5f5;
  position: relative;
  padding-bottom: 120rpx;
}
.header-bg {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 500rpx;
  background: linear-gradient(135deg, #FF9A9E, #FECFEF);
  border-radius: 0 0 60rpx 60rpx;
}
.logo-section {
  position: relative;
  padding-top: 80rpx;
  text-align: center;
}
.logo {
  width: 140rpx;
  height: 140rpx;
  border-radius: 30rpx;
  box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.15);
}
.app-name {
  display: block;
  margin-top: 20rpx;
  font-size: 36rpx;
  font-weight: bold;
  color: #fff;
}
.slogan {
  display: block;
  margin-top: 8rpx;
  font-size: 24rpx;
  color: rgba(255, 255, 255, 0.8);
}
.form-card {
  position: relative;
  margin: 40rpx 40rpx 0;
  padding: 40rpx;
  background: #fff;
  border-radius: 30rpx;
  box-shadow: 0 8rpx 40rpx rgba(0, 0, 0, 0.1);
}
.form-title {
  text-align: center;
  font-size: 36rpx;
  font-weight: bold;
  color: #333;
  margin-bottom: 40rpx;
}
.huawei-login-section {
  margin-bottom: 30rpx;
}
.huawei-login-btn {
  display: block;
  width: 100%;
  height: 96rpx;
  margin-bottom: 20rpx;
}
.login-link {
  margin-top: 30rpx;
  text-align: center;
}
.link-text {
  font-size: 28rpx;
  color: #999;
}
.link-btn {
  font-size: 28rpx;
  color: #FF9A9E;
  font-weight: bold;
}
.privacy-check {
  display: flex;
  align-items: center;
  margin-top: 30rpx;
}
.checkbox-wrapper {
  padding: 10rpx;
}
.checkbox {
  width: 40rpx;
  height: 40rpx;
  border: 2rpx solid #ddd;
  border-radius: 8rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24rpx;
  color: #fff;
}
.checkbox.checked {
  background: linear-gradient(135deg, #FF9A9E, #FECFEF);
  border-color: #FF9A9E;
}
.privacy-text {
  font-size: 26rpx;
  color: #666;
  margin-left: 10rpx;
}
.privacy-link {
  font-size: 26rpx;
  color: #FF9A9E;
}
</style>

注意 import '@/uni_modules/jack-hwilogin' 这行,它的作用是触发 login.ets 中的 defineNativeEmbed 注册。如果不 import,<embed tag="hwilogin"> 就找不到对应的原生组件。另外整个 import 和 embed 标签都包在 #ifdef APP-HARMONY 条件编译里,这样在其他平台编译时不会报错。

登录时序

整个一键登录的完整时序如下:

  1. 用户勾选隐私政策。
  2. 点击华为登录按钮,embed 组件触发登录。
  3. 执行 executeRequest(quickLoginAnonymousPhone),弹出授权面板(显示脱敏手机号)。
  4. 用户确认授权,返回 authorizationCode + anonymousPhone + OpenID + UnionID。
  5. @success 回调,前端将 authorizationCode 发送给后端 /api/auth/huawei-login。
  6. 后端调用华为 oauth2/v6/quickLogin/getPhoneNumber 接口换取真实手机号。
  7. 后端创建/更新用户记录,签发 JWT Token。
  8. 前端保存 Token,跳转首页。

后端验证示例

客户端拿到 authorizationCode 后,需要发给后端去华为服务器换取真实手机号。下面是一个简化的 Express 后端示例:

const express = require('express')
const axios = require('axios')
const jwt = require('jsonwebtoken')

const app = express()
app.use(express.json())

// 华为账号服务配置(从 AGC 后台获取)
const HUAWEI_CONFIG = {
  clientId: 'your_client_id',
  clientSecret: 'your_client_secret',
  apiUrl: 'https://account-api.cloud.huawei.com/oauth2/v6/quickLogin/getPhoneNumber'
}

const JWT_SECRET = 'your_jwt_secret'

/**
 * 华为一键登录接口
 * 接收客户端传来的 authorizationCode,向华为服务器换取真实手机号
 */
app.post('/api/auth/huawei-login', async (req, res) => {
  try {
    const { authorizationCode, unionId, openId } = req.body
    if (!authorizationCode || !unionId || !openId) {
      return res.status(400).json({ success: false, message: '缺少必需参数' })
    }

    // 第一步:用 authorizationCode 向华为服务器换取真实手机号
    let phoneNumber = null
    try {
      const response = await axios.post(HUAWEI_CONFIG.apiUrl, {
        code: authorizationCode,
        clientId: HUAWEI_CONFIG.clientId,
        clientSecret: HUAWEI_CONFIG.clientSecret
      }, {
        headers: { 'Content-Type': 'application/json;charset=UTF-8' }
      })
      if (response.data && response.data.phoneNumber) {
        phoneNumber = response.data.phoneNumber
      } else {
        return res.status(400).json({ success: false, message: '获取手机号失败' })
      }
    } catch (apiError) {
      console.error('华为 API 调用失败:', apiError.message)
      return res.status(500).json({ success: false, message: '华为账号服务异常' })
    }

    // 第二步:根据 unionId 查找或创建用户
    // 这里用伪代码表示数据库操作,实际项目中替换为你的 ORM
    let user = await db.findUserByUnionId(unionId)
    let isNewUser = false
    if (!user) {
      // 新用户,创建记录
      user = await db.createUser({
        huaweiUnionId: unionId,
        huaweiOpenId: openId,
        phone: phoneNumber,
        loginType: 'huawei'
      })
      isNewUser = true
    } else {
      // 老用户,更新手机号等信息
      await db.updateUser(user.id, { huaweiOpenId: openId, phone: phoneNumber })
    }

    // 第三步:签发 JWT Token
    const token = jwt.sign({ userId: user.id }, JWT_SECRET, { expiresIn: '7d' })

    res.json({
      success: true,
      token,
      isNewUser,
      user: {
        id: user.id,
        phone: phoneNumber,
        nickname: user.nickname
      }
    })
  } catch (error) {
    console.error('登录失败:', error)
    res.status(500).json({ success: false, message: '服务器错误' })
  }
})

app.listen(3000, () => console.log('Server running on port 3000'))

后端的核心逻辑就三步:拿 code 换手机号 → 查找/创建用户 → 签发 Token。华为的 getPhoneNumber 接口需要传 code(就是客户端给你的 authorizationCode)、clientId 和 clientSecret,返回的 phoneNumber 就是用户的真实手机号。

这里要注意 clientSecret 绝对不能暴露在客户端代码里,只能放在服务端。

踩坑与注意事项

授权页面拉不起来

大概率是签名证书指纹的问题。在 AGC 后台配置的 SHA-256 指纹必须和你打包用的证书完全一致。开发阶段换了调试证书记得同步更新。另外确认 Account Kit 服务已经在 AGC 后台开通。

authorizationCode 只能用一次

华为返回的 authorizationCode 是一次性的,后端用它换完手机号之后就失效了。如果你的后端接口因为网络问题重试,第二次调用华为 API 会失败。建议在后端做好幂等处理。

条件编译不能少

所有涉及 @kit.AccountKit 的代码都只能在鸿蒙平台运行。Vue 页面里的 import 和模板都要用 #ifdef APP-HARMONY 包裹,否则在其他平台编译会报错。

OpenID vs UnionID

OpenID 是同一个应用下唯一的用户标识,不同应用拿到的 OpenID 不同。UnionID 是同一个开发者账号下唯一的,如果你有多个 App 需要打通用户体系,应该用 UnionID 作为用户关联的主键。

总结

华为一键登录的接入并不复杂,核心就是理解 authorizationCode 的流转:客户端获取 → 传给后端 → 后端去华为换手机号。该方案提供了 JS API 和原生按钮两种方式,基本覆盖了常见的登录场景。

目录

  1. 什么是华为一键登录
  2. 整体架构
  3. 前置准备
  4. 插件结构
  5. 核心实现:UTS 插件层
  6. JS API 方式 (index.uts)
  7. 原生按钮组件 (login.ets)
  8. 客户端完整页面
  9. 登录时序
  10. 后端验证示例
  11. 踩坑与注意事项
  12. 总结

更多推荐文章

查看全部
  • CLI-Anything:自动化生成 CLI 接口,让 AI 代理控制任意软件
  • Spring Boot 集成 Spring AI OpenAI Starter 教程
  • WebPShop 插件:Photoshop 支持 WebP 格式的解决方案
  • 后端数据脱敏:概念、场景与 Java 落地方案
  • FP8 量化技术降低显存需求,实现 AI 绘画低配运行
  • 大模型时代:新手与程序员如何转型入局
  • HDFS 读写机制深度解析:分布式存储核心原理
  • Python 第三方库实战:键盘监听与学生管理系统
  • 基于 LVM 部署 MinIO 分布式高可用集群
  • ONLYOFFICE AI 功能详解与使用指南
  • Xilinx FPGA 开发:Vivado 流与 Vitis 流对比解析
  • 2025 年开源图生图模型盘点:Kandinsky 5.0、Qwen-Image 与 Z-Image
  • Python 函数、列表与元组核心用法及实战案例
  • Linux 内核链表分析与实现原理
  • Llama-Factory 与传统微调平台深度对比测评
  • C++ 内存管理技巧与最佳实践
  • Linux 文件描述符与重定向实战:从原理到 minishell 实现
  • FlashAttention-4 算法解析:Blackwell 架构下的性能优化
  • 区块链共识机制深度解析:PBFT、Tendermint 与 DAG
  • PyCharm 2023 汉化及常用插件安装配置指南

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online