小红书小程序开发踩坑日记(持续更新)

1,665 阅读3分钟

组件库选型

经过我的一系列实践,截止至2025年3月

WOT组件库对小红书小程序的兼容性极差,请谨慎使用!!!

WOT组件库对小红书小程序的兼容性极差,请谨慎使用!!!

WOT组件库对小红书小程序的兼容性极差,请谨慎使用!!!

登录流程,code2Session接口报错

前端:通过xhs.login获取 code 传给后端

后端:

  1. 首先通过app_id和app_secret调用https://miniapp.xiaohongshu.com/api/rmp/token获取access_token应用调用凭证,access_token有效时间为两小时
  2. 调用code2Session的接口换取session_keyopenid,这里需要传三个参数,但是值得注意的是官网文档说的code这个参数应该放在body中,但是实际上经过我的使用发现code 必须放在 query 中

image.png

另外还需要注意 code 的有效时间为 5分钟,过期后会报如下错

{
	"success": false,
	"msg": "应用访问令牌不匹配",
	"data": null,
	"code": 410101
}

小红书小程序i18n国际化TabBar、NavBar无效

该项目是基于uniapp开发的,框架上选择了unibest。

首先是在各家小程序中,uniapp的国际化对TabBar和NavBar无效应该都是通病,因为NavBar和TabBar的数据不是响应式的

针对NavBar国际化无效的解决方案

在每个页面的 onShow 生命周期中执行 uni.setNavigationBarTitle,其中t为unibest提供的国际化翻译函数

onShow(() => {
  uni.setNavigationBarTitle({
    title: t('app.name')
  });
});

针对NavBar国际化无效的解决方案

首先是在APP组件的onLaunch生命周期中执行uni.setTabBarItem手动的切换到当前语言的tabbar,此处可以考虑条件编译因为uni.setTabBarItem在H5和APP端是不必要的

注意:这里除了文本设置进去之外,还需要将图标iconPath & selectedIconPath一起设置了,否则在真机调试中会出现切换语言后没有图标的情况

添加这个之后一定要使用hbuilder重新编译小程序,然后重启小红书开发者工具,否则很可能没有效果(小红书开发者工具至少在我用的windows端经常出问题)

// src/app.vue
onLaunch(async () => {
    // #ifndef H5
    await uni.setTabBarItem({
      index: 0,
      text: t('tabbar.home'),
      iconPath: 'static/tabbar/home.png',
      selectedIconPath: 'static/tabbar/homeHL.png'
    });
    await uni.setTabBarItem({
      index: 1,
      text: t('tabbar.profile'),
      iconPath: 'static/tabbar/personal.png',
      selectedIconPath: 'static/tabbar/personalHL.png'
    });
    await uni.setTabBarItem({
      index: 2,
      text: t('tabbar.about'),
      iconPath: 'static/tabbar/example.png',
      selectedIconPath: 'static/tabbar/exampleHL.png'
    });
    // #endif
});

然后是在切换国际化的地方每次切换都执行uni.setTabBarItemuni.setNavigationBarTitle,这两个方法需要在存在TabBar或Navbar的页面中才会执行成功,参考如下代码

首先我将上文中提到的两个方法抽取到了函数中如下

// src/utils/common.ts
export const updateTabBarI18n = async () => {
  // #ifndef H5
  await uni.setTabBarItem({
    index: 0,
    text: t('tabbar.home'),
    iconPath: 'static/tabbar/home.png',
    selectedIconPath: 'static/tabbar/homeHL.png'
  });
  await uni.setTabBarItem({
    index: 1,
    text: t('tabbar.profile'),
    iconPath: 'static/tabbar/personal.png',
    selectedIconPath: 'static/tabbar/personalHL.png'
  });
  await uni.setTabBarItem({
    index: 2,
    text: t('tabbar.about'),
    iconPath: 'static/tabbar/example.png',
    selectedIconPath: 'static/tabbar/exampleHL.png'
  });
  // #endif
};

export const updateNavBarI18n = async (name: string) => {
  await uni.setNavigationBarTitle({
    title: t(name)
  });
};

定义切换按钮组件

// src/components/i18n-switch.vue
<template>
  <view
    class="float-btn h-64rpx w-64rpx rounded-50% bg-#3A9A8F flex flex-justify-center flex-items-center cursor-pointer"
    :style="{ bottom: safeAreaInsets.bottom + 24 + 'rpx' }"
    draggable="true"
    @click="handleClick">
    <i class="iconfont text-44rpx text-white" :class="icon"></i>
  </view>
</template>

<script setup lang="ts">
import { t } from '@/locale';
import { updateNavBarI18n, updateTabBarI18n } from '@/utils/common';

defineOptions({
  name: 'I18nSwitch'
});

const props = defineProps<{
  active?: boolean;
}>();

const emits = defineEmits<{
  (event: 'update:active', value: boolean): void; // 定义一个名为 'update:active' 的事件,传递一个布尔值参数
}>();

const active = ref(props.active ?? false);
const icon = ref<string>(uni.getLocale() === 'zh-Hans' ? 'icon-i18n-zh' : 'icon-i18n-en');

const { safeAreaInsets } = uni.getSystemInfoSync();

const handleClick = async () => {
  active.value = !active.value;
  const nextLang = uni.getLocale() === 'zh-Hans' ? 'en' : 'zh-Hans';

  uni.setLocale(nextLang);
  if (nextLang === 'zh-Hans') {
    icon.value = 'icon-i18n-zh';
  } else {
    icon.value = 'icon-i18n-en';
  }

  // 更新tabBar
  await updateTabBarI18n();
  // 更新navBar
  await updateNavBarI18n('app.name');

  uni.showToast({
    title: t('home.i18n-switch'),
    icon: 'none',
    duration: 2000
  });
};

watch(
  () => props.active,
  value => {
    emits('update:active', value);
  }
);
</script>

<style scoped lang="scss">
.float-btn {
  position: fixed;
  right: 24rpx;
  transition: background-color 0.2s;
  &:active {
    background-color: darken($app-primary-color, 15%);
  }
}
</style>

最终效果如下

xhs小程序国际化.gif