Taro 就是可以用 React 语法写小程序的框架,拥有多端转换能力,一套代码可编译为微信小程序、百度小程序、支付宝小程序、H5、RN 等
1、入门
1.1、安装 CLI 及项目初始化
1 2
| npm install -g @tarojs/cli taro init 项目名
|
可以选择使用 SCSS 、TS、Redux
1.2、编译至各种平台
1 2 3 4 5 6 7
| // 编译为小程序 npm run dev:weapp npm run build:weapp // 编译为 H5 npm run dev:h5 // 编译为 RN npm run dev:rn
|
编译为小程序时,小程序代码位于 dist 目录下
1.3、微信小程序须知
- 小程序注册
注册地址,注意一个邮箱只能注册一个小程序
- 小程序后台
后台地址,后台可查看当前小程序版本,添加开发者,查看小程序 AppID 和 AppSecret 等功能
- 小程序开发者工具
下载地址
- 小程序开发流程
1、在开发者工具中新建项目,填入对应的 AppID
2、在小程序后台配置服务器域名(开发-服务器域名)
- 小程序发布流程
1、在开发者工具中上传代码
2、在管理后台-版本管理-开发版本中提交审核,注意提交审核前可先生成体验版,确认体验版没问题后再提交审核
2、注意点
由于 Taro 编译后的代码已经经过了转义和压缩,因此还需要注意微信开发者工具的项目设置
- 只能在 render 里使用 jsx 语法
- 不能在包含 JSX 元素的 map 循环中使用 if 表达式
尽量在 map 循环中使用条件表达式或逻辑表达式
- 不能使用 Array.map 之外的方法操作 JSX 数组
先处理好需要遍历的数组,然后再用处理好的数组调用 map 方法。
- 不能在 JSX 参数中使用匿名函数
使用 bind 或 类参数绑定函数。
- 不能在 JSX 参数中使用对象展开符
开发者自行赋值:
1 2
| <View {...props} /> // wrong <View id={id} title={title} /> // ok
|
- 不允许在 JSX 参数(props)中传入 JSX 元素
- 不支持无状态组件(Stateless Component)
- 函数名驼峰,且不能包含数字,不能以下划线开始或结尾以及长度不超过20
- 必须使用单引号,不支持双引号
- 对于 process.env,建议直接书写 process.env.NODE_ENV,而不是解构
- 组件传递函数属性名以 on 开头
- 小程序端不要将在模板中用到的数据设置为 undefined
- 小程序端不要在组件中打印 this.props.children
- 组件属性传递注意
不要以 id、class、style 作为自定义组件的属性与内部 state 的名称,因为这些属性名在微信小程序中会丢失。
- 组件 state 与 props 里字段重名的问题
不要在 state 与 props 上用同名的字段,因为这些被字段在微信小程序中都会挂在 data 上。
- 小程序中页面生命周期 componentWillMount 不一致问题
- 组件的 constructor 与 render 提前调用
3、Taro 实战
3.1、相关库介绍
- @tarojs/taro
taro 核心库,相当于 react
1 2 3
| import Taro, { Component } from '@tarojs/taro' class App extends Component {} Taro.render(<App />, document.getElementById('app'))
|
- @tarojs/redux
taro 状态管理辅助库,相当于 react-redux
1
| import { Provider,connect } from '@tarojs/redux'
|
- @tarojs/components
taro 为屏蔽多端差异而制定的标准组件库,在 taro 中不能直接写常规的 HTML 标签,而必须用这个组件库里的标签,就像写 RN 一样:
1 2 3 4 5 6 7
| import { View, Button, Text } from "@tarojs/components"; <View className='index'> <Button className='add_btn' onClick={this.props.add}> + </Button> <Text> Hello, World </Text> </View>
|
- @tarojs/async-await
taro 支持 async await 写法库
- taro-ui
taro 为屏蔽多端差异而制定的业务组件库,比如 Tabs,Modal,Menu 之类的常用的业务组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import { AtTabs, AtTabsPane } from "taro-ui"; <AtTabs current={this.state.current} tabList={tabList} onClick={this.handleClick.bind(this)} > <AtTabsPane current={this.state.current} index={0}> <AllContainer /> </AtTabsPane> <AtTabsPane current={this.state.current} index={1}> <View style='padding: 100px 50px;background-color: #FAFBFC;text-align: center;'> 标签页二的内容 </View> </AtTabsPane> <AtTabsPane current={this.state.current} index={2}> <View style='padding: 100px 50px;background-color: #FAFBFC;text-align: center;'> 标签页三的内容 </View> </AtTabsPane> </AtTabs>
|
3.2、常用工具类封装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import Taro from "@tarojs/taro";
class Store { removeItem(key) { return Taro.removeStorageSync(key); } getItem(key) { return Taro.getStorageSync(key); } setItem(key, value) { return Taro.setStorageSync(key, value); } clear() { return Taro.clearStorageSync(); } }
export default new Store();
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| import Taro from '@tarojs/taro' import { API_USER_LOGIN } from '@constants/api'
const CODE_SUCCESS = '200' const CODE_AUTH_EXPIRED = '600'
function getStorage(key) { return Taro.getStorage({ key }).then(res => res.data).catch(() => '') }
function updateStorage(data = {}) { return Promise.all([ Taro.setStorage({ key: 'token', data: data['3rdSession'] || '' }), Taro.setStorage({ key: 'uid', data: data['uid'] || '' }) ]) }
/** * 简易封装网络请求 * // NOTE 需要注意 RN 不支持 *StorageSync,此处用 async/await 解决 * @param {*} options */ export default async function fetch(options) { const { url, payload, method = 'GET', showToast = true } = options const token = await getStorage('token') const header = token ? { 'WX-PIN-SESSION': token, 'X-WX-3RD-Session': token } : {} if (method === 'POST') { header['content-type'] = 'application/json' }
return Taro.request({ url, method, data: payload, header }).then(async (res) => { const { code, data } = res.data if (code !== CODE_SUCCESS) { if (code === CODE_AUTH_EXPIRED) { await updateStorage({}) } return Promise.reject(res.data) }
if (url === API_USER_LOGIN) { await updateStorage(data) } return data }).catch((err) => { const defaultMsg = err.code === CODE_AUTH_EXPIRED ? '登录失效' : '请求异常' if (showToast) { Taro.showToast({ title: err && err.errorMsg || defaultMsg, icon: 'none' }) }
return Promise.reject({ message: defaultMsg, ...err }) }) }
|
3.3 常用 API 介绍
1 2 3 4 5 6 7 8 9
| <Button className='btn-max-w' plain type='primary' open-type='getUserInfo' onGetUserInfo={this.handleUserInfo} > 授权 </Button>
|
1
| Taro.getLocation({type:'gcj02'}).then(data=>console.log(data))
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Taro.showToast({ title: "成功", icon: "success" });
Taro.setTabBarBadge({ index: 1, text: "1" });
Taro.showLoading({ title: "加载中..." }).then(res => setTimeout(() => { Taro.hideLoading(); }, 2000) );
|
4、其他问题记录
1 2 3 4
| // app.scss page { height: 100%; }
|
- Css Modules 支持
配置 config/index.js 下的 h5 和 weapp 中的 module.cssModules 即可
1 2 3 4 5 6 7 8
| // css modules 功能开关与相关配置 cssModules: { enable: true, // 默认为 false,如需使用 css modules 功能,则设为 true config: { namingPattern: 'module', // 转换模式,取值为 global/module,下文详细说明 generateScopedName: '[name]__[local]___[hash:base64:5]' } }
|
- 配置路径别名
配置 config/index.js 下的 alias
1 2 3 4 5 6 7 8 9
| alias: { 'components': path.resolve(__dirname, '..', 'src/components'), 'pages': path.resolve(__dirname, '..', 'src/pages'), 'store': path.resolve(__dirname, '..', 'src/store'), 'constants': path.resolve(__dirname, '..', 'src/constants'), 'api': path.resolve(__dirname, '..', 'src/api'), 'assets': path.resolve(__dirname, '..', 'src/assets'), 'utils': path.resolve(__dirname, '..', 'src/utils'), },
|
- 多端兼容性问题,引入 lodash 包,支付宝不支持
引入 utils.js 文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| global.Object = Object global.Array = Array global.Buffer = Buffer global.DataView = DataView global.Date = Date global.Error = Error global.Float32Array = Float32Array global.Float64Array = Float64Array global.Function = Function global.Int8Array = Int8Array global.Int16Array = Int16Array global.Int32Array = Int32Array global.Map = Map global.Math = Math global.Promise = Promise global.RegExp = RegExp global.Set = Set global.String = String global.Symbol = Symbol global.TypeError = TypeError global.Uint8Array = Uint8Array global.Uint8ClampedArray = Uint8ClampedArray global.Uint16Array = Uint16Array global.Uint32Array = Uint32Array global.WeakMap = WeakMap global.clearTimeout = clearTimeout global.isFinite = isFinite global.parseInt = parseInt global.setTimeout = setTimeout
|
npm 包版本不统一导致
1 2 3 4 5 6 7 8 9 10 11 12 13
| "dependencies": { "@tarojs/components": "3.0.18", "@tarojs/react": "3.0.18", "@tarojs/runtime": "3.0.18", "@tarojs/taro": "3.0.18", }, "devDependencies": { "@tarojs/cli": "3.0.18", "@tarojs/mini-runner": "3.0.18", "@tarojs/webpack-runner": "3.0.18", "babel-preset-taro": "3.0.18", "eslint-config-taro": "3.0.18", },
|
config.js 中可以设置,但是必须要重新启动才可以
1 2 3
| sass: { resource: path.resolve(__dirname, '..', 'src/assets/styles/global.scss'), },
|
nginx 配合 fiddler 代理转发可以实现本地开发,app 端进行调试
可以通过 componentDidShow 或者 useDidShow 实现
用法实现 order.get(status)
利用回调函数,函数式编程实现
百度(bd09ll)、高德(gcj02)、腾讯(gcj02)
polyline, label, marker
动态设置展示区域
includePoints,并且通过 updateComponent 强制渲染,手机端和开发工具展示不一致问题
1 2 3 4 5 6 7 8 9 10 11 12 13
| this.mapCtx.updateComponents({ markers, includePoints, polyline, longitude: cLocation.lng, latitude: cLocation.lat, setting: { showCompass: 0, showScale: 0, gestureEnable: 1, tiltGesturesEnabled: 0, }, })
|
static 静态局部变量
发布订阅的 service 类库封装
- 定时器(汽车位置,订单状态,订单详情,支付状态),状态扭转类,支付类
类封装