Taro:高性能小程序的最佳实践

这篇具有很好参考价值的文章主要介绍了Taro:高性能小程序的最佳实践。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

作为一个开放式的跨端跨框架解决方案,Taro 在大量的小程序和 H5 应用中得到了广泛应用。我们经常收到开发者的反馈,例如“渲染速度较慢”、“滑动不够流畅”、“性能与原生应用相比有差距” 等。这表明性能问题一直是困扰开发者的一个重要问题。

熟悉 Taro 的开发者应该知道,相比于 Taro 1/2,Taro 3 是一个更加注重运行时而轻量化编译时的框架。它的优势在于提供了更高效的代码编写方式,并拥有更丰富的生态系统。然而,这也意味着在性能方面可能会有一些损耗。

但是,使用 Taro 3 并不意味着我们必须牺牲应用的性能。事实上,Taro 已经提供了一系列的性能优化方法,并且不断探索更加极致的优化方案。

本文将为大家提供一些小程序开发的最佳实践,帮助大家最大程度地提升小程序应用的性能表现。

一、如何提升初次渲染性能

如果初次渲染的数据量非常大,可能会导致页面在加载过程中出现一段时间的白屏。为了解决这个问题,Taro 提供了预渲染功能(Prerender)。

使用 Prerender 非常简单,只需在项目根目录下的 config 文件夹中找到 index.js/dev.js/prod.js 三者中的任意一个项目配置文件,并根据项目情况进行修改。在编译时,Taro CLI 会根据你的配置自动启动预渲染功能。

const config = {
  ...
  mini: {
    prerender: {
      match: 'pages/shop/**', // 所有以 `pages/shop/` 开头的页面都参与 prerender
      include: ['pages/any/way/index'], // `pages/any/way/index` 也会参与 prerender
      exclude: ['pages/shop/index/index'] // `pages/shop/index/index` 不用参与 prerender
    }
  }
};

module.exports = config

更详细说明请参考官方文档: https://taro-docs.jd.com/docs/prerender

二、如何提升更新性能

由于 Taro 使用小程序的 template 进行渲染,这会引发一个问题:所有的 setData 更新都需要由页面对象调用。当页面结构较为复杂时,更新的性能可能会下降。

当层级过深时,setData 的数据结构如下:

page.setData({
  'root.cn.[0].cn.[0].cn.[0].cn.[0].markers': [],
})

期望的 setData 数据结构:

component.setData({
  'cn.[0].cn.[0].markers': [],
})

目前有两种方法可以实现上述结构,以实现局部更新的效果,从而提升更新性能:

1. 全局配置项 baseLevel

对于不支持模板递归的小程序(例如微信、QQ、京东小程序等),当 DOM 层级达到一定数量后,Taro 会利用原生自定义组件来辅助递归渲染。简单来说,当 DOM 结构超过 N 层时,Taro 将使用原生自定义组件进行渲染(可以通过修改配置项 baseLevel 来调整 N 的值,建议设置为 8 或 4)。

需要注意的是,由于这是全局设置,可能会带来一些问题,例如:

  • 在跨原生自定义组件时,flex 布局会失效(这是影响最大的问题);
  • SelectorQuery.select 方法中,跨自定义组件的后代选择器写法需要增加 >>>:.the-ancestor >>> .the-descendant

2. 使用 CustomWrapper 组件

CustomWrapper 组件的作用是创建一个原生自定义组件,用于调用后代节点的 setData 方法,以实现局部更新的效果。

我们可以使用它来包裹那些遇到更新性能问题的模块,例如:

import { View, Text } from '@tarojs/components'

export default function () {
  return (
    <View className="index">
      <Text>Demo</Text>
      <CustomWrapper>
        <GoodsList />
      </CustomWrapper>
    </View>
  )
}

三、如何提升长列表性能

长列表是常见的组件,当生成或加载的数据量非常大时,可能会导致严重的性能问题,尤其在低端机上可能会出现明显的卡顿现象。

为了解决长列表的问题,Taro 提供了 VirtualList 组件和 VirtualWaterfall 组件。它们的原理是只渲染当前可见区域(Visible Viewport)的视图,非可见区域的视图在用户滚动到可见区域时再进行渲染,以提高长列表滚动的流畅性。

1. VirtualList 组件(虚拟列表)

以 React Like 框架使用为例,可以直接引入组件:

import VirtualList from '@tarojs/components/virtual-list'

一个最简单的长列表组件如下所示:

