Taro 在京东购物小程序上的实践
-
文章作者: HM 于 2018-09-11 17:00:00
Taro 简介
Taro
是一个基于React
语法规范的多端统一开发框架,大家可以通过 taro.aotu.io 进一步了解。而前段时间 Taro 发布后,京东购物小程序就开始了部分页面基于 Taro 的重构工作,本文便是对商品分类页使用 Taro 进行代码重构的一些实践分享。混合开发模式
过去的京东购物小程序未使用任何第三方框架,而是在原生小程序模式的基础上,进行了页面/组件基类、网络请求、本地存储、页面跳转等模块的封装。由于项目庞大(涉及 100 多个页面),把整个项目直接改造成 Taro 的开发方式肯定是不可行的,于是采用这么一种原生小程序与 Taro 相混合的开发模式,将部分旧页面使用 Taro 重构,部分新的页面则直接使用 Taro 进行开发。这里以商品分类页为例,先来看下原京东购物小程序项目的目录结构:
├── dist │ ├── app.js │ ├── app.json │ ├── app.wxss │ ├── assets/ │ ├── common/ │ ├── libs/ │ └── pages │ ├── cate │ │ ├── components/ │ │ ├── index.js │ │ ├── index.json │ │ ├── index.wxml │ │ └── index.wxss │ └── index/ ├── src/ ├── README.md ├── gulpfile.js ├── package.json └── node_modules/
1. 初始化 Taro
在项目根目录处运行命令
taro init jdwxa-taro
进行初始化,完成后会新增一个名为jdwxa-taro
的目录,Taro 相关的源代码就写在该目录中:├── dist/ ├── src/ └── jdwxa-taro ├── config │ ├── dev.js │ ├── index.js │ └── prod.js ├── node_modules/ ├── package.json ├── project.config.json └── src ├── app.js ├── app.scss ├── index.html └── pages └── cate ├── index.js └── index.scss
2. Taro 配置
独立的 Taro 项目会将包括
app.js
、app.json
、app.wxss
及页面文件均生成在 dist/ 目录中,而混合开发模式下只需要生成单个页面,这里需要对 Taro 进行一些配置,打开并编辑config/index.js
文件:const config = { outputRoot: '../dist', weapp: { appOutput: false, npm: { dir: '../../dist/common', name: 'taro' } }, // ... }
如代码所示,
outputRoot
字段为生成目标页面的存放路径,这里把它指向顶层(即原项目)的 dist/ 目录;weapp
部分,我们把appOutput
设置成 false, 这样就不会生成 app.js、app.json、app.wxss 三个文件了,npm
字段则表示 Taro 运行时框架文件的存放目录,这里遵循原项目的规范,把它指定为 common/ 目录。这样 Taro 编译生成的目标文件就完美地融入进了原小程序项目。3. 页面开发
页面开发过程中,跟原生小程序最大的不同就是
React
+JSX
的编码方式了,习惯了原生小程序的同学可能要一些适应过程,具体的编码就不细说了,这里提几点注意事项:- 与小程序的
setData
方法不同,Taro 用于更新页面数据的setState
是异步的,相关代码的执行时序需要特别注意; - 为了方便 JSX 模板的书写,原先很长的 WXML 内容建议拆分成一些小的组件;
- 关于旧组件的复用,无论是小程序原生组件、普通 JS 模块、样式文件或是第三方组件库,都能很好的进行引入调用,这点无需担心;
- 目前对于 Taro 编译生成的目标代码,调试起来会有些困难,但对
SourceMap
的支持正在积极开发中。
4. 最终效果
如今重构后的商品分类页已经在线上稳定运行有一段时间了,可以扫描下面的小程序码进行体验:
Taro 带来的收益
多端运行
最大的收益便是可以生成多端版本,避免重复工作、节省开发成本。以分类页为例,只需运行
npm run build:h5
便可生成 H5 版本的分类页,运行效果和小程序一致,大家可以扫描下面的二维码进行体验:注:以上仅为 Taro 生成的示例页面,由于一些业务组件尚未完全适配两端,所以 H5 版本暂时没有正式投入使用。
性能提升
小程序项目中遇到的性能问题,大多是频繁地调用 setData 造成的,这是由于每调用一次
setData
,小程序内部都会将该部分数据在逻辑层(运行环境JSCore
)进行类似序列化的操作,将数据转换成字符串形式传递给视图层(运行环境WebView
),视图层通过反序列化拿到数据后再进行页面渲染,这个过程下来有一定性能开销。所以开发过程中,我们建议尽量对
setData
进行合并,减少调用次数,例如:this.setData({ foo: 'Strawberry' }) this.setData({ foo: 'Strawberry', bar: 'Fields' }) this.setData({ baz: 'Forever' })
以上代码调用了 3 次
setData
,造成不必要的性能开销,应对其进行合并:this.setData({ foo: 'Strawberry', bar: 'Fields', baz: 'Forever', })
而使用 Taro 之后,更新数据时调用的
setState
为异步方法,它会自动地对同一事件循环里的多次setState
调用进行合并处理,此外还会进行数据diff
优化,自动剔除那些未变更的数据,从而有效避免了此类性能问题。例如:// 初始时 this.state = { foo: '1967', bar: { foo: 'Strawberry', bar: 'Fields', baz: 'Forever', } } // 第一次更新 this.setState({ bar: { foo: 'Norwegian', bar: 'Fields', baz: 'Forever', } }) // 紧接着进行第二次更新 this.setState({ foo: '1967', bar: { foo: 'Norwegian', bar: 'Wood', baz: 'Forever', } })
以上代码虽然经过两次
setState
,但只有 bar.foo 和 bar.bar 的数据更新了,此时 Taro 内部会自动对数据进行合并、并剔除重复数据,最终执行代码为:// this.$scope 在小程序环境中为 page 实例 this.$scope.setData({ 'bar.foo': 'Norwegian', 'bar.bar': 'Wood', })
其他收益
比起原生小程序开发,Taro 带来了许多激动人心的特性(如支持 TypeScript、NPM、丰富的 JSX 语法、更高级的 ES 特性等等),不仅提升了开发体验,对自动化测试、持续构建等也会有不小的帮助。
举个例子,京东购物小程序里封装了一个
getImg
方法,该方法接受一个图片 url 及可选的宽高作为参数,然后根据设备类型决定是否使用 webp 格式、根据当前网络环境应用适当的图片压缩率、自动处理协议头和域名转换,最后生成符合目标大小的图片 url。我们要求所有的图片都必须经过getImg
方法处理后才能进行展示,但由于 JS 方法只能在逻辑层进行调用,处理好后再传递给WXML
进行展示,使得很难在自动化工具中进行检测,及时发现未调用getImg
输出图片的情况。而使用 Taro 之后,可以直接在
JSX
模板的Image
标签输出时对src
调用getImg
方法进行处理,将此种写法作为规范明确后,就很容易通过自动化工具进行检测了:render () { return <Image src={getImg(url, 750)} /> }
So 对于现有项目来说,不需要进行整体重构,也能很好的将 Taro 集成进去。还等什么,赶紧试试吧~
- 与小程序的