preface

I wrote a gadget back in Taro 0. X, although few people play it. Recently it happened that Taro 1.2.x has been released

React style applet framework, partial compromise for applet. Find something to test the waters and see how it improves.

Just the company has a need to do a mobile phone end of the report management procedures, the meeting pulled over;

In addition to basic React skills, you need to know both React and React.

  • Small program own document:TaroMost of the components are packaged based on official apis
  • Support for Async await/special problem handling/best practices /Async await

Although this project (only wechat terminal) is not very big, there are still a lot of things to sort out, other nonsense not to say, straight to the theme

The problem summary

Life cycle andJSXthe{}Arrow functions are not supported

[Fixed] Using arrow functions produces uncontrollable results.

The most common is error reporting;

  • JSXIt only supportsonClick={this.xxxx.bind(this,args)This kind of writing
  • Life cycle use can lead to,storeorrenderAbnormal results (such as execution order and value errors)
  • Other general functions support the arrow function notation

Dynamic style

Although Taro officially supports CSS Modules, if you’re considering multiple platforms.. It is recommended to maintain it with a regular naming scheme

As for the dynamic CSS class handling of classnames.. I prefer to use the Classnames library

Classname: The most common usage is as follows


// Taro is used in the same way as React. Taro should be placed in {}, otherwise an error will be reported (= classnames).

import classnames from 'classnames'

 <View className={classnames({
  "tab-operator": true."show": userIdList.length>0."hide": userIdList.length === 0})} ><View className="tab-content-top-counter">
    <View className="left-text">{userIdList.length === 0 ? 'Please check the following items if you want to operate items! ': 'The ${useridlist. length} bar'} is selected.</View>
    {userIdList.length === 0 ? null : <View className="unselect" onClick={this.unselect.bind(this)}>cancel</View>}
   
</View>

Copy the code

Missing tooltip type for its own wrapped component (TS)

For example, if you expose components that rely on components packaged by Taro,

The feature you added will be missing, causing the editor to display an error message.

The easiest way to do this is to use type or interface, so you don’t get an error. Such as the following

/ / way
type staticPropsSearchPanel={
  open: boolean, onClose? :(a)= > void
} 

// We can also use interface, which can inherit from other interfaces
2 / / way
interface staticPropsSearchPanel {
     open: boolean, onClose? :(a)= > void
}


class SearchPanel extends Component<staticPropsSearchPanel>{}

Copy the code

Component support

  • Functional components are not supported: As of 1.2.x, there is no support for class XX extends Component

  • Multiple class XX extends references within the same file are not supported

How many state managers are allowed to access?

Dva, MOBx, and Redux all have corresponding taro access solutions. The latter two are officially maintained by Taro

Whether to supportalias

The latest version is supported (available) and exposes configuration files in the Config directory, although many other WebPack configurations also have some direct exposure

As for alias symbols that esLint does not recognize, there is no solution for this. I have tried some schemes in the community, and they don’t seem to work!

Precautions for route redirection

  • Underlined pit jumps do not support underlined pit jumps (currently), unknown later

Development mode and real machine debugging can compile normally, package upload is not recognized… Waste a lot of my time..

  • The pit of the path

The forward URL must be the full path!!!!! , such as

// Redirect, which provides a back button
Taro.redirectTo({ url: '/pages/list/index' })

Reload the entire program, close all other pages (the one that clears the stack), and then open the page you specify
// This is ideal when authentication fails or expires. Open only the registration page
Taro.reLaunch({ url:'/pages/login/index'})

// And any other 'navigate' items that taro encapsulates are generally written in wechat documents

Copy the code

Authentication page rendering obtrusive improves posture!

If you do an authentication jump on the first page, it’s very easy to get a jump on the render section

The visual feedback is not very good, for this, write an intermediate authentication page as the first page, the jump will improve a lot (visually)

Because the effects can be customized without rendering a lot of unnecessary components

For example, my entry page is Auth

import './index.less';

import { View } from '@tarojs/components';
import Taro, { Component, Config } from '@tarojs/taro';

class Auth extends Component {

  /** * Specify config to be declared as: Taro.Config ** Since typescript can deduce only basic types of Key for object types * for things like navigationBarTextStyle: 'black' such a derived type is string * navigationBarTextStyle tips and statement: 'black' | 'white' type of conflict, the need to display the statement type * /
  config: Config = {
    navigationBarTitleText: 'Xx little Assistant'
  }


  static options = {
    addGlobalClass: true
  }

  // If you have a token, you can enter the content area.
  // No token to login
  componentDidShow() {
    const token = Taro.getStorageSync('token');
    if(!!!!! token) { Taro.redirectTo({url: '/pages/list/index' })
      return
    }
    Taro.redirectTo({ url: '/pages/login/index' })
  
  }

  render() {
    return (
      <View className='auth-page'>loading....</View>)}}export default Auth
Copy the code

componentDidShowThe attention of the points

PreviewImage (click on the full screen preview of the image), when closed will trigger the lifecycle again..

So putting the request here requires weighing up your own.. For example, after my list is expanded, clicking the picture to close will cause the list to brush again;

If YOU move it to componentWillMount, it’s not affected by the previewImage

mobxAccess and data observation?

Mobx access is almost the same as regular access and usage is almost the same..

From Mox-React to @tarojsw/mobx, provided by taro package

Devtools. Applets are currently only visible from developer tools,

It’s not as clear as professional DevTools, but you can see the organization and response of the data in general, as shown

In combination withmobxPre-request before jump?

For example, the detail page, the page showing the class, we usually use typeId to get the specific details, and then display

The normal thing to do is to go in and trigger the request in componentDidMount, and then render the result set to the page,

But if you go in and you show the default data and you replace it, it’s a little bit awkward; We definitely want to improve the user experience, so pre-request the data

We can start with the life cycle before the jump based on the actual scenario, for example redirecTo can call the function Dispatch within componentDidHide

ReLuanch can now trigger in componentWillUnmount.

Jump to the past page, you can get the render directly from props, it’s not so obtrusive

Timestamp and common date format conversion

For date processing, we most commonly use the time stamp for both postures and the more readable YYYY-MM-DD for presentation

So there’s no need to introduce the moment library. It’s a dayJS library. It’s a very small library.

  • dayjs

Of course, you can also encapsulate a transformation with a function yourself, so you don’t need to introduce an additional library.

Note to obtain node information

To specify a node within the component itself,this must be this.$scope

This.$scope represents the component itself (instance) in taro.


  componentDidMount() {
    const query = Taro.createSelectorQuery().in(this.$scope);
    query.select('#list-card').boundingClientRect((res) = > {
      console.log('res: ', res);
    }).exec()
  }
Copy the code

Change the basic information of the project (wechat mini program)

Checking directly in the developer tools option will not be saved to the project, such as base library switch;

What works is to directly manipulate project. Config. json in the root directory

// The parameters of this configuration can be specified by wechat official explanation, which will be more comprehensive // https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html?search-key=%E9%A1%B9%E7%9B%AE%E9%85%8D%E7%B D%AE {"miniprogramRoot": "Package output path"."projectname": "Project Name"."description": "Sound Management applet."."appid": "xxxx"."setting": {
		"urlCheck": true, // Whether to check the secure domain name and TLS version"es6": false, // Whether to enable ES6 conversion"postcss": true// Enable postCSS support"minified": false// Whether to compress the code"newFeature": true// Enable new feature support},"compileType": "miniprogram"// Compile type"libVersion": "2.5.0"// Specify the base library version"condition": {}}Copy the code

Other applets have corresponding configuration files, see the official link

  • Project configuration

It’s a package of little things

Request Encapsulation (TS)

  • request.tsx
    • To support routingprefix
    • headerThe merger of
    • Response interception

/* * @author: CRPER * @lasteditors: CRPER * @github: https://github.com/crper * @motto: It is fun to be a hero. If you don't learn, you share. * @description: request interface encapsulation */
import Taro from '@tarojs/taro';
import '@tarojs/async-await';

interface options {
  header: any, method? :string, dataType? :string, responseType? :string, success? :Function, fail? :Function, complete? :Function
}

/** ** @param URL: interface path * @param method: request method (RESTFUL, but PATCH is not available) * @param data: passed data * @param options: * @param prefix: The extra prefix of the interface */
export default async function(url: string, method? :string,data? :string | [any] | Object, options? : options, prefix? :string){
  
  // Patch!!!!! is not supported Wechat's own request itself does not support patch!!

  // wechat caches tokens by itself
  const wxToken:string|void =await Taro.getStorage({ key: 'token' })
    .then(res= > res.data).catch(err= > {
      if(err)  return})/ / the default value
  const defaultOtions: options = {
    method: 'GET',
    header:{}
  }




  // If there is a token, grant it
  if (wxToken) {
    defaultOtions.header.Authorization = wxToken
  }

  const baseUrl: string = process.env.NODE_ENV === 'development' ? 'https://xxx.com/api/web' : 'https://xxx.com/api/web';
  const newUrl = prefix ? `${baseUrl}${prefix}${url}` : `${baseUrl}${url}`

  const requestObject: any= { url: newUrl, ... defaultOtions, ... options, method, data }const codeMessage: Object = {
    200: 'The server successfully returned the requested data. '.201: 'Creating or modifying data succeeded. '.202: 'A request has been queued in the background (asynchronous task). '.204: 'Deleting data succeeded. '.400: The server did not create or modify data. '.401: 'User has no permissions (wrong token, username, password). '.403: 'The user is authorized, but access is forbidden. '.404: 'The request was made for a nonexistent record, and the server did not act on it. '.406: 'Requested format not available. '.410: 'The requested resource is permanently deleted and will not be retrieved. '.412: 'Access denied, please log in again'.422: 'A validation error occurred while creating an object. '.500: 'Server error, please check server. '.502: 'Gateway error. '.503: 'Service unavailable, server temporarily overloaded or maintained. '.504: 'Gateway timed out. '};// Check the request status
  const checkStatusAndFilter = (response):Promise<any> | undefined= > {
    if (response.statusCode >= 200 && response.statusCode < 300) {
      if (response.statusCode === 200 || response.statusCode === 304) {
        return response.data
      }
      return response;
    }

    // All other errors traversed above the error message throw an exception
    const errortext = codeMessage[response.statusCode] || response.errMsg;
    Taro.showToast({
      title: errortext,
      mask: true,
      icon: 'none',
      duration: 2000
    })
    return Promise.reject(response)
  };



  try {
     return await Taro.request(requestObject)
      .then(checkStatusAndFilter)
      .then(res= > {
        // This block is negotiated between me and the backend. If the internal interface is 1, the error occurs
        if (res.code === 1) {
          const errMsg = res.msg ? res.msg : 'Wrong interface';
          Taro.showToast({
            title: errMsg,
            mask: true,
            icon: 'none',
            duration: 2000
          })
          Promise.reject(errMsg)
        }
        if (res.code === 0) {
          if (res.data) {
            return res.data
          }
          return null
        }
        return res
      }).catch(errRes= > {
        if (errRes.statusCode === 412) {
          Taro.reLaunch({ url:'/pages/login/index'})}})}catch (err) {
    Taro.showToast({
      title: 'Code execution exception',
      mask: true,
      icon: 'none',
      duration: 2000}}})Copy the code
  • usage
// I configured the alias
import wxfetch from '@utils/request';

  // For example, one of the requests in my code, processing behavior
  // Cut the list data
  spliceList = (dataIdArr: Array<string | number> = []) = > {
    const {list, paginate: {total}} = this.state;
    // If there is only one item, try the request list to see if there is any new data
    if (list.length <= 1) {
      this.getList()
    }
    let tempArr: Array<Object> = list.filter((item) = > {
      for (let i = 0; i < dataIdArr.length; i++) {
        let innerItemId = Number(dataIdArr[i]);
        if(item.id ! == innerItemId) {return item
        }
      }
    })
    this.setState({
      list: tempArr,
      paginate: {
        total: total - dataIdArr.length
      },
      dataIdArr: []})}// Handle behavior
  handleActionSheetClick = async (e: number): Promise<any> => {
    try {
      const actionParam = {operationType: e};
      const {dataIdArr, operationNote} = this.state;
      constisActionNoValid: boolean = ! e || e ===0| | -Array.isArray(dataIdArr) && dataIdArr.length === 0);

      if (isActionNoValid) {
        Taro.atMessage({
          'message': 'Please check again if your behavior is normal, such as check data! '.'type': 'error'.'duration': 1000
        })
        return false;
      }

      await wxfetch('/suspiciousDatas'.'POST', { dataIdArr, operationNote, ... actionParam });// Cut the array and close the mask layer
      this.spliceList(dataIdArr);
      this.handleActionSheetClose();
    } catch (err) {
      console.log(err); }}Copy the code

Simplified version of throttle (TS)

  • throttle.tsx

/* * @author: CRPER * @lasteditors: CRPER * @github: https://github.com/crper * @motto: It is fun to be a hero. If you don't learn, you share. * @description: simplified version of the throttle function */

 
/** * @param fn: callback function * @param threshold: time, in milliseconds */
export default function throttle(fn: Function, threshold: number = 1500) {
  if (threshold === null) {
    threshold = 1500
  }
  let _lastExecTime: null | number = null;
  let context = this
  return function (. args:any[]) :void {
    let _nowTime: number = new Date().getTime();
    if (_nowTime - Number(_lastExecTime) > threshold || ! _lastExecTime) { fn.apply(context, args); _lastExecTime = _nowTime } } }Copy the code
  • usage

On the basis of this.xxx.bind

import throttle from '@utils/throttle';

// Scroll to the top to trigger
onScrolltoupper = throttle((a)= > {
    console.log('1111');
},3000)

Copy the code

Drop-down refresh shows the built-inloading.

Is wechat own three small points, this needs to configure some of the next page of its own properties.

Taro can declare page properties within a component simply by introducing Config


import Taro, { Component, Config } from '@tarojs/taro';
class ReportList extends Component {

  /** * Specify config to be declared as: Taro.Config ** Since typescript can deduce only basic types of Key for object types * for things like navigationBarTextStyle: 'black' such a derived type is string * navigationBarTextStyle tips and statement: 'black' | 'white' type of conflict, the need to display the statement type * /
  config: Config = {
    navigationBarTitleText: 'Suspicious data Summary',
    enablePullDownRefresh: true.// Enable the pull-down refresh feature
    backgroundTextStyle: "dark".// Change the color of the displayed text to dark or light. You can't see the background because it's the same color
    backgroundColor:'#f7f7f7' // The background color of the page}}// After enable, remember to add the corresponding condition to close, otherwise will always display
 // Drop refresh
 onPullDownRefresh = () :void= > {
    // This loading is the navigation bar, the page title shows a loading, built-in wechat
    Taro.showLoading({
      title: 'loading.... '
    })
    
    // Since my interface requests are async await gestures, they can be queued
    this.getList(); 
    this.unselect();
    
    // After the interface request is complete, hide two loading, title and drop down area
    Taro.hideLoading();
    Taro.stopPullDownRefresh();
  }


Copy the code

Implement component style transitions?

Implementing a component transition enhances the experience to a certain extent, essentially CSS3 writes transitions,

For example, look at my side to achieve an effect, I feel still look past

  • style
Animation-opacity: 0; // Animation-opacity: 0; // Animation-opacity: 0; The transform: translateY (100 vh) rotate (270 deg) scale (0.5); &.fadeIn{ opacity: 1; transform: translateY(0) rotate(0deg); The transition: all 0.3 s ease - in-out; } &.fadeOut{ opacity: 0; The transform: the rotate (270 deg) scale (0.2) translateX (- 100 vw); The transition: all 0.3 s ease - in-out; }}Copy the code
  • Function area

We use classnames to dynamically append classes

<View className={classnames({ "search-panel": true, 'fadeIn': open, 'fadeOut': ! open})} > </View>Copy the code

Transition of node element height (CSS3)

It’s a transition between unfolding and folding,

After N attempts (cannot set height to element!!) Max-height :0,

Other transitions can be resolved by setting the appropriate max-height

Support in the face of events in Taro

Some documents did not say, can only go to the source… See common.d.ts for obvious reasons, such as long press events

Github.com/NervJS/taro…

CSS 3 loading is introduced into

In fact, with the common development mode on the writing method is not bad, basic or CSS3 function,DIV can be identified by the node only.. Such as Taro


// Style section

 .no-data-text {
    background-color: rgba(233.228.228.0.726);
    color: # 333;
    height: 100vh;
    width: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    font-size: 50px;
    font-weight: 700;
    .loading-text{
      font-size:28px;
      color:# 555; }}.spinner {
  width: 200px;
  height: 70px;
  text-align: center;
  font-size: 10px;
}
 
.spinner .rect {
  background-color: rgb(123.176.225);
  height: 100%;
  width: 10px;
  margin:0 5px;
  display: inline-block;
   
  -webkit-animation: stretchdelay 1.2 s infinite ease-in-out;
  animation: stretchdelay 1.2 s infinite ease-in-out;
}
 
.spinner .rect2 {
  -webkit-animation-delay: -1.1 s;
  animation-delay: -1.1 s;
}
 
.spinner .rect3 {
  -webkit-animation-delay: -1.0 s;
  animation-delay: -1.0 s;
}
 
.spinner .rect4 {
  -webkit-animation-delay: -0.9 s;
  animation-delay: -0.9 s;
}
 
.spinner .rect5 {
  -webkit-animation-delay: -0.8 s;
  animation-delay: -0.8 s;
}
 
@-webkit-keyframes stretchdelay {
  0%.40%.100% { -webkit-transform: scaleY(0.4)}20% { -webkit-transform: scaleY(1.0)}}@keyframes stretchdelay {
  0%.40%.100% {
    transform: scaleY(0.4);
    -webkit-transform: scaleY(0.4);
  }  20% {
    transform: scaleY(1.0);
    -webkit-transform: scaleY(1.0); }}Copy the code
<! -- Node part -->
<View className="no-data-text">
    <View className="spinner">
      <View className="rect rect1"></View>
      <View className="rect rect2"></View>
      <View className="rect rect3"></View>
      <View className="rect rect4"></View>
      <View className="rect rect5"></View>
    </View>
    <View className="loading-text">Loading......</View>
</View>
Copy the code

conclusion

By the time this article is output,Taro’s version

👽 Taro v1.2.7 Taro CLI 1.2.7 Environment Info: System: OS: macOS 10.14.2 Shell: 5.3 - /bin/zsh Binaries: Node: 10.14.2 - / usr /local/bin/node
      Yarn: 1.13.0 - /usr/local/bin/yarn
      npm: 6.5.0 - /usr/local/bin/npm npmPackages: @tarojs/async-await: 1.2.7 => 1.2.7 @tarojs/components: 1.2.7 => 1.2.7 @tarojs/mobx: 1.2.7 => 1.2.7 @tarojs/ mobx-h5:1.2.7 => 1.2.7 @tarojs/ mobx-RN: 1.2.7 => 1.2.7 @tarojs/plugin-babel: 1.2.7 => 1.2.7@tarojs /plugin-csso: 1.2.7 => 1.2.7@tarojs /plugin-less: 1.2.7 => 1.2.7@tarojs /plugin-sass: 1.2.7 => 1.2.7@tarojs /plugin-uglifyjs: 1.2.7 => 1.2.7@tarojs/Rn-runner: 1.2.7 => 1.2.7@tarojs /router: 1.2.7 => 1.2.7 @tarojs/taro: 1.2.7 => 1.2.7 @tarojs/taro-h5: 1.2.7 => 1.2.7 @tarojs/taro-h5: 1.2.7 => 1.2.7@tarojs /taro-swan: 1.2.7 => 1.2.7@tarojs/taro-TT: 1.2.7 => 1.2.7@tarojs/taro-appellate: 1.2.7 => 1.2.7@tarojs/webpack-Runner: 1.2.7 => 1.2.7 ESlint-config-taro: 1.2.7 => 1.2.7 eslint-plugin-taro: 1.2.7 = > 1.2.7Copy the code

The current version of Taro and THE Taro UI still needs to be improved to support TS, and will occasionally encounter a lack of types

If the project is not big, for those who want to save worry, it is recommended to directly masturbate JS version;

The Taro community is still very active, and as such, two or three iterations of X.Y.Z(y-bit) should be useful.

The benefits of TS are obvious. The editor can float inferred types directly, and many errors can be avoided during development.

This is the end of hydrology, there is something wrong please leave a message, will be timely correction, thank you for reading.