因为自己有小程序和 React 的相关经验,最近上手了一下 Taro,但是小程序一直是用 vue 相关的框架写的,所以还是感到他们之间的区别。 其实是从 vue 转变为 react
相同
生命周期对比
Taro 官方是基于 16 之前,所以一些生命周期还是保留下来。
复制
挂载
当组件实例被创建并插入 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 要排在所传递参数的后面。
复制 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 有吗
复制 // 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 配置文件,里面的配置项还是很丰富的。以下是部分配置项
复制 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 达到同等的需求,其中就包括但不限于:
使用 this.$scope.triggerEvent 调用通过 props 传递的函数;
通过 this.$scope.selectComponent 和 wx.createSelectorQuery 实现 ref;
通过 getCurrentPages 等相关方法访问路由;
修改编译后文件 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
优化
下面是官网摘录的相关优化方式:
复制 this.setState({
value: this.state.value + 1,
}); // ✗ 错误
this.setState((prevState) => ({ value: prevState.value + 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>