Taro實踐 - 快速開發【知乎】多端應用

來自團隊小夥伴 - 阿集,關於 Taro 上手實踐的一篇文章,但願對你們的使用有所幫助。css

1. Taro 簡介

Taro 是由凹凸實驗室打造的一套遵循 React 語法規範的多端統一開發框架。html

使用 Taro,咱們能夠只書寫一套代碼,再經過 Taro 的編譯工具,將源代碼分別編譯出能夠在不一樣端(微信小程序、H五、App 端等)運行的代碼。目前 Taro 支持編譯出支持微信小程序、H5 運行的代碼,RN、快應用及支付寶小程序的支持還在開發中。git

具體介紹請看文章 《多端統一開發框架 - Taro 》,以及GitHub倉庫 https://github.com/NervJS/tarogithub

2. 前言

爲了學習 Taro,本人在 github 找了知乎的小程序demo,本文經過修改該代碼,實現了 Taro 版的知乎H五、小程序 demo ,對 Taro 有興趣的同窗能夠 star 或 fork 下來學習,GitHub地址web

3. 安裝

安裝 Taro 開發工具 @tarojs/clinpm

使用 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
複製代碼

4. 運行代碼

文件目錄以下:微信小程序

├── 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
複製代碼

5. 開發前注意

若使用 微信小程序預覽模式 ,則需下載並使用 微信開發者工具 添加項目進行預覽,此時須要注意微信開發者工具的項目設置

  • 須要設置關閉ES6轉ES5功能,開啓可能報錯
  • 須要設置關閉上傳代碼時樣式自動補全,開啓可能報錯
  • 須要設置關閉代碼壓縮上傳,開啓可能報錯

項目設置

6. 功能實現詳解

6.1 小程序全局配置

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')) 複製代碼

6.2 首頁

頁面效果以下:

功能描述:上滑刷新,下拉加載更多,數據請求,點擊跳轉

刷新及繼續加載的動做, 依靠的是ScrollView組件,並在組件上綁定 onScrolltoupperonScrolltolower 來綁定滾動到頂部及底部所觸發的事件, 同時 upperThresholdlowerThreshold 可以調整觸發時距邊界的距離。

數據請求 api 使用 Taro.request, 跟wx.request 使用方法基本一致,不一樣的是 Taro.request 自然支持 promise 化,mock 數據使用 easy-mock 來提供 。

點擊跳轉使用 Taro.navigateTo,跟 wx.navigateTo 也基本一致,在寫跳轉的時候,一開始想用匿名函數的形式寫,發現 Taro 目前還不支持,聽說就要支持了,Taro 的迭代速度仍是很快的。

將首頁進行頁面重構的時候,遇到最費時間的問題,應該是wxss轉化爲scssTaro 的組件轉化爲微信小程序和 web 後,標籤是不同的,好比Image組件會變成imageimg標籤,然而原來的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>
    )
  }
}

複製代碼

6.3 Taro 組件

其實我一直搞不懂微信小程序的 Component 組件跟Page 頁面的生命週期函數要搞的不同,頁面的生命週期方法有 onLoad、onReady、onUnload 等,而到了組件中則是 created、attached 、ready 等,相比之下 Taro 就比較統一了,無論是頁面仍是組件,寫法都跟 React 的生命週期一致,統一的api開發起來也順暢了許多。

然而 Taro 組件目前仍是有不少侷限,好比,不支持直接渲染 children, 即不支持 this.props.childrenprops 不能傳遞jsx

在抽象組件的時候,主要有如下注意點

  • 在寫法上,Taro 組件首字母要大寫並採用駝峯命名法,好比在 wxml裏面的標籤是view、scroll-view、image,在 Taro 要寫成View、ScrollView、ImageTaro 的事件綁定事件綁定都以 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}的方式來解構賦值,發現也暫時不支持,心裏有點奔潰。

不過所幸的是,Taro的錯誤提醒很人性化,沒有讓我在這裏浪費不少時間調試,因而我就老老實實一個一個賦值啦。

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} /> }) 複製代碼

6.4 「發現頁面」的 tab 切換功能

tab切換原理: 在組件上綁定 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> 
    )
  }
}

複製代碼

6.5 輪播功能

輪播頁使用了 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>
複製代碼

7. 使用中發現的問題總結

  • Taro 的組件轉化爲微信小程序跟web後,標籤是不同的,好比Image組件會變成imageimg標籤,因此不建議使用標籤名命名css,建議直接用 class
  • 目前的事件綁定不支持匿名函數
// 不支持
<View onClick={()=> this.navigateTo('/pages/answer/answer')} >  </View>
複製代碼
  • Taro 組件在 web 端有許多屬性和事件暫不支持
  • 運行環境不一樣,某些 api 須要根據環境不一樣處理,好比 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}

8. 總結

將該項目遷移到 Taro 成本並不高,絕大多數工做是作語法的變換。Taro採用 React 的寫法寫微信小程序,整體體驗是很是不錯的,有 React 開發經驗的小夥伴相信能很快上手。同一份代碼就能同時支持web端跟小程序端,確實很讓人驚豔,雖然目前 web 端的支持還有待完善,但方向是沒有錯的,接下來只是時間問題。期待 Taro 在接下來的表現。最後對該項目有興趣的同窗能夠下載代碼下來跑一跑 GitHub地址

相關文章
相關標籤/搜索