function buildData(offset = 0) {
  return Array(100)
    .fill(0)
    .map((_, i) => i + offset)
}

const Row = React.memo(({ id, index, data }) => {
  return (
    <View id={id} className={index % 2 ? 'ListItemOdd' : 'ListItemEven'}>
      Row {index} : {data[index]}
    </View>
  )
})

export default class Index extends Component {
  state = {
    data: buildData(0),
  }

  render() {
    const { data } = this.state
    const dataLen = data.length
    return (
      <VirtualList
        height={800} /* 列表的高度 */
        width="100%" /* 列表的宽度 */
        item={Row} /* 列表单项组件,这里只能传入一个组件 */
        itemData={data} /* 渲染列表的数据 */
        itemCount={dataLen} /* 渲染列表的长度 */
        itemSize={100} /* 列表单项的高度  */
      />
    )
  }
}

更多详情可以参考官方文档: https://taro-docs.jd.com/docs/virtual-list

2. VirtualWaterfall 组件(虚拟瀑布流)

以 React Like 框架使用为例,可以直接引入组件:

import { VirtualWaterfall } from `@tarojs/components-advanced`

一个最简单的长列表组件如下所示:

function buildData(offset = 0) {
  return Array(100)
    .fill(0)
    .map((_, i) => i + offset)
}

const Row = React.memo(({ id, index, data }) => {
  return (
    <View id={id} className={index % 2 ? 'ListItemOdd' : 'ListItemEven'}>
      Row {index} : {data[index]}
    </View>
  )
})

export default class Index extends Component {
  state = {
    data: buildData(0),
  }

  render() {
    const { data } = this.state
    const dataLen = data.length
    return (
      <VirtualWaterfall
        height={800} /* 列表的高度 */
        width="100%" /* 列表的宽度 */
        item={Row} /* 列表单项组件,这里只能传入一个组件 */
        itemData={data} /* 渲染列表的数据 */
        itemCount={dataLen} /* 渲染列表的长度 */
        itemSize={100} /* 列表单项的高度  */
      />
    )
  }
}

更多详情可以参考官方文档: https://taro-docs.jd.com/docs/virtual-waterfall

四、如何避免 setData 数据量较大

众所周知,对小程序性能的影响较大的主要有两个因素,即 setData 的数据量和单位时间内调用 setData 函数的次数。在 Taro 中,会对 setData 进行批量更新操作,因此通常只需要关注 setData 的数据量大小。下面通过几个例子来说明如何避免数据量过大的问题:

例子 1:删除楼层节点要谨慎处理

目前 Taro 在处理节点删除方面存在一些缺陷。假设存在以下代码写法:

<View>
  <!-- 轮播 -->
  <Slider />
  <!-- 商品组 -->
  <Goods />
  <!-- 模态弹窗 -->
  {isShowModal && <Modal />}
</View>

isShowModaltrue 变为 false 时,模态弹窗会消失。此时,Modal 组件的兄弟节点都会被更新,setData 的数据是 Slider + Goods 组件的 DOM 节点信息。

一般情况下,这不会对性能产生太大影响。然而,如果待删除节点的兄弟节点的 DOM 结构非常复杂,比如一个个楼层组件,删除操作的副作用会导致 setData 的数据量变大,从而影响性能。

为了解决这个问题,可以通过隔离删除操作来进行优化。

<View>
  <!-- 轮播 -->
  <Slider />
  <!-- 商品组 -->
  <Goods />
  <!-- 模态弹窗 -->
  <View>
    {isShowModal && <Modal />}
  </View>
</View>

例子 2:基础组件的属性要保持引用

当基础组件(例如 ViewInput 等)的属性值为非基本类型时,假设存在以下代码写法:

<Map
  latitude={22.53332}
  longitude={113.93041}
  markers={[
    {
      latitude: 22.53332,
      longitude: 113.93041,
    },
  ]}
/>

每次渲染时,React 会对基础组件的属性进行浅比较。如果发现 markers 的引用不同,就会触发组件属性的更新。这最终导致了 setData 操作的频繁执行和数据量的增加。 为了解决这个问题,可以使用状态(state)或闭包等方法来保持对象的引用,从而避免不必要的更新。

<Map
  latitude={22.53332}
  longitude={113.93041}
  markers={this.state.markers}
/>

五、更多最佳实践

1. 阻止滚动穿透

