在小程序中使用base64展示图片

小程序

背景

  • 项目使用taro进行开发
  • 需求是通过api请求, 获取图片的base64字符串, 并且将图片展示出来
  • 小程序中单次调用setData数据不能超过1024kb

分析

根据setData的限制, 图片的大小不能超过1024kb, 否则setData的时候会超过限制而报错。

因此, 可以考虑使用数组来存储数据, 并且将数据分批写入。

const arr = []
const baseStr = '....' // > 1024kb
const step = 1024 * 1024
for (let i = 0, j = 0; i < baseStr.length; i = i + step, j++) {
  arr[j] = baseStr.subStr(i, step)
}

然而, 对于taro而言, 数据的修改通过this.setState来完成, 不能像原生小程序那样只修改部分数据。 比如:

// taro
this.setState({
  a: {
    ...this.state.a,
    b: 1
  },
  c: [{ b: 1 }, ...this.state.c.slice(1)]
})

// 小程序
this.setData({
  'a.b': 1,
  'c[0].b': 1
})

因此, taro每次的setState都会把整个数据带上. 不符合setData限制。

倘若taro能像setData一样可以支持单独修改某一部分数据, 是否可以?

答案也是否定的

taro中, 在render方法用到的数据, 最终都会被挂在this.__state上, 而this.__state最终也会反应到data上, 所以也是不可取。

class C extends Taro.Component {
  render() {
    const a = 1
    return <View>{a}</View>
  }
}
// 编译后
// ...
var a = 1
Object.assign(this.__state, {
  aa: aa
})
// ...

最终还是只能用原生的方法来实现。

解决

对于数据处理, 则像上面一样, 分次写入数据。

Page({
  data: {
    arr: []
  },
  setImg() {
    const arr = []
    const baseStr = '....' // > 1024kb
    const step = 1024 * 1024
    for (let i = 0, j = 0; i < baseStr.length; i = i + step, j++) {
      const key = `arr[${i}]`
      this.setData({
        [key]: baseStr.subStr(i, step)
      })
    }
  }
})

数据处理完毕后, 需要将数据拼接成原字符串, 并且赋值给src, 而小程序wxml不支持嵌入复杂表达式。

但是小程序中的wxs却可以进行复杂运算, 并且在wxml中使用, 请参考。 那么便可以将拼接字符串的操作放在wxs中, 然后在wxml中调用即可

<wxs module="m1">
  function join(arr) { return arr.join('') } module.exports.join = join;
</wxs>
<image src="{{m1.join(arr)}}" mode="aspectFill" lazy-load></image>

这样便能使用base64来展示图片。