本文适合已经熟练掌握react、微信小程序、ReactNative的开发者阅读。
既然能读到这里想必已经知道Taro是什么,这里不多介绍了。
本文主要从react的角度出发,探讨两者之间不同的一些点,以及H5和小程序兼容性在开发过程中要注意的问题。
启动一下
1 2 3 4 |
npm install -g @tarojs/cli // 全局安装taro taro init myApp // 初始化一个taro项目 npm run dev:weapp // 编译成微信小程序 npm run dev:h5 // 编译成H5 |
需要注意的是编译好的目录是dist,所以使用微信小程序开发工具打开的目录应该是dist目录,而不是Taro整个项目的目录,H5同理。
生命周期函数
生命周期函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
import Taro, { Component } from '@tarojs/taro' import { View, Text } from '@tarojs/components' import './index.less' export default class Index extends Component { config = { navigationBarTitleText: '首页' }; state = { name:'bing' }; componentWillMount () { console.log('第一次渲染之前执行,只执行一次') } componentDidMount () { console.log('第一次渲染之后执行,只执行一次') } componentWillUnmount () { console.log('卸载时执行,页面跳转时卸载,或被关闭刷新时卸载,只执行一次') } componentWillUpdate(nextProps, nextState, nextContext) { console.log('state数据将要更新') } componentDidUpdate(prevProps, prevState, prevContext) { console.log('state数据更新过后') } shouldComponentUpdate(nextProps, nextState, nextContext) { // 检查此次setState是否要进行render调用 // 返回true - 调用render 返回false - 不render // 一般用来多次的setState调用时,提升render性能 if(this.state.text === 'bing'){ return true; }else{ return false; } } componentWillReceiveProps(nextProps, nextContext) { // 父组件传递给子组件的参数发生改变的时候触发 } <span style="color: #ff0000;">componentDidShow () { // 在react中是不存在的 // 是为了支持小程序中的,也支持H5 console.log('页面显示时触发'); } componentDidHide () { // 在react中是不存在的 // 是为了支持小程序中的,也支持H5 console.log('页面隐藏的时触发') } </span> getName(){ return '111'; } render () { const {name} = this.state; return ( <View className='index'> <Text>{this.getName()}</Text> <Text>{name}</Text> </View> ) } } |
- 注意和react一样 state更新一定是异步的,也就是说更新之后不能够立刻拿到setState的值,要通过回调才行:
1 2 3 |
this.setState({name:'bing2'},()=>{ console.log(this.state.name) }) |
Props要注意的点
- 需要注意的是子组件的class的name必须和import的组件名保持一致,不会像react那样去找index
有时候子组件不确定父组件有没有传值,这个时候可以先默认给子组件props的默认值,当父组件传的是undefined的时候就会从默认的props中读取
1 2 3 |
Child.defaultProps={ name:{} }; |
另外一点需要特别注意的是在函数传值的时候需要传on比如说ontest,取的时候也要加上on,在H5中可以不加因为H5是真正的react,但是在小程序中必须加,所以都加上吧
路由资源及资源引用
使用路由
- 在Taro中,路由功能是自带的,不需要开发者进行额外的路由配置
- Taro路由相当于通过小程序的配置适配了小程序和H5的路由问题
- Taro默认根据配置路径生成了Route
- 我们只需要在入口文件的config中配置指定好pages,然后就可以在代码中通过taro提供的API来跳转到目的页面
- 放在最前面的就是首页的路径,我们只要将首页放在最前面即可
1 2 3 4 5 6 7 |
config = { pages: [ 'pages/test/test', 'pages/index/index' ], ... } |
- 用taro开发的时候要注意小程序的限制,路由最多只能开5层,下面是路由的跳转,由于要适配小程序,一定要注意什么时候用navigateTo什么时候用redirectTo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import Taro, { Component } from '@tarojs/taro' import { View, Button } from '@tarojs/components' import './index.less' export default class Index extends Component { config = { navigationBarTitleText: '测试页面' }; handleNavigate(){ <span style="color: #ff0000;"> Taro.navigateTo({url:'/pages/index/index'}); // Taro.redirectTo({url:'/pages/index/index'});</span> } render () { return ( <View> <Button onClick={this.handleNavigate}>跳转</Button> </View> ) } } |
路由传参
- 我们可以通过在所有跳转的url后面添加查询字符串参数进行跳转传参,例如
- 传入参数id=2&type=test,直接加载url后面即可
- 这样的话,在跳转成功的目标页的生命周期方法里面就能通过,this.$router.params获取到传入的参数,在目标页面的componentWillMount生命周期里获取收到的参数
带参数跳转
1 2 3 4 |
handleNavigate(){ Taro.navigateTo({url:'/pages/index/index?name=bing'}); // Taro.redirectTo({url:'/pages/index/index'}); } |
接收参数
1 2 3 4 5 6 |
componentWillMount () { console.log('第一次渲染之前执行,只执行一次'); const {name} = <span style="color: #ff0000;">this.$router.params</span> // this.$router.params.name结构赋值 console.log(name); } |
静态资源引用
- 在Taro中可以像使用webpack那样自由的引用静态资源,而且不需要安装任何的loader
- 引用样式文件
- 可以直接通过ES5的import语法来引用样式文件
引用js文件
- 可以直接通过ES6的import语法来引用JS文件,可以全部引入也可以使用结构赋值的方式来应用js文件中导出的方法
比如说一个js文件是这样(有多个导出)
1 2 3 4 5 6 7 |
export function setDate() { console.log('setDate'); } export function getDate() { console.log('getDate'); } |
那么就可以解构赋值来引入setDate,然后直接就可以调用setDate()了
1 |
import {setDate} from "./utils"; |
引用图片、音频、字体等文件
- 本地文件可以直接通过ES6的import语法来引用此类文件,拿到文件后直接在jsx中进行使用,直接写地址是获取不到的
- 也可以使用require来进行应用,如果是线上图片直接赋值即可
- 一定要注意小程序的大小限制
- 如果是图片必须用Taro提供的Image标签
- 也可以直接在src={require(‘../../Img/logo.jpg’)}
1 |
import img from '../../Img/logo.jpg' |
1 |
<Image src={img}></Image> |
- 引用样式直接引入使用就可以了,taro不需要配置loader
1 |
import './index.less' |
1 |
<Image className='img' src={img}></Image> |
引用的样式是
1 2 3 4 |
.img{ width: 100%; margin-top: <span style="color: #ff0000;">50PX;</span> } |
这里特别要注意的是PX要大写,如果写成小写会转换成rem的形式,与真实的像素是不符合的!
另外注意:
在less中不能使用ID选择器、标签选择器、属性选择器、>的方式,必须使用类选择!必须定义className 并且less只对当前组件有效(没有antdesign的:global),全局样式对自定义组件是无效的
以下方式都是错误的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<span style="color: #ff0000;">#{ 错误 } div span{ 错误 } span[class='name']{ 属性选择器 错误 } .a > .b{ 不一定生效 }</span> |
列表渲染与Children
条件渲染:
- 由于JSX中不能使用条件渲染,因此我们只能使用短路表达式或三元表达式,值得注意的是,小程序在短路表达式渲染的时候,会出现true或者false的短暂出现,所以如果要适配小程序最好采用三元表达式
三元表达式渲染
1 2 3 4 5 |
<View> { true ? <Button onClick={this.handleNavigate}>跳转</Button> : null } </View> |
短路表达式(不要使用)
1 2 3 4 5 |
<View> { !true || <Button onClick={this.handleNavigate}>跳转</Button> } </View> |
也可以把逻辑放在一个变量里面
1 2 3 4 5 6 7 8 9 10 11 12 13 |
render () { let dom = null; <span style="color: #ff0000;">dom = !true || <Button onClick={this.handleNavigate}>跳转</Button> </span> return ( <View> { dom } </View> ) } |
需要注意的是,一定不能够在render之外的位置去定义JSX,比如说在H5中经常会使用一个函数来返回JSX,但是这个在Taro中是错误的!!!这是小程序的编译引起限制
1 2 3 4 5 6 7 8 9 10 11 12 |
showSomething(){ return (<View>Something</View>) // 不能再render方法之外去定义JSX } render () { return ( <View> {this.showSomething()} // 这是错误的 </View> ) } } |
列表渲染,就用map就可以了,和react一样记得给KEY
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
import Taro, { Component } from '@tarojs/taro' import { View, Button, Text } from '@tarojs/components' import './index.less' export default class Index extends Component { config = { navigationBarTitleText: '测试页面' }; handleNavigate(){ Taro.navigateTo({url:'/pages/index/index?name=bing'}); // Taro.redirectTo({url:'/pages/index/index'}); } state={ list:[ {id:1, name:'项目1'}, {id:2, name:'项目2'}, {id:3, name:'项目3'}, {id:4, name:'项目4'}, {id:5, name:'项目5'}, {id:6, name:'项目6'}, {id:7, name:'项目7'}, {id:8, name:'项目8'}, ] }; render () { let {list}=this.state; return ( <View> { list.map((item, index)=>{ return (<View key={item.id}><Text>{item.name}</Text></View>) }) } </View> ) } } |
需要注意的是在Taro中的map中是不可以写if else的,因此只能够先处理数组,再用处理好的数组进行渲染!
Children
注意不要对this.props.children进行任何操作,因为小程序的限制
这里假设一个有一个弹窗(仅仅是一个框),这个时候弹窗内部的内容完全由父组件来进行控制。
父
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import Taro, { Component } from '@tarojs/taro' import { View, Button,Text,Image } from '@tarojs/components' import './index.less' import Dialog from './dialog'; export default class TestDialog extends Component { render () { return ( <View> <Dialog> <Text>我是text传入的</Text> </Dialog> <Dialog> <Image src={require('../../Img/logo.jpg')} /> </Dialog> <Dialog> <Button>我是按钮</Button> </Dialog> </View> ) } } |
子组件接收父组件传过来的children
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import Taro, { Component } from '@tarojs/taro' import { View, Button } from '@tarojs/components' import './index.less' export default class Dialog extends Component { render () { return ( <View> 我是弹窗组件 { this.props.children } </View> ) } } |
事件处理与样式表
- Taro事件采用驼峰命名
- 在Taro中阻止事件冒泡。你必须明确使用stopPropagation
注意要.bind(this)否则,this指向的就是onClick,因此this.state拿不到数据
阻止事件冒泡和传方法给子组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import Taro, { Component } from '@tarojs/taro' import { View, Button } from '@tarojs/components' export default class Dialog extends Component { state={ name:'张三' }; test(name,<span style="color: #ff0000;">event</span>){ console.log(arguments); console.log(name); <span style="color: #ff0000;">event.stopPropagation(); // 阻止事件冒泡必须通过这种方式</span> } render () { return ( <View> <Button ontest={this.test} // 这样传给子组件,子组件拿到的是一个函数 test={this.test} // 不加on传给子组件,子组件拿到的是一个字符串 <span style="color: #ff0000;">onClick={this.test.bind(this,this.state.name)}>测试事件</Button></span> </View> ) } } |
- 需要注意的是事件冒泡是通过event.stopPropagation()来进行的,而且这是唯一的方法,这个event参数一定会是最后一个参数,可以通过console.log(argument)来看一下
- 传给子组件的方法一定要加onxxxx否则传过去的是一个字符串不是一个方法,任何组件的事件传递都要以on开头,只要当JSX组件传入的参数是函数,参数名就必须以on开头,这是为了适配小程序H5不关注
taro的环境变量
通过
1 |
const<span style="color: #ff0000;"> env=process.env.TARO_ENV;</span> |
获取,返回当前的环境比如H5或者weapp,可以通过环境变量在不同的环境下进行一定的操作,比如说在不同环境拉取不同环境的样式文件
1 2 3 4 5 6 |
const isH5=process.env.TARO_ENV==='h5'; if(isH5){ require('./h5.less'); }else{ require('./weapp.less'); } |