在小程序开发中,当存在滑动蒙层、弹窗等覆盖式元素时,滑动事件会冒泡到页面上,导致页面元素也会跟着滑动。通常我们会通过设置 catchTouchMove 来阻止事件冒泡。

然而,由于 Taro3 事件机制的限制,小程序事件都是以 bind 的形式进行绑定。因此,与 Taro1/2 不同,调用 e.stopPropagation() 并不能阻止滚动事件的穿透。

解决办法 1:使用样式(推荐)

可以为需要禁用滚动的组件编写以下样式:

{
  overflow:hidden;
  height: 100vh;
}

解决办法 2:使用 catchMove

对于极个别的组件,比如 Map 组件,即使使用样式固定宽高也无法阻止滚动,因为这些组件本身具有滚动的功能。因此,第一种方法无法处理冒泡到 Map 组件上的滚动事件。 在这种情况下,可以为 View 组件添加 catchMove 属性:

// 这个 View 组件会绑定 catchtouchmove 事件而不是 bindtouchmove
<View catchMove />

2. 跳转预加载

在小程序中,当调用 Taro.navigateTo 等跳转类 API 后,新页面的 onLoad 事件会有一定的延时。因此,为了提高用户体验,可以将一些操作(如网络请求)提前到调用跳转 API 之前执行。

对于熟悉 Taro 的开发者来说,可能会记得在 Taro 1/2 中有一个名为 componentWillPreload 的钩子函数。然而,在 Taro 3 中,这个钩子函数已经被移除了。不过,开发者可以使用 Taro.preload() 方法来实现跳转预加载的效果:

// pages/index.js
Taro.preload(fetchSomething())
Taro.navigateTo({ url: '/pages/detail' })

// pages/detail.js
console.log(getCurrentInstance().preloadData)

3. 建议把 Taro.getCurrentInstance() 的结果保存下来

在开发过程中,我们经常会使用 Taro.getCurrentInstance() 方法来获取小程序的 apppage 对象以及路由参数等数据。然而,频繁地调用该方法可能会导致一些问题。

因此,建议将 Taro.getCurrentInstance() 的结果保存在组件中,并在需要时直接使用,以避免频繁调用该方法。这样可以提高代码的执行效率和性能。

class Index extends React.Component {
  inst = Taro.getCurrentInstance()

  componentDidMount() {
    console.log(this.inst)
  }
}

六、预告:小程序编译模式(CompileMode)

Taro 一直追求并不断突破性能的极限,除了以上提供的最佳实践,我们即将推出小程序编译模式(CompileMode)。

什么是 CompileMode?

前面已经说过,Taro3 是一种重运行时的框架,当节点数量增加到一定程度时,渲染性能会显著下降。 因此,为了解决这个问题,Taro 引入了 CompileMode 编译模式。

CompileMode 在编译阶段对开发者的代码进行扫描,将 JSXVue template 代码提前编译为相应的小程序模板代码。这样可以减少小程序渲染层虚拟 DOM 树节点的数量,从而提高渲染性能。 通过使用 CompileMode,可以有效减少小程序的渲染负担,提升应用的性能表现。

如何使用?

开发者只需为小程序的基础组件添加 compileMode 属性,该组件及其子组件将会被编译为独立的小程序模板。

function GoodsItem () {
  return (
    <View compileMode>
      ...
    </View>
  )
}

目前第一阶段的开发工作已经完成,我们即将发布 Beta 版本,欢迎大家关注! 想提前了解的可以查看 RFC 文档: https://github.com/NervJS/taro-rfcs/blob/feat/compile-mode/rfcs/0000-compile-mode.md

结尾

通过采用 Taro 的最佳实践,我们相信您的小程序应用性能一定会有显著的提升。未来,我们将持续探索更多优化方案,覆盖更广泛的应用场景,为开发者提供更高效、更优秀的开发体验。

如果您在项目中有任何经验总结或思考,欢迎向我们投稿并进行交流,让我们一起分享给更多开发者,非常感谢您的支持!

作者:京东零售 利齐诺

来源:京东云开发者社区 转载请注明来源文章来源地址https://www.toymoban.com/news/detail-747473.html

