Taro 就是可以用 React 语法写小程序的框架,拥有多端转换能力,一套代码可编译为微信小程序、百度小程序、支付宝小程序、H5、RN等
1、入门
1.1、安装 CLI 及项目初始化
npm install -g @tarojs/cli
taro init 项目名
可以选择使用 SCSS 、TS、Redux
1.2、编译至各种平台
// 编译为小程序
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 参数中使用对象展开符
开发者自行赋值:
<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
import Taro, { Component } from '@tarojs/taro'
class App extends Component {}
Taro.render(<App />, document.getElementById('app'))
- @tarojs/redux
taro 状态管理辅助库,相当于 react-redux
import { Provider,connect } from '@tarojs/redux'
- @tarojs/components
taro 为屏蔽多端差异而制定的标准组件库,在taro中不能直接写常规的HTML标签,而必须用这个组件库里的标签,就像写RN一样:
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之类的常用的业务组件
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、常用工具类封装
- 本地存储
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();
- 请求封装
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介绍
- 授权
<Button
className='btn-max-w'
plain
type='primary'
open-type='getUserInfo'
onGetUserInfo={this.handleUserInfo}
>
授权
</Button>
- 获取位置
Taro.getLocation({type:'gcj02 '}).then(data=>console.log(data))
- 操作反馈
Taro.showToast({
title: "成功",
icon: "success"
});
Taro.setTabBarBadge({ index: 1, text: "1" });
Taro.showLoading({
title: "加载中..."
}).then(res =>
setTimeout(() => {
Taro.hideLoading();
}, 2000)
);
4、其他问题记录
- 设置页面全屏
// app.scss
page {
height: 100%;
}
- Css Modules 支持
配置 config/index.js 下的 h5 和 weapp 中的 module.cssModules 即可
// css modules 功能开关与相关配置
cssModules: {
enable: true, // 默认为 false,如需使用 css modules 功能,则设为 true
config: {
namingPattern: 'module', // 转换模式,取值为 global/module,下文详细说明
generateScopedName: '[name]__[local]___[hash:base64:5]'
}
}
- 配置路径别名
配置 config/index.js 下的 alias
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'),
},