# Taro+Nut UI开发中的坑
| 依赖 | 版本 | 
|---|---|
| taro | 3.6.8 | 
| vue | 3.3.4 | 
| nutui | 4.0.4 | 
# 音频实例
# createInnerAudioContext 报错 Cannot read property 'then' of undefined
const state = reactive<any>({
  //...
  InnerAudioCtx: null,
})
//创建音频实例
function createAudioText() {
  state.InnerAudioCtx = Taro.createInnerAudioContext();
  state.InnerAudioCtx.onPlay(() => {
    console.log('开始了');
  });
  state.InnerAudioCtx.onError((res) => {
    console.log(res);
  });
}
// 其他的代码..
function play() {
  state.InnerAudioCtx.src = 'https://...'
  state.InnerAudioCtx.play()
}
这是一个很普通的创建音频和播放的代码。
然而神奇的是src赋值之后在微信小程序(web没问题)中你会得到这么一个报错,并且无法查看WAServiceMainContext.js这个文件报什么错,source那边提示开发者工具隐藏了

我在网上查了很久,有看到遇到相同问题的,但是没有解决的结果,因为不能看到具体什么代码报错,因此只能不断地尝试;
- 本地src路径
- 基础库
- ..
# 解决结果
web和微信小程序都可以
最后我把目光放到了proxy上面,这里的实例我把它放到了reactive中,因此它经过proxy,会不会src属性因此受到什么影响;于是我把它改为了一个普通的变量,就此解决了
/** 音频播放器实例,不可以使用reactive,否则微信小程序会发生错误 */
let InnerAudioCtx: Taro.InnerAudioContext | null = null;
//创建音频实例
function createAudioText() {
  InnerAudioCtx = Taro.createInnerAudioContext();
  InnerAudioCtx.onPlay(() => {
    console.log('开始了');
  });
  InnerAudioCtx.onError((res) => {
    console.log(res);
  });
}
// 其他的代码..
function play() {
  InnerAudioCtx!.src = 'https://...'
  InnerAudioCtx!.play()
}
# 在web中调用音频实例的销毁方法报错
const audioContext = Taro.createInnerAudioContext()
audioContext.destroy() // 报错

我理解的报错原因是
web实际上就是创建一个audio元素,但是Taro压根就没有插入body中,destroy却调用了document.body.removeChild,所以就报错了

# 解决结果
更新一下:针对这个问题我提了一个PR (opens new window),已被
Merged,发布于v3.6.9 (opens new window)
web不调用destroy方法,手动把实例设置为null || undefined
# 上传文件
web端存在问题
微信小程序没有问题
# 上传文件的接口要求form-data的格式传递参数
上传文件的接口要求form-data的格式传递参数
Taro.uploadFile({
  url: uploadCfg.value.upUrl,
  filePath: item.src,
  timeout: 60000,
  name: 'file',
  header: {
    'Content-Type': 'multipart/form-data',
  },
  formData: {
    token: uploadCfg.value.token,
  },
  success: (res) => {}
})
这里我们手动指定了Content-Type,浏览器解析的结果不对
于是,把header去掉,使用默认的即可
# 文件名称服务端无法从我们传递的file对象解析到
 文件名称服务端无法从我们传递的
file对象解析到
web端获取到的file对象和微信小程序的不一致,估计是Taro处理过。打印web端的file对象可以发现,它上面还有一个属性名为originalFileObj
这个originalFileObj原始对象就有文件的名称等信息
所以我的解决方案是在originalFileObj获取文件名,并在 formData中传多一个fileName(和服务端约定好的)字段给接口
# 行内样式
如果涉及到单位,一定要使用Taro.pxTransform,不然taro会直接把相关的样式代码直接删除
# definePageConfig不生效?
taro版本高于3.4,页面用definePageConfig修改导航栏文字等,运行后没有生效
# 解决结果
这个问题产生的原因非常的凑巧,假设我现有一个分包packageMy
- 我有一个Index.vue,页面路径自然就是packageMy/Index
- 同时我有packageMy/index.ts处理了其他的事情
所以这个路径和文件重名了,因为taro优先取路径名.ts(官网描述的是叫page.config.js)作为页面的配置,因为definePageConfig是v3.4后面才有的
# Nut UI
# textarea
# autofocus属性web端不生效
 # 解决结果