到了这里,关于Taro:高性能小程序的最佳实践的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Docker与Django:实践高性能的Django应用

    Docker是一种开源的应用容器引擎,它使用特定于Host的linux容器来运行和管理应用,为开发人员提供了轻量级、可移植的环境。Django是一种Python网络应用框架,它提供了快速的Web开发。在实际应用中,Docker和Django可以相互配合,实现高性能的Django应用。 在本文中,我们将讨论如

    2024年02月22日
    浏览(38)
  • LLM 模型融合实践指南:低成本构建高性能语言模型

    编者按 :随着大语言模型技术的快速发展,模型融合成为一种低成本但高性能的模型构建新途径。本文作者 Maxime Labonne 利用 mergekit 库探索了四种模型融合方法:SLERP、TIES、DARE和passthrough。通过配置示例和案例分析,作者详细阐释了这些算法的原理及实践操作。 作者的核

    2024年02月22日
    浏览(42)
  • 构建高性能的MongoDB数据迁移工具:Java的开发实践

    随着大数据时代的到来,数据迁移成为许多企业和组织必须面对的挑战之一。作为一种非关系型数据库,MongoDB在应用开发中得到了广泛的应用。为了满足数据迁移的需求,我们需要一个高性能、稳定可靠的MongoDB数据迁移工具。下面将分享使用Java开发高性能MongoDB数据迁移工具

    2024年02月13日
    浏览(56)
  • 网易NDH基于Impala的高性能SQL引擎建设实践

    导读:本文将从四个方面来进行介绍。首先是分析在网易NDH中使用 Impala 过程遇到的一些痛点;第二个部分是基于这些痛点问题,我们提出了建设高性能SQL引擎的方案,以及这些方案是基于什么原则来创建的;第三个是基于这些原则,我们做了哪些的优化实践的尝试;最后会

    2024年02月09日
    浏览(42)
  • 大语言模型推理提速:TensorRT-LLM 高性能推理实践

    作者:顾静 大型语言模型(Large language models,LLM)是基于大量数据进行预训练的超大型深度学习模型。底层转换器是一组神经网络,这些神经网络由具有 self-attention 的编码器和解码器组成。编码器和解码器从一系列文本中提取含义,并理解其中的单词和短语之间的关系。 当前

    2024年01月25日
    浏览(58)
  • 高性能 RPC 框架 CloudWeGo-Kitex 内外统一的开源实践

    日前,字节跳动技术社区 ByteTech 举办的第七期字节跳动技术沙龙圆满落幕,本期沙龙以《字节高性能开源微服务框架:CloudWeGo》为主题。在沙龙中,字节跳动字节跳动基础架构服务框架资深研发工程师 杨芮 ,跟大家分享了《高性能 RPC 框架 Kitex 内外统一的开源实践》,本文

    2024年02月03日
    浏览(49)
  • 如何让ES低成本、高性能?滴滴落地ZSTD压缩算法的实践分享

    前文分别介绍了滴滴自研的ES强一致性多活是如何实现的、以及如何提升ES的性能潜力。由于滴滴ES日志场景每天写入量在5PB-10PB量级,写入压力和业务成本压力大,为了提升ES的写入性能,我们让ES支持ZSTD压缩算法,本篇文章详细展开滴滴在落地ZSTD压缩算法上的思考和实践。

    2024年02月13日
    浏览(47)
  • 《Python高并发与高性能编程:原理与实践》——小解送书第六期

    目录 书籍介绍  抽奖 Python成为时下技术革新的弄潮儿,全民Python的发展趋势让人们不再满足于简单地运行Python程序,逐步探索其更为广泛的日常应用和高性能设计。以ChatGPT为代表的大模型产品对初级程序开发人员提出了挑战,要想在开发领域站稳脚跟、有发展,必须掌握更

    2024年02月14日
    浏览(50)
  • 【洁洁送书第一期】Python高并发与高性能编程: 原理与实践

    Python成为时下技术革新的弄潮儿,全民Python的发展趋势让人们不再满足于简单地运行Python程序,逐步探索其更为广泛的日常应用和高性能设计。 以ChatGPT为代表的大模型产品对初级程序开发人员提出了挑战,要想在开发领域站稳脚跟、有发展,必须掌握更高级的技巧和能力。

    2024年02月16日
    浏览(64)
  • 【大虾送书第三期】《Python高并发与高性能编程: 原理与实践》

    目录 ✨写在前面 ✨主要内容 ✨本书特色 ✨关于作者      🦐博客主页:大虾好吃吗的博客      🦐专栏地址:免费送书活动专栏地址 Python成为时下技术革新的弄潮儿,全民Python的发展趋势让人们不再满足于简单地运行Python程序,逐步探索其更为广泛的日常应用和高性能

    2024年02月16日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包