关于 Taro
什么是 Taro
Taro 是由 JDC·凹凸实验室 倾力打造的一款多端开发解决方案的框架工具,它遵循 React 语法规范,采用与 React 一致的组件化思想。使用 Taro 进行开发可以获得和 React 一致的开发体验,它的组件生命周期和 React 完全保持一致,同时也支持 JSX 语法。通过使用 Taro 框架工具开发,仅需要写一套代码就可以利用 Taro 的编译工具,将源代码分别编译出可以在不同端(微信 / 京东 / 百度 / 支付宝 / 字节跳动 小程序、快应用、H5、React-Native 等)运行的代码。
开始上手
首先需要使用 npm 或者 yarn 工具进行全局安装 Taro 开发工具 @tarojs/cli
# 使用 npm/yarn/cnpm 安装 CLI
$ npm/yarn/cnpm install -g @tarojs/cli
安装完成后使用 Taro 的 init 命令来创建模板项目
$ taro init myDemo
在创建模板项目过程中,大家可以根据各自日常开发项目的习惯来选择相应的模板,同时 Taro 会默认安装项目所需要的依赖,安装使用的工具会按 yarn > cnpm > npm 顺序进行检测,如果在安装依赖的过程中发生中断或者失败,需要自己在项目目录下使用安装工具进行安装相应的依赖项。
以上就是运用 Taro 创建项目的整体流程,一个简单的 init 命令就帮我们完成了所有的操作,创建中大家只需要选择相应的模板即可,Taro 会默认为开发者安装项目所需要的依赖,只能说快速,便捷,简约而不简单。
小易介绍
小易是什么
‘小易来了’是针对收件人在完成订单签收后,物流侧同事无法对终端配送人员进行质量监督以及对物流服务进行跟踪反馈等弊端,为调研客户对物流侧现有服务的评价以及未来迫切需要所研发的物流服务小程序。它为企业事业部客户解决了采购终端全生命周期的运营环节,其中包括物流跟踪,订单查询,签收,评价以及发票追踪,售后申请等功能。
概览
该项目目前经历了四期的迭代,现已全部上线。
一期 | 二期 | 三期 | 四期 |
---|---|---|---|
首页 | 订单评价 | 首页改版 | 配送清单 |
个人中心 | 扫码签收 | 发票查询 | 售后申请 |
我的订单 | |||
物流跟踪 | |||
电子签收单 |
作为一个开发者,在经历了四期的迭代开发过程中也是深有感触。从项目的每一次迭代立项到项目开发完成上线的这个整体过程中,我深深感受到了产品同学的认真负责的态度,可以说将这个产品当做自己的 baby 看待,细心照料;感受到交互同学的细心思考,设计同学的完美出品;感受到研发同学的积极态度,在配合开发联调过程中也是相当给力;感受到测试同学的严谨,本着一丝一毫都不能差的初衷去测试整个流程。这就是一个产品的诞生的整个流程,每一步都包含着我们这些人的辛勤付出,我相信不管是用户还是我们自身都会很爱惜我们的小易。
项目展示
技术选型
‘小易来了’小程序项目选择使用 Taro 框架工具开发,配合 Taro UI 组件库完成,之所以没有选择使用原生微信小程序进行开发,是因为通过对比,Taro 框架具备以下优势。
快速上手
- 原生微信小程序开发过程中无法使用 npm 来进行第三方库的管理,同时也无法使用一些比较新的 ES 规范。然而 Taro 支持使用 npm/yarn 安装管理第三方依赖,支持使用 ES7/ES8 甚至更新的 ES 规范,可自行配置,支持使用 CSS 预编译器以及使用 Redux 进行状态管理等。
- Taro 的这些相关特性足以和我们平时的项目开发工作相吻合,尤其是经常使用 React 技术栈开发项目的同学,通过使用 Taro 框架进行开发小程序,我们不再需要重新去学习原生微信开发语言规范,降低了学习成本,同时还提高了开发效率。在 Taro 团队不断的开发迭代中,目前多端框架 Taro 发布了 3.0 RC 版本,已经开始支持 Vue 语言,后续项目开发可以 React/Vue 自由选择了。
丰富的 Taro UI 组件库
众所周知原生小程序开发没有配套的组件库,项目开发过程中所用到的组件都是由开发者自行完成,对于开发者而言这是一个相对繁琐的过程,对于项目而言则会增加项目开发复杂度,影响开发效率。然而 Taro 拥有一款基于自身开发的多端 UI 组件库—Taro UI.它目前拥有大部分的通用业务组件,可在微信/京东/支付宝/百度小程序,H5 等多端适配运行,提供友好的 API ,可灵活配置使用。Taro UI 目前只有一套默认的主题配色,为满足业务多样化的视觉需求,目前提供了三种自定义主题的方式( SCSS 变量覆盖 globalClass 全局样式类/配置 customStyle 属性),具体使用方式可查看官方文档。通过 Taro UI 组件库的配合使用,我们就不必再为业务组件而烦忧,大大的提高我们的开发效率。
支持多端开发转化
目前大部分项目都会涉及到多端,包括原生,H5 ,各个平台的小程序,尤其是我们现在对接的大客户业务,动不动就需要三端同时运行上线,对于我们前端开发者来说只需要关注 H5 和小程序,不同的端去编写多套代码的成本显然非常高,针对多端开发适配耗时耗力的弊端,Taro 框架做到了仅需要写一套代码,通过 Taro 的编译工具,将源代码分别编译出可以在不同端(微信 / 京东 / 百度 / 支付宝 / 字节跳动 小程序、快应用、H5、React-Native 等)运行的代码。
综上所述:使用 Taro 框架进行开发小程序,不仅降低了我们的学习成本,通过配合 Taro UI 还有助于提高我们的开发效率,尤其是对于需要多端运行的项目,一套代码完美适配多端转化,只能说真香,Taro 你值得拥有。
遇到的问题
相信大多数的开发者在平时项目开发过程中都会多多少少的遇到一些比较棘手的问题,对于首次使用 Taro 框架开发小程序的我来说也是忧虑多多,果不其然在开发过程中确实遇到了一些问题,每次遇到这种情况都会给自己来一个三连:别慌,稳住,问题不大。在经过一系列的操作尝试下最终这些问题都被一个个的攻克解决掉。
订单搜索
在小易来了小程序列表页顶部有一个搜索订单的需求功能,我们通过引入 Taro 框架中基础组件 Input 来实现搜索的功能,看似一个很常见的功能,然而在开发使用过程中却发现了以下问题:
首先我们引入 Input 基础组件,然后监听了它的 onInput 事件,在输入搜索关键词后,Input 会通过监听输入框内容然后触发 onInput 事件作出相应的响应,在此过程中我们会发现只要输入内容就会触发该事件,频繁调用后台接口,导致页面性能降低甚至卡死现象。因此我们加了定时器来做处理,通过节流来实现延迟搜索。虽然我们对比做了节流处理,看似是实现了延迟搜索,不再频繁调用后台接口,但是在中文搜索的时候我们发现只要在输入中文过程中间断就会调用接口,我们都知道在 H5 开发中通过监听 Input 的监听两个事件 compositionstart 和 compositionend 就可以区分中英文,但是我们发现在小程序 Input 组件的 Api 中的并不存在这两个 Api。
handleChange = (e) => {
this.timer && clearTimeout(this.timer);
this.timer = setTimeout(() => {
let value = e.target.value
this.setState({ value });
if (!value) {
this.handleClear();
return;
}
const { onSearch } = this.props;
onSearch && onSearch(value);
clearTimeout(this.timer)
}, 1000);
}
为了规避该问题,我们不再进行实时搜索,采用用户主动点击完成或者搜索键时再去触发搜索,回到 Taro 基础组件 Input 的官方文档查询,发现该组件除了 onInput Api 外,还存在一个 onConfirm 事件,通过监听该事件完美的规避了实时搜索的问题,同时大多数 H5 项目的搜索也都很少是实时搜索,建议后续小程序中的搜索也都采用用户主动触发搜索。
handleChange = (e) => {
let value = e.target.value
this.setState({ value });
if (!value) {
this.handleClear();
return;
}
const { onSearch } = this.props;
onSearch && onSearch(value);
}
内嵌 H5 的那些坑
在小易来了首屏的顶部轮播图是外链的 H5 ,要想在小程序里面运行 H5 页面,需要嵌在 web-view 里面。在 Taro 框架中我们直接将 webview 引入即可使用。
import Taro from '@tarojs/taro';
import { View,webView} from '@tarojs/components';
class webview extends Taro.Component {
constructor() {
super(...arguments);
this.state = {
url:''
};
}
componentDidMount() {
this.setState({
url:decodeURIComponent(this.$router.params.url)
})
}
render() {
return (
);
}
}
小程序中外链 H5 需要注意以下几点:
1.同一个项目只能有一个 webview。
2.web-view 组件的属性 src ,正如上图我们所看到的,src 就是用来设置网页链接的,但是需要值得注意的是 web-view 的 src 必须配置 https 协议的链接;
3.web-view 组件只要配置了链接,它是铺满全屏且自动跳转的,因此我将其单独作为一个路由页,在首屏轮播图位置通过触发点击事件获取外链的 url 将其作为参数传进来。
4.web-view 跳转的接口域名,需要配置在小程序开发设置的业务域名列表中,否则将会无法访问。
5.web-view 外链的 url 需要解码一下,不然可能会导致乱码或者参数丢失问题(encodeURIComponent(url),decodeURIComponent(url))
//首页
jumpLink(jumpUrl) {
console.log(1,jumpUrl)
Taro.navigateTo({url: `/pages/webview/webview?url=${jumpUrl}`});
}
//webview内嵌H5页
componentDidMount() {
console.log(2,this.$router.params.url)
this.setState({
url:decodeURIComponent(this.$router.params.url)
})
}
//首页
jumpLink(jumpUrl) {
console.log(1,jumpUrl)
Taro.navigateTo({url: `/pages/webview/webview?url=${jumpUrl}`});
}
//webview内嵌H5页
componentDidMount() {
console.log(2,this.$router.params.url)
this.setState({
url:decodeURIComponent(this.$router.params.url)
})
}
项目优化
微信小程序不是原生开发的产物,它的渲染载体不是原生的客户端,仍然是浏览器的内核。相对于传统网页来说,微信小程序开启了双线程模型,一个单独的线程用来渲染 UI 视图层,另一个单独的线程用来执行 JS 逻辑,控制视图层的展示。线程之间的数据传输都会存在一定的延时,也就是说小程序的视图层和逻辑层之间的通信都是异步操作,此时就会面临着页面渲染速度,首屏加载白屏等一系列问题的出现,为了提升小程序性能,规避这些问题我们对此采取了一系列的解决方案。
组件颗粒度细化
众所周知目前大家都在推崇组件化开发,推崇组件化开发并不是说把每一个小模块都要细化提取出组件,如果对于自定义组件的颗粒度太细化,对于自己来说都是一项相对复杂且繁琐的过程,对于其他开发者来说,该项目代码的阅读性就会变得很不友好。每个组件内都有自己的独立节点以及各自相应的逻辑,如果对于自定义组件的颗粒度太粗,组件内部逻辑过多,就会影响组件内部新旧节点 diff 的效率,从而影响数据渲染的性能,因此对于自定义的组件细化应该还是要把握一个适当的度。
在小易来了小程序中我们的组件化处理的有列表页的数据项,搜索,数据为空提示页,选择项页,其中主要介绍下搜索组件,该组件复用到了订单列表页,电子签收页,售后申请页,发票查询页;最初项目一期开发时只有订单列表页有搜索的功能,当时并没有提取搜索组件出来,在后期项目的迭代中涉及到发票查询,售后申请页等都需要搜索功能,我们才将其提取为公共组件,这也就是上面所述的组件细化需要具体情况具体分析,对于项目中需要复用该组件的页面比较多,同时各个页面在复用该组件时不需要增加区分逻辑或只需增加很少的区分逻辑时就可以将其提取为公共组件,就比如小易中的搜索组件,只需区分显示的默认搜索文案即可。
通过适当的去处理组件细化,既可以减小数据更新时的影响范围,又能支持组件复用,对于项目而言是一个相对较合适的方案。对于组件细化的程度没有一个具体的规定,具体项目需要具体分析,切记不可过度拆分组件,否则该工程整体代码的可读性将会变得极其差。
图片资源优化
图片资源对于移动端系统中是占据大流量的部分,在小程序中也是一样,尤其是对于加载带有相关商品图的列表页,优化图片资源的加载可以有效地加快页面响应时间,提升页面渲染速度。
使用 WebP 格式图片
- WebP 是 Google 推出的一种支持有损/无损压缩的图片文件格式,据官方描述,WebP 无损压缩体积比 PNG 小 26%,有损压缩体积比 JPEG 小 25-34%。目前大多数项目列表的渲染都是由后台接口返回相关的数据,再由前端拿到相应的数据去进行渲染,对于返回图片的格式往往都是 .jpg 或者 .png 的格式,针对返回的图片格式,要想优化图片资源,此时只能由前端去处理相关的操作,对于图片的格式处理一般情况下我们由两种方式,一是将图片 url 缓存下来,进行格式替换后再去渲染;二是在列表渲染时直接替换格式。
- 在小易项目中我们将返回的图片格式和处理后的图片格式做了下对比,发现原有 .jpg 格式的图片 size 为 37.3KB,处理后的 .webp 格式的图片 size 为 15.7KB,体积上几乎能达到 40% 的差距。通过处理将原图片格式改为 webp 格式,减少了图片的体积,减小了服务器压力,提升了渲染性能。
注意一点:确保服务器存在 webp 格式的图片资源,否则图片会无法渲染(可在 onError 事件做兼容性处理使用图片源地址)
虚拟列表
我们平常开发中大多数的列表页都是滚动分页加载,每次加载固定的条数,对于每次加载的条数我们都是一次性将其全量渲染完成。对于渲染数据量较大的列表时就会导致一定的性能问题,存在页面卡死的隐患,针对该隐患问题,我们需要做到可视化区域渲染,而不是全量渲染,非可视化区域的列表数据在滚动到可视区域后再去渲染,就类似于图片懒加载。Taro 框架中针对此类问题,封装了一个内置组件,命名为虚拟列表(Virtual List,该组件现在内置在 Taro 中,在 React/Vue 或各种小程序及 H5 皆可使用,在使用时我们直接将虚拟列表(VirtualList)组件引入到相应的页面即可。
import VirtualList from `@tarojs/components/virtual-list`
数据预加载
数据预拉取( Taro 中的预加载)能够在小程序冷启动的时候通过微信后台提前向第三方服务器拉取业务数据,当代码包加载完时可以更快地渲染页面,减少用户等待时间,从而提升小程序的打开速度。它的原理其实就是提前把响应数据存储在本地客户端供小程序前端调取,当小程序加载完成后,只需调用 Taro 提供的 API getBackgroundFetchData 从本地缓存获取数据即可。
在小易小程序中列表页没有增加数据预拉取的操作,仅仅是在部分页面跳转时尝试进行了预拉取操作。之所以没有在列表页进行数据预拉取,是因为数据预拉取虽然会提升页面的渲染速度,但也会存在一定的弊端,由于数据请求是由微信的后台服务器发出,请求到的数据会在本地存在一定的缓存时间,在缓存时间有效期内不会重新发起请求,这样对于实时频繁更新的列表就会造成一定的风险,因此我们在小易小程序中只是在部分不需要实时频繁更新数据的页面做了跳转时预拉取的操作。
我们为了尽快的获取服务器数据渲染页面,通常情况下都是在页面的 componentWillMount 的生命周期内发送请求来获取后台服务器数据,要知道路由跳转到对应的页面直到页面加载完成的过程中,小程序要完成环境初始化和页面实例初始化一系列的操作,此时就会产生一定的耗时,为了有效地避免这部分耗时,我们采取在页面路由跳转之前,也就是在调用 Taro.navigateTo 之前就请求将要跳转到的页面的接口,将请求到的数据缓存下来,存储在 Redux 状态管理器中,等到下一个页面加载完成后直接从 Redux 状态管理器中读取数据即可,这样就能保证页面在加载时快速拿到相应的数据渲染,提升了页面渲染速度,降低了等待时间。
除此之外 Taro 还提供了 componentWillPreload 钩子,它接收页面跳转的参数作为参数。可以把需要预加载的内容通过 return 返回,然后在页面触发 componentWillMount 后即可通过 this.$preloadData 获取到预加载的内容。有一点需要注意的是调用跳转方法时需要使用绝对路径,相对路径不会触发此钩子,具体使用可查阅 预加载的相关文档。
总结
该项目是我第一次使用 Taro+Taro UI 来开发小程序,通过查阅官方文档,网上调研等方式解决了开发过程中所遇到的一些问题,同时在本次项目开发中加入了性能提升方面的解决方案,通过测试对比之前在性能方面得到了可观的提升,自己也是收获颇丰,受益良多。目前小易还在迭代期,在之前的配送清单需求开发中我已经尝试了使用 Hooks 开发,后续的需求迭代时计划全部使用 Hooks 开发并将其之前的页面改造为 TS+Hooks 开发。目前 Taro 框架也在不断的迭代中,自 2.2 开始,Taro 引入了插件化机制,允许开发者通过编写插件的方式来为 Taro 拓展更多功能或者为自身业务定制个性化功能,在近期发布的 3.0 候选版本也已经支持使用 Vue 语言,作为一个支持多端转化的工具框架值得大家选择。