IOS有限制,无法解决
通过ref直接执行dom的focus方法
<NutTextarea
  ref="editTextAreaRef"
  max-length="50"
  autofocus
></NutTextarea>
// 省略isWeb editTextAreaRef声明
if (isWeb) {
  // h5 autofocus不生效
  nextTick(() => {
    editTextAreaRef.value.textareaRef.focus();
  });
}
# Taro - 支付宝小程序
# 使用Antv/F2 (opens new window)
# 前言
一开始我就有一个困惑,我用
tarovue3去写支付宝小程序,那我究竟是看如何在Vue中使用 (opens new window)还是如何在小程序中使用 (opens new window)的文档呢❓
随即我开始了踩坑之旅,通过实践发现F2的vue版本是针对web端的,因此当我在小程序跑起来的时候直接报了一个node.parentNode获取不到的错误
上面说的是4.x版本。但从一开始我并没有直接用4.x,因为4.x开始,F2都是以jsx的方式去使用组件;我在vue3使用script setup去编写代码,所以暂时无法编写jsx。还有一个很重要的是旧项目(微信小程序)那边的F2用的是3.x (opens new window)
这两个版本在使用方式上改变还是挺大的,3.x用起来没有那么的挑;也正如所愿,我成功的跑起了示例,不曾想用安卓机一看,人都麻了,出现了以下的情况
- 图表有时候满屏(我设置的宽度100%),有时候又变成了4分之一小
- 使用v-if控制,有概率渲染不了,canvas节点在,就是没有渲染,使用v-show直接就不渲染了
上诉情况没有任何的报错,无从解决;因此我只能转向4.x继续踩坑
p.s.
IOS啥毛病都没有,动画还贼丝滑
# 支付宝小程序接入Antv/F2 4.x版本
正式开始
# 1. 安装 F2 依赖
# 安装 F2 依赖
npm i @antv/f2 --save
# 安装f2-context
npm i @antv/f2-context --save
# 2. 配置 jsx transform
如果项目已有 jsx 编译,可忽略此步骤
npm i @babel/plugin-transform-react-jsx -D
在config/index.js增加配置
mini: {
  webpackChain(chain) {
    chain
      .merge({
      module: {
        rule: {
          F2: {
            test: /\.jsx$/,
            use: {
              babelLoader: {
                loader: require.resolve('babel-loader'),
                options: {
                  plugins: [
                    [
                      '@babel/plugin-transform-react-jsx',
                      {
                        runtime: 'automatic',
                        importSource: '@antv/f2',
                      },
                    ],
                  ],
                },
              },
            },
          },
        },
      },
    })
  }
}
# 3.下载f2-my的源码 (opens new window)
点击上面的地址把src文件夹的内容下载到本地,假设放到了项目的/src/components/F2Chart,下面将以这个路径举例
这也是为什么我在第一步的时候没有说安装@antv/f2-my,这里有两个坑,我们需要改一下源码
位置: /src/components/F2Chart/index.js
- 从文档 (opens new window)得知,我们等下在使用组件的时候需要传递 - onRender这个props,但是在实践过程中,通过这个props名称我无法把我们自己的函数传递过去,我尝试新加一个props,从而解决了问题- Component({ props: { onRender: function onRender(_props) {}, // 上面的onrender无法执行到,因此写了一个新的自定义prop render: function onRender(_props) {}, // width height 会作为元素兜底的宽高使用 width: null, height: null, type: '2d', // canvas 2d, 基础库 2.7 以上支持 }, })- 首先在props选项中增加了 - render,接着把原来调用- props.onRender的地方改为- props.render;- 分别是在 - didUpdate和- canvasRender这两个地方
- 还有就是他的 - click函数,从源码可以看到是点击- canvas时触发的,但是一点击控制台就收到一个报错,说事件的- Event的- detail是- undefined,我打印了整个- Event对象,确实如此,连同源码中的- target.offsetLeft也是没有的,我怀疑这部分是复制了- web的过来然后没改;因此我的- click函数改成了如下,我暂时用不到- click: function click(e) { // 支付宝的点击event对象没有detail,这里只能改造一下不执行了 var canvasEl = this.canvasEl; if (!canvasEl) { return; } var event = wrapEvent(e); canvasEl.dispatchEvent('click', event); }- 补充一下:在真机上有源码需要的 - e.detail.x,但是- e.target.offsetLeft还是没有
# 4.编写我们的业务代码
下面举例写一个官方的上手例子
和官方一样,先写一个Chart.jsx
import { Chart, Interval, Axis } from '@antv/f2';
export default (props) => {
  const { data } = props;
  return (
    <Chart data={data}>
      <Axis field="genre" />
      <Axis field="sold" />
      <Interval x="genre" y="sold" color="genre" />
    </Chart>
  );
};
接着我们写一个taro的vue页面,F2.vue
<template>
  <view>
    <button @click="show = !show">change</button>
    <view class="container" v-show="show">
      <gh-canvas :render="onRenderChart"></gh-canvas>
    </view>
  </view>
</template>
<script lang="tsx">
  import { reactive, toRefs } from 'vue';
  import Chart from './Chart';
  import { createElement } from '@antv/f2';
  definePageConfig({
    usingComponents: {
      'gh-canvas': '../../components/F2Chart',
    },
  });
  export default {
    setup() {
      const state = reactive({
        show: true,
      });
      const data = [
        { genre: 'Sports', sold: 275 },
        { genre: 'Strategy', sold: 115 },
        { genre: 'Action', sold: 120 },
        { genre: 'Shooter', sold: 350 },
        { genre: 'Other', sold: 150 },
      ];
      function onRenderChart() {
        return createElement(Chart, {
          data: data,
        });
      }
      return {
        ...toRefs(state),
        onRenderChart,
      };
    },
  };
</script>
<style lang="scss">
  .container {
    width: 100%;
    height: 600px;
  }
</style>
这里有个注意点,在页面先注册一下我们下载到本地的antv/F2组件;这个时候如果你直接跑起来你会收到一个警告,虽然是个警告,但是你的代码是不行的。
[Vue warn]: Failed to resolve component: gh-canvas If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement.
这个提示很明显了,我们自己取的这个组件名,gh-canvas,在构建的时候把他当做vue组件去解析了;解决办法就是配置一下vue-loader让他跳过我们的组件
但是吧,又不能在webpackchain中直接配置覆盖,于是我一顿找,终于找到了一个老哥的贡献
https://github.com/NervJS/taro/pull/11694
接下来就简单了,让我们再次来到config/index.js增加以下配置
plugins: [
  // 你的其他插件
  // https://github.com/NervJS/taro/pull/11694
  [
    '@tarojs/plugin-framework-vue3',
    {
      vueLoaderOption: {
        compilerOptions: {
          isCustomElement: (tag) => tag.startsWith('gh-'),
        },
      },
    },
  ],
],
这个组件名前缀你喜欢改什么就改什么,记得在vue的template的引用同步更改就行
重新运行pnpm dev:alipay,大功告成~🎉 4.x版本在安卓上已经没有上面提到的两个问题了
# 总结
这里踩坑主要还是配置问题,因为我不熟悉webpack、webpackchain因此废了很大劲。还有找资料还是优先到github查找一下有没有类似的issue或者贡献,什么chatGPT没啥用,不停的说谎给出错误答案
← vite插件-shared react →