Taro practice-rapid development [knowledge] multi-terminal applications

Taro practice-rapid development [knowledge] multi-terminal applications

From the teammate-Aji, an article about Taro's hands-on practice, I hope it will be helpful to everyone.

1. Introduction to Taro

Taro Is a set of follow React multiport unified development framework syntax specification.

Using Taro, we can only write a code, and then by Tarothe compilation tools, respectively, the source code can be compiled to run at different ends (WeChat applet, H5, App end, etc.) code. Currently Tarosupports building a small micro-channel support program, to support H5 run code, RN, fast application and Alipay small program is still in development.

For specific introduction, please see the article "Multi-terminal unified development framework-Taro" , and the GitHub repository https://github.com/NervJS/taro

2. Introduction

In order to learn Taro, I found Zhihu s small program demo on github . This article implemented the Taro version of Zhihu H5 and small program demo by modifying the code. Students who are interested in Taro can star or fork to learn. GitHub address .

3. Installation

Installation TaroDevelopment Tools@tarojs/cli

Use npmor yarnglobal installation

npm install -g @tarojs/cli
// 
yarn global add @tarojs/cli
 

Download code

git clone https://github.com/jimczj/taro_zhihu
#  
cd taro_zhihu
npm i
 

4. Run the code

The file directory is as follows:

  dist                    
  config                  
|     dev.js              
|     index.js            
|     prod.js             
  src                     
|     pages               
|   |     index          index 
|   |   |     index.js   index 
|   |   |     index.css  index 
|     app.css             
|     app.js              
  package.json
 

Enter the project directory to start development, you can choose the applet preview mode or h5 preview mode. If you use the WeChat applet preview mode, you need to download and open the WeChat developer tool by yourself , and select the preview project root directory.

WeChat applet compilation preview mode:

# npm script
npm run dev:weapp
#    
taro build --type weapp --watch
 

H5 compilation preview mode:

# npm script
npm run dev:h5
#    
taro build --type h5 --watch
 

5. Attention before development

The use of micro-channel applet preview mode, you need to download and use the micro-channel developer tools add items to preview, then need to pay attention to micro-channel Developer Tools project settings

  • Need to turn off the ES6 to ES5 function, open it may report an error
  • You need to set to turn off the automatic completion of the style when uploading the code, and an error may be reported when it is turned on
  • You need to set the code compression upload to be turned off, and an error may be reported when it is turned on

6. Detailed function realization

6.1 Mini Program Global Configuration

app.jsonThe file is used to configure the WeChat applet globally, determine the path of the page file, window performance, set network timeout time, set multiple tabs, etc. From the original app.jsoninto a Taroproject app.jswith little cost, basically the same configuration. Just one thing to note is that the static image resource folder should be placed under the src directory, so that when the code is compiled and packaged, the image resource will be copied and packaged. At first, I put the static image resource folder at the same level as the src, and then I couldn't find the image resources, which wasted a lot of time. Well, I did not talk much to say, look at the comparison below code, you know it very easy to migrate, turn written Reactwording, looks pleasing to the eye.

The original WeChat applet app.json (with deletion) code is as follows:

{
  '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'
    }]
  }
}
 

Translated into Taro code as follows:

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 Home

The page effect is as follows:

Function description: slide up to refresh, pull down to load more, data request, click to jump

Refresh and continues to load action, rely on ScrollViewcomponents, and bound to the component onScrolltoupperand onScrolltolowerto bind to scroll to the top and bottom of the events triggered while upperThresholdand lowerThresholdthe distance from the boundary of the time be able to adjust the trigger.

Api use data request Taro.request, with the wx.requestuse basically the same, except that the Taro.requestnatural support promiseof, the mock data using the easy-mock provided.

Hit the jump to use Taro.navigateTo, with wx.navigateTobasically the same in writing to jump when a start would like to write in the form of an anonymous function, we found that Tarocurrently does not support, will reportedly support, and Taroiteration speed is very fast.

The home page for page reconstruction time, a problem most time-consuming, should be wxssconverted into scss, Tarothe micro-channel components into applets and web, the label is not the same, such as Imagecomponents become imageor imglabel, but the original wxssThe tag name is used to name the css, which causes the inconsistency between the WeChat applet and the web style. So it is not recommended to use the tag name to name css, it is recommended to use class directly.

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 components

In fact, I always do not understand the small micro-channel program Componentcomponents with Pagethe life cycle of a page function is not the same wants, the page lifecycle methods onLoad onReady onUnload, etc., and to the component is created attached readyand so on, in contrast Taroto a more unified, no matter what The page is still a component, and the writing method is the sameReact the same life cycle, it is also developing a unified api lot smoother.

However, Tarothe component is still there are many limitations, for example, does not support direct rendering children, that is not supported this.props.children; propsnot passjsx ;

When abstracting components, there are mainly the following points to note

  • In writing, Taroassembly and use the first letter capitalized hump nomenclature, such as the wxmllabel is inside view scroll-view image, in Taroshould be rewritten View ScrollView Image. TaroEvent binding event bindings to onbegin with and adopt hump nomenclature
// 
<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>
 
  • Applet directly reference the local static resources srcrelative path on writing, Tarorefer to the local static resources need to import come in re-use, in order to allow the deployment of h5 when the picture is not wrong path, the best pictures on the server, and then write directly to http path
//   
<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> 
 
  • Through the list of differences, the applet use template language, and Tarousejsx
// 
<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}/>
    })
}

 

In the abstract process, the answer component wants to be written directly in pure functional form

// 
const Text = ({ answer }) => 
  <p>{answer}</p>
 

Found that it is not currently supported, so honestly write it in the form of 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>
    )
  }
}
 

When using the components, I wanted to use {...item}the method to deconstruct and assign values, but I found that it was temporarily not supported, and my heart was a little broken.

But fortunately, Taro's error reminders are very user-friendly and didn't make me waste a lot of time debugging here, so I honestly assigned them one by one.

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 switching function of "discovery page"

tab switching principles: bind the component onClickevent changes this.state.currentNavtabvalue, then determination is achieved by tabswitching to the function parameter passing this.switchTab.bind(this,index), specific code as follows:

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 Carousel function

Carousel page uses Swipercomponent parameters are small programs with one to one, specific detailed documentation can be viewed in the reconstruction process is mainly wxmlinto jsxform.

<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. Summary of problems found in use

  • TaroAfter the components are converted into WeChat applets and web, the tags are different, for example, the Imagecomponents will become imageor imgtags, so it is not recommended to use the tag name to name css, it is recommended to use class directly
  • The current event binding does not support anonymous functions
// 
<View onClick={()=> this.navigateTo('/pages/answer/answer')} >  </View>
 
  • Taro The component has many properties and events on the web side that are not currently supported
  • Different operating environment, some api processing according to different environments, such as wx.getUserInfoin a web end does not exist, then we need to determine the environment to execute the code
if (Taro.getEnv() === Taro.ENV_TYPE.WEAPP) {
    // 
} else if (Taro.getEnv() === Taro.ENV_TYPE.WEB ) {
    //WEB(H5) 
}
 
  • TaroCurrently not supported SVG(after all applet does not support) the current third-party library is still relatively small (after all, Tarojust come out soon, I hope the next community to be out various ui library)
  • Taro The component does not support pure functions for the time being, and does not support destructuring assignment {...item}

8. Summary

The project to migrate Tarocost is not high, the vast majority of work is done transform syntax. TaroUsing Reactthe wording of the letter write micro small program, the overall experience is very good, have Reactexperience in the development of small partners believe that we can get started quickly. The same code can support both the web and the applet, which is really amazing. Although the current web support needs to be improved, the direction is correct. The next step is just a matter of time. We look forward to Tarothe next performance. Finally, students who are interested in the project can download the code and run the GitHub address .