# 前文

因为自己有小程序和 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，当组件在进行更新之前，会执行的函数

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

## 区别

### 事件点击方式

当你通过 bind 方式向监听函数传参， 在类组件中定义的监听函数， 事件对象 e 要排在所传递参数的后面。

```js
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 有吗

```js
// utils/format.js
export const currency = (value) => `$${value.toFixed(2)}`;

// 组件
import { currency } from "../utils/format";

const MyComponent = () => {
  const price = 123.456;
  return <View>{currency(price)}</View>;
};
```

### 项目配置

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

```js
  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 })

```js

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, 可以看下面这个例子:

```js
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 调用方式，

```js
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

```js
// 如果 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 是不是不能在函数式组件中使用 果然如此

```js
  this.cat = Taro.createRef()
  roar () {
    // 会打印 `miao, miao, miao~`
    this.cat.current.miao()
  }
```

```jsx
//无效
<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()  这种判断函数是否传入来进行调用的写法是完全支持的。

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

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

### MobX 状态管理与 vuex

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

## 优化

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

1. 不要直接更新状态

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

this.setState((prevState) => ({ value: prevState.value + 1 })); // ✓ 正确
```

2. 状态更新一定是异步的

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

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

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

## css 样式

### classname

```js
    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>

```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://shenjunhong.gitbook.io/blog/chang-jian-zong-jie/jin-qi/xiao-cheng-xu/cong-mpvue-dao-taro-shang-shou-zhi-nan.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
