从Mpvue到Taro上手指南

因为自己有小程序和 React 的相关经验,最近上手了一下Taro,但是小程序一直是用vue 相关的框架写的,所以还是感到他们之间的区别。 其实是从vue 转变为react

相同

生命周期对比

Taro官方是基于16之前,所以一些生命周期还是保留下来。 ```对比下react 官网 的生命周期 挂载 当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:

  • constructor()

  • static getDerivedStateFromProps()

  • render()

  • componentDidMount()

componentwillMount 将被舍弃 更新 当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:

  • static getDerivedStateFromProps()

  • shouldComponentUpdate()

  • render()

  • getSnapshotBeforeUpdate()

  • componentDidUpdate()

卸载 当组件从 DOM 中移除时会调用如下方法:

  • componentWillUnmount()

舍弃了:

  • componentWillMount,是当组件在进行挂载操作前,执行的函数,一般紧跟着 constructor 函数后执行

  • componentWillReceiveProps,当组件收到新的 props 时会执行的函数,传入的参数就是 nextProps ,你可以在这里根据新的 props 来执行一些相关的操作,例如某些功能初始化等

  • componentWillUpdate,当组件在进行更新之前,会执行的函数

离开的顺序: index的跳转方法 后面跟着componentdDidHide
进入的顺序: componentWillMount componentDidShow componentDidMount

区别

事件点击方式

当你通过 bind 方式向监听函数传参, 在类组件中定义的监听函数, 事件对象 e 要排在所传递参数的后面。
class Popper extends Component {
    constructor() {
        super(...arguments)
        this.state = {
            name: 'Hello world!'
        }
    }
    // 你可以通过 bind 传入多个参数
    preventPop(name, test, e) { //事件对象 e 要放在最后
        e.stopPropagation()
    }
    render() {
        return <Button onClick = {
            this.preventPop.bind(this, this.state.name, 'test')
        } > < /Button>
    }

Mpvue中的filter, Taro有吗

项目配置

Taro 有专门的config 配置文件,里面的配置项还是很丰富的。以下是部分配置项

  deviceRatio: {
    '375': 1 / 2,
    '640': 2.34 / 2,
    '750': 1,
    '828': 1.81 / 2
  },
  alias: {
    '@/utils': path.resolve(__dirname, '../src/utils'),
    '@/components': path.resolve(__dirname, '../src/components'),
    '@/servers': path.resolve(__dirname, '../src/servers'),
    '@/assets': path.resolve(__dirname, '../src/assets'),
    '@/constants': path.resolve(__dirname, '../src/constants')
  },

事件循环

不能使用 Array#map 之外的方法操作 JSX 数组 numbers.filter(isOdd).map((number) => ) for (let index = 0; index < array.length; index++) { // do you thing with array } const element = array.map(item => { return })

taro组件使用keys
小程序原生组件使用taroKeys
const numbers = [...Array(100).keys()] // [0, 1, 2, ..., 98, 99]
const listItems = numbers.map((number) => {
  return (
    // native component
    <g-list
      taroKey={String(number)}
      className='g-list'
    >
    我是第 {number + 1} 个数字
    </g-list>
  )
})
 Taro 中,JSX 会编译成微信小程序模板字符串,因此你不能把 map 函数生成的模板当做一个数组来处理。当你需要这么做时,应该先处理需要循环的数组,再用处理好的数组来调用 map 函数。例如上例应该写成:
const list = this.state.list
  .filter(l => l.selected)
  .map(l => {
    return <li>{l.text}</li>
  })

api

首先是api 的命名方式不同, 在 Taro 里面没有wx,这个api开头,所有的一切都要转变成Taro, 可以看下面这个例子:

    Taro.chooseImage({
        sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
        sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
        success: function(res) {
            Taro.showLoading({
                title: '上传中',
            });
            // 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片
            let filePath = res.tempFilePaths[0];
            const name = Math.random() * 1000000;
            const cloudPath = name + filePath.match(/\.[^.]+?$/)[0]
            Taro.$upload(cloudPath, filePath);
        },
        fail: e => {
            console.error('[上传图片] 失败:', e)
        },
        complete: () => {
            Taro.hideLoading()
        }
    })

微信提供的回调是bindgetuserinfo,但是Taro将bind事件都封装成了on事件,这个需要注意一下 你基本都能使用小程序本身提供的 API 达到同等的需求,其中就包括但不限于:

  1. 使用 this.$scope.triggerEvent 调用通过 props 传递的函数;

  2. 通过 this.$scope.selectComponent 和 wx.createSelectorQuery 实现 ref;

  3. 通过 getCurrentPages 等相关方法访问路由;

  4. 修改编译后文件 createComponent 函数创建的对象

ref

Trao 给了三种 ref 调用方式,

    const refDay = (node) => this.MDay = node // `this.MDay` 会变成 `MDay` 组件实例的引用
    const refDialog = (node) => this.MDialog = node // `this.MDialog` 会变成 `MDialog` 组件实例的引用

slot

Children 与组合 相当于slot 请不要对 this.props.children 进行任何操作。 this.props.children && this.props.children、this.props.children[0] 在 Taro 中都是非法的。 this.props.children 无法用 defaultProps 设置默认内容。 不能把 this.props.children 分解为变量再使用 通过字符串创建 ref 只需要把一个字符串的名称赋给 ref prop 跟vue 不同的点。不允许在jsx 参数中传入jsx元素 不能使用内置组件化的slot 功能 通过字符串创建 ref 只需要把一个字符串的名称赋给 ref prop

// 如果 ref 的是小程序原生组件,那只有在 didMount 生命周期之后才能通过

    // this.refs.input 访问到小程序原生组件
    if (process.env.TARO_ENV === 'weapp') {
      // 这里 this.refs.input 访问的时候通过 `wx.createSeletorQuery` 取到的小程序原生组件
    } else if (process.env.TARO_ENV === 'h5') {
      // 这里 this.refs.input 访问到的是 `@tarojs/components` 的 `Input` 组件实例
    }

通过传递一个函数创建 ref, 在函数中被引用的组件会作为函数的第一个参数传递。 通过函数创建的ref 是不是不能在函数式组件中使用 果然如此

  this.cat = Taro.createRef()
  roar () {
    // 会打印 `miao, miao, miao~`
    this.cat.current.miao()
  }
//无效
<Custom child={<View />} />
<Custom child={() => <View />} />
<Custom child={function () { <View /> }} />
<Custom child={ary.map(a => <View />)} />

解决方案 通过 props 传值在 JSX 模板中预先判定显示内容,或通过 props.children 来嵌套子组件。 小程序端不要在组件中打印传入的函数 this.props.onXxx && this.props.onXxx() 这种判断函数是否传入来进行调用的写法是完全支持的。

render () {
  // 增加一个兼容判断
  return this.state.abc && <Custom adc={abc} />
}

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

mobx 状态管理与vuex

mobx: computed(function) 创建的函数只有当它有自己的观察者时才会重新计算,否则它的值会被认为是不相关的。 经验法则:如果你有一个函数应该自动运行,但不会产生一个新的值,请使用 autoRun。 vuex

优化

下面是官网摘录的相关优化方式:

  1. 不要直接更新状态

this.setState({
  value: this.state.value + 1
})   // ✗ 错误

this.setState(prevState => ({ value: prevState.value + 1 }))    // ✓ 正确
  1. 状态更新一定是异步的

    Taro 可以将多个 setState() 调用合并成一个调用来提高性能。 因为 this.state 和 props 一定是异步更新的,所以你不能在 setState 马上拿到 state 的值,例如:

假设我们之前设置了 this.state.counter = 0
updateCounter() {
    this.setState({
        counter: 1
    }, () => {
        // 在这个函数内你可以拿到 setState 之后的值
    })
}

合并是浅合并,所以 this.setState({comments}) 不会改变 this.state.posts 的值,但会完全替换 this.state.comments 的值。

css样式

classname

    const vStyle = classNames({
      playing: true,
      'vStyle-a': id === 'A',
      'vStyle-b': id === 'B',
      'vStyle-c': id === 'C'
    });
    const pStyle = classNames(
      'circle',
      { 'whiteCircle': playState === 'PLAY_START' },
      { 'blueCircle': playState === 'PLAY_LOAD' },
      { 'whiteCircle': playState === 'PLAY_STOP' }
    )
<View className={vStyle}>
    <View className={ `${pStyle}` } onClick={this.clickPlay}>
      {Triangle
      ?<Image className='Triangle' src={play}></Image>
      :
      <Image className='Triangle' src={stop}></Image>
      }
</View>

最后更新于