Taro 小程序采坑

6,030 阅读3分钟

Taro 简介

Taro 是一套遵循 React 语法规范的 多端开发的解决方案。

Taro 开发微信小程序

首次使用必须安装 Taro 命令行工具

# 使用 npm 安装 CLI
$ npm install -g @tarojs/cli
# OR 使用 yarn 安装 CLI
$ yarn global add @tarojs/cli

项目初始化

$ taro init myApp

运行

# yarn
$ yarn dev:weapp
# npm script
$ npm run dev:weapp

Taro 项目结构

├── config                 配置目录
|   ├── dev.js             开发时配置
|   ├── index.js           默认配置
|   └── prod.js            打包时配置
├── src                    源码目录
|   ├── components         公共组件目录
|   ├── pages              页面文件目录
|   |   ├── index          index 页面目录
|   |   |   ├── banner     页面 index 私有组件
|   |   |   ├── index.js   index 页面逻辑
|   |   |   └── index.css  index 页面样式
|   ├── utils              公共方法库
|   ├── app.css            项目总通用样式
|   └── app.js             项目入口文件
└── package.json

Taro 设计稿及尺寸单位

需要在项目配置 config/index.js 中进行设置

# 目前 Taro 支持 750、 640 、 828 三种尺寸设计稿
const config = {
  projectName: 'businessAC',
  date: '2019-9-23',
  designWidth: 750,
  deviceRatio: {
    '640': 2.34 / 2,
    '750': 1,
    '828': 1.81 / 2
 },
# 行内样式可通过 Taro.pxTransform 来进行转换
Taro.pxTransform(10) // 小程序:rpx,H5:rem
# 默认配置会对所有的 px 单位进行转换,有大写字母的 Px 或 PX 则会被忽略。
{
  onePxTransform: true, # 设置 1px 是否需要被转换
  unitPrecision: 5, # REM 单位允许的小数位。
  propList: ['*'], # 允许转换的属性。
  selectorBlackList: [], # 黑名单里的选择器将会被忽略。
  replace: true, # 直接替换而不是追加一条进行覆盖。
  mediaQuery: false, # 允许媒体查询里的 px 单位转换
  minPixelValue: 0 # 设置一个可被转换的最小 px 值
}

Taro 路由跳转

// 保留当前页面,跳转到应用内的某个页面,调用 navigateTo 跳转时,调用该方法的页面会被加入堆栈,使用wx.navigateBack可以返回到上一级页面。
Taro.navigateTo({
  url: '/pages/page/path/name'
})

// 关闭当前页面,跳转到应用内的某个页面。当前页面将不在路由堆中。
Taro.redirectTo({
  url: '/pages/page/path/name'
})

// 关闭当前页面,返回上一页面或多级页面。可通过 Taro.getCurrentPages() 获取当前的页面栈,决定需要返回几层。
Taro.navigateBack({
  delta: 2
})

// 关闭所有页面,打开到应用内的某个页面。
Taro.reLanch({
  url: 'page/home/home?params=xxx'
})

静态资源的引入

# 引入 scss
import './css/path/name.scss'
# 引入 js
import { functionName } from './css/path/name.js'
# 引入 图片
import namedPng from '../../images/path/named.png'

条件渲染

# 逻辑运算符 &&
{isLoggedIn && <Text>已登录</Text>}
{!isLoggedIn && <Text>未登录</Text>}

# 三元运算符(条件表达式)
{isLoggedIn ? <Text>已登录</Text> : <Text>未登录</Text>}

# 枚举条件渲染 (0.0 看文档才发现还可以这样...)
{
    {
      'loading': loadingText,
      'fail': <View onClick={onRetry}> 加载失败, 点击重试 </View>,
      'no-more': '没有更多了'
    }[loadingStatus] # loadingStatus 是 `loading`、`fail`、`no-more`  其中一种状态 
}

组件使用 PropTypes 检查类型 与 defaultProps 设置默认值

export default class Picker extends PureComponent {

  static propTypes = {
    data: PropTypes.array,
    itemHeight: PropTypes.number,
    tempIndex: PropTypes.number,
    onJustTemp: PropTypes.func,
  }

  static defaultProps = {
    data: [],
    itemHeight: 100,
    tempIndex: 1,
    onJustTemp: () => console.log('please attach a method to Card Component')
  }
  
  render() {
    return (
      <h1>Hello, {this.props.itemHeight}</h1>
    );
  }

外部样式类 可以使用 externalClasses

# CustomComp.js
export default class CustomComp extends Component {
  static externalClasses = ['my-class']

  render () {
    return <View className="my-class">这段文本的颜色由组件外的 class 决定</View>
  }
}

# MyPage.js
export default class MyPage extends Component {
  render () {
    return <CustomComp my-class="red-text" />
  }
}
# MyPage.scss 
.red-text {
  color: red;
}

注意:externalClasses 需要使用 短横线命名法 (kebab-case),而不是 React 惯用的 驼峰命名法 (camelCase)。否则无效。

全局样式类

# 使用外部样式类可以让组件使用指定的组件外样式类,
# 如果希望组件外样式类能够完全影响组件内部,
# 可以将组件构造器中的 options.addGlobalClass 字段置为 true。
# CustomComp.js 
export default class CustomComp extends Component {
  static options = {
    addGlobalClass: true
  }

  render () {
    return <View className="red-text">这段文本的颜色由组件外的 class 决定</View>
  }
}

# 组件外的样式定义 
.red-text {
  color: red;
}

预加载

在微信小程序、支付宝小程序、字节跳动小程序和QQ轻应用中,从调用 Taro.navigateTo、Taro.redirectTo 或 Taro.switchTab 后,到页面触发 componentWillMount 会有一定延时。因此一些网络请求可以提前到发起跳转前一刻去请求。

Taro 提供了 componentWillPreload 钩子,它接收页面跳转的参数作为参数。可以把需要预加载的内容通过 return 返回,然后在页面触发 componentWillMount 后即可通过 this.$preloadData 获取到预加载的内容。

class Index extends Component {
  componentWillMount () {
    console.log('isFetching: ', this.isFetching)
    this.$preloadData
      .then(res => {
        console.log('res: ', res)
        this.isFetching = false
      })
  }

  componentWillPreload (params) {
    return this.fetchData(params.url)
  }

  fetchData () {
    this.isFetching = true
    ...
  }
}

在小程序中,可以使用 this.$preload 函数进行页面跳转传参

用法:this.$preload(key: String | Object, [ value: Any ])

之所以命名为 $preload,因为它也有一点预加载数据的意味。

如果觉得每次页面跳转传参时,需要先把参数 stringify 后加到 url 的查询字符串中很繁琐,可以利用 this.$preload 进行传参。

另外如果传入的是下一个页面的数据请求 promise,也有上一点提到的“预加载”功能,也能够绕过 componentWillMount 延时。不同点主要在于代码管理,开发者可酌情使用。

// 传入单个参数

// A 页面
// 调用跳转方法前使用 this.$preload
this.$preload('key', 'val')
Taro.navigateTo({ url: '/pages/B/B' })

// B 页面
// 可以于 this.$router.preload 中访问到 this.$preload 传入的参数
componentWillMount () {
  console.log('preload: ', this.$router.preload.key)
}

// 传入多个参数

// A 页面
this.$preload({
  x: 1,
  y: 2
})
Taro.navigateTo({ url: '/pages/B/B' })

// B 页面
componentWillMount () {
  console.log('preload: ', this.$router.preload)
}

全局变量

在 Taro 中推荐使用 Redux 来进行全局变量的管理,但是对于一些小型的应用, Redux 就可能显得比较重了,这时候如果想使用全局变量,推荐如下使用。

新增一个自行命名的 JS 文件,例如 global_data.js,示例代码如下

const globalData = {}

export function set (key, val) {
  globalData[key] = val
}

export function get (key) {
  return globalData[key]
}

# 使用
import { set as setGlobalData, get as getGlobalData } from './path/name/global_data'

setGlobalData('test', 1)

getGlobalData('test')

入口文件的生命周期

生命周期方法 作用 说明
componentWillMount 程序被载入 在微信小程序中这一生命周期方法对应 app 的 onLaunch
componentDidMount 程序被载入 在微信小程序中这一生命周期方法对应 app 的 onLaunch,在 componentWillMount 后执行
componentDidShow 程序展示出来 在微信小程序中这一生命周期方法对应 onShow,在 H5 中同样实现
componentDidHide 程序被隐藏 在微信小程序中这一生命周期方法对应 onHide,在 H5 中同样实现
componentDidCatchError 错误监听函数 在微信小程序中这一生命周期方法对应 onError
componentDidNotFound 页面不存在监听函数 在微信小程序中这一生命周期方法对应 onPageNotFound

微信小程序中 onLaunch 通常带有一个参数 options,在 Taro 中你可以在所有生命周期和普通事件方法中通过 this.$router.params 访问到

页面的生命周期

生命周期方法 作用 说明
componentWillMount 页面被载入 在微信小程序中这一生命周期方法对应 onLoad
componentDidMount 页面渲染完成 在微信小程序中这一生命周期方法对应 onReady
componentWillReceiveProps props改变
shouldComponentUpdate 页面是否需要更新
componentWillUpdate 页面即将更新
componentDidUpdate 页面更新完毕
componentWillUnmount 页面退出 在微信小程序中这一生命周期方法对应 onUnload
componentDidShow 页面展示出来 在微信小程序中这一生命周期方法对应 onShow,在 H5 中同样实现
componentDidHide 页面被隐藏 在微信小程序中这一生命周期方法对应 onHide,在 H5 中同样实现

小程序 navigateBack 返回上一级页面 componentWillMount 周期不再执行

未解决... 由于 navigateBack 的页面会执行 componentDidShow 所以把请求函数写在 componentDidShow 这一周期里 0.0

调用 this.setState 时使用 this.state

this.setState({ value: this.state.value + 1 })   # ✗ 错误
this.setState(prevState => ({ value: prevState.value + 1 }))    # ✓ 正确

在 componentWillUpdate/componentDidUpdate/render 中调用 this.setState

# componentWillReceiveProps 可以在这一生命周期里更新 最新的 props 从 nextProps 获取
componentWillReceiveProps = (nextProps) => {
    this.setState({ pickIndex: nextProps.tempIndex })
}

不能在包含 JSX 元素的 map 循环中使用 if 表达式

# 会被 ESLint 提示警告,同时在 Taro(小程序端)也不会有效
numbers.map((number) => {
  let element = null
  const isOdd = number % 2
  if (isOdd) {
    element = <Custom />
  }
  return element
})
# 可以写成这样
numbers.map((number) => {
  let isOdd = false
  if (number % 2) {
    isOdd = true
  }
  return isOdd && <Custom />
})