來自團隊小夥伴 - 阿集,關於 Taro 上手實踐的一篇文章,但願對你們的使用有所幫助。css
Taro
是由凹凸實驗室打造的一套遵循 React
語法規範的多端統一開發框架。html
使用 Taro
,咱們能夠只書寫一套代碼,再經過 Taro
的編譯工具,將源代碼分別編譯出能夠在不一樣端(微信小程序、H五、App 端等)運行的代碼。目前 Taro
支持編譯出支持微信小程序、H5 運行的代碼,RN、快應用及支付寶小程序的支持還在開發中。git
具體介紹請看文章 《多端統一開發框架 - Taro 》,以及GitHub倉庫 https://github.com/NervJS/tarogithub
爲了學習 Taro,本人在 github 找了知乎的小程序demo,本文經過修改該代碼,實現了 Taro 版的知乎H五、小程序 demo ,對 Taro 有興趣的同窗能夠 star 或 fork 下來學習,GitHub地址。web
安裝 Taro
開發工具 @tarojs/cli
npm
使用 npm
或者 yarn
全局安裝json
npm install -g @tarojs/cli
// 或
yarn global add @tarojs/cli
複製代碼
下載代碼小程序
git clone https://github.com/jimczj/taro_zhihu
# 安裝依賴
cd taro_zhihu
npm i
複製代碼
文件目錄以下:微信小程序
├── dist 編譯結果目錄
├── config 配置目錄
| ├── dev.js 開發時配置
| ├── index.js 默認配置
| └── prod.js 打包時配置
├── src 源碼目錄
| ├── pages 頁面文件目錄
| | ├── index index頁面目錄
| | | ├── index.js index頁面邏輯
| | | └── index.css index頁面樣式
| ├── app.css 項目總通用樣式
| └── app.js 項目入口文件
└── package.json
複製代碼
進入項目目錄開始開發,能夠選擇小程序預覽模式,或者h5預覽模式,若使用微信小程序預覽模式,則須要自行下載並打開微信開發者工具,選擇預覽項目根目錄。api
微信小程序編譯預覽模式:
# npm script
npm run dev:weapp
# 或 僅限全局安裝
taro build --type weapp --watch
複製代碼
H5編譯預覽模式:
# npm script
npm run dev:h5
# 或 僅限全局安裝
taro build --type h5 --watch
複製代碼
若使用 微信小程序預覽模式 ,則需下載並使用 微信開發者工具 添加項目進行預覽,此時須要注意微信開發者工具的項目設置
app.json
文件用來對微信小程序進行全局配置,決定頁面文件的路徑、窗口表現、設置網絡超時時間、設置多 tab 等。從原來的 app.json
轉化爲 Taro
項目的 app.js
幾乎沒有什麼成本,配置基本一致。只是有一點須要注意,靜態圖片資源文件夾要放在src目錄下面,這樣代碼在編譯打包的時候,纔會把圖片資源給複製打包過去。我一開始將靜態圖片資源文件夾跟src同層級,而後各類找不到圖片資源,浪費了許多時間。好了,話很少說,看看下面代碼對比,你就清楚這遷移無比輕鬆,轉寫成 React
寫法,看起來也順眼多了。
原微信小程序 app.json (有刪減)代碼以下:
{
'pages':[
'pages/index/index',
'pages/discovery/discovery',
'pages/more/more',
'pages/answer/answer',
'pages/question/question'
],
'window':{
'backgroundTextStyle':'light',
'navigationBarBackgroundColor': '#0068C4',
'navigationBarTitleText': '知乎',
'navigationBarTextStyle':'white',
'enablePullDownRefresh':true
},
'tabBar': {
'color': '#626567',
'selectedColor': '#2A8CE5',
'backgroundColor': '#FBFBFB',
'borderStyle': 'white',
'list': [{
'pagePath': 'pages/index/index',
'text': '首頁',
'iconPath': 'images/index.png',
'selectedIconPath': 'images/index_focus.png'
}, {
'pagePath': 'pages/discovery/discovery',
'text': '發現',
'iconPath': 'images/discovery.png',
'selectedIconPath': 'images/discovery_focus.png'
}, {
'pagePath': 'pages/more/more',
'text': '個人',
'iconPath': 'images/burger.png',
'selectedIconPath': 'images/burger_focus.png'
}]
}
}
複製代碼
轉寫成Taro 代碼以下:
import Taro, { Component } from '@tarojs/taro'
import Index from './pages/index'
import './app.scss'
class App extends Component {
config = {
pages: [
'pages/index/index',
'pages/discovery/discovery',
'pages/more/more',
'pages/answer/answer',
'pages/question/question'
],
window: {
backgroundTextStyle: 'light',
navigationBarBackgroundColor: '#0068C4',
navigationBarTitleText: 'Taro知乎',
navigationBarTextStyle: 'white',
enablePullDownRefresh: true
},
tabBar: {
color: '#626567',
selectedColor: '#2A8CE5',
backgroundColor: '#FBFBFB',
borderStyle: 'white',
list: [{
pagePath: 'pages/index/index',
text: '首頁',
iconPath: './asset/images/index.png',
selectedIconPath: './asset/images/index_focus.png'
},{
pagePath: 'pages/discovery/discovery',
text: '發現',
iconPath: './asset/images/discovery.png',
selectedIconPath: './asset/images/discovery_focus.png'
},
{
pagePath: 'pages/more/more',
text: '個人',
iconPath: './asset/images/burger.png',
selectedIconPath: './asset/images/burger_focus.png'
}]
}
}
render () {
return (
<Index /> ) } } Taro.render(<App />, document.getElementById('app')) 複製代碼
頁面效果以下:
功能描述:上滑刷新,下拉加載更多,數據請求,點擊跳轉刷新及繼續加載的動做, 依靠的是ScrollView
組件,並在組件上綁定 onScrolltoupper
和 onScrolltolower
來綁定滾動到頂部及底部所觸發的事件, 同時 upperThreshold
和 lowerThreshold
可以調整觸發時距邊界的距離。
數據請求 api 使用 Taro.request
, 跟wx.request
使用方法基本一致,不一樣的是 Taro.request
自然支持 promise
化,mock 數據使用 easy-mock 來提供 。
點擊跳轉使用 Taro.navigateTo
,跟 wx.navigateTo
也基本一致,在寫跳轉的時候,一開始想用匿名函數的形式寫,發現 Taro
目前還不支持,聽說就要支持了,Taro
的迭代速度仍是很快的。
將首頁進行頁面重構的時候,遇到最費時間的問題,應該是wxss
轉化爲scss
,Taro
的組件轉化爲微信小程序和 web 後,標籤是不同的,好比Image
組件會變成image
或img
標籤,然而原來的wxss
裏面使用了標籤名來命名 css,這樣一來就形成了微信小程序跟 web 樣式表現不一致。因此不建議使用標籤名命名 css,建議直接用 class。
export default class Index extends Component {
config = {
navigationBarTitleText: '首頁'
}
constructor() {
super(...arguments)
this.state = {
loading:true,
list:[]
}
}
componentDidMount () {
// 獲取遠程數據
this.updateList()
}
updateList() {
Taro.showLoading({title: '加載中'})
Taro.request({
url: 'https://easy-mock.com/mock/5b21d97f6b88957fa8a502f2/example/feed'
}).then(res => {
Taro.hideLoading()
if (res.data.success) {
this.setState({
loading:false,
list:res.data.data
})
}
})
}
appendNextPageList() {
Taro.showLoading({title: '加載中'})
Taro.request({
url: 'https://easy-mock.com/mock/5b21d97f6b88957fa8a502f2/example/feed'
}).then(res => {
Taro.hideLoading()
if (res.data.success) {
this.setState({
list: this.state.list.concat(res.data.data)
})
}
})
}
render () {
return (<ScrollView className='container'
scrollY
scrollWithAnimation
scrollTop='0'
lowerThreshold='10'
upperThreshold='10'
onScrolltoupper={this.updateList}
onScrolltolower={this.appendNextPageList}
>
<View className='search flex-wrp'>
<View className='search-left flex-item'>
<View className='flex-wrp'>
<View className='flex1'><Image src={searchPng}></Image></View>
<View className='flex6'><Input type='text' placeholder='搜索話題, 問題或人' placeholderClass='search-placeholder'/></View>
</View>
</View>
<View className='search-right flex-item'>
<Image src={lightingPng}></Image>
</View>
</View>
{
this.state.loading
? <View className='txcenter'><Text>加載中</Text></View>
: this.state.list.map((item,index)=>{
return <Feed key={item} />})
}
</ScrollView>
)
}
}
複製代碼
其實我一直搞不懂微信小程序的 Component
組件跟Page
頁面的生命週期函數要搞的不同,頁面的生命週期方法有 onLoad、onReady、onUnload
等,而到了組件中則是 created、attached 、ready
等,相比之下 Taro
就比較統一了,無論是頁面仍是組件,寫法都跟 React
的生命週期一致,統一的api開發起來也順暢了許多。
然而 Taro
組件目前仍是有不少侷限,好比,不支持直接渲染 children
, 即不支持 this.props.children
;props
不能傳遞jsx
;
在抽象組件的時候,主要有如下注意點
Taro
組件首字母要大寫並採用駝峯命名法,好比在 wxml
裏面的標籤是view、scroll-view、image
,在 Taro
要寫成View、ScrollView、Image
。Taro
的事件綁定事件綁定都以 on
開頭並採用駝峯命名法// 小程序代碼
<scroll-view scroll-y='true' class='container' bindscrolltoupper='upper' upper-threshold='10' lower-threshold='5' bindscrolltolower='lower'>
</scroll-view> // Taro 代碼 <ScrollView className='container' scrollY scrollWithAnimation scrollTop='0' lowerThreshold='10' upperThreshold='10' onScrolltoupper={this.upper.bind(this)} onScrolltolower={this.lower.bind(this)} > </ScrollView> 複製代碼
src
寫上相對路徑,Taro
引用本地靜態資源須要先 import
進來再使用,爲了讓 h5 部署的時候圖片路徑不出錯,最好把圖片放在服務器上,而後直接寫 http 路徑// 小程序 引用本地靜態資源
<image src='../../images/search.png'></image>
// Taro 引用本地靜態資源
import searchPng from '../../asset/images/search.png'
// ...此處省略無數代碼
<Image src={searchPng}></Image>
// 最好把圖片放在服務器上,而後寫http 路徑
<Image src='https://image.ibb.co/kUissy/search.png'></Image>
複製代碼
Taro
使用 jsx
// 小程序
<block wx:for='{{feed}}' wx:for-index='idx' wx:for-item='item' data-idx='{{idx}}'>
<view class='feed-item'>
...
</view>
</block>
// Taro 代碼
{
this.state.list.map((item,index)=>{
return <Feed {...item} key={index} />
})
}
複製代碼
答案組件在抽象過程當中,想直接寫成純函數形式
// 暫不支持這種寫法
const Text = ({ answer }) =>
<p>{answer}</p>
複製代碼
發現目前還不支持,因而老老實實寫成class的形式了:
export default class Feed extends Component {
navigateTo(url) {
Taro.navigateTo({url:url})
}
render() {
return (
<View className='feed-item'> <View className='feed-source'> <View className='avatar flex1'> <Image src={this.props.feed_source_img}></Image> </View> <View className='flex8'> <Text className='feed-source-txt'>{this.props.feed_source_name}{this.props.feed_source_txt}</Text> </View> <View className='flex1'> <Image className='item-more' mode='aspectFit' src={more}></Image> </View> </View> <View className='feed-content'> <View className='question' onClick={this.navigateTo.bind(this,'/pages/question/question')}> <View className='question-link'> <Text>{this.props.question}</Text> </View> </View> <View className='answer-body'> <View> <Text className='answer-txt' onClick={this.navigateTo.bind(this,'/pages/answer/answer')} >{this.props.answer_ctnt}</Text> </View> <View className='answer-actions'> <View className='like dot'> <View>{this.props.good_num} 贊同 </View> </View> <View className='comments dot'> <View>{this.props.comment_num} 評論 </View> </View> <View className='follow-it'> <View>關注問題</View> </View> </View> </View> </View> </View>
)
}
}
複製代碼
在使用的組件的時候,想使用{...item}
的方式來解構賦值,發現也暫時不支持,心裏有點奔潰。
this.state.list.map((item,index) => {
return <Feed feed_source_img={item.feed_source_img} feed_source_txt={item.feed_source_txt} question={item.question} answer_ctnt={item.answer_ctnt} good_num={item.good_num} comment_num={item.comment_num} key={index} /> }) 複製代碼
onClick
事件 改變
this.state.currentNavtab
值,再經過判斷來實現
tab
切換,函數參數傳遞方式爲
this.switchTab.bind(this,index)
,具體代碼以下:
export default class Discovery extends Component {
constructor() {
super(...arguments)
this.state = {
currentNavtab: 0,
navTab: ['推薦', '圓桌', '熱門', '收藏'],
}
}
switchTab(index,e) {
this.setState({
currentNavtab: index
});
}
render () {
return (
<View> <View className='top-tab flex-wrp flex-tab' > { this.state.navTab.map((item,index) => { return (<View className={this.state.currentNavtab === index ? 'toptab flex-item active' : 'toptab flex-item' } key={index} onClick={this.switchTab.bind(this,index)}> {item} </View>) }) } </View> <ScrollView scroll-y className='container discovery withtab'> <View className='ctnt0' hidden={this.state.currentNavtab==0 ? false : true}> ... </View> <View className='txcenter' hidden={this.state.currentNavtab==1 ? false : true}> <Text>圓桌</Text> </View> <View className='txcenter' hidden={this.state.currentNavtab==2 ? false : true}> <Text>熱門</Text> </View> <View className='txcenter' hidden={this.state.currentNavtab==3 ? false : true}> <Text>收藏</Text> </View> </ScrollView> </View>
)
}
}
複製代碼
輪播頁使用了 Swiper
組件,參數跟小程序都是一一對應,具體能夠查看詳細文檔,在重構過程也主要是把 wxml
換成 jsx
的形式。
<Swiper className='activity' indicatorDots='true'
autoplay='true' interval='5000' duration='500'>
{this.state.imgUrls.map((item,index) => {
return (<SwiperItem key={index}> <Image src={item} className='slide-image' width='355' height='375' /> </SwiperItem>) })} </Swiper>
複製代碼
Taro
的組件轉化爲微信小程序跟web後,標籤是不同的,好比Image
組件會變成image
或img
標籤,因此不建議使用標籤名命名css,建議直接用 class// 不支持
<View onClick={()=> this.navigateTo('/pages/answer/answer')} > </View>
複製代碼
Taro
組件在 web 端有許多屬性和事件暫不支持wx.getUserInfo
在 web 端是不存在的,此時咱們須要判斷環境來執行代碼if (Taro.getEnv() === Taro.ENV_TYPE.WEAPP) {
// 小程序環境
} else if (Taro.getEnv() === Taro.ENV_TYPE.WEB ) {
// WEB(H5)環境
}
複製代碼
Taro
目前不支持 SVG
(畢竟小程序不支持) 目前的第三方庫還比較少(畢竟 Taro
剛出來不久,但願接下來社區能出來各類 ui 庫)Taro
組件暫時不支持純函數,不支持解構賦值 {...item}
將該項目遷移到 Taro
成本並不高,絕大多數工做是作語法的變換。Taro
採用 React
的寫法寫微信小程序,整體體驗是很是不錯的,有 React
開發經驗的小夥伴相信能很快上手。同一份代碼就能同時支持web端跟小程序端,確實很讓人驚豔,雖然目前 web 端的支持還有待完善,但方向是沒有錯的,接下來只是時間問題。期待 Taro
在接下來的表現。最後對該項目有興趣的同窗能夠下載代碼下來跑一跑 GitHub